File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / curl / lib / security.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Wed Jun 3 10:01:15 2020 UTC (4 years, 1 month ago) by misho
Branches: curl, MAIN
CVS tags: v7_70_0p4, HEAD
curl

    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, 2017 Kungliga Tekniska Högskolan
   11:  * (Royal Institute of Technology, Stockholm, Sweden).
   12:  *
   13:  * Copyright (C) 2001 - 2019, 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 "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: #include <limits.h>
   54: 
   55: #include "urldata.h"
   56: #include "curl_base64.h"
   57: #include "curl_memory.h"
   58: #include "curl_sec.h"
   59: #include "ftp.h"
   60: #include "sendf.h"
   61: #include "strcase.h"
   62: #include "warnless.h"
   63: #include "strdup.h"
   64: /* The last 3 #include files should be in this order */
   65: #include "curl_printf.h"
   66: #include "curl_memory.h"
   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: {
   93:   switch(level) {
   94:   case PROT_CLEAR:
   95:     return 'C';
   96:   case PROT_SAFE:
   97:     return 'S';
   98:   case PROT_CONFIDENTIAL:
   99:     return 'E';
  100:   case PROT_PRIVATE:
  101:     return 'P';
  102:   case PROT_CMD:
  103:     /* Fall through */
  104:   default:
  105:     /* Those 2 cases should not be reached! */
  106:     break;
  107:   }
  108:   DEBUGASSERT(0);
  109:   /* Default to the most secure alternative. */
  110:   return 'P';
  111: }
  112: 
  113: /* Send an FTP command defined by |message| and the optional arguments. The
  114:    function returns the ftp_code. If an error occurs, -1 is returned. */
  115: static int ftp_send_command(struct connectdata *conn, const char *message, ...)
  116: {
  117:   int ftp_code;
  118:   ssize_t nread = 0;
  119:   va_list args;
  120:   char print_buffer[50];
  121: 
  122:   va_start(args, message);
  123:   mvsnprintf(print_buffer, sizeof(print_buffer), message, args);
  124:   va_end(args);
  125: 
  126:   if(Curl_ftpsend(conn, print_buffer)) {
  127:     ftp_code = -1;
  128:   }
  129:   else {
  130:     if(Curl_GetFTPResponse(&nread, conn, &ftp_code))
  131:       ftp_code = -1;
  132:   }
  133: 
  134:   (void)nread; /* Unused */
  135:   return ftp_code;
  136: }
  137: 
  138: /* Read |len| from the socket |fd| and store it in |to|. Return a CURLcode
  139:    saying whether an error occurred or CURLE_OK if |len| was read. */
  140: static CURLcode
  141: socket_read(curl_socket_t fd, void *to, size_t len)
  142: {
  143:   char *to_p = to;
  144:   CURLcode result;
  145:   ssize_t nread = 0;
  146: 
  147:   while(len > 0) {
  148:     result = Curl_read_plain(fd, to_p, len, &nread);
  149:     if(!result) {
  150:       len -= nread;
  151:       to_p += nread;
  152:     }
  153:     else {
  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:       if(result == CURLE_AGAIN)
  182:         continue;
  183:       return result;
  184:     }
  185:   }
  186:   return CURLE_OK;
  187: }
  188: 
  189: static CURLcode read_data(struct connectdata *conn,
  190:                           curl_socket_t fd,
  191:                           struct krb5buffer *buf)
  192: {
  193:   int len;
  194:   CURLcode result;
  195: 
  196:   result = socket_read(fd, &len, sizeof(len));
  197:   if(result)
  198:     return result;
  199: 
  200:   if(len) {
  201:     /* only realloc if there was a length */
  202:     len = ntohl(len);
  203:     buf->data = Curl_saferealloc(buf->data, len);
  204:   }
  205:   if(!len || !buf->data)
  206:     return CURLE_OUT_OF_MEMORY;
  207: 
  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 sread(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:   return total_read;
  265: }
  266: 
  267: /* Send |length| bytes from |from| to the |fd| socket taking care of encoding
  268:    and negotiating with the server. |from| can be NULL. */
  269: static void do_sec_send(struct connectdata *conn, curl_socket_t fd,
  270:                         const char *from, int length)
  271: {
  272:   int bytes, htonl_bytes; /* 32-bit integers for htonl */
  273:   char *buffer = NULL;
  274:   char *cmd_buffer;
  275:   size_t cmd_size = 0;
  276:   CURLcode error;
  277:   enum protection_level prot_level = conn->data_prot;
  278:   bool iscmd = (prot_level == PROT_CMD)?TRUE:FALSE;
  279: 
  280:   DEBUGASSERT(prot_level > PROT_NONE && prot_level < PROT_LAST);
  281: 
  282:   if(iscmd) {
  283:     if(!strncmp(from, "PASS ", 5) || !strncmp(from, "ACCT ", 5))
  284:       prot_level = PROT_PRIVATE;
  285:     else
  286:       prot_level = conn->command_prot;
  287:   }
  288:   bytes = conn->mech->encode(conn->app_data, from, length, prot_level,
  289:                              (void **)&buffer);
  290:   if(!buffer || bytes <= 0)
  291:     return; /* error */
  292: 
  293:   if(iscmd) {
  294:     error = Curl_base64_encode(conn->data, buffer, curlx_sitouz(bytes),
  295:                                &cmd_buffer, &cmd_size);
  296:     if(error) {
  297:       free(buffer);
  298:       return; /* error */
  299:     }
  300:     if(cmd_size > 0) {
  301:       static const char *enc = "ENC ";
  302:       static const char *mic = "MIC ";
  303:       if(prot_level == PROT_PRIVATE)
  304:         socket_write(conn, fd, enc, 4);
  305:       else
  306:         socket_write(conn, fd, mic, 4);
  307: 
  308:       socket_write(conn, fd, cmd_buffer, cmd_size);
  309:       socket_write(conn, fd, "\r\n", 2);
  310:       infof(conn->data, "Send: %s%s\n", prot_level == PROT_PRIVATE?enc:mic,
  311:             cmd_buffer);
  312:       free(cmd_buffer);
  313:     }
  314:   }
  315:   else {
  316:     htonl_bytes = htonl(bytes);
  317:     socket_write(conn, fd, &htonl_bytes, sizeof(htonl_bytes));
  318:     socket_write(conn, fd, buffer, curlx_sitouz(bytes));
  319:   }
  320:   free(buffer);
  321: }
  322: 
  323: static ssize_t sec_write(struct connectdata *conn, curl_socket_t fd,
  324:                          const char *buffer, size_t length)
  325: {
  326:   ssize_t tx = 0, len = conn->buffer_size;
  327: 
  328:   len -= conn->mech->overhead(conn->app_data, conn->data_prot,
  329:                               curlx_sztosi(len));
  330:   if(len <= 0)
  331:     len = length;
  332:   while(length) {
  333:     if(length < (size_t)len)
  334:       len = length;
  335: 
  336:     do_sec_send(conn, fd, buffer, curlx_sztosi(len));
  337:     length -= len;
  338:     buffer += len;
  339:     tx += len;
  340:   }
  341:   return tx;
  342: }
  343: 
  344: /* Matches Curl_send signature */
  345: static ssize_t sec_send(struct connectdata *conn, int sockindex,
  346:                         const void *buffer, size_t len, CURLcode *err)
  347: {
  348:   curl_socket_t fd = conn->sock[sockindex];
  349:   *err = CURLE_OK;
  350:   return sec_write(conn, fd, buffer, len);
  351: }
  352: 
  353: int Curl_sec_read_msg(struct connectdata *conn, char *buffer,
  354:                       enum protection_level level)
  355: {
  356:   /* decoded_len should be size_t or ssize_t but conn->mech->decode returns an
  357:      int */
  358:   int decoded_len;
  359:   char *buf;
  360:   int ret_code = 0;
  361:   size_t decoded_sz = 0;
  362:   CURLcode error;
  363: 
  364:   if(!conn->mech)
  365:     /* not inititalized, return error */
  366:     return -1;
  367: 
  368:   DEBUGASSERT(level > PROT_NONE && level < PROT_LAST);
  369: 
  370:   error = Curl_base64_decode(buffer + 4, (unsigned char **)&buf, &decoded_sz);
  371:   if(error || decoded_sz == 0)
  372:     return -1;
  373: 
  374:   if(decoded_sz > (size_t)INT_MAX) {
  375:     free(buf);
  376:     return -1;
  377:   }
  378:   decoded_len = curlx_uztosi(decoded_sz);
  379: 
  380:   decoded_len = conn->mech->decode(conn->app_data, buf, decoded_len,
  381:                                    level, conn);
  382:   if(decoded_len <= 0) {
  383:     free(buf);
  384:     return -1;
  385:   }
  386: 
  387:   if(conn->data->set.verbose) {
  388:     buf[decoded_len] = '\n';
  389:     Curl_debug(conn->data, CURLINFO_HEADER_IN, buf, decoded_len + 1);
  390:   }
  391: 
  392:   buf[decoded_len] = '\0';
  393:   if(decoded_len <= 3)
  394:     /* suspiciously short */
  395:     return 0;
  396: 
  397:   if(buf[3] != '-')
  398:     /* safe to ignore return code */
  399:     (void)sscanf(buf, "%d", &ret_code);
  400: 
  401:   if(buf[decoded_len - 1] == '\n')
  402:     buf[decoded_len - 1] = '\0';
  403:   strcpy(buffer, buf);
  404:   free(buf);
  405:   return ret_code;
  406: }
  407: 
  408: static int sec_set_protection_level(struct connectdata *conn)
  409: {
  410:   int code;
  411:   enum protection_level level = conn->request_data_prot;
  412: 
  413:   DEBUGASSERT(level > PROT_NONE && level < PROT_LAST);
  414: 
  415:   if(!conn->sec_complete) {
  416:     infof(conn->data, "Trying to change the protection level after the"
  417:                       " completion of the data exchange.\n");
  418:     return -1;
  419:   }
  420: 
  421:   /* Bail out if we try to set up the same level */
  422:   if(conn->data_prot == level)
  423:     return 0;
  424: 
  425:   if(level) {
  426:     char *pbsz;
  427:     static unsigned int buffer_size = 1 << 20; /* 1048576 */
  428: 
  429:     code = ftp_send_command(conn, "PBSZ %u", buffer_size);
  430:     if(code < 0)
  431:       return -1;
  432: 
  433:     if(code/100 != 2) {
  434:       failf(conn->data, "Failed to set the protection's buffer size.");
  435:       return -1;
  436:     }
  437:     conn->buffer_size = buffer_size;
  438: 
  439:     pbsz = strstr(conn->data->state.buffer, "PBSZ=");
  440:     if(pbsz) {
  441:       /* ignore return code, use default value if it fails */
  442:       (void)sscanf(pbsz, "PBSZ=%u", &buffer_size);
  443:       if(buffer_size < conn->buffer_size)
  444:         conn->buffer_size = buffer_size;
  445:     }
  446:   }
  447: 
  448:   /* Now try to negiociate the protection level. */
  449:   code = ftp_send_command(conn, "PROT %c", level_to_char(level));
  450: 
  451:   if(code < 0)
  452:     return -1;
  453: 
  454:   if(code/100 != 2) {
  455:     failf(conn->data, "Failed to set the protection level.");
  456:     return -1;
  457:   }
  458: 
  459:   conn->data_prot = level;
  460:   if(level == PROT_PRIVATE)
  461:     conn->command_prot = level;
  462: 
  463:   return 0;
  464: }
  465: 
  466: int
  467: Curl_sec_request_prot(struct connectdata *conn, const char *level)
  468: {
  469:   enum protection_level l = name_to_level(level);
  470:   if(l == PROT_NONE)
  471:     return -1;
  472:   DEBUGASSERT(l > PROT_NONE && l < PROT_LAST);
  473:   conn->request_data_prot = l;
  474:   return 0;
  475: }
  476: 
  477: static CURLcode choose_mech(struct connectdata *conn)
  478: {
  479:   int ret;
  480:   struct Curl_easy *data = conn->data;
  481:   void *tmp_allocation;
  482:   const struct Curl_sec_client_mech *mech = &Curl_krb5_client_mech;
  483: 
  484:   tmp_allocation = realloc(conn->app_data, mech->size);
  485:   if(tmp_allocation == NULL) {
  486:     failf(data, "Failed realloc of size %zu", mech->size);
  487:     mech = NULL;
  488:     return CURLE_OUT_OF_MEMORY;
  489:   }
  490:   conn->app_data = tmp_allocation;
  491: 
  492:   if(mech->init) {
  493:     ret = mech->init(conn->app_data);
  494:     if(ret) {
  495:       infof(data, "Failed initialization for %s. Skipping it.\n",
  496:             mech->name);
  497:       return CURLE_FAILED_INIT;
  498:     }
  499:   }
  500: 
  501:   infof(data, "Trying mechanism %s...\n", mech->name);
  502:   ret = ftp_send_command(conn, "AUTH %s", mech->name);
  503:   if(ret < 0)
  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:     conn->in_buffer.eof_flag = 0;
  571:   }
  572:   conn->sec_complete = 0;
  573:   conn->data_prot = PROT_CLEAR;
  574:   conn->mech = NULL;
  575: }
  576: 
  577: #endif /* HAVE_GSSAPI */
  578: 
  579: #endif /* CURL_DISABLE_FTP */

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>