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