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 #include "setup.h"
00042
00043 #ifndef CURL_DISABLE_FTP
00044 #if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
00045
00046 #define _MPRINTF_REPLACE
00047 #include <curl/mprintf.h>
00048
00049 #include <stdlib.h>
00050 #include <string.h>
00051 #include <netdb.h>
00052
00053 #ifdef HAVE_UNISTD_H
00054 #include <unistd.h>
00055 #endif
00056
00057 #include "urldata.h"
00058 #include "krb4.h"
00059 #include "base64.h"
00060 #include "sendf.h"
00061 #include "ftp.h"
00062 #include "memory.h"
00063
00064
00065 #include "memdebug.h"
00066
00067 #define min(a, b) ((a) < (b) ? (a) : (b))
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(curl_strnequal(level_names[i].name, name, strlen(name)))
00085 return level_names[i].level;
00086 return (enum protection_level)-1;
00087 }
00088
00089 static const struct Curl_sec_client_mech * const mechs[] = {
00090 #ifdef HAVE_GSSAPI
00091 &Curl_krb5_client_mech,
00092 #endif
00093 #ifdef HAVE_KRB4
00094 &Curl_krb4_client_mech,
00095 #endif
00096 NULL
00097 };
00098
00099 int
00100 Curl_sec_getc(struct connectdata *conn, FILE *F)
00101 {
00102 if(conn->sec_complete && conn->data_prot) {
00103 char c;
00104 if(Curl_sec_read(conn, fileno(F), &c, 1) <= 0)
00105 return EOF;
00106 return c;
00107 }
00108 else
00109 return getc(F);
00110 }
00111
00112 static int
00113 block_read(int fd, void *buf, size_t len)
00114 {
00115 unsigned char *p = buf;
00116 int b;
00117 while(len) {
00118 b = read(fd, p, len);
00119 if (b == 0)
00120 return 0;
00121 else if (b < 0 && (errno == EINTR || errno == EAGAIN))
00122 continue;
00123 else if (b < 0)
00124 return -1;
00125 len -= b;
00126 p += b;
00127 }
00128 return p - (unsigned char*)buf;
00129 }
00130
00131 static int
00132 block_write(int fd, const void *buf, size_t len)
00133 {
00134 const unsigned char *p = buf;
00135 int b;
00136 while(len) {
00137 b = write(fd, p, len);
00138 if (b < 0 && (errno == EINTR || errno == EAGAIN))
00139 continue;
00140 else if(b < 0)
00141 return -1;
00142 len -= b;
00143 p += b;
00144 }
00145 return p - (unsigned char*)buf;
00146 }
00147
00148 static int
00149 sec_get_data(struct connectdata *conn,
00150 int fd, struct krb4buffer *buf)
00151 {
00152 int len;
00153 int b;
00154
00155 b = block_read(fd, &len, sizeof(len));
00156 if (b == 0)
00157 return 0;
00158 else if (b < 0)
00159 return -1;
00160 len = ntohl(len);
00161 buf->data = realloc(buf->data, len);
00162 b = buf->data ? block_read(fd, buf->data, len) : -1;
00163 if (b == 0)
00164 return 0;
00165 else if (b < 0)
00166 return -1;
00167 buf->size = (conn->mech->decode)(conn->app_data, buf->data, len,
00168 conn->data_prot, conn);
00169 buf->index = 0;
00170 return 0;
00171 }
00172
00173 static size_t
00174 buffer_read(struct krb4buffer *buf, void *data, size_t len)
00175 {
00176 len = min(len, buf->size - buf->index);
00177 memcpy(data, (char*)buf->data + buf->index, len);
00178 buf->index += len;
00179 return len;
00180 }
00181
00182 static size_t
00183 buffer_write(struct krb4buffer *buf, void *data, size_t len)
00184 {
00185 if(buf->index + len > buf->size) {
00186 void *tmp;
00187 if(buf->data == NULL)
00188 tmp = malloc(1024);
00189 else
00190 tmp = realloc(buf->data, buf->index + len);
00191 if(tmp == NULL)
00192 return -1;
00193 buf->data = tmp;
00194 buf->size = buf->index + len;
00195 }
00196 memcpy((char*)buf->data + buf->index, data, len);
00197 buf->index += len;
00198 return len;
00199 }
00200
00201 int
00202 Curl_sec_read(struct connectdata *conn, int fd, void *buffer, int length)
00203 {
00204 size_t len;
00205 int rx = 0;
00206
00207 if(conn->sec_complete == 0 || conn->data_prot == 0)
00208 return read(fd, buffer, length);
00209
00210 if(conn->in_buffer.eof_flag){
00211 conn->in_buffer.eof_flag = 0;
00212 return 0;
00213 }
00214
00215 len = buffer_read(&conn->in_buffer, buffer, length);
00216 length -= len;
00217 rx += len;
00218 buffer = (char*)buffer + len;
00219
00220 while(length) {
00221 if(sec_get_data(conn, fd, &conn->in_buffer) < 0)
00222 return -1;
00223 if(conn->in_buffer.size == 0) {
00224 if(rx)
00225 conn->in_buffer.eof_flag = 1;
00226 return rx;
00227 }
00228 len = buffer_read(&conn->in_buffer, buffer, length);
00229 length -= len;
00230 rx += len;
00231 buffer = (char*)buffer + len;
00232 }
00233 return rx;
00234 }
00235
00236 static int
00237 sec_send(struct connectdata *conn, int fd, char *from, int length)
00238 {
00239 int bytes;
00240 void *buf;
00241 enum protection_level protlevel = conn->data_prot;
00242 int iscmd = protlevel == prot_cmd;
00243
00244 if(iscmd) {
00245 if(!strncmp(from, "PASS ", 5) || !strncmp(from, "ACCT ", 5))
00246 protlevel = prot_private;
00247 else
00248 protlevel = conn->command_prot;
00249 }
00250 bytes = (conn->mech->encode)(conn->app_data, from, length, protlevel,
00251 &buf, conn);
00252 if(iscmd) {
00253 char *cmdbuf;
00254
00255 bytes = Curl_base64_encode(conn->data, (char *)buf, bytes, &cmdbuf);
00256 if(bytes > 0) {
00257 if(protlevel == prot_private)
00258 block_write(fd, "ENC ", 4);
00259 else
00260 block_write(fd, "MIC ", 4);
00261 block_write(fd, cmdbuf, bytes);
00262 block_write(fd, "\r\n", 2);
00263 Curl_infof(conn->data, "%s %s\n", protlevel == prot_private ? "ENC" : "MIC", cmdbuf);
00264 free(cmdbuf);
00265 }
00266 } else {
00267 bytes = htonl(bytes);
00268 block_write(fd, &bytes, sizeof(bytes));
00269 block_write(fd, buf, ntohl(bytes));
00270 }
00271 free(buf);
00272 return length;
00273 }
00274
00275 int
00276 Curl_sec_fflush_fd(struct connectdata *conn, int fd)
00277 {
00278 if(conn->data_prot != prot_clear) {
00279 if(conn->out_buffer.index > 0){
00280 Curl_sec_write(conn, fd,
00281 conn->out_buffer.data, conn->out_buffer.index);
00282 conn->out_buffer.index = 0;
00283 }
00284 sec_send(conn, fd, NULL, 0);
00285 }
00286 return 0;
00287 }
00288
00289 int
00290 Curl_sec_write(struct connectdata *conn, int fd, char *buffer, int length)
00291 {
00292 int len = conn->buffer_size;
00293 int tx = 0;
00294
00295 if(conn->data_prot == prot_clear)
00296 return write(fd, buffer, length);
00297
00298 len -= (conn->mech->overhead)(conn->app_data, conn->data_prot, len);
00299 if(len <= 0)
00300 len = length;
00301 while(length){
00302 if(length < len)
00303 len = length;
00304 sec_send(conn, fd, buffer, len);
00305 length -= len;
00306 buffer += len;
00307 tx += len;
00308 }
00309 return tx;
00310 }
00311
00312 ssize_t
00313 Curl_sec_send(struct connectdata *conn, int num, char *buffer, int length)
00314 {
00315 curl_socket_t fd = conn->sock[num];
00316 return (ssize_t)Curl_sec_write(conn, fd, buffer, length);
00317 }
00318
00319 int
00320 Curl_sec_putc(struct connectdata *conn, int c, FILE *F)
00321 {
00322 char ch = c;
00323 if(conn->data_prot == prot_clear)
00324 return putc(c, F);
00325
00326 buffer_write(&conn->out_buffer, &ch, 1);
00327 if(c == '\n' || conn->out_buffer.index >= 1024 ) {
00328 Curl_sec_write(conn, fileno(F), conn->out_buffer.data,
00329 conn->out_buffer.index);
00330 conn->out_buffer.index = 0;
00331 }
00332 return c;
00333 }
00334
00335 int
00336 Curl_sec_read_msg(struct connectdata *conn, char *s, int level)
00337 {
00338 int len;
00339 unsigned char *buf;
00340 int code;
00341
00342 len = Curl_base64_decode(s + 4, &buf);
00343 if(len > 0)
00344 len = (conn->mech->decode)(conn->app_data, buf, len, level, conn);
00345 else
00346 return -1;
00347
00348 if(len < 0) {
00349 free(buf);
00350 return -1;
00351 }
00352
00353 if(conn->data->set.verbose) {
00354 buf[len] = '\n';
00355 Curl_debug(conn->data, CURLINFO_HEADER_IN, (char *)buf, len + 1, conn);
00356 }
00357
00358 buf[len] = '\0';
00359
00360 if(buf[3] == '-')
00361 code = 0;
00362 else
00363 sscanf((char *)buf, "%d", &code);
00364 if(buf[len-1] == '\n')
00365 buf[len-1] = '\0';
00366 strcpy(s, (char *)buf);
00367 free(buf);
00368 return code;
00369 }
00370
00371 enum protection_level
00372 Curl_set_command_prot(struct connectdata *conn, enum protection_level level)
00373 {
00374 enum protection_level old = conn->command_prot;
00375 conn->command_prot = level;
00376 return old;
00377 }
00378
00379 static int
00380 sec_prot_internal(struct connectdata *conn, int level)
00381 {
00382 char *p;
00383 unsigned int s = 1048576;
00384 ssize_t nread;
00385
00386 if(!conn->sec_complete){
00387 infof(conn->data, "No security data exchange has taken place.\n");
00388 return -1;
00389 }
00390
00391 if(level){
00392 int code;
00393 if(Curl_ftpsendf(conn, "PBSZ %u", s))
00394 return -1;
00395
00396 if(Curl_GetFTPResponse(&nread, conn, &code))
00397 return -1;
00398
00399 if(code/100 != 2){
00400 failf(conn->data, "Failed to set protection buffer size.");
00401 return -1;
00402 }
00403 conn->buffer_size = s;
00404
00405 p = strstr(conn->data->state.buffer, "PBSZ=");
00406 if(p)
00407 sscanf(p, "PBSZ=%u", &s);
00408 if(s < conn->buffer_size)
00409 conn->buffer_size = s;
00410 }
00411
00412 if(Curl_ftpsendf(conn, "PROT %c", level["CSEP"]))
00413 return -1;
00414
00415 if(Curl_GetFTPResponse(&nread, conn, NULL))
00416 return -1;
00417
00418 if(conn->data->state.buffer[0] != '2'){
00419 failf(conn->data, "Failed to set protection level.");
00420 return -1;
00421 }
00422
00423 conn->data_prot = (enum protection_level)level;
00424 if(level == prot_private)
00425 conn->command_prot = (enum protection_level)level;
00426 return 0;
00427 }
00428
00429 void
00430 Curl_sec_set_protection_level(struct connectdata *conn)
00431 {
00432 if(conn->sec_complete && conn->data_prot != conn->request_data_prot)
00433 sec_prot_internal(conn, conn->request_data_prot);
00434 }
00435
00436
00437 int
00438 Curl_sec_request_prot(struct connectdata *conn, const char *level)
00439 {
00440 int l = name_to_level(level);
00441 if(l == -1)
00442 return -1;
00443 conn->request_data_prot = (enum protection_level)l;
00444 return 0;
00445 }
00446
00447 int
00448 Curl_sec_login(struct connectdata *conn)
00449 {
00450 int ret;
00451 const struct Curl_sec_client_mech * const *m;
00452 ssize_t nread;
00453 struct SessionHandle *data=conn->data;
00454 int ftpcode;
00455
00456 for(m = mechs; *m && (*m)->name; m++) {
00457 void *tmp;
00458
00459 tmp = realloc(conn->app_data, (*m)->size);
00460 if (tmp == NULL) {
00461 failf (data, "realloc %u failed", (*m)->size);
00462 return -1;
00463 }
00464 conn->app_data = tmp;
00465
00466 if((*m)->init && (*(*m)->init)(conn->app_data) != 0) {
00467 infof(data, "Skipping %s...\n", (*m)->name);
00468 continue;
00469 }
00470 infof(data, "Trying %s...\n", (*m)->name);
00471
00472 if(Curl_ftpsendf(conn, "AUTH %s", (*m)->name))
00473 return -1;
00474
00475 if(Curl_GetFTPResponse(&nread, conn, &ftpcode))
00476 return -1;
00477
00478 if(conn->data->state.buffer[0] != '3'){
00479 switch(ftpcode) {
00480 case 504:
00481 infof(data,
00482 "%s is not supported by the server.\n", (*m)->name);
00483 break;
00484 case 534:
00485 infof(data, "%s rejected as security mechanism.\n", (*m)->name);
00486 break;
00487 default:
00488 if(conn->data->state.buffer[0] == '5') {
00489 infof(data, "The server doesn't support the FTP "
00490 "security extensions.\n");
00491 return -1;
00492 }
00493 break;
00494 }
00495 continue;
00496 }
00497
00498 ret = (*(*m)->auth)(conn->app_data, conn);
00499
00500 if(ret == AUTH_CONTINUE)
00501 continue;
00502 else if(ret != AUTH_OK){
00503
00504 return -1;
00505 }
00506 conn->mech = *m;
00507 conn->sec_complete = 1;
00508 conn->command_prot = prot_safe;
00509
00510
00511 Curl_sec_set_protection_level(conn);
00512 break;
00513 }
00514
00515 return *m == NULL;
00516 }
00517
00518 void
00519 Curl_sec_end(struct connectdata *conn)
00520 {
00521 if (conn->mech != NULL) {
00522 if(conn->mech->end)
00523 (conn->mech->end)(conn->app_data);
00524 memset(conn->app_data, 0, conn->mech->size);
00525 free(conn->app_data);
00526 conn->app_data = NULL;
00527 }
00528 conn->sec_complete = 0;
00529 conn->data_prot = (enum protection_level)0;
00530 conn->mech=NULL;
00531 }
00532
00533 #endif
00534 #endif