1/* This source code was modified by Martin Hedenfalk <mhe@stacken.kth.se> for 2 * use in Curl. His latest changes were done 2000-09-18. 3 * 4 * It has since been patched and modified a lot by Daniel Stenberg 5 * <daniel@haxx.se> to make it better applied to curl conditions, and to make 6 * it not use globals, pollute name space and more. This source code awaits a 7 * rewrite to work around the paragraph 2 in the BSD licenses as explained 8 * below. 9 * 10 * Copyright (c) 1998, 1999 Kungliga Tekniska H�gskolan 11 * (Royal Institute of Technology, Stockholm, Sweden). 12 * 13 * Copyright (C) 2001 - 2011, Daniel Stenberg, <daniel@haxx.se>, et al. 14 * 15 * All rights reserved. 16 * 17 * Redistribution and use in source and binary forms, with or without 18 * modification, are permitted provided that the following conditions 19 * are met: 20 * 21 * 1. Redistributions of source code must retain the above copyright 22 * notice, this list of conditions and the following disclaimer. 23 * 24 * 2. Redistributions in binary form must reproduce the above copyright 25 * notice, this list of conditions and the following disclaimer in the 26 * documentation and/or other materials provided with the distribution. 27 * 28 * 3. Neither the name of the Institute nor the names of its contributors 29 * may be used to endorse or promote products derived from this software 30 * without specific prior written permission. 31 * 32 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 33 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 34 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 35 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 36 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 37 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 38 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 39 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 40 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 41 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 42 * SUCH DAMAGE. */ 43 44#include "curl_setup.h" 45 46#ifndef CURL_DISABLE_FTP 47#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI) 48 49#ifdef HAVE_NETDB_H 50#include <netdb.h> 51#endif 52 53#ifdef HAVE_LIMITS_H 54#include <limits.h> 55#endif 56 57#include "urldata.h" 58#include "curl_base64.h" 59#include "curl_memory.h" 60#include "krb4.h" 61#include "ftp.h" 62#include "sendf.h" 63#include "rawstr.h" 64#include "warnless.h" 65 66/* The last #include file should be: */ 67#include "memdebug.h" 68 69static const struct { 70 enum protection_level level; 71 const char *name; 72} level_names[] = { 73 { PROT_CLEAR, "clear" }, 74 { PROT_SAFE, "safe" }, 75 { PROT_CONFIDENTIAL, "confidential" }, 76 { PROT_PRIVATE, "private" } 77}; 78 79static enum protection_level 80name_to_level(const char *name) 81{ 82 int i; 83 for(i = 0; i < (int)sizeof(level_names)/(int)sizeof(level_names[0]); i++) 84 if(checkprefix(name, level_names[i].name)) 85 return level_names[i].level; 86 return PROT_NONE; 87} 88 89/* Convert a protocol |level| to its char representation. 90 We take an int to catch programming mistakes. */ 91static char level_to_char(int level) { 92 switch(level) { 93 case PROT_CLEAR: 94 return 'C'; 95 case PROT_SAFE: 96 return 'S'; 97 case PROT_CONFIDENTIAL: 98 return 'E'; 99 case PROT_PRIVATE: 100 return 'P'; 101 case PROT_CMD: 102 /* Fall through */ 103 default: 104 /* Those 2 cases should not be reached! */ 105 break; 106 } 107 DEBUGASSERT(0); 108 /* Default to the most secure alternative. */ 109 return 'P'; 110} 111 112static const struct Curl_sec_client_mech * const mechs[] = { 113#if defined(HAVE_GSSAPI) 114 &Curl_krb5_client_mech, 115#endif 116#if defined(HAVE_KRB4) 117 &Curl_krb4_client_mech, 118#endif 119 NULL 120}; 121 122/* Send an FTP command defined by |message| and the optional arguments. The 123 function returns the ftp_code. If an error occurs, -1 is returned. */ 124static int ftp_send_command(struct connectdata *conn, const char *message, ...) 125{ 126 int ftp_code; 127 ssize_t nread; 128 va_list args; 129 char print_buffer[50]; 130 131 va_start(args, message); 132 vsnprintf(print_buffer, sizeof(print_buffer), message, args); 133 va_end(args); 134 135 if(Curl_ftpsendf(conn, print_buffer) != CURLE_OK) { 136 ftp_code = -1; 137 } 138 else { 139 if(Curl_GetFTPResponse(&nread, conn, &ftp_code) != CURLE_OK) 140 ftp_code = -1; 141 } 142 143 (void)nread; /* Unused */ 144 return ftp_code; 145} 146 147/* Read |len| from the socket |fd| and store it in |to|. Return a CURLcode 148 saying whether an error occurred or CURLE_OK if |len| was read. */ 149static CURLcode 150socket_read(curl_socket_t fd, void *to, size_t len) 151{ 152 char *to_p = to; 153 CURLcode code; 154 ssize_t nread; 155 156 while(len > 0) { 157 code = Curl_read_plain(fd, to_p, len, &nread); 158 if(code == CURLE_OK) { 159 len -= nread; 160 to_p += nread; 161 } 162 else { 163 /* FIXME: We are doing a busy wait */ 164 if(code == CURLE_AGAIN) 165 continue; 166 return code; 167 } 168 } 169 return CURLE_OK; 170} 171 172 173/* Write |len| bytes from the buffer |to| to the socket |fd|. Return a 174 CURLcode saying whether an error occurred or CURLE_OK if |len| was 175 written. */ 176static CURLcode 177socket_write(struct connectdata *conn, curl_socket_t fd, const void *to, 178 size_t len) 179{ 180 const char *to_p = to; 181 CURLcode code; 182 ssize_t written; 183 184 while(len > 0) { 185 code = Curl_write_plain(conn, fd, to_p, len, &written); 186 if(code == CURLE_OK) { 187 len -= written; 188 to_p += written; 189 } 190 else { 191 /* FIXME: We are doing a busy wait */ 192 if(code == CURLE_AGAIN) 193 continue; 194 return code; 195 } 196 } 197 return CURLE_OK; 198} 199 200static CURLcode read_data(struct connectdata *conn, 201 curl_socket_t fd, 202 struct krb4buffer *buf) 203{ 204 int len; 205 void* tmp; 206 CURLcode ret; 207 208 ret = socket_read(fd, &len, sizeof(len)); 209 if(ret != CURLE_OK) 210 return ret; 211 212 len = ntohl(len); 213 tmp = realloc(buf->data, len); 214 if(tmp == NULL) 215 return CURLE_OUT_OF_MEMORY; 216 217 buf->data = tmp; 218 ret = socket_read(fd, buf->data, len); 219 if(ret != CURLE_OK) 220 return ret; 221 buf->size = conn->mech->decode(conn->app_data, buf->data, len, 222 conn->data_prot, conn); 223 buf->index = 0; 224 return CURLE_OK; 225} 226 227static size_t 228buffer_read(struct krb4buffer *buf, void *data, size_t len) 229{ 230 if(buf->size - buf->index < len) 231 len = buf->size - buf->index; 232 memcpy(data, (char*)buf->data + buf->index, len); 233 buf->index += len; 234 return len; 235} 236 237/* Matches Curl_recv signature */ 238static ssize_t sec_recv(struct connectdata *conn, int sockindex, 239 char *buffer, size_t len, CURLcode *err) 240{ 241 size_t bytes_read; 242 size_t total_read = 0; 243 curl_socket_t fd = conn->sock[sockindex]; 244 245 *err = CURLE_OK; 246 247 /* Handle clear text response. */ 248 if(conn->sec_complete == 0 || conn->data_prot == PROT_CLEAR) 249 return read(fd, buffer, len); 250 251 if(conn->in_buffer.eof_flag) { 252 conn->in_buffer.eof_flag = 0; 253 return 0; 254 } 255 256 bytes_read = buffer_read(&conn->in_buffer, buffer, len); 257 len -= bytes_read; 258 total_read += bytes_read; 259 buffer += bytes_read; 260 261 while(len > 0) { 262 if(read_data(conn, fd, &conn->in_buffer) != CURLE_OK) 263 return -1; 264 if(conn->in_buffer.size == 0) { 265 if(bytes_read > 0) 266 conn->in_buffer.eof_flag = 1; 267 return bytes_read; 268 } 269 bytes_read = buffer_read(&conn->in_buffer, buffer, len); 270 len -= bytes_read; 271 total_read += bytes_read; 272 buffer += bytes_read; 273 } 274 /* FIXME: Check for overflow */ 275 return total_read; 276} 277 278/* Send |length| bytes from |from| to the |fd| socket taking care of encoding 279 and negociating with the server. |from| can be NULL. */ 280/* FIXME: We don't check for errors nor report any! */ 281static void do_sec_send(struct connectdata *conn, curl_socket_t fd, 282 const char *from, int length) 283{ 284 int bytes, htonl_bytes; /* 32-bit integers for htonl */ 285 char *buffer = NULL; 286 char *cmd_buffer; 287 size_t cmd_size = 0; 288 CURLcode error; 289 enum protection_level prot_level = conn->data_prot; 290 bool iscmd = (prot_level == PROT_CMD)?TRUE:FALSE; 291 292 DEBUGASSERT(prot_level > PROT_NONE && prot_level < PROT_LAST); 293 294 if(iscmd) { 295 if(!strncmp(from, "PASS ", 5) || !strncmp(from, "ACCT ", 5)) 296 prot_level = PROT_PRIVATE; 297 else 298 prot_level = conn->command_prot; 299 } 300 bytes = conn->mech->encode(conn->app_data, from, length, prot_level, 301 (void**)&buffer, conn); 302 if(!buffer || bytes <= 0) 303 return; /* error */ 304 305 if(iscmd) { 306 error = Curl_base64_encode(conn->data, buffer, curlx_sitouz(bytes), 307 &cmd_buffer, &cmd_size); 308 if(error) { 309 free(buffer); 310 return; /* error */ 311 } 312 if(cmd_size > 0) { 313 static const char *enc = "ENC "; 314 static const char *mic = "MIC "; 315 if(prot_level == PROT_PRIVATE) 316 socket_write(conn, fd, enc, 4); 317 else 318 socket_write(conn, fd, mic, 4); 319 320 socket_write(conn, fd, cmd_buffer, cmd_size); 321 socket_write(conn, fd, "\r\n", 2); 322 infof(conn->data, "Send: %s%s\n", prot_level == PROT_PRIVATE?enc:mic, 323 cmd_buffer); 324 free(cmd_buffer); 325 } 326 } 327 else { 328 htonl_bytes = htonl(bytes); 329 socket_write(conn, fd, &htonl_bytes, sizeof(htonl_bytes)); 330 socket_write(conn, fd, buffer, curlx_sitouz(bytes)); 331 } 332 free(buffer); 333} 334 335static ssize_t sec_write(struct connectdata *conn, curl_socket_t fd, 336 const char *buffer, size_t length) 337{ 338 /* FIXME: Check for overflow */ 339 ssize_t tx = 0, len = conn->buffer_size; 340 341 len -= conn->mech->overhead(conn->app_data, conn->data_prot, 342 curlx_sztosi(len)); 343 if(len <= 0) 344 len = length; 345 while(length) { 346 if(len >= 0 || length < (size_t)len) { 347 /* FIXME: Check for overflow. */ 348 len = length; 349 } 350 do_sec_send(conn, fd, buffer, curlx_sztosi(len)); 351 length -= len; 352 buffer += len; 353 tx += len; 354 } 355 return tx; 356} 357 358/* Matches Curl_send signature */ 359static ssize_t sec_send(struct connectdata *conn, int sockindex, 360 const void *buffer, size_t len, CURLcode *err) 361{ 362 curl_socket_t fd = conn->sock[sockindex]; 363 *err = CURLE_OK; 364 return sec_write(conn, fd, buffer, len); 365} 366 367int Curl_sec_read_msg(struct connectdata *conn, char *buffer, 368 enum protection_level level) 369{ 370 /* decoded_len should be size_t or ssize_t but conn->mech->decode returns an 371 int */ 372 int decoded_len; 373 char *buf; 374 int ret_code; 375 size_t decoded_sz = 0; 376 CURLcode error; 377 378 DEBUGASSERT(level > PROT_NONE && level < PROT_LAST); 379 380 error = Curl_base64_decode(buffer + 4, (unsigned char **)&buf, &decoded_sz); 381 if(error || decoded_sz == 0) 382 return -1; 383 384 if(decoded_sz > (size_t)INT_MAX) { 385 free(buf); 386 return -1; 387 } 388 decoded_len = curlx_uztosi(decoded_sz); 389 390 decoded_len = conn->mech->decode(conn->app_data, buf, decoded_len, 391 level, conn); 392 if(decoded_len <= 0) { 393 free(buf); 394 return -1; 395 } 396 397 if(conn->data->set.verbose) { 398 buf[decoded_len] = '\n'; 399 Curl_debug(conn->data, CURLINFO_HEADER_IN, buf, decoded_len + 1, conn); 400 } 401 402 buf[decoded_len] = '\0'; 403 DEBUGASSERT(decoded_len > 3); 404 if(buf[3] == '-') 405 ret_code = 0; 406 else { 407 /* Check for error? */ 408 sscanf(buf, "%d", &ret_code); 409 } 410 411 if(buf[decoded_len - 1] == '\n') 412 buf[decoded_len - 1] = '\0'; 413 /* FIXME: Is |buffer| length always greater than |decoded_len|? */ 414 strcpy(buffer, buf); 415 free(buf); 416 return ret_code; 417} 418 419/* FIXME: The error code returned here is never checked. */ 420static int sec_set_protection_level(struct connectdata *conn) 421{ 422 int code; 423 char* pbsz; 424 static unsigned int buffer_size = 1 << 20; /* 1048576 */ 425 enum protection_level level = conn->request_data_prot; 426 427 DEBUGASSERT(level > PROT_NONE && level < PROT_LAST); 428 429 if(!conn->sec_complete) { 430 infof(conn->data, "Trying to change the protection level after the" 431 "completion of the data exchange.\n"); 432 return -1; 433 } 434 435 /* Bail out if we try to set up the same level */ 436 if(conn->data_prot == level) 437 return 0; 438 439 if(level) { 440 code = ftp_send_command(conn, "PBSZ %u", buffer_size); 441 if(code < 0) 442 return -1; 443 444 if(code/100 != 2) { 445 failf(conn->data, "Failed to set the protection's buffer size."); 446 return -1; 447 } 448 conn->buffer_size = buffer_size; 449 450 pbsz = strstr(conn->data->state.buffer, "PBSZ="); 451 if(pbsz) { 452 /* FIXME: Checks for errors in sscanf? */ 453 sscanf(pbsz, "PBSZ=%u", &buffer_size); 454 if(buffer_size < conn->buffer_size) 455 conn->buffer_size = buffer_size; 456 } 457 } 458 459 /* Now try to negiociate the protection level. */ 460 code = ftp_send_command(conn, "PROT %c", level_to_char(level)); 461 462 if(code < 0) 463 return -1; 464 465 if(code/100 != 2) { 466 failf(conn->data, "Failed to set the protection level."); 467 return -1; 468 } 469 470 conn->data_prot = level; 471 if(level == PROT_PRIVATE) 472 conn->command_prot = level; 473 474 return 0; 475} 476 477int 478Curl_sec_request_prot(struct connectdata *conn, const char *level) 479{ 480 enum protection_level l = name_to_level(level); 481 if(l == PROT_NONE) 482 return -1; 483 DEBUGASSERT(l > PROT_NONE && l < PROT_LAST); 484 conn->request_data_prot = l; 485 return 0; 486} 487 488static CURLcode choose_mech(struct connectdata *conn) 489{ 490 int ret; 491 struct SessionHandle *data = conn->data; 492 const struct Curl_sec_client_mech * const *mech; 493 void *tmp_allocation; 494 const char *mech_name; 495 496 for(mech = mechs; (*mech); ++mech) { 497 mech_name = (*mech)->name; 498 /* We have no mechanism with a NULL name but keep this check */ 499 DEBUGASSERT(mech_name != NULL); 500 if(mech_name == NULL) { 501 infof(data, "Skipping mechanism with empty name (%p)\n", mech); 502 continue; 503 } 504 tmp_allocation = realloc(conn->app_data, (*mech)->size); 505 if(tmp_allocation == NULL) { 506 failf(data, "Failed realloc of size %u", (*mech)->size); 507 mech = NULL; 508 return CURLE_OUT_OF_MEMORY; 509 } 510 conn->app_data = tmp_allocation; 511 512 if((*mech)->init) { 513 ret = (*mech)->init(conn->app_data); 514 if(ret != 0) { 515 infof(data, "Failed initialization for %s. Skipping it.\n", mech_name); 516 continue; 517 } 518 } 519 520 infof(data, "Trying mechanism %s...\n", mech_name); 521 ret = ftp_send_command(conn, "AUTH %s", mech_name); 522 if(ret < 0) 523 /* FIXME: This error is too generic but it is OK for now. */ 524 return CURLE_COULDNT_CONNECT; 525 526 if(ret/100 != 3) { 527 switch(ret) { 528 case 504: 529 infof(data, "Mechanism %s is not supported by the server (server " 530 "returned ftp code: 504).\n", mech_name); 531 break; 532 case 534: 533 infof(data, "Mechanism %s was rejected by the server (server returned " 534 "ftp code: 534).\n", mech_name); 535 break; 536 default: 537 if(ret/100 == 5) { 538 infof(data, "server does not support the security extensions\n"); 539 return CURLE_USE_SSL_FAILED; 540 } 541 break; 542 } 543 continue; 544 } 545 546 /* Authenticate */ 547 ret = (*mech)->auth(conn->app_data, conn); 548 549 if(ret == AUTH_CONTINUE) 550 continue; 551 else if(ret != AUTH_OK) { 552 /* Mechanism has dumped the error to stderr, don't error here. */ 553 return -1; 554 } 555 DEBUGASSERT(ret == AUTH_OK); 556 557 conn->mech = *mech; 558 conn->sec_complete = 1; 559 conn->recv[FIRSTSOCKET] = sec_recv; 560 conn->send[FIRSTSOCKET] = sec_send; 561 conn->recv[SECONDARYSOCKET] = sec_recv; 562 conn->send[SECONDARYSOCKET] = sec_send; 563 conn->command_prot = PROT_SAFE; 564 /* Set the requested protection level */ 565 /* BLOCKING */ 566 (void)sec_set_protection_level(conn); 567 break; 568 } 569 570 return mech != NULL ? CURLE_OK : CURLE_FAILED_INIT; 571} 572 573CURLcode 574Curl_sec_login(struct connectdata *conn) 575{ 576 return choose_mech(conn); 577} 578 579 580void 581Curl_sec_end(struct connectdata *conn) 582{ 583 if(conn->mech != NULL && conn->mech->end) 584 conn->mech->end(conn->app_data); 585 if(conn->app_data) { 586 free(conn->app_data); 587 conn->app_data = NULL; 588 } 589 if(conn->in_buffer.data) { 590 free(conn->in_buffer.data); 591 conn->in_buffer.data = NULL; 592 conn->in_buffer.size = 0; 593 conn->in_buffer.index = 0; 594 /* FIXME: Is this really needed? */ 595 conn->in_buffer.eof_flag = 0; 596 } 597 conn->sec_complete = 0; 598 conn->data_prot = PROT_CLEAR; 599 conn->mech = NULL; 600} 601 602#endif /* HAVE_KRB4 || HAVE_GSSAPI */ 603 604#endif /* CURL_DISABLE_FTP */ 605