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