Home | History | Annotate | Download | only in lib
      1 /***************************************************************************
      2  *                                  _   _ ____  _
      3  *  Project                     ___| | | |  _ \| |
      4  *                             / __| | | | |_) | |
      5  *                            | (__| |_| |  _ <| |___
      6  *                             \___|\___/|_| \_\_____|
      7  *
      8  * Copyright (C) 2009, 2011, Markus Moeller, <markus_moeller (at) compuserve.com>
      9  * Copyright (C) 2012 - 2015, Daniel Stenberg, <daniel (at) haxx.se>, et al.
     10  *
     11  * This software is licensed as described in the file COPYING, which
     12  * you should have received as part of this distribution. The terms
     13  * are also available at http://curl.haxx.se/docs/copyright.html.
     14  *
     15  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
     16  * copies of the Software, and permit persons to whom the Software is
     17  * furnished to do so, under the terms of the COPYING file.
     18  *
     19  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
     20  * KIND, either express or implied.
     21  *
     22  ***************************************************************************/
     23 
     24 #include "curl_setup.h"
     25 
     26 #if defined(HAVE_GSSAPI) && !defined(CURL_DISABLE_PROXY)
     27 
     28 #include "curl_gssapi.h"
     29 #include "urldata.h"
     30 #include "sendf.h"
     31 #include "connect.h"
     32 #include "timeval.h"
     33 #include "socks.h"
     34 #include "warnless.h"
     35 #include "curl_printf.h"
     36 #include "curl_memory.h"
     37 /* The last #include file should be: */
     38 #include "memdebug.h"
     39 
     40 static gss_ctx_id_t gss_context = GSS_C_NO_CONTEXT;
     41 
     42 /*
     43  * Helper GSS-API error functions.
     44  */
     45 static int check_gss_err(struct SessionHandle *data,
     46                          OM_uint32 major_status,
     47                          OM_uint32 minor_status,
     48                          const char* function)
     49 {
     50   if(GSS_ERROR(major_status)) {
     51     OM_uint32 maj_stat, min_stat;
     52     OM_uint32 msg_ctx = 0;
     53     gss_buffer_desc status_string;
     54     char buf[1024];
     55     size_t len;
     56 
     57     len = 0;
     58     msg_ctx = 0;
     59     while(!msg_ctx) {
     60       /* convert major status code (GSS-API error) to text */
     61       maj_stat = gss_display_status(&min_stat, major_status,
     62                                     GSS_C_GSS_CODE,
     63                                     GSS_C_NULL_OID,
     64                                     &msg_ctx, &status_string);
     65       if(maj_stat == GSS_S_COMPLETE) {
     66         if(sizeof(buf) > len + status_string.length + 1) {
     67           strcpy(buf+len, (char*) status_string.value);
     68           len += status_string.length;
     69         }
     70         gss_release_buffer(&min_stat, &status_string);
     71         break;
     72       }
     73       gss_release_buffer(&min_stat, &status_string);
     74     }
     75     if(sizeof(buf) > len + 3) {
     76       strcpy(buf+len, ".\n");
     77       len += 2;
     78     }
     79     msg_ctx = 0;
     80     while(!msg_ctx) {
     81       /* convert minor status code (underlying routine error) to text */
     82       maj_stat = gss_display_status(&min_stat, minor_status,
     83                                     GSS_C_MECH_CODE,
     84                                     GSS_C_NULL_OID,
     85                                     &msg_ctx, &status_string);
     86       if(maj_stat == GSS_S_COMPLETE) {
     87         if(sizeof(buf) > len + status_string.length)
     88           strcpy(buf+len, (char*) status_string.value);
     89         gss_release_buffer(&min_stat, &status_string);
     90         break;
     91       }
     92       gss_release_buffer(&min_stat, &status_string);
     93     }
     94     failf(data, "GSS-API error: %s failed:\n%s", function, buf);
     95     return 1;
     96   }
     97 
     98   return 0;
     99 }
    100 
    101 CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex,
    102                                       struct connectdata *conn)
    103 {
    104   struct SessionHandle *data = conn->data;
    105   curl_socket_t sock = conn->sock[sockindex];
    106   CURLcode code;
    107   ssize_t actualread;
    108   ssize_t written;
    109   int result;
    110   OM_uint32 gss_major_status, gss_minor_status, gss_status;
    111   OM_uint32 gss_ret_flags;
    112   int gss_conf_state, gss_enc;
    113   gss_buffer_desc  service = GSS_C_EMPTY_BUFFER;
    114   gss_buffer_desc  gss_send_token = GSS_C_EMPTY_BUFFER;
    115   gss_buffer_desc  gss_recv_token = GSS_C_EMPTY_BUFFER;
    116   gss_buffer_desc  gss_w_token = GSS_C_EMPTY_BUFFER;
    117   gss_buffer_desc* gss_token = GSS_C_NO_BUFFER;
    118   gss_name_t       server = GSS_C_NO_NAME;
    119   gss_name_t       gss_client_name = GSS_C_NO_NAME;
    120   unsigned short   us_length;
    121   char             *user=NULL;
    122   unsigned char socksreq[4]; /* room for GSS-API exchange header only */
    123   char *serviceptr = data->set.str[STRING_SOCKS5_GSSAPI_SERVICE];
    124 
    125   /*   GSS-API request looks like
    126    * +----+------+-----+----------------+
    127    * |VER | MTYP | LEN |     TOKEN      |
    128    * +----+------+----------------------+
    129    * | 1  |  1   |  2  | up to 2^16 - 1 |
    130    * +----+------+-----+----------------+
    131    */
    132 
    133   /* prepare service name */
    134   if(strchr(serviceptr, '/')) {
    135     service.value = malloc(strlen(serviceptr));
    136     if(!service.value)
    137       return CURLE_OUT_OF_MEMORY;
    138     service.length = strlen(serviceptr);
    139     memcpy(service.value, serviceptr, service.length);
    140 
    141     gss_major_status = gss_import_name(&gss_minor_status, &service,
    142                                        (gss_OID) GSS_C_NULL_OID, &server);
    143   }
    144   else {
    145     service.value = malloc(strlen(serviceptr) +strlen(conn->proxy.name)+2);
    146     if(!service.value)
    147       return CURLE_OUT_OF_MEMORY;
    148     service.length = strlen(serviceptr) +strlen(conn->proxy.name)+1;
    149     snprintf(service.value, service.length+1, "%s@%s",
    150              serviceptr, conn->proxy.name);
    151 
    152     gss_major_status = gss_import_name(&gss_minor_status, &service,
    153                                        GSS_C_NT_HOSTBASED_SERVICE, &server);
    154   }
    155 
    156   gss_release_buffer(&gss_status, &service); /* clear allocated memory */
    157 
    158   if(check_gss_err(data, gss_major_status,
    159                    gss_minor_status, "gss_import_name()")) {
    160     failf(data, "Failed to create service name.");
    161     gss_release_name(&gss_status, &server);
    162     return CURLE_COULDNT_CONNECT;
    163   }
    164 
    165   /* As long as we need to keep sending some context info, and there's no  */
    166   /* errors, keep sending it...                                            */
    167   for(;;) {
    168     gss_major_status = Curl_gss_init_sec_context(data,
    169                                                  &gss_minor_status,
    170                                                  &gss_context,
    171                                                  server,
    172                                                  &Curl_krb5_mech_oid,
    173                                                  NULL,
    174                                                  gss_token,
    175                                                  &gss_send_token,
    176                                                  TRUE,
    177                                                  &gss_ret_flags);
    178 
    179     if(gss_token != GSS_C_NO_BUFFER)
    180       gss_release_buffer(&gss_status, &gss_recv_token);
    181     if(check_gss_err(data, gss_major_status,
    182                      gss_minor_status, "gss_init_sec_context")) {
    183       gss_release_name(&gss_status, &server);
    184       gss_release_buffer(&gss_status, &gss_recv_token);
    185       gss_release_buffer(&gss_status, &gss_send_token);
    186       gss_delete_sec_context(&gss_status, &gss_context, NULL);
    187       failf(data, "Failed to initial GSS-API token.");
    188       return CURLE_COULDNT_CONNECT;
    189     }
    190 
    191     if(gss_send_token.length != 0) {
    192       socksreq[0] = 1;    /* GSS-API subnegotiation version */
    193       socksreq[1] = 1;    /* authentication message type */
    194       us_length = htons((short)gss_send_token.length);
    195       memcpy(socksreq+2, &us_length, sizeof(short));
    196 
    197       code = Curl_write_plain(conn, sock, (char *)socksreq, 4, &written);
    198       if(code || (4 != written)) {
    199         failf(data, "Failed to send GSS-API authentication request.");
    200         gss_release_name(&gss_status, &server);
    201         gss_release_buffer(&gss_status, &gss_recv_token);
    202         gss_release_buffer(&gss_status, &gss_send_token);
    203         gss_delete_sec_context(&gss_status, &gss_context, NULL);
    204         return CURLE_COULDNT_CONNECT;
    205       }
    206 
    207       code = Curl_write_plain(conn, sock, (char *)gss_send_token.value,
    208                               gss_send_token.length, &written);
    209 
    210       if(code || ((ssize_t)gss_send_token.length != written)) {
    211         failf(data, "Failed to send GSS-API authentication token.");
    212         gss_release_name(&gss_status, &server);
    213         gss_release_buffer(&gss_status, &gss_recv_token);
    214         gss_release_buffer(&gss_status, &gss_send_token);
    215         gss_delete_sec_context(&gss_status, &gss_context, NULL);
    216         return CURLE_COULDNT_CONNECT;
    217       }
    218 
    219     }
    220 
    221     gss_release_buffer(&gss_status, &gss_send_token);
    222     gss_release_buffer(&gss_status, &gss_recv_token);
    223     if(gss_major_status != GSS_S_CONTINUE_NEEDED) break;
    224 
    225     /* analyse response */
    226 
    227     /*   GSS-API response looks like
    228      * +----+------+-----+----------------+
    229      * |VER | MTYP | LEN |     TOKEN      |
    230      * +----+------+----------------------+
    231      * | 1  |  1   |  2  | up to 2^16 - 1 |
    232      * +----+------+-----+----------------+
    233      */
    234 
    235     result=Curl_blockread_all(conn, sock, (char *)socksreq, 4, &actualread);
    236     if(result || (actualread != 4)) {
    237       failf(data, "Failed to receive GSS-API authentication response.");
    238       gss_release_name(&gss_status, &server);
    239       gss_delete_sec_context(&gss_status, &gss_context, NULL);
    240       return CURLE_COULDNT_CONNECT;
    241     }
    242 
    243     /* ignore the first (VER) byte */
    244     if(socksreq[1] == 255) { /* status / message type */
    245       failf(data, "User was rejected by the SOCKS5 server (%d %d).",
    246             socksreq[0], socksreq[1]);
    247       gss_release_name(&gss_status, &server);
    248       gss_delete_sec_context(&gss_status, &gss_context, NULL);
    249       return CURLE_COULDNT_CONNECT;
    250     }
    251 
    252     if(socksreq[1] != 1) { /* status / messgae type */
    253       failf(data, "Invalid GSS-API authentication response type (%d %d).",
    254             socksreq[0], socksreq[1]);
    255       gss_release_name(&gss_status, &server);
    256       gss_delete_sec_context(&gss_status, &gss_context, NULL);
    257       return CURLE_COULDNT_CONNECT;
    258     }
    259 
    260     memcpy(&us_length, socksreq+2, sizeof(short));
    261     us_length = ntohs(us_length);
    262 
    263     gss_recv_token.length=us_length;
    264     gss_recv_token.value=malloc(us_length);
    265     if(!gss_recv_token.value) {
    266       failf(data,
    267             "Could not allocate memory for GSS-API authentication "
    268             "response token.");
    269       gss_release_name(&gss_status, &server);
    270       gss_delete_sec_context(&gss_status, &gss_context, NULL);
    271       return CURLE_OUT_OF_MEMORY;
    272     }
    273 
    274     result=Curl_blockread_all(conn, sock, (char *)gss_recv_token.value,
    275                               gss_recv_token.length, &actualread);
    276 
    277     if(result || (actualread != us_length)) {
    278       failf(data, "Failed to receive GSS-API authentication token.");
    279       gss_release_name(&gss_status, &server);
    280       gss_release_buffer(&gss_status, &gss_recv_token);
    281       gss_delete_sec_context(&gss_status, &gss_context, NULL);
    282       return CURLE_COULDNT_CONNECT;
    283     }
    284 
    285     gss_token = &gss_recv_token;
    286   }
    287 
    288   gss_release_name(&gss_status, &server);
    289 
    290   /* Everything is good so far, user was authenticated! */
    291   gss_major_status = gss_inquire_context (&gss_minor_status, gss_context,
    292                                           &gss_client_name, NULL, NULL, NULL,
    293                                           NULL, NULL, NULL);
    294   if(check_gss_err(data, gss_major_status,
    295                    gss_minor_status, "gss_inquire_context")) {
    296     gss_delete_sec_context(&gss_status, &gss_context, NULL);
    297     gss_release_name(&gss_status, &gss_client_name);
    298     failf(data, "Failed to determine user name.");
    299     return CURLE_COULDNT_CONNECT;
    300   }
    301   gss_major_status = gss_display_name(&gss_minor_status, gss_client_name,
    302                                       &gss_send_token, NULL);
    303   if(check_gss_err(data, gss_major_status,
    304                    gss_minor_status, "gss_display_name")) {
    305     gss_delete_sec_context(&gss_status, &gss_context, NULL);
    306     gss_release_name(&gss_status, &gss_client_name);
    307     gss_release_buffer(&gss_status, &gss_send_token);
    308     failf(data, "Failed to determine user name.");
    309     return CURLE_COULDNT_CONNECT;
    310   }
    311   user=malloc(gss_send_token.length+1);
    312   if(!user) {
    313     gss_delete_sec_context(&gss_status, &gss_context, NULL);
    314     gss_release_name(&gss_status, &gss_client_name);
    315     gss_release_buffer(&gss_status, &gss_send_token);
    316     return CURLE_OUT_OF_MEMORY;
    317   }
    318 
    319   memcpy(user, gss_send_token.value, gss_send_token.length);
    320   user[gss_send_token.length] = '\0';
    321   gss_release_name(&gss_status, &gss_client_name);
    322   gss_release_buffer(&gss_status, &gss_send_token);
    323   infof(data, "SOCKS5 server authencticated user %s with GSS-API.\n",user);
    324   free(user);
    325   user=NULL;
    326 
    327   /* Do encryption */
    328   socksreq[0] = 1;    /* GSS-API subnegotiation version */
    329   socksreq[1] = 2;    /* encryption message type */
    330 
    331   gss_enc = 0; /* no data protection */
    332   /* do confidentiality protection if supported */
    333   if(gss_ret_flags & GSS_C_CONF_FLAG)
    334     gss_enc = 2;
    335   /* else do integrity protection */
    336   else if(gss_ret_flags & GSS_C_INTEG_FLAG)
    337     gss_enc = 1;
    338 
    339   infof(data, "SOCKS5 server supports GSS-API %s data protection.\n",
    340         (gss_enc==0)?"no":((gss_enc==1)?"integrity":"confidentiality"));
    341   /* force for the moment to no data protection */
    342   gss_enc = 0;
    343   /*
    344    * Sending the encryption type in clear seems wrong. It should be
    345    * protected with gss_seal()/gss_wrap(). See RFC1961 extract below
    346    * The NEC reference implementations on which this is based is
    347    * therefore at fault
    348    *
    349    *  +------+------+------+.......................+
    350    *  + ver  | mtyp | len  |   token               |
    351    *  +------+------+------+.......................+
    352    *  + 0x01 | 0x02 | 0x02 | up to 2^16 - 1 octets |
    353    *  +------+------+------+.......................+
    354    *
    355    *   Where:
    356    *
    357    *  - "ver" is the protocol version number, here 1 to represent the
    358    *    first version of the SOCKS/GSS-API protocol
    359    *
    360    *  - "mtyp" is the message type, here 2 to represent a protection
    361    *    -level negotiation message
    362    *
    363    *  - "len" is the length of the "token" field in octets
    364    *
    365    *  - "token" is the GSS-API encapsulated protection level
    366    *
    367    * The token is produced by encapsulating an octet containing the
    368    * required protection level using gss_seal()/gss_wrap() with conf_req
    369    * set to FALSE.  The token is verified using gss_unseal()/
    370    * gss_unwrap().
    371    *
    372    */
    373   if(data->set.socks5_gssapi_nec) {
    374     us_length = htons((short)1);
    375     memcpy(socksreq+2, &us_length, sizeof(short));
    376   }
    377   else {
    378     gss_send_token.length = 1;
    379     gss_send_token.value = malloc(1);
    380     if(!gss_send_token.value) {
    381       gss_delete_sec_context(&gss_status, &gss_context, NULL);
    382       return CURLE_OUT_OF_MEMORY;
    383     }
    384     memcpy(gss_send_token.value, &gss_enc, 1);
    385 
    386     gss_major_status = gss_wrap(&gss_minor_status, gss_context, 0,
    387                                 GSS_C_QOP_DEFAULT, &gss_send_token,
    388                                 &gss_conf_state, &gss_w_token);
    389 
    390     if(check_gss_err(data, gss_major_status, gss_minor_status, "gss_wrap")) {
    391       gss_release_buffer(&gss_status, &gss_send_token);
    392       gss_release_buffer(&gss_status, &gss_w_token);
    393       gss_delete_sec_context(&gss_status, &gss_context, NULL);
    394       failf(data, "Failed to wrap GSS-API encryption value into token.");
    395       return CURLE_COULDNT_CONNECT;
    396     }
    397     gss_release_buffer(&gss_status, &gss_send_token);
    398 
    399     us_length = htons((short)gss_w_token.length);
    400     memcpy(socksreq+2, &us_length, sizeof(short));
    401   }
    402 
    403   code = Curl_write_plain(conn, sock, (char *)socksreq, 4, &written);
    404   if(code  || (4 != written)) {
    405     failf(data, "Failed to send GSS-API encryption request.");
    406     gss_release_buffer(&gss_status, &gss_w_token);
    407     gss_delete_sec_context(&gss_status, &gss_context, NULL);
    408     return CURLE_COULDNT_CONNECT;
    409   }
    410 
    411   if(data->set.socks5_gssapi_nec) {
    412     memcpy(socksreq, &gss_enc, 1);
    413     code = Curl_write_plain(conn, sock, socksreq, 1, &written);
    414     if(code || ( 1 != written)) {
    415       failf(data, "Failed to send GSS-API encryption type.");
    416       gss_delete_sec_context(&gss_status, &gss_context, NULL);
    417       return CURLE_COULDNT_CONNECT;
    418     }
    419   }
    420   else {
    421     code = Curl_write_plain(conn, sock, (char *)gss_w_token.value,
    422                             gss_w_token.length, &written);
    423     if(code || ((ssize_t)gss_w_token.length != written)) {
    424       failf(data, "Failed to send GSS-API encryption type.");
    425       gss_release_buffer(&gss_status, &gss_w_token);
    426       gss_delete_sec_context(&gss_status, &gss_context, NULL);
    427       return CURLE_COULDNT_CONNECT;
    428     }
    429     gss_release_buffer(&gss_status, &gss_w_token);
    430   }
    431 
    432   result=Curl_blockread_all(conn, sock, (char *)socksreq, 4, &actualread);
    433   if(result || (actualread != 4)) {
    434     failf(data, "Failed to receive GSS-API encryption response.");
    435     gss_delete_sec_context(&gss_status, &gss_context, NULL);
    436     return CURLE_COULDNT_CONNECT;
    437   }
    438 
    439   /* ignore the first (VER) byte */
    440   if(socksreq[1] == 255) { /* status / message type */
    441     failf(data, "User was rejected by the SOCKS5 server (%d %d).",
    442           socksreq[0], socksreq[1]);
    443     gss_delete_sec_context(&gss_status, &gss_context, NULL);
    444     return CURLE_COULDNT_CONNECT;
    445   }
    446 
    447   if(socksreq[1] != 2) { /* status / messgae type */
    448     failf(data, "Invalid GSS-API encryption response type (%d %d).",
    449           socksreq[0], socksreq[1]);
    450     gss_delete_sec_context(&gss_status, &gss_context, NULL);
    451     return CURLE_COULDNT_CONNECT;
    452   }
    453 
    454   memcpy(&us_length, socksreq+2, sizeof(short));
    455   us_length = ntohs(us_length);
    456 
    457   gss_recv_token.length= us_length;
    458   gss_recv_token.value=malloc(gss_recv_token.length);
    459   if(!gss_recv_token.value) {
    460     gss_delete_sec_context(&gss_status, &gss_context, NULL);
    461     return CURLE_OUT_OF_MEMORY;
    462   }
    463   result=Curl_blockread_all(conn, sock, (char *)gss_recv_token.value,
    464                             gss_recv_token.length, &actualread);
    465 
    466   if(result || (actualread != us_length)) {
    467     failf(data, "Failed to receive GSS-API encryptrion type.");
    468     gss_release_buffer(&gss_status, &gss_recv_token);
    469     gss_delete_sec_context(&gss_status, &gss_context, NULL);
    470     return CURLE_COULDNT_CONNECT;
    471   }
    472 
    473   if(!data->set.socks5_gssapi_nec) {
    474     gss_major_status = gss_unwrap(&gss_minor_status, gss_context,
    475                                   &gss_recv_token, &gss_w_token,
    476                                   0, GSS_C_QOP_DEFAULT);
    477 
    478     if(check_gss_err(data, gss_major_status, gss_minor_status, "gss_unwrap")) {
    479       gss_release_buffer(&gss_status, &gss_recv_token);
    480       gss_release_buffer(&gss_status, &gss_w_token);
    481       gss_delete_sec_context(&gss_status, &gss_context, NULL);
    482       failf(data, "Failed to unwrap GSS-API encryption value into token.");
    483       return CURLE_COULDNT_CONNECT;
    484     }
    485     gss_release_buffer(&gss_status, &gss_recv_token);
    486 
    487     if(gss_w_token.length != 1) {
    488       failf(data, "Invalid GSS-API encryption response length (%d).",
    489             gss_w_token.length);
    490       gss_release_buffer(&gss_status, &gss_w_token);
    491       gss_delete_sec_context(&gss_status, &gss_context, NULL);
    492       return CURLE_COULDNT_CONNECT;
    493     }
    494 
    495     memcpy(socksreq, gss_w_token.value, gss_w_token.length);
    496     gss_release_buffer(&gss_status, &gss_w_token);
    497   }
    498   else {
    499     if(gss_recv_token.length != 1) {
    500       failf(data, "Invalid GSS-API encryption response length (%d).",
    501             gss_recv_token.length);
    502       gss_release_buffer(&gss_status, &gss_recv_token);
    503       gss_delete_sec_context(&gss_status, &gss_context, NULL);
    504       return CURLE_COULDNT_CONNECT;
    505     }
    506 
    507     memcpy(socksreq, gss_recv_token.value, gss_recv_token.length);
    508     gss_release_buffer(&gss_status, &gss_recv_token);
    509   }
    510 
    511   infof(data, "SOCKS5 access with%s protection granted.\n",
    512         (socksreq[0]==0)?"out GSS-API data":
    513         ((socksreq[0]==1)?" GSS-API integrity":" GSS-API confidentiality"));
    514 
    515   conn->socks5_gssapi_enctype = socksreq[0];
    516   if(socksreq[0] == 0)
    517     gss_delete_sec_context(&gss_status, &gss_context, NULL);
    518 
    519   return CURLE_OK;
    520 }
    521 
    522 #endif /* HAVE_GSSAPI && !CURL_DISABLE_PROXY */
    523