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