Home | History | Annotate | Download | only in lib
      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