00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044 #include "curl_setup.h"
00045
00046 #ifndef CURL_DISABLE_FTP
00047 #ifdef HAVE_GSSAPI
00048
00049 #ifdef HAVE_NETDB_H
00050 #include <netdb.h>
00051 #endif
00052
00053 #ifdef HAVE_LIMITS_H
00054 #include <limits.h>
00055 #endif
00056
00057 #include "urldata.h"
00058 #include "curl_base64.h"
00059 #include "curl_memory.h"
00060 #include "curl_sec.h"
00061 #include "ftp.h"
00062 #include "sendf.h"
00063 #include "strcase.h"
00064 #include "warnless.h"
00065 #include "strdup.h"
00066
00067 #include "memdebug.h"
00068
00069 static const struct {
00070 enum protection_level level;
00071 const char *name;
00072 } level_names[] = {
00073 { PROT_CLEAR, "clear" },
00074 { PROT_SAFE, "safe" },
00075 { PROT_CONFIDENTIAL, "confidential" },
00076 { PROT_PRIVATE, "private" }
00077 };
00078
00079 static enum protection_level
00080 name_to_level(const char *name)
00081 {
00082 int i;
00083 for(i = 0; i < (int)sizeof(level_names)/(int)sizeof(level_names[0]); i++)
00084 if(checkprefix(name, level_names[i].name))
00085 return level_names[i].level;
00086 return PROT_NONE;
00087 }
00088
00089
00090
00091 static char level_to_char(int level)
00092 {
00093 switch(level) {
00094 case PROT_CLEAR:
00095 return 'C';
00096 case PROT_SAFE:
00097 return 'S';
00098 case PROT_CONFIDENTIAL:
00099 return 'E';
00100 case PROT_PRIVATE:
00101 return 'P';
00102 case PROT_CMD:
00103
00104 default:
00105
00106 break;
00107 }
00108 DEBUGASSERT(0);
00109
00110 return 'P';
00111 }
00112
00113
00114
00115 static int ftp_send_command(struct connectdata *conn, const char *message, ...)
00116 {
00117 int ftp_code;
00118 ssize_t nread=0;
00119 va_list args;
00120 char print_buffer[50];
00121
00122 va_start(args, message);
00123 vsnprintf(print_buffer, sizeof(print_buffer), message, args);
00124 va_end(args);
00125
00126 if(Curl_ftpsend(conn, print_buffer)) {
00127 ftp_code = -1;
00128 }
00129 else {
00130 if(Curl_GetFTPResponse(&nread, conn, &ftp_code))
00131 ftp_code = -1;
00132 }
00133
00134 (void)nread;
00135 return ftp_code;
00136 }
00137
00138
00139
00140 static CURLcode
00141 socket_read(curl_socket_t fd, void *to, size_t len)
00142 {
00143 char *to_p = to;
00144 CURLcode result;
00145 ssize_t nread;
00146
00147 while(len > 0) {
00148 result = Curl_read_plain(fd, to_p, len, &nread);
00149 if(!result) {
00150 len -= nread;
00151 to_p += nread;
00152 }
00153 else {
00154
00155 if(result == CURLE_AGAIN)
00156 continue;
00157 return result;
00158 }
00159 }
00160 return CURLE_OK;
00161 }
00162
00163
00164
00165
00166
00167 static CURLcode
00168 socket_write(struct connectdata *conn, curl_socket_t fd, const void *to,
00169 size_t len)
00170 {
00171 const char *to_p = to;
00172 CURLcode result;
00173 ssize_t written;
00174
00175 while(len > 0) {
00176 result = Curl_write_plain(conn, fd, to_p, len, &written);
00177 if(!result) {
00178 len -= written;
00179 to_p += written;
00180 }
00181 else {
00182
00183 if(result == CURLE_AGAIN)
00184 continue;
00185 return result;
00186 }
00187 }
00188 return CURLE_OK;
00189 }
00190
00191 static CURLcode read_data(struct connectdata *conn,
00192 curl_socket_t fd,
00193 struct krb5buffer *buf)
00194 {
00195 int len;
00196 void *tmp = NULL;
00197 CURLcode result;
00198
00199 result = socket_read(fd, &len, sizeof(len));
00200 if(result)
00201 return result;
00202
00203 if(len) {
00204
00205 len = ntohl(len);
00206 tmp = Curl_saferealloc(buf->data, len);
00207 }
00208 if(tmp == NULL)
00209 return CURLE_OUT_OF_MEMORY;
00210
00211 buf->data = tmp;
00212 result = socket_read(fd, buf->data, len);
00213 if(result)
00214 return result;
00215 buf->size = conn->mech->decode(conn->app_data, buf->data, len,
00216 conn->data_prot, conn);
00217 buf->index = 0;
00218 return CURLE_OK;
00219 }
00220
00221 static size_t
00222 buffer_read(struct krb5buffer *buf, void *data, size_t len)
00223 {
00224 if(buf->size - buf->index < len)
00225 len = buf->size - buf->index;
00226 memcpy(data, (char *)buf->data + buf->index, len);
00227 buf->index += len;
00228 return len;
00229 }
00230
00231
00232 static ssize_t sec_recv(struct connectdata *conn, int sockindex,
00233 char *buffer, size_t len, CURLcode *err)
00234 {
00235 size_t bytes_read;
00236 size_t total_read = 0;
00237 curl_socket_t fd = conn->sock[sockindex];
00238
00239 *err = CURLE_OK;
00240
00241
00242 if(conn->sec_complete == 0 || conn->data_prot == PROT_CLEAR)
00243 return read(fd, buffer, len);
00244
00245 if(conn->in_buffer.eof_flag) {
00246 conn->in_buffer.eof_flag = 0;
00247 return 0;
00248 }
00249
00250 bytes_read = buffer_read(&conn->in_buffer, buffer, len);
00251 len -= bytes_read;
00252 total_read += bytes_read;
00253 buffer += bytes_read;
00254
00255 while(len > 0) {
00256 if(read_data(conn, fd, &conn->in_buffer))
00257 return -1;
00258 if(conn->in_buffer.size == 0) {
00259 if(bytes_read > 0)
00260 conn->in_buffer.eof_flag = 1;
00261 return bytes_read;
00262 }
00263 bytes_read = buffer_read(&conn->in_buffer, buffer, len);
00264 len -= bytes_read;
00265 total_read += bytes_read;
00266 buffer += bytes_read;
00267 }
00268
00269 return total_read;
00270 }
00271
00272
00273
00274
00275 static void do_sec_send(struct connectdata *conn, curl_socket_t fd,
00276 const char *from, int length)
00277 {
00278 int bytes, htonl_bytes;
00279 char *buffer = NULL;
00280 char *cmd_buffer;
00281 size_t cmd_size = 0;
00282 CURLcode error;
00283 enum protection_level prot_level = conn->data_prot;
00284 bool iscmd = (prot_level == PROT_CMD)?TRUE:FALSE;
00285
00286 DEBUGASSERT(prot_level > PROT_NONE && prot_level < PROT_LAST);
00287
00288 if(iscmd) {
00289 if(!strncmp(from, "PASS ", 5) || !strncmp(from, "ACCT ", 5))
00290 prot_level = PROT_PRIVATE;
00291 else
00292 prot_level = conn->command_prot;
00293 }
00294 bytes = conn->mech->encode(conn->app_data, from, length, prot_level,
00295 (void **)&buffer);
00296 if(!buffer || bytes <= 0)
00297 return;
00298
00299 if(iscmd) {
00300 error = Curl_base64_encode(conn->data, buffer, curlx_sitouz(bytes),
00301 &cmd_buffer, &cmd_size);
00302 if(error) {
00303 free(buffer);
00304 return;
00305 }
00306 if(cmd_size > 0) {
00307 static const char *enc = "ENC ";
00308 static const char *mic = "MIC ";
00309 if(prot_level == PROT_PRIVATE)
00310 socket_write(conn, fd, enc, 4);
00311 else
00312 socket_write(conn, fd, mic, 4);
00313
00314 socket_write(conn, fd, cmd_buffer, cmd_size);
00315 socket_write(conn, fd, "\r\n", 2);
00316 infof(conn->data, "Send: %s%s\n", prot_level == PROT_PRIVATE?enc:mic,
00317 cmd_buffer);
00318 free(cmd_buffer);
00319 }
00320 }
00321 else {
00322 htonl_bytes = htonl(bytes);
00323 socket_write(conn, fd, &htonl_bytes, sizeof(htonl_bytes));
00324 socket_write(conn, fd, buffer, curlx_sitouz(bytes));
00325 }
00326 free(buffer);
00327 }
00328
00329 static ssize_t sec_write(struct connectdata *conn, curl_socket_t fd,
00330 const char *buffer, size_t length)
00331 {
00332 ssize_t tx = 0, len = conn->buffer_size;
00333
00334 len -= conn->mech->overhead(conn->app_data, conn->data_prot,
00335 curlx_sztosi(len));
00336 if(len <= 0)
00337 len = length;
00338 while(length) {
00339 if(length < (size_t)len)
00340 len = length;
00341
00342 do_sec_send(conn, fd, buffer, curlx_sztosi(len));
00343 length -= len;
00344 buffer += len;
00345 tx += len;
00346 }
00347 return tx;
00348 }
00349
00350
00351 static ssize_t sec_send(struct connectdata *conn, int sockindex,
00352 const void *buffer, size_t len, CURLcode *err)
00353 {
00354 curl_socket_t fd = conn->sock[sockindex];
00355 *err = CURLE_OK;
00356 return sec_write(conn, fd, buffer, len);
00357 }
00358
00359 int Curl_sec_read_msg(struct connectdata *conn, char *buffer,
00360 enum protection_level level)
00361 {
00362
00363
00364 int decoded_len;
00365 char *buf;
00366 int ret_code = 0;
00367 size_t decoded_sz = 0;
00368 CURLcode error;
00369
00370 DEBUGASSERT(level > PROT_NONE && level < PROT_LAST);
00371
00372 error = Curl_base64_decode(buffer + 4, (unsigned char **)&buf, &decoded_sz);
00373 if(error || decoded_sz == 0)
00374 return -1;
00375
00376 if(decoded_sz > (size_t)INT_MAX) {
00377 free(buf);
00378 return -1;
00379 }
00380 decoded_len = curlx_uztosi(decoded_sz);
00381
00382 decoded_len = conn->mech->decode(conn->app_data, buf, decoded_len,
00383 level, conn);
00384 if(decoded_len <= 0) {
00385 free(buf);
00386 return -1;
00387 }
00388
00389 if(conn->data->set.verbose) {
00390 buf[decoded_len] = '\n';
00391 Curl_debug(conn->data, CURLINFO_HEADER_IN, buf, decoded_len + 1, conn);
00392 }
00393
00394 buf[decoded_len] = '\0';
00395 if(decoded_len <= 3)
00396
00397 return 0;
00398
00399 if(buf[3] != '-')
00400
00401 (void)sscanf(buf, "%d", &ret_code);
00402
00403 if(buf[decoded_len - 1] == '\n')
00404 buf[decoded_len - 1] = '\0';
00405
00406 strcpy(buffer, buf);
00407 free(buf);
00408 return ret_code;
00409 }
00410
00411
00412 static int sec_set_protection_level(struct connectdata *conn)
00413 {
00414 int code;
00415 char *pbsz;
00416 static unsigned int buffer_size = 1 << 20;
00417 enum protection_level level = conn->request_data_prot;
00418
00419 DEBUGASSERT(level > PROT_NONE && level < PROT_LAST);
00420
00421 if(!conn->sec_complete) {
00422 infof(conn->data, "Trying to change the protection level after the"
00423 "completion of the data exchange.\n");
00424 return -1;
00425 }
00426
00427
00428 if(conn->data_prot == level)
00429 return 0;
00430
00431 if(level) {
00432 code = ftp_send_command(conn, "PBSZ %u", buffer_size);
00433 if(code < 0)
00434 return -1;
00435
00436 if(code/100 != 2) {
00437 failf(conn->data, "Failed to set the protection's buffer size.");
00438 return -1;
00439 }
00440 conn->buffer_size = buffer_size;
00441
00442 pbsz = strstr(conn->data->state.buffer, "PBSZ=");
00443 if(pbsz) {
00444
00445 (void)sscanf(pbsz, "PBSZ=%u", &buffer_size);
00446 if(buffer_size < conn->buffer_size)
00447 conn->buffer_size = buffer_size;
00448 }
00449 }
00450
00451
00452 code = ftp_send_command(conn, "PROT %c", level_to_char(level));
00453
00454 if(code < 0)
00455 return -1;
00456
00457 if(code/100 != 2) {
00458 failf(conn->data, "Failed to set the protection level.");
00459 return -1;
00460 }
00461
00462 conn->data_prot = level;
00463 if(level == PROT_PRIVATE)
00464 conn->command_prot = level;
00465
00466 return 0;
00467 }
00468
00469 int
00470 Curl_sec_request_prot(struct connectdata *conn, const char *level)
00471 {
00472 enum protection_level l = name_to_level(level);
00473 if(l == PROT_NONE)
00474 return -1;
00475 DEBUGASSERT(l > PROT_NONE && l < PROT_LAST);
00476 conn->request_data_prot = l;
00477 return 0;
00478 }
00479
00480 static CURLcode choose_mech(struct connectdata *conn)
00481 {
00482 int ret;
00483 struct Curl_easy *data = conn->data;
00484 void *tmp_allocation;
00485 const struct Curl_sec_client_mech *mech = &Curl_krb5_client_mech;
00486
00487 tmp_allocation = realloc(conn->app_data, mech->size);
00488 if(tmp_allocation == NULL) {
00489 failf(data, "Failed realloc of size %u", mech->size);
00490 mech = NULL;
00491 return CURLE_OUT_OF_MEMORY;
00492 }
00493 conn->app_data = tmp_allocation;
00494
00495 if(mech->init) {
00496 ret = mech->init(conn->app_data);
00497 if(ret) {
00498 infof(data, "Failed initialization for %s. Skipping it.\n",
00499 mech->name);
00500 return CURLE_FAILED_INIT;
00501 }
00502 }
00503
00504 infof(data, "Trying mechanism %s...\n", mech->name);
00505 ret = ftp_send_command(conn, "AUTH %s", mech->name);
00506 if(ret < 0)
00507
00508 return CURLE_COULDNT_CONNECT;
00509
00510 if(ret/100 != 3) {
00511 switch(ret) {
00512 case 504:
00513 infof(data, "Mechanism %s is not supported by the server (server "
00514 "returned ftp code: 504).\n", mech->name);
00515 break;
00516 case 534:
00517 infof(data, "Mechanism %s was rejected by the server (server returned "
00518 "ftp code: 534).\n", mech->name);
00519 break;
00520 default:
00521 if(ret/100 == 5) {
00522 infof(data, "server does not support the security extensions\n");
00523 return CURLE_USE_SSL_FAILED;
00524 }
00525 break;
00526 }
00527 return CURLE_LOGIN_DENIED;
00528 }
00529
00530
00531 ret = mech->auth(conn->app_data, conn);
00532
00533 if(ret != AUTH_CONTINUE) {
00534 if(ret != AUTH_OK) {
00535
00536 return -1;
00537 }
00538 DEBUGASSERT(ret == AUTH_OK);
00539
00540 conn->mech = mech;
00541 conn->sec_complete = 1;
00542 conn->recv[FIRSTSOCKET] = sec_recv;
00543 conn->send[FIRSTSOCKET] = sec_send;
00544 conn->recv[SECONDARYSOCKET] = sec_recv;
00545 conn->send[SECONDARYSOCKET] = sec_send;
00546 conn->command_prot = PROT_SAFE;
00547
00548
00549 (void)sec_set_protection_level(conn);
00550 }
00551
00552 return CURLE_OK;
00553 }
00554
00555 CURLcode
00556 Curl_sec_login(struct connectdata *conn)
00557 {
00558 return choose_mech(conn);
00559 }
00560
00561
00562 void
00563 Curl_sec_end(struct connectdata *conn)
00564 {
00565 if(conn->mech != NULL && conn->mech->end)
00566 conn->mech->end(conn->app_data);
00567 free(conn->app_data);
00568 conn->app_data = NULL;
00569 if(conn->in_buffer.data) {
00570 free(conn->in_buffer.data);
00571 conn->in_buffer.data = NULL;
00572 conn->in_buffer.size = 0;
00573 conn->in_buffer.index = 0;
00574
00575 conn->in_buffer.eof_flag = 0;
00576 }
00577 conn->sec_complete = 0;
00578 conn->data_prot = PROT_CLEAR;
00579 conn->mech = NULL;
00580 }
00581
00582 #endif
00583
00584 #endif