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 "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_UNISTD_H 54#include <unistd.h> 55#endif 56 57#ifdef HAVE_LIMITS_H 58#include <limits.h> 59#endif 60 61#include "urldata.h" 62#include "curl_base64.h" 63#include "curl_memory.h" 64#include "krb4.h" 65#include "ftp.h" 66#include "sendf.h" 67#include "rawstr.h" 68#include "warnless.h" 69 70/* The last #include file should be: */ 71#include "memdebug.h" 72 73static const struct { 74 enum protection_level level; 75 const char *name; 76} level_names[] = { 77 { PROT_CLEAR, "clear" }, 78 { PROT_SAFE, "safe" }, 79 { PROT_CONFIDENTIAL, "confidential" }, 80 { PROT_PRIVATE, "private" } 81}; 82 83static enum protection_level 84name_to_level(const char *name) 85{ 86 int i; 87 for(i = 0; i < (int)sizeof(level_names)/(int)sizeof(level_names[0]); i++) 88 if(checkprefix(name, level_names[i].name)) 89 return level_names[i].level; 90 return PROT_NONE; 91} 92 93/* Convert a protocol |level| to its char representation. 94 We take an int to catch programming mistakes. */ 95static char level_to_char(int level) { 96 switch(level) { 97 case PROT_CLEAR: 98 return 'C'; 99 case PROT_SAFE: 100 return 'S'; 101 case PROT_CONFIDENTIAL: 102 return 'E'; 103 case PROT_PRIVATE: 104 return 'P'; 105 case PROT_CMD: 106 /* Fall through */ 107 default: 108 /* Those 2 cases should not be reached! */ 109 break; 110 } 111 DEBUGASSERT(0); 112 /* Default to the most secure alternative. */ 113 return 'P'; 114} 115 116static const struct Curl_sec_client_mech * const mechs[] = { 117#if defined(HAVE_GSSAPI) 118 &Curl_krb5_client_mech, 119#endif 120#if defined(HAVE_KRB4) 121 &Curl_krb4_client_mech, 122#endif 123 NULL 124}; 125 126/* Send an FTP command defined by |message| and the optional arguments. The 127 function returns the ftp_code. If an error occurs, -1 is returned. */ 128static int ftp_send_command(struct connectdata *conn, const char *message, ...) 129{ 130 int ftp_code; 131 ssize_t nread; 132 va_list args; 133 char print_buffer[50]; 134 135 va_start(args, message); 136 vsnprintf(print_buffer, sizeof(print_buffer), message, args); 137 va_end(args); 138 139 if(Curl_ftpsendf(conn, print_buffer) != CURLE_OK) { 140 ftp_code = -1; 141 } 142 else { 143 if(Curl_GetFTPResponse(&nread, conn, &ftp_code) != CURLE_OK) 144 ftp_code = -1; 145 } 146 147 (void)nread; /* Unused */ 148 return ftp_code; 149} 150 151/* Read |len| from the socket |fd| and store it in |to|. Return a CURLcode 152 saying whether an error occurred or CURLE_OK if |len| was read. */ 153static CURLcode 154socket_read(curl_socket_t fd, void *to, size_t len) 155{ 156 char *to_p = to; 157 CURLcode code; 158 ssize_t nread; 159 160 while(len > 0) { 161 code = Curl_read_plain(fd, to_p, len, &nread); 162 if(code == CURLE_OK) { 163 len -= nread; 164 to_p += nread; 165 } 166 else { 167 /* FIXME: We are doing a busy wait */ 168 if(code == CURLE_AGAIN) 169 continue; 170 return code; 171 } 172 } 173 return CURLE_OK; 174} 175 176 177/* Write |len| bytes from the buffer |to| to the socket |fd|. Return a 178 CURLcode saying whether an error occurred or CURLE_OK if |len| was 179 written. */ 180static CURLcode 181socket_write(struct connectdata *conn, curl_socket_t fd, const void *to, 182 size_t len) 183{ 184 const char *to_p = to; 185 CURLcode code; 186 ssize_t written; 187 188 while(len > 0) { 189 code = Curl_write_plain(conn, fd, to_p, len, &written); 190 if(code == CURLE_OK) { 191 len -= written; 192 to_p += written; 193 } 194 else { 195 /* FIXME: We are doing a busy wait */ 196 if(code == CURLE_AGAIN) 197 continue; 198 return code; 199 } 200 } 201 return CURLE_OK; 202} 203 204static CURLcode read_data(struct connectdata *conn, 205 curl_socket_t fd, 206 struct krb4buffer *buf) 207{ 208 int len; 209 void* tmp; 210 CURLcode ret; 211 212 ret = socket_read(fd, &len, sizeof(len)); 213 if(ret != CURLE_OK) 214 return ret; 215 216 len = ntohl(len); 217 tmp = realloc(buf->data, len); 218 if(tmp == NULL) 219 return CURLE_OUT_OF_MEMORY; 220 221 buf->data = tmp; 222 ret = socket_read(fd, buf->data, len); 223 if(ret != CURLE_OK) 224 return ret; 225 buf->size = conn->mech->decode(conn->app_data, buf->data, len, 226 conn->data_prot, conn); 227 buf->index = 0; 228 return CURLE_OK; 229} 230 231static size_t 232buffer_read(struct krb4buffer *buf, void *data, size_t len) 233{ 234 if(buf->size - buf->index < len) 235 len = buf->size - buf->index; 236 memcpy(data, (char*)buf->data + buf->index, len); 237 buf->index += len; 238 return len; 239} 240 241/* Matches Curl_recv signature */ 242static ssize_t sec_recv(struct connectdata *conn, int sockindex, 243 char *buffer, size_t len, CURLcode *err) 244{ 245 size_t bytes_read; 246 size_t total_read = 0; 247 curl_socket_t fd = conn->sock[sockindex]; 248 249 *err = CURLE_OK; 250 251 /* Handle clear text response. */ 252 if(conn->sec_complete == 0 || conn->data_prot == PROT_CLEAR) 253 return read(fd, buffer, len); 254 255 if(conn->in_buffer.eof_flag) { 256 conn->in_buffer.eof_flag = 0; 257 return 0; 258 } 259 260 bytes_read = buffer_read(&conn->in_buffer, buffer, len); 261 len -= bytes_read; 262 total_read += bytes_read; 263 buffer += bytes_read; 264 265 while(len > 0) { 266 if(read_data(conn, fd, &conn->in_buffer) != CURLE_OK) 267 return -1; 268 if(conn->in_buffer.size == 0) { 269 if(bytes_read > 0) 270 conn->in_buffer.eof_flag = 1; 271 return bytes_read; 272 } 273 bytes_read = buffer_read(&conn->in_buffer, buffer, len); 274 len -= bytes_read; 275 total_read += bytes_read; 276 buffer += bytes_read; 277 } 278 /* FIXME: Check for overflow */ 279 return total_read; 280} 281 282/* Send |length| bytes from |from| to the |fd| socket taking care of encoding 283 and negociating with the server. |from| can be NULL. */ 284/* FIXME: We don't check for errors nor report any! */ 285static void do_sec_send(struct connectdata *conn, curl_socket_t fd, 286 const char *from, int length) 287{ 288 int bytes, htonl_bytes; /* 32-bit integers for htonl */ 289 char *buffer = NULL; 290 char *cmd_buffer; 291 size_t cmd_size = 0; 292 CURLcode error; 293 enum protection_level prot_level = conn->data_prot; 294 bool iscmd = (prot_level == PROT_CMD)?TRUE:FALSE; 295 296 DEBUGASSERT(prot_level > PROT_NONE && prot_level < PROT_LAST); 297 298 if(iscmd) { 299 if(!strncmp(from, "PASS ", 5) || !strncmp(from, "ACCT ", 5)) 300 prot_level = PROT_PRIVATE; 301 else 302 prot_level = conn->command_prot; 303 } 304 bytes = conn->mech->encode(conn->app_data, from, length, prot_level, 305 (void**)&buffer, conn); 306 if(!buffer || bytes <= 0) 307 return; /* error */ 308 309 if(iscmd) { 310 error = Curl_base64_encode(conn->data, buffer, curlx_sitouz(bytes), 311 &cmd_buffer, &cmd_size); 312 if(error) { 313 free(buffer); 314 return; /* error */ 315 } 316 if(cmd_size > 0) { 317 static const char *enc = "ENC "; 318 static const char *mic = "MIC "; 319 if(prot_level == PROT_PRIVATE) 320 socket_write(conn, fd, enc, 4); 321 else 322 socket_write(conn, fd, mic, 4); 323 324 socket_write(conn, fd, cmd_buffer, cmd_size); 325 socket_write(conn, fd, "\r\n", 2); 326 infof(conn->data, "Send: %s%s\n", prot_level == PROT_PRIVATE?enc:mic, 327 cmd_buffer); 328 free(cmd_buffer); 329 } 330 } 331 else { 332 htonl_bytes = htonl(bytes); 333 socket_write(conn, fd, &htonl_bytes, sizeof(htonl_bytes)); 334 socket_write(conn, fd, buffer, curlx_sitouz(bytes)); 335 } 336 free(buffer); 337} 338 339static ssize_t sec_write(struct connectdata *conn, curl_socket_t fd, 340 const char *buffer, size_t length) 341{ 342 /* FIXME: Check for overflow */ 343 ssize_t len = conn->buffer_size; 344 int tx = 0; 345 346 len -= conn->mech->overhead(conn->app_data, conn->data_prot, len); 347 if(len <= 0) 348 len = length; 349 while(length) { 350 if(len >= 0 || length < (size_t)len) { 351 /* FIXME: Check for overflow. */ 352 len = length; 353 } 354 do_sec_send(conn, fd, buffer, len); 355 length -= len; 356 buffer += len; 357 tx += len; 358 } 359 return tx; 360} 361 362/* Matches Curl_send signature */ 363static ssize_t sec_send(struct connectdata *conn, int sockindex, 364 const void *buffer, size_t len, CURLcode *err) 365{ 366 curl_socket_t fd = conn->sock[sockindex]; 367 *err = CURLE_OK; 368 return sec_write(conn, fd, buffer, len); 369} 370 371int Curl_sec_read_msg(struct connectdata *conn, char *buffer, 372 enum protection_level level) 373{ 374 /* decoded_len should be size_t or ssize_t but conn->mech->decode returns an 375 int */ 376 int decoded_len; 377 char *buf; 378 int ret_code; 379 size_t decoded_sz = 0; 380 CURLcode error; 381 382 DEBUGASSERT(level > PROT_NONE && level < PROT_LAST); 383 384 error = Curl_base64_decode(buffer + 4, (unsigned char **)&buf, &decoded_sz); 385 if(error || decoded_sz == 0) 386 return -1; 387 388 if(decoded_sz > (size_t)INT_MAX) { 389 free(buf); 390 return -1; 391 } 392 decoded_len = curlx_uztosi(decoded_sz); 393 394 decoded_len = conn->mech->decode(conn->app_data, buf, decoded_len, 395 level, conn); 396 if(decoded_len <= 0) { 397 free(buf); 398 return -1; 399 } 400 401 if(conn->data->set.verbose) { 402 buf[decoded_len] = '\n'; 403 Curl_debug(conn->data, CURLINFO_HEADER_IN, buf, decoded_len + 1, conn); 404 } 405 406 buf[decoded_len] = '\0'; 407 DEBUGASSERT(decoded_len > 3); 408 if(buf[3] == '-') 409 ret_code = 0; 410 else { 411 /* Check for error? */ 412 sscanf(buf, "%d", &ret_code); 413 } 414 415 if(buf[decoded_len - 1] == '\n') 416 buf[decoded_len - 1] = '\0'; 417 /* FIXME: Is |buffer| length always greater than |decoded_len|? */ 418 strcpy(buffer, buf); 419 free(buf); 420 return ret_code; 421} 422 423/* FIXME: The error code returned here is never checked. */ 424static int sec_set_protection_level(struct connectdata *conn) 425{ 426 int code; 427 char* pbsz; 428 static unsigned int buffer_size = 1 << 20; /* 1048576 */ 429 enum protection_level level = conn->request_data_prot; 430 431 DEBUGASSERT(level > PROT_NONE && level < PROT_LAST); 432 433 if(!conn->sec_complete) { 434 infof(conn->data, "Trying to change the protection level after the" 435 "completion of the data exchange.\n"); 436 return -1; 437 } 438 439 /* Bail out if we try to set up the same level */ 440 if(conn->data_prot == level) 441 return 0; 442 443 if(level) { 444 code = ftp_send_command(conn, "PBSZ %u", buffer_size); 445 if(code < 0) 446 return -1; 447 448 if(code/100 != 2) { 449 failf(conn->data, "Failed to set the protection's buffer size."); 450 return -1; 451 } 452 conn->buffer_size = buffer_size; 453 454 pbsz = strstr(conn->data->state.buffer, "PBSZ="); 455 if(pbsz) { 456 /* FIXME: Checks for errors in sscanf? */ 457 sscanf(pbsz, "PBSZ=%u", &buffer_size); 458 if(buffer_size < conn->buffer_size) 459 conn->buffer_size = buffer_size; 460 } 461 } 462 463 /* Now try to negiociate the protection level. */ 464 code = ftp_send_command(conn, "PROT %c", level_to_char(level)); 465 466 if(code < 0) 467 return -1; 468 469 if(code/100 != 2) { 470 failf(conn->data, "Failed to set the protection level."); 471 return -1; 472 } 473 474 conn->data_prot = level; 475 if(level == PROT_PRIVATE) 476 conn->command_prot = level; 477 478 return 0; 479} 480 481int 482Curl_sec_request_prot(struct connectdata *conn, const char *level) 483{ 484 enum protection_level l = name_to_level(level); 485 if(l == PROT_NONE) 486 return -1; 487 DEBUGASSERT(l > PROT_NONE && l < PROT_LAST); 488 conn->request_data_prot = l; 489 return 0; 490} 491 492static CURLcode choose_mech(struct connectdata *conn) 493{ 494 int ret; 495 struct SessionHandle *data = conn->data; 496 const struct Curl_sec_client_mech * const *mech; 497 void *tmp_allocation; 498 const char *mech_name; 499 500 for(mech = mechs; (*mech); ++mech) { 501 mech_name = (*mech)->name; 502 /* We have no mechanism with a NULL name but keep this check */ 503 DEBUGASSERT(mech_name != NULL); 504 if(mech_name == NULL) { 505 infof(data, "Skipping mechanism with empty name (%p)\n", mech); 506 continue; 507 } 508 tmp_allocation = realloc(conn->app_data, (*mech)->size); 509 if(tmp_allocation == NULL) { 510 failf(data, "Failed realloc of size %u", (*mech)->size); 511 mech = NULL; 512 return CURLE_OUT_OF_MEMORY; 513 } 514 conn->app_data = tmp_allocation; 515 516 if((*mech)->init) { 517 ret = (*mech)->init(conn->app_data); 518 if(ret != 0) { 519 infof(data, "Failed initialization for %s. Skipping it.\n", mech_name); 520 continue; 521 } 522 } 523 524 infof(data, "Trying mechanism %s...\n", mech_name); 525 ret = ftp_send_command(conn, "AUTH %s", mech_name); 526 if(ret < 0) 527 /* FIXME: This error is too generic but it is OK for now. */ 528 return CURLE_COULDNT_CONNECT; 529 530 if(ret/100 != 3) { 531 switch(ret) { 532 case 504: 533 infof(data, "Mechanism %s is not supported by the server (server " 534 "returned ftp code: 504).\n", mech_name); 535 break; 536 case 534: 537 infof(data, "Mechanism %s was rejected by the server (server returned " 538 "ftp code: 534).\n", mech_name); 539 break; 540 default: 541 if(ret/100 == 5) { 542 infof(data, "server does not support the security extensions\n"); 543 return CURLE_USE_SSL_FAILED; 544 } 545 break; 546 } 547 continue; 548 } 549 550 /* Authenticate */ 551 ret = (*mech)->auth(conn->app_data, conn); 552 553 if(ret == AUTH_CONTINUE) 554 continue; 555 else if(ret != AUTH_OK) { 556 /* Mechanism has dumped the error to stderr, don't error here. */ 557 return -1; 558 } 559 DEBUGASSERT(ret == AUTH_OK); 560 561 conn->mech = *mech; 562 conn->sec_complete = 1; 563 conn->recv[FIRSTSOCKET] = sec_recv; 564 conn->send[FIRSTSOCKET] = sec_send; 565 conn->recv[SECONDARYSOCKET] = sec_recv; 566 conn->send[SECONDARYSOCKET] = sec_send; 567 conn->command_prot = PROT_SAFE; 568 /* Set the requested protection level */ 569 /* BLOCKING */ 570 (void)sec_set_protection_level(conn); 571 break; 572 } 573 574 return mech != NULL ? CURLE_OK : CURLE_FAILED_INIT; 575} 576 577CURLcode 578Curl_sec_login(struct connectdata *conn) 579{ 580 return choose_mech(conn); 581} 582 583 584void 585Curl_sec_end(struct connectdata *conn) 586{ 587 if(conn->mech != NULL && conn->mech->end) 588 conn->mech->end(conn->app_data); 589 if(conn->app_data) { 590 free(conn->app_data); 591 conn->app_data = NULL; 592 } 593 if(conn->in_buffer.data) { 594 free(conn->in_buffer.data); 595 conn->in_buffer.data = NULL; 596 conn->in_buffer.size = 0; 597 conn->in_buffer.index = 0; 598 /* FIXME: Is this really needed? */ 599 conn->in_buffer.eof_flag = 0; 600 } 601 conn->sec_complete = 0; 602 conn->data_prot = PROT_CLEAR; 603 conn->mech = NULL; 604} 605 606#endif /* HAVE_KRB4 || HAVE_GSSAPI */ 607 608#endif /* CURL_DISABLE_FTP */ 609