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#ifdef HAVE_NETDB_H 48#include <netdb.h> 49#endif 50#include <krb.h> 51#include <des.h> 52 53#ifdef HAVE_UNISTD_H 54#include <unistd.h> /* for getpid() */ 55#endif 56 57#include "urldata.h" 58#include "curl_base64.h" 59#include "ftp.h" 60#include "sendf.h" 61#include "krb4.h" 62#include "inet_ntop.h" 63#include "curl_memory.h" 64 65/* The last #include file should be: */ 66#include "memdebug.h" 67 68#define LOCAL_ADDR (&conn->local_addr) 69#define REMOTE_ADDR conn->ip_addr->ai_addr 70#define myctladdr LOCAL_ADDR 71#define hisctladdr REMOTE_ADDR 72 73struct krb4_data { 74 des_cblock key; 75 des_key_schedule schedule; 76 char name[ANAME_SZ]; 77 char instance[INST_SZ]; 78 char realm[REALM_SZ]; 79}; 80 81#ifndef HAVE_STRLCPY 82/* if it ever goes non-static, make it Curl_ prefixed! */ 83static size_t 84strlcpy (char *dst, const char *src, size_t dst_sz) 85{ 86 size_t n; 87 char *p; 88 89 for(p = dst, n = 0; 90 n + 1 < dst_sz && *src != '\0'; 91 ++p, ++src, ++n) 92 *p = *src; 93 *p = '\0'; 94 if(*src == '\0') 95 return n; 96 else 97 return n + strlen (src); 98} 99#else 100size_t strlcpy (char *dst, const char *src, size_t dst_sz); 101#endif 102 103static int 104krb4_check_prot(void *app_data, int level) 105{ 106 app_data = NULL; /* prevent compiler warning */ 107 if(level == PROT_CONFIDENTIAL) 108 return -1; 109 return 0; 110} 111 112static int 113krb4_decode(void *app_data, void *buf, int len, int level, 114 struct connectdata *conn) 115{ 116 MSG_DAT m; 117 int e; 118 struct krb4_data *d = app_data; 119 120 if(level == PROT_SAFE) 121 e = krb_rd_safe(buf, len, &d->key, 122 (struct sockaddr_in *)REMOTE_ADDR, 123 (struct sockaddr_in *)LOCAL_ADDR, &m); 124 else 125 e = krb_rd_priv(buf, len, d->schedule, &d->key, 126 (struct sockaddr_in *)REMOTE_ADDR, 127 (struct sockaddr_in *)LOCAL_ADDR, &m); 128 if(e) { 129 struct SessionHandle *data = conn->data; 130 infof(data, "krb4_decode: %s\n", krb_get_err_text(e)); 131 return -1; 132 } 133 memmove(buf, m.app_data, m.app_length); 134 return m.app_length; 135} 136 137static int 138krb4_overhead(void *app_data, int level, int len) 139{ 140 /* no arguments are used, just init them to prevent compiler warnings */ 141 app_data = NULL; 142 level = 0; 143 len = 0; 144 return 31; 145} 146 147static int 148krb4_encode(void *app_data, const void *from, int length, int level, void **to, 149 struct connectdata *conn) 150{ 151 struct krb4_data *d = app_data; 152 *to = malloc(length + 31); 153 if(!*to) 154 return -1; 155 if(level == PROT_SAFE) 156 /* NOTE that the void* cast is safe, krb_mk_safe/priv don't modify the 157 * input buffer 158 */ 159 return krb_mk_safe((void*)from, *to, length, &d->key, 160 (struct sockaddr_in *)LOCAL_ADDR, 161 (struct sockaddr_in *)REMOTE_ADDR); 162 else if(level == PROT_PRIVATE) 163 return krb_mk_priv((void*)from, *to, length, d->schedule, &d->key, 164 (struct sockaddr_in *)LOCAL_ADDR, 165 (struct sockaddr_in *)REMOTE_ADDR); 166 else 167 return -1; 168} 169 170static int 171mk_auth(struct krb4_data *d, KTEXT adat, 172 const char *service, char *host, int checksum) 173{ 174 int ret; 175 CREDENTIALS cred; 176 char sname[SNAME_SZ], inst[INST_SZ], realm[REALM_SZ]; 177 178 strlcpy(sname, service, sizeof(sname)); 179 strlcpy(inst, krb_get_phost(host), sizeof(inst)); 180 strlcpy(realm, krb_realmofhost(host), sizeof(realm)); 181 ret = krb_mk_req(adat, sname, inst, realm, checksum); 182 if(ret) 183 return ret; 184 strlcpy(sname, service, sizeof(sname)); 185 strlcpy(inst, krb_get_phost(host), sizeof(inst)); 186 strlcpy(realm, krb_realmofhost(host), sizeof(realm)); 187 ret = krb_get_cred(sname, inst, realm, &cred); 188 memmove(&d->key, &cred.session, sizeof(des_cblock)); 189 des_key_sched(&d->key, d->schedule); 190 memset(&cred, 0, sizeof(cred)); 191 return ret; 192} 193 194#ifdef HAVE_KRB_GET_OUR_IP_FOR_REALM 195int krb_get_our_ip_for_realm(char *, struct in_addr *); 196#endif 197 198static int 199krb4_auth(void *app_data, struct connectdata *conn) 200{ 201 int ret; 202 char *p; 203 unsigned char *ptr; 204 size_t len = 0; 205 KTEXT_ST adat; 206 MSG_DAT msg_data; 207 int checksum; 208 u_int32_t cs; 209 struct krb4_data *d = app_data; 210 char *host = conn->host.name; 211 ssize_t nread; 212 int l = sizeof(conn->local_addr); 213 struct SessionHandle *data = conn->data; 214 CURLcode result; 215 size_t base64_sz = 0; 216 217 if(getsockname(conn->sock[FIRSTSOCKET], 218 (struct sockaddr *)LOCAL_ADDR, &l) < 0) 219 perror("getsockname()"); 220 221 checksum = getpid(); 222 ret = mk_auth(d, &adat, "ftp", host, checksum); 223 if(ret == KDC_PR_UNKNOWN) 224 ret = mk_auth(d, &adat, "rcmd", host, checksum); 225 if(ret) { 226 infof(data, "%s\n", krb_get_err_text(ret)); 227 return AUTH_CONTINUE; 228 } 229 230#ifdef HAVE_KRB_GET_OUR_IP_FOR_REALM 231 if(krb_get_config_bool("nat_in_use")) { 232 struct sockaddr_in *localaddr = (struct sockaddr_in *)LOCAL_ADDR; 233 struct in_addr natAddr; 234 235 if(krb_get_our_ip_for_realm(krb_realmofhost(host), 236 &natAddr) != KSUCCESS 237 && krb_get_our_ip_for_realm(NULL, &natAddr) != KSUCCESS) 238 infof(data, "Can't get address for realm %s\n", 239 krb_realmofhost(host)); 240 else { 241 if(natAddr.s_addr != localaddr->sin_addr.s_addr) { 242 char addr_buf[128]; 243 if(Curl_inet_ntop(AF_INET, natAddr, addr_buf, sizeof(addr_buf))) 244 infof(data, "Using NAT IP address (%s) for kerberos 4\n", addr_buf); 245 localaddr->sin_addr = natAddr; 246 } 247 } 248 } 249#endif 250 251 result = Curl_base64_encode(conn->data, (char *)adat.dat, adat.length, 252 &p, &base64_sz); 253 if(result) { 254 Curl_failf(data, "base64-encoding: %s", curl_easy_strerror(result)); 255 return AUTH_CONTINUE; 256 } 257 258 result = Curl_ftpsendf(conn, "ADAT %s", p); 259 260 free(p); 261 262 if(result) 263 return -2; 264 265 if(Curl_GetFTPResponse(&nread, conn, NULL)) 266 return -1; 267 268 if(data->state.buffer[0] != '2') { 269 Curl_failf(data, "Server didn't accept auth data"); 270 return AUTH_ERROR; 271 } 272 273 p = strstr(data->state.buffer, "ADAT="); 274 if(!p) { 275 Curl_failf(data, "Remote host didn't send adat reply"); 276 return AUTH_ERROR; 277 } 278 p += 5; 279 result = Curl_base64_decode(p, &ptr, &len); 280 if(result) { 281 Curl_failf(data, "base64-decoding: %s", curl_easy_strerror(result)); 282 return AUTH_ERROR; 283 } 284 if(len > sizeof(adat.dat)-1) { 285 free(ptr); 286 ptr = NULL; 287 len = 0; 288 } 289 if(!len || !ptr) { 290 Curl_failf(data, "Failed to decode base64 from server"); 291 return AUTH_ERROR; 292 } 293 memcpy((char *)adat.dat, ptr, len); 294 free(ptr); 295 adat.length = len; 296 ret = krb_rd_safe(adat.dat, adat.length, &d->key, 297 (struct sockaddr_in *)hisctladdr, 298 (struct sockaddr_in *)myctladdr, &msg_data); 299 if(ret) { 300 Curl_failf(data, "Error reading reply from server: %s", 301 krb_get_err_text(ret)); 302 return AUTH_ERROR; 303 } 304 krb_get_int(msg_data.app_data, &cs, 4, 0); 305 if(cs - checksum != 1) { 306 Curl_failf(data, "Bad checksum returned from server"); 307 return AUTH_ERROR; 308 } 309 return AUTH_OK; 310} 311 312struct Curl_sec_client_mech Curl_krb4_client_mech = { 313 "KERBEROS_V4", 314 sizeof(struct krb4_data), 315 NULL, /* init */ 316 krb4_auth, 317 NULL, /* end */ 318 krb4_check_prot, 319 krb4_overhead, 320 krb4_encode, 321 krb4_decode 322}; 323 324static enum protection_level 325krb4_set_command_prot(struct connectdata *conn, enum protection_level level) 326{ 327 enum protection_level old = conn->command_prot; 328 DEBUGASSERT(level > PROT_NONE && level < PROT_LAST); 329 conn->command_prot = level; 330 return old; 331} 332 333CURLcode Curl_krb_kauth(struct connectdata *conn) 334{ 335 des_cblock key; 336 des_key_schedule schedule; 337 KTEXT_ST tkt, tktcopy; 338 char *name; 339 char *p; 340 char passwd[100]; 341 size_t tmp = 0; 342 ssize_t nread; 343 enum protection_level save; 344 CURLcode result; 345 unsigned char *ptr; 346 size_t base64_sz = 0; 347 348 save = krb4_set_command_prot(conn, PROT_PRIVATE); 349 350 result = Curl_ftpsendf(conn, "SITE KAUTH %s", conn->user); 351 352 if(result) 353 return result; 354 355 result = Curl_GetFTPResponse(&nread, conn, NULL); 356 if(result) 357 return result; 358 359 if(conn->data->state.buffer[0] != '3') { 360 krb4_set_command_prot(conn, save); 361 return CURLE_FTP_WEIRD_SERVER_REPLY; 362 } 363 364 p = strstr(conn->data->state.buffer, "T="); 365 if(!p) { 366 Curl_failf(conn->data, "Bad reply from server"); 367 krb4_set_command_prot(conn, save); 368 return CURLE_FTP_WEIRD_SERVER_REPLY; 369 } 370 371 p += 2; 372 result = Curl_base64_decode(p, &ptr, &tmp); 373 if(result) { 374 Curl_failf(conn->data, "base64-decoding: %s", curl_easy_strerror(result)); 375 return result; 376 } 377 if(tmp >= sizeof(tkt.dat)) { 378 free(ptr); 379 ptr = NULL; 380 tmp = 0; 381 } 382 if(!tmp || !ptr) { 383 Curl_failf(conn->data, "Failed to decode base64 in reply"); 384 krb4_set_command_prot(conn, save); 385 return CURLE_FTP_WEIRD_SERVER_REPLY; 386 } 387 memcpy((char *)tkt.dat, ptr, tmp); 388 free(ptr); 389 tkt.length = tmp; 390 tktcopy.length = tkt.length; 391 392 p = strstr(conn->data->state.buffer, "P="); 393 if(!p) { 394 Curl_failf(conn->data, "Bad reply from server"); 395 krb4_set_command_prot(conn, save); 396 return CURLE_FTP_WEIRD_SERVER_REPLY; 397 } 398 name = p + 2; 399 for(; *p && *p != ' ' && *p != '\r' && *p != '\n'; p++); 400 *p = 0; 401 402 des_string_to_key (conn->passwd, &key); 403 des_key_sched(&key, schedule); 404 405 des_pcbc_encrypt((void *)tkt.dat, (void *)tktcopy.dat, 406 tkt.length, 407 schedule, &key, DES_DECRYPT); 408 if(strcmp ((char*)tktcopy.dat + 8, 409 KRB_TICKET_GRANTING_TICKET) != 0) { 410 afs_string_to_key(passwd, 411 krb_realmofhost(conn->host.name), 412 &key); 413 des_key_sched(&key, schedule); 414 des_pcbc_encrypt((void *)tkt.dat, (void *)tktcopy.dat, 415 tkt.length, 416 schedule, &key, DES_DECRYPT); 417 } 418 memset(key, 0, sizeof(key)); 419 memset(schedule, 0, sizeof(schedule)); 420 memset(passwd, 0, sizeof(passwd)); 421 result = Curl_base64_encode(conn->data, (char *)tktcopy.dat, tktcopy.length, 422 &p, &base64_sz); 423 if(result) { 424 Curl_failf(conn->data, "base64-encoding: %s", curl_easy_strerror(result)); 425 krb4_set_command_prot(conn, save); 426 return result; 427 } 428 memset (tktcopy.dat, 0, tktcopy.length); 429 430 result = Curl_ftpsendf(conn, "SITE KAUTH %s %s", name, p); 431 free(p); 432 if(result) 433 return result; 434 435 result = Curl_GetFTPResponse(&nread, conn, NULL); 436 if(result) 437 return result; 438 krb4_set_command_prot(conn, save); 439 440 return CURLE_OK; 441} 442 443#endif /* HAVE_KRB4 */ 444#endif /* CURL_DISABLE_FTP */ 445