1/* This source code was modified by Martin Hedenfalk <mhe@stacken.kth.se> for 2 * use in Curl. Martin's latest changes were done 2000-09-18. 3 * 4 * It has since been patched away like a madman by Daniel Stenberg to make it 5 * better applied to curl conditions, and to make it not use globals, pollute 6 * name space and more. 7 * 8 * Copyright (c) 1995, 1996, 1997, 1998, 1999 Kungliga Tekniska H�gskolan 9 * (Royal Institute of Technology, Stockholm, Sweden). 10 * Copyright (c) 2004 - 2011 Daniel Stenberg 11 * All rights reserved. 12 * 13 * Redistribution and use in source and binary forms, with or without 14 * modification, are permitted provided that the following conditions 15 * are met: 16 * 17 * 1. Redistributions of source code must retain the above copyright 18 * notice, this list of conditions and the following disclaimer. 19 * 20 * 2. Redistributions in binary form must reproduce the above copyright 21 * notice, this list of conditions and the following disclaimer in the 22 * documentation and/or other materials provided with the distribution. 23 * 24 * 3. Neither the name of the Institute nor the names of its contributors 25 * may be used to endorse or promote products derived from this software 26 * without specific prior written permission. 27 * 28 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 29 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 30 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 31 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 32 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 33 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 34 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 35 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 36 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 37 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 38 * SUCH DAMAGE. 39 * 40 */ 41 42#include "setup.h" 43 44#ifndef CURL_DISABLE_FTP 45#ifdef HAVE_KRB4 46 47#include <stdlib.h> 48#ifdef HAVE_NETDB_H 49#include <netdb.h> 50#endif 51#include <string.h> 52#include <krb.h> 53#include <des.h> 54 55#ifdef HAVE_UNISTD_H 56#include <unistd.h> /* for getpid() */ 57#endif 58 59#include "urldata.h" 60#include "curl_base64.h" 61#include "ftp.h" 62#include "sendf.h" 63#include "krb4.h" 64#include "inet_ntop.h" 65#include "curl_memory.h" 66 67/* The last #include file should be: */ 68#include "memdebug.h" 69 70#define LOCAL_ADDR (&conn->local_addr) 71#define REMOTE_ADDR conn->ip_addr->ai_addr 72#define myctladdr LOCAL_ADDR 73#define hisctladdr REMOTE_ADDR 74 75struct krb4_data { 76 des_cblock key; 77 des_key_schedule schedule; 78 char name[ANAME_SZ]; 79 char instance[INST_SZ]; 80 char realm[REALM_SZ]; 81}; 82 83#ifndef HAVE_STRLCPY 84/* if it ever goes non-static, make it Curl_ prefixed! */ 85static size_t 86strlcpy (char *dst, const char *src, size_t dst_sz) 87{ 88 size_t n; 89 char *p; 90 91 for(p = dst, n = 0; 92 n + 1 < dst_sz && *src != '\0'; 93 ++p, ++src, ++n) 94 *p = *src; 95 *p = '\0'; 96 if(*src == '\0') 97 return n; 98 else 99 return n + strlen (src); 100} 101#else 102size_t strlcpy (char *dst, const char *src, size_t dst_sz); 103#endif 104 105static int 106krb4_check_prot(void *app_data, int level) 107{ 108 app_data = NULL; /* prevent compiler warning */ 109 if(level == PROT_CONFIDENTIAL) 110 return -1; 111 return 0; 112} 113 114static int 115krb4_decode(void *app_data, void *buf, int len, int level, 116 struct connectdata *conn) 117{ 118 MSG_DAT m; 119 int e; 120 struct krb4_data *d = app_data; 121 122 if(level == PROT_SAFE) 123 e = krb_rd_safe(buf, len, &d->key, 124 (struct sockaddr_in *)REMOTE_ADDR, 125 (struct sockaddr_in *)LOCAL_ADDR, &m); 126 else 127 e = krb_rd_priv(buf, len, d->schedule, &d->key, 128 (struct sockaddr_in *)REMOTE_ADDR, 129 (struct sockaddr_in *)LOCAL_ADDR, &m); 130 if(e) { 131 struct SessionHandle *data = conn->data; 132 infof(data, "krb4_decode: %s\n", krb_get_err_text(e)); 133 return -1; 134 } 135 memmove(buf, m.app_data, m.app_length); 136 return m.app_length; 137} 138 139static int 140krb4_overhead(void *app_data, int level, int len) 141{ 142 /* no arguments are used, just init them to prevent compiler warnings */ 143 app_data = NULL; 144 level = 0; 145 len = 0; 146 return 31; 147} 148 149static int 150krb4_encode(void *app_data, const void *from, int length, int level, void **to, 151 struct connectdata *conn) 152{ 153 struct krb4_data *d = app_data; 154 *to = malloc(length + 31); 155 if(!*to) 156 return -1; 157 if(level == PROT_SAFE) 158 /* NOTE that the void* cast is safe, krb_mk_safe/priv don't modify the 159 * input buffer 160 */ 161 return krb_mk_safe((void*)from, *to, length, &d->key, 162 (struct sockaddr_in *)LOCAL_ADDR, 163 (struct sockaddr_in *)REMOTE_ADDR); 164 else if(level == PROT_PRIVATE) 165 return krb_mk_priv((void*)from, *to, length, d->schedule, &d->key, 166 (struct sockaddr_in *)LOCAL_ADDR, 167 (struct sockaddr_in *)REMOTE_ADDR); 168 else 169 return -1; 170} 171 172static int 173mk_auth(struct krb4_data *d, KTEXT adat, 174 const char *service, char *host, int checksum) 175{ 176 int ret; 177 CREDENTIALS cred; 178 char sname[SNAME_SZ], inst[INST_SZ], realm[REALM_SZ]; 179 180 strlcpy(sname, service, sizeof(sname)); 181 strlcpy(inst, krb_get_phost(host), sizeof(inst)); 182 strlcpy(realm, krb_realmofhost(host), sizeof(realm)); 183 ret = krb_mk_req(adat, sname, inst, realm, checksum); 184 if(ret) 185 return ret; 186 strlcpy(sname, service, sizeof(sname)); 187 strlcpy(inst, krb_get_phost(host), sizeof(inst)); 188 strlcpy(realm, krb_realmofhost(host), sizeof(realm)); 189 ret = krb_get_cred(sname, inst, realm, &cred); 190 memmove(&d->key, &cred.session, sizeof(des_cblock)); 191 des_key_sched(&d->key, d->schedule); 192 memset(&cred, 0, sizeof(cred)); 193 return ret; 194} 195 196#ifdef HAVE_KRB_GET_OUR_IP_FOR_REALM 197int krb_get_our_ip_for_realm(char *, struct in_addr *); 198#endif 199 200static int 201krb4_auth(void *app_data, struct connectdata *conn) 202{ 203 int ret; 204 char *p; 205 unsigned char *ptr; 206 size_t len; 207 KTEXT_ST adat; 208 MSG_DAT msg_data; 209 int checksum; 210 u_int32_t cs; 211 struct krb4_data *d = app_data; 212 char *host = conn->host.name; 213 ssize_t nread; 214 int l = sizeof(conn->local_addr); 215 struct SessionHandle *data = conn->data; 216 CURLcode result; 217 218 if(getsockname(conn->sock[FIRSTSOCKET], 219 (struct sockaddr *)LOCAL_ADDR, &l) < 0) 220 perror("getsockname()"); 221 222 checksum = getpid(); 223 ret = mk_auth(d, &adat, "ftp", host, checksum); 224 if(ret == KDC_PR_UNKNOWN) 225 ret = mk_auth(d, &adat, "rcmd", host, checksum); 226 if(ret) { 227 infof(data, "%s\n", krb_get_err_text(ret)); 228 return AUTH_CONTINUE; 229 } 230 231#ifdef HAVE_KRB_GET_OUR_IP_FOR_REALM 232 if(krb_get_config_bool("nat_in_use")) { 233 struct sockaddr_in *localaddr = (struct sockaddr_in *)LOCAL_ADDR; 234 struct in_addr natAddr; 235 236 if(krb_get_our_ip_for_realm(krb_realmofhost(host), 237 &natAddr) != KSUCCESS 238 && krb_get_our_ip_for_realm(NULL, &natAddr) != KSUCCESS) 239 infof(data, "Can't get address for realm %s\n", 240 krb_realmofhost(host)); 241 else { 242 if(natAddr.s_addr != localaddr->sin_addr.s_addr) { 243 char addr_buf[128]; 244 if(Curl_inet_ntop(AF_INET, natAddr, addr_buf, sizeof(addr_buf))) 245 infof(data, "Using NAT IP address (%s) for kerberos 4\n", addr_buf); 246 localaddr->sin_addr = natAddr; 247 } 248 } 249 } 250#endif 251 252 if(Curl_base64_encode(conn->data, (char *)adat.dat, adat.length, &p) < 1) { 253 Curl_failf(data, "Out of memory base64-encoding"); 254 return AUTH_CONTINUE; 255 } 256 257 result = Curl_ftpsendf(conn, "ADAT %s", p); 258 259 free(p); 260 261 if(result) 262 return -2; 263 264 if(Curl_GetFTPResponse(&nread, conn, NULL)) 265 return -1; 266 267 if(data->state.buffer[0] != '2'){ 268 Curl_failf(data, "Server didn't accept auth data"); 269 return AUTH_ERROR; 270 } 271 272 p = strstr(data->state.buffer, "ADAT="); 273 if(!p) { 274 Curl_failf(data, "Remote host didn't send adat reply"); 275 return AUTH_ERROR; 276 } 277 p += 5; 278 len = Curl_base64_decode(p, &ptr); 279 if(len > sizeof(adat.dat)-1) { 280 free(ptr); 281 len=0; 282 } 283 if(!len || !ptr) { 284 Curl_failf(data, "Failed to decode base64 from server"); 285 return AUTH_ERROR; 286 } 287 memcpy((char *)adat.dat, ptr, len); 288 free(ptr); 289 adat.length = len; 290 ret = krb_rd_safe(adat.dat, adat.length, &d->key, 291 (struct sockaddr_in *)hisctladdr, 292 (struct sockaddr_in *)myctladdr, &msg_data); 293 if(ret) { 294 Curl_failf(data, "Error reading reply from server: %s", 295 krb_get_err_text(ret)); 296 return AUTH_ERROR; 297 } 298 krb_get_int(msg_data.app_data, &cs, 4, 0); 299 if(cs - checksum != 1) { 300 Curl_failf(data, "Bad checksum returned from server"); 301 return AUTH_ERROR; 302 } 303 return AUTH_OK; 304} 305 306struct Curl_sec_client_mech Curl_krb4_client_mech = { 307 "KERBEROS_V4", 308 sizeof(struct krb4_data), 309 NULL, /* init */ 310 krb4_auth, 311 NULL, /* end */ 312 krb4_check_prot, 313 krb4_overhead, 314 krb4_encode, 315 krb4_decode 316}; 317 318static enum protection_level 319krb4_set_command_prot(struct connectdata *conn, enum protection_level level) 320{ 321 enum protection_level old = conn->command_prot; 322 DEBUGASSERT(level > PROT_NONE && level < PROT_LAST); 323 conn->command_prot = level; 324 return old; 325} 326 327CURLcode Curl_krb_kauth(struct connectdata *conn) 328{ 329 des_cblock key; 330 des_key_schedule schedule; 331 KTEXT_ST tkt, tktcopy; 332 char *name; 333 char *p; 334 char passwd[100]; 335 size_t tmp; 336 ssize_t nread; 337 enum protection_level save; 338 CURLcode result; 339 unsigned char *ptr; 340 341 save = krb4_set_command_prot(conn, PROT_PRIVATE); 342 343 result = Curl_ftpsendf(conn, "SITE KAUTH %s", conn->user); 344 345 if(result) 346 return result; 347 348 result = Curl_GetFTPResponse(&nread, conn, NULL); 349 if(result) 350 return result; 351 352 if(conn->data->state.buffer[0] != '3'){ 353 krb4_set_command_prot(conn, save); 354 return CURLE_FTP_WEIRD_SERVER_REPLY; 355 } 356 357 p = strstr(conn->data->state.buffer, "T="); 358 if(!p) { 359 Curl_failf(conn->data, "Bad reply from server"); 360 krb4_set_command_prot(conn, save); 361 return CURLE_FTP_WEIRD_SERVER_REPLY; 362 } 363 364 p += 2; 365 tmp = Curl_base64_decode(p, &ptr); 366 if(tmp >= sizeof(tkt.dat)) { 367 free(ptr); 368 tmp=0; 369 } 370 if(!tmp || !ptr) { 371 Curl_failf(conn->data, "Failed to decode base64 in reply"); 372 krb4_set_command_prot(conn, save); 373 return CURLE_FTP_WEIRD_SERVER_REPLY; 374 } 375 memcpy((char *)tkt.dat, ptr, tmp); 376 free(ptr); 377 tkt.length = tmp; 378 tktcopy.length = tkt.length; 379 380 p = strstr(conn->data->state.buffer, "P="); 381 if(!p) { 382 Curl_failf(conn->data, "Bad reply from server"); 383 krb4_set_command_prot(conn, save); 384 return CURLE_FTP_WEIRD_SERVER_REPLY; 385 } 386 name = p + 2; 387 for(; *p && *p != ' ' && *p != '\r' && *p != '\n'; p++); 388 *p = 0; 389 390 des_string_to_key (conn->passwd, &key); 391 des_key_sched(&key, schedule); 392 393 des_pcbc_encrypt((void *)tkt.dat, (void *)tktcopy.dat, 394 tkt.length, 395 schedule, &key, DES_DECRYPT); 396 if(strcmp ((char*)tktcopy.dat + 8, 397 KRB_TICKET_GRANTING_TICKET) != 0) { 398 afs_string_to_key(passwd, 399 krb_realmofhost(conn->host.name), 400 &key); 401 des_key_sched(&key, schedule); 402 des_pcbc_encrypt((void *)tkt.dat, (void *)tktcopy.dat, 403 tkt.length, 404 schedule, &key, DES_DECRYPT); 405 } 406 memset(key, 0, sizeof(key)); 407 memset(schedule, 0, sizeof(schedule)); 408 memset(passwd, 0, sizeof(passwd)); 409 if(Curl_base64_encode(conn->data, (char *)tktcopy.dat, tktcopy.length, &p) 410 < 1) { 411 failf(conn->data, "Out of memory base64-encoding."); 412 krb4_set_command_prot(conn, save); 413 return CURLE_OUT_OF_MEMORY; 414 } 415 memset (tktcopy.dat, 0, tktcopy.length); 416 417 result = Curl_ftpsendf(conn, "SITE KAUTH %s %s", name, p); 418 free(p); 419 if(result) 420 return result; 421 422 result = Curl_GetFTPResponse(&nread, conn, NULL); 423 if(result) 424 return result; 425 krb4_set_command_prot(conn, save); 426 427 return CURLE_OK; 428} 429 430#endif /* HAVE_KRB4 */ 431#endif /* CURL_DISABLE_FTP */ 432