socks_gssapi.c
Go to the documentation of this file.
00001 /***************************************************************************
00002  *                                  _   _ ____  _
00003  *  Project                     ___| | | |  _ \| |
00004  *                             / __| | | | |_) | |
00005  *                            | (__| |_| |  _ <| |___
00006  *                             \___|\___/|_| \_\_____|
00007  *
00008  * Copyright (C) 2009, 2011, Markus Moeller, <markus_moeller@compuserve.com>
00009  * Copyright (C) 2012 - 2016, Daniel Stenberg, <daniel@haxx.se>, et al.
00010  *
00011  * This software is licensed as described in the file COPYING, which
00012  * you should have received as part of this distribution. The terms
00013  * are also available at https://curl.haxx.se/docs/copyright.html.
00014  *
00015  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
00016  * copies of the Software, and permit persons to whom the Software is
00017  * furnished to do so, under the terms of the COPYING file.
00018  *
00019  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
00020  * KIND, either express or implied.
00021  *
00022  ***************************************************************************/
00023 
00024 #include "curl_setup.h"
00025 
00026 #if defined(HAVE_GSSAPI) && !defined(CURL_DISABLE_PROXY)
00027 
00028 #include "curl_gssapi.h"
00029 #include "urldata.h"
00030 #include "sendf.h"
00031 #include "connect.h"
00032 #include "timeval.h"
00033 #include "socks.h"
00034 #include "warnless.h"
00035 
00036 /* The last 3 #include files should be in this order */
00037 #include "curl_printf.h"
00038 #include "curl_memory.h"
00039 #include "memdebug.h"
00040 
00041 static gss_ctx_id_t gss_context = GSS_C_NO_CONTEXT;
00042 
00043 /*
00044  * Helper GSS-API error functions.
00045  */
00046 static int check_gss_err(struct Curl_easy *data,
00047                          OM_uint32 major_status,
00048                          OM_uint32 minor_status,
00049                          const char *function)
00050 {
00051   if(GSS_ERROR(major_status)) {
00052     OM_uint32 maj_stat, min_stat;
00053     OM_uint32 msg_ctx = 0;
00054     gss_buffer_desc status_string;
00055     char buf[1024];
00056     size_t len;
00057 
00058     len = 0;
00059     msg_ctx = 0;
00060     while(!msg_ctx) {
00061       /* convert major status code (GSS-API error) to text */
00062       maj_stat = gss_display_status(&min_stat, major_status,
00063                                     GSS_C_GSS_CODE,
00064                                     GSS_C_NULL_OID,
00065                                     &msg_ctx, &status_string);
00066       if(maj_stat == GSS_S_COMPLETE) {
00067         if(sizeof(buf) > len + status_string.length + 1) {
00068           strcpy(buf+len, (char *) status_string.value);
00069           len += status_string.length;
00070         }
00071         gss_release_buffer(&min_stat, &status_string);
00072         break;
00073       }
00074       gss_release_buffer(&min_stat, &status_string);
00075     }
00076     if(sizeof(buf) > len + 3) {
00077       strcpy(buf+len, ".\n");
00078       len += 2;
00079     }
00080     msg_ctx = 0;
00081     while(!msg_ctx) {
00082       /* convert minor status code (underlying routine error) to text */
00083       maj_stat = gss_display_status(&min_stat, minor_status,
00084                                     GSS_C_MECH_CODE,
00085                                     GSS_C_NULL_OID,
00086                                     &msg_ctx, &status_string);
00087       if(maj_stat == GSS_S_COMPLETE) {
00088         if(sizeof(buf) > len + status_string.length)
00089           strcpy(buf+len, (char *) status_string.value);
00090         gss_release_buffer(&min_stat, &status_string);
00091         break;
00092       }
00093       gss_release_buffer(&min_stat, &status_string);
00094     }
00095     failf(data, "GSS-API error: %s failed:\n%s", function, buf);
00096     return 1;
00097   }
00098 
00099   return 0;
00100 }
00101 
00102 CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex,
00103                                       struct connectdata *conn)
00104 {
00105   struct Curl_easy *data = conn->data;
00106   curl_socket_t sock = conn->sock[sockindex];
00107   CURLcode code;
00108   ssize_t actualread;
00109   ssize_t written;
00110   int result;
00111   OM_uint32 gss_major_status, gss_minor_status, gss_status;
00112   OM_uint32 gss_ret_flags;
00113   int gss_conf_state, gss_enc;
00114   gss_buffer_desc  service = GSS_C_EMPTY_BUFFER;
00115   gss_buffer_desc  gss_send_token = GSS_C_EMPTY_BUFFER;
00116   gss_buffer_desc  gss_recv_token = GSS_C_EMPTY_BUFFER;
00117   gss_buffer_desc  gss_w_token = GSS_C_EMPTY_BUFFER;
00118   gss_buffer_desc* gss_token = GSS_C_NO_BUFFER;
00119   gss_name_t       server = GSS_C_NO_NAME;
00120   gss_name_t       gss_client_name = GSS_C_NO_NAME;
00121   unsigned short   us_length;
00122   char             *user=NULL;
00123   unsigned char socksreq[4]; /* room for GSS-API exchange header only */
00124   const char *serviceptr = data->set.str[STRING_PROXY_SERVICE_NAME] ?
00125                            data->set.str[STRING_PROXY_SERVICE_NAME] : "rcmd";
00126   const size_t serviceptr_length = strlen(serviceptr);
00127 
00128   /*   GSS-API request looks like
00129    * +----+------+-----+----------------+
00130    * |VER | MTYP | LEN |     TOKEN      |
00131    * +----+------+----------------------+
00132    * | 1  |  1   |  2  | up to 2^16 - 1 |
00133    * +----+------+-----+----------------+
00134    */
00135 
00136   /* prepare service name */
00137   if(strchr(serviceptr, '/')) {
00138     service.length = serviceptr_length;
00139     service.value = malloc(service.length);
00140     if(!service.value)
00141       return CURLE_OUT_OF_MEMORY;
00142     memcpy(service.value, serviceptr, service.length);
00143 
00144     gss_major_status = gss_import_name(&gss_minor_status, &service,
00145                                        (gss_OID) GSS_C_NULL_OID, &server);
00146   }
00147   else {
00148     service.value = malloc(serviceptr_length +
00149                            strlen(conn->socks_proxy.host.name)+2);
00150     if(!service.value)
00151       return CURLE_OUT_OF_MEMORY;
00152     service.length = serviceptr_length + strlen(conn->socks_proxy.host.name)+1;
00153     snprintf(service.value, service.length+1, "%s@%s",
00154              serviceptr, conn->socks_proxy.host.name);
00155 
00156     gss_major_status = gss_import_name(&gss_minor_status, &service,
00157                                        GSS_C_NT_HOSTBASED_SERVICE, &server);
00158   }
00159 
00160   gss_release_buffer(&gss_status, &service); /* clear allocated memory */
00161 
00162   if(check_gss_err(data, gss_major_status,
00163                    gss_minor_status, "gss_import_name()")) {
00164     failf(data, "Failed to create service name.");
00165     gss_release_name(&gss_status, &server);
00166     return CURLE_COULDNT_CONNECT;
00167   }
00168 
00169   /* As long as we need to keep sending some context info, and there's no  */
00170   /* errors, keep sending it...                                            */
00171   for(;;) {
00172     gss_major_status = Curl_gss_init_sec_context(data,
00173                                                  &gss_minor_status,
00174                                                  &gss_context,
00175                                                  server,
00176                                                  &Curl_krb5_mech_oid,
00177                                                  NULL,
00178                                                  gss_token,
00179                                                  &gss_send_token,
00180                                                  TRUE,
00181                                                  &gss_ret_flags);
00182 
00183     if(gss_token != GSS_C_NO_BUFFER)
00184       gss_release_buffer(&gss_status, &gss_recv_token);
00185     if(check_gss_err(data, gss_major_status,
00186                      gss_minor_status, "gss_init_sec_context")) {
00187       gss_release_name(&gss_status, &server);
00188       gss_release_buffer(&gss_status, &gss_recv_token);
00189       gss_release_buffer(&gss_status, &gss_send_token);
00190       gss_delete_sec_context(&gss_status, &gss_context, NULL);
00191       failf(data, "Failed to initial GSS-API token.");
00192       return CURLE_COULDNT_CONNECT;
00193     }
00194 
00195     if(gss_send_token.length != 0) {
00196       socksreq[0] = 1;    /* GSS-API subnegotiation version */
00197       socksreq[1] = 1;    /* authentication message type */
00198       us_length = htons((short)gss_send_token.length);
00199       memcpy(socksreq+2, &us_length, sizeof(short));
00200 
00201       code = Curl_write_plain(conn, sock, (char *)socksreq, 4, &written);
00202       if(code || (4 != written)) {
00203         failf(data, "Failed to send GSS-API authentication request.");
00204         gss_release_name(&gss_status, &server);
00205         gss_release_buffer(&gss_status, &gss_recv_token);
00206         gss_release_buffer(&gss_status, &gss_send_token);
00207         gss_delete_sec_context(&gss_status, &gss_context, NULL);
00208         return CURLE_COULDNT_CONNECT;
00209       }
00210 
00211       code = Curl_write_plain(conn, sock, (char *)gss_send_token.value,
00212                               gss_send_token.length, &written);
00213 
00214       if(code || ((ssize_t)gss_send_token.length != written)) {
00215         failf(data, "Failed to send GSS-API authentication token.");
00216         gss_release_name(&gss_status, &server);
00217         gss_release_buffer(&gss_status, &gss_recv_token);
00218         gss_release_buffer(&gss_status, &gss_send_token);
00219         gss_delete_sec_context(&gss_status, &gss_context, NULL);
00220         return CURLE_COULDNT_CONNECT;
00221       }
00222 
00223     }
00224 
00225     gss_release_buffer(&gss_status, &gss_send_token);
00226     gss_release_buffer(&gss_status, &gss_recv_token);
00227     if(gss_major_status != GSS_S_CONTINUE_NEEDED) break;
00228 
00229     /* analyse response */
00230 
00231     /*   GSS-API response looks like
00232      * +----+------+-----+----------------+
00233      * |VER | MTYP | LEN |     TOKEN      |
00234      * +----+------+----------------------+
00235      * | 1  |  1   |  2  | up to 2^16 - 1 |
00236      * +----+------+-----+----------------+
00237      */
00238 
00239     result=Curl_blockread_all(conn, sock, (char *)socksreq, 4, &actualread);
00240     if(result || (actualread != 4)) {
00241       failf(data, "Failed to receive GSS-API authentication response.");
00242       gss_release_name(&gss_status, &server);
00243       gss_delete_sec_context(&gss_status, &gss_context, NULL);
00244       return CURLE_COULDNT_CONNECT;
00245     }
00246 
00247     /* ignore the first (VER) byte */
00248     if(socksreq[1] == 255) { /* status / message type */
00249       failf(data, "User was rejected by the SOCKS5 server (%d %d).",
00250             socksreq[0], socksreq[1]);
00251       gss_release_name(&gss_status, &server);
00252       gss_delete_sec_context(&gss_status, &gss_context, NULL);
00253       return CURLE_COULDNT_CONNECT;
00254     }
00255 
00256     if(socksreq[1] != 1) { /* status / messgae type */
00257       failf(data, "Invalid GSS-API authentication response type (%d %d).",
00258             socksreq[0], socksreq[1]);
00259       gss_release_name(&gss_status, &server);
00260       gss_delete_sec_context(&gss_status, &gss_context, NULL);
00261       return CURLE_COULDNT_CONNECT;
00262     }
00263 
00264     memcpy(&us_length, socksreq+2, sizeof(short));
00265     us_length = ntohs(us_length);
00266 
00267     gss_recv_token.length=us_length;
00268     gss_recv_token.value=malloc(us_length);
00269     if(!gss_recv_token.value) {
00270       failf(data,
00271             "Could not allocate memory for GSS-API authentication "
00272             "response token.");
00273       gss_release_name(&gss_status, &server);
00274       gss_delete_sec_context(&gss_status, &gss_context, NULL);
00275       return CURLE_OUT_OF_MEMORY;
00276     }
00277 
00278     result=Curl_blockread_all(conn, sock, (char *)gss_recv_token.value,
00279                               gss_recv_token.length, &actualread);
00280 
00281     if(result || (actualread != us_length)) {
00282       failf(data, "Failed to receive GSS-API authentication token.");
00283       gss_release_name(&gss_status, &server);
00284       gss_release_buffer(&gss_status, &gss_recv_token);
00285       gss_delete_sec_context(&gss_status, &gss_context, NULL);
00286       return CURLE_COULDNT_CONNECT;
00287     }
00288 
00289     gss_token = &gss_recv_token;
00290   }
00291 
00292   gss_release_name(&gss_status, &server);
00293 
00294   /* Everything is good so far, user was authenticated! */
00295   gss_major_status = gss_inquire_context(&gss_minor_status, gss_context,
00296                                          &gss_client_name, NULL, NULL, NULL,
00297                                          NULL, NULL, NULL);
00298   if(check_gss_err(data, gss_major_status,
00299                    gss_minor_status, "gss_inquire_context")) {
00300     gss_delete_sec_context(&gss_status, &gss_context, NULL);
00301     gss_release_name(&gss_status, &gss_client_name);
00302     failf(data, "Failed to determine user name.");
00303     return CURLE_COULDNT_CONNECT;
00304   }
00305   gss_major_status = gss_display_name(&gss_minor_status, gss_client_name,
00306                                       &gss_send_token, NULL);
00307   if(check_gss_err(data, gss_major_status,
00308                    gss_minor_status, "gss_display_name")) {
00309     gss_delete_sec_context(&gss_status, &gss_context, NULL);
00310     gss_release_name(&gss_status, &gss_client_name);
00311     gss_release_buffer(&gss_status, &gss_send_token);
00312     failf(data, "Failed to determine user name.");
00313     return CURLE_COULDNT_CONNECT;
00314   }
00315   user=malloc(gss_send_token.length+1);
00316   if(!user) {
00317     gss_delete_sec_context(&gss_status, &gss_context, NULL);
00318     gss_release_name(&gss_status, &gss_client_name);
00319     gss_release_buffer(&gss_status, &gss_send_token);
00320     return CURLE_OUT_OF_MEMORY;
00321   }
00322 
00323   memcpy(user, gss_send_token.value, gss_send_token.length);
00324   user[gss_send_token.length] = '\0';
00325   gss_release_name(&gss_status, &gss_client_name);
00326   gss_release_buffer(&gss_status, &gss_send_token);
00327   infof(data, "SOCKS5 server authencticated user %s with GSS-API.\n",user);
00328   free(user);
00329   user=NULL;
00330 
00331   /* Do encryption */
00332   socksreq[0] = 1;    /* GSS-API subnegotiation version */
00333   socksreq[1] = 2;    /* encryption message type */
00334 
00335   gss_enc = 0; /* no data protection */
00336   /* do confidentiality protection if supported */
00337   if(gss_ret_flags & GSS_C_CONF_FLAG)
00338     gss_enc = 2;
00339   /* else do integrity protection */
00340   else if(gss_ret_flags & GSS_C_INTEG_FLAG)
00341     gss_enc = 1;
00342 
00343   infof(data, "SOCKS5 server supports GSS-API %s data protection.\n",
00344         (gss_enc==0)?"no":((gss_enc==1)?"integrity":"confidentiality"));
00345   /* force for the moment to no data protection */
00346   gss_enc = 0;
00347   /*
00348    * Sending the encryption type in clear seems wrong. It should be
00349    * protected with gss_seal()/gss_wrap(). See RFC1961 extract below
00350    * The NEC reference implementations on which this is based is
00351    * therefore at fault
00352    *
00353    *  +------+------+------+.......................+
00354    *  + ver  | mtyp | len  |   token               |
00355    *  +------+------+------+.......................+
00356    *  + 0x01 | 0x02 | 0x02 | up to 2^16 - 1 octets |
00357    *  +------+------+------+.......................+
00358    *
00359    *   Where:
00360    *
00361    *  - "ver" is the protocol version number, here 1 to represent the
00362    *    first version of the SOCKS/GSS-API protocol
00363    *
00364    *  - "mtyp" is the message type, here 2 to represent a protection
00365    *    -level negotiation message
00366    *
00367    *  - "len" is the length of the "token" field in octets
00368    *
00369    *  - "token" is the GSS-API encapsulated protection level
00370    *
00371    * The token is produced by encapsulating an octet containing the
00372    * required protection level using gss_seal()/gss_wrap() with conf_req
00373    * set to FALSE.  The token is verified using gss_unseal()/
00374    * gss_unwrap().
00375    *
00376    */
00377   if(data->set.socks5_gssapi_nec) {
00378     us_length = htons((short)1);
00379     memcpy(socksreq+2, &us_length, sizeof(short));
00380   }
00381   else {
00382     gss_send_token.length = 1;
00383     gss_send_token.value = malloc(1);
00384     if(!gss_send_token.value) {
00385       gss_delete_sec_context(&gss_status, &gss_context, NULL);
00386       return CURLE_OUT_OF_MEMORY;
00387     }
00388     memcpy(gss_send_token.value, &gss_enc, 1);
00389 
00390     gss_major_status = gss_wrap(&gss_minor_status, gss_context, 0,
00391                                 GSS_C_QOP_DEFAULT, &gss_send_token,
00392                                 &gss_conf_state, &gss_w_token);
00393 
00394     if(check_gss_err(data, gss_major_status, gss_minor_status, "gss_wrap")) {
00395       gss_release_buffer(&gss_status, &gss_send_token);
00396       gss_release_buffer(&gss_status, &gss_w_token);
00397       gss_delete_sec_context(&gss_status, &gss_context, NULL);
00398       failf(data, "Failed to wrap GSS-API encryption value into token.");
00399       return CURLE_COULDNT_CONNECT;
00400     }
00401     gss_release_buffer(&gss_status, &gss_send_token);
00402 
00403     us_length = htons((short)gss_w_token.length);
00404     memcpy(socksreq+2, &us_length, sizeof(short));
00405   }
00406 
00407   code = Curl_write_plain(conn, sock, (char *)socksreq, 4, &written);
00408   if(code  || (4 != written)) {
00409     failf(data, "Failed to send GSS-API encryption request.");
00410     gss_release_buffer(&gss_status, &gss_w_token);
00411     gss_delete_sec_context(&gss_status, &gss_context, NULL);
00412     return CURLE_COULDNT_CONNECT;
00413   }
00414 
00415   if(data->set.socks5_gssapi_nec) {
00416     memcpy(socksreq, &gss_enc, 1);
00417     code = Curl_write_plain(conn, sock, socksreq, 1, &written);
00418     if(code || ( 1 != written)) {
00419       failf(data, "Failed to send GSS-API encryption type.");
00420       gss_delete_sec_context(&gss_status, &gss_context, NULL);
00421       return CURLE_COULDNT_CONNECT;
00422     }
00423   }
00424   else {
00425     code = Curl_write_plain(conn, sock, (char *)gss_w_token.value,
00426                             gss_w_token.length, &written);
00427     if(code || ((ssize_t)gss_w_token.length != written)) {
00428       failf(data, "Failed to send GSS-API encryption type.");
00429       gss_release_buffer(&gss_status, &gss_w_token);
00430       gss_delete_sec_context(&gss_status, &gss_context, NULL);
00431       return CURLE_COULDNT_CONNECT;
00432     }
00433     gss_release_buffer(&gss_status, &gss_w_token);
00434   }
00435 
00436   result=Curl_blockread_all(conn, sock, (char *)socksreq, 4, &actualread);
00437   if(result || (actualread != 4)) {
00438     failf(data, "Failed to receive GSS-API encryption response.");
00439     gss_delete_sec_context(&gss_status, &gss_context, NULL);
00440     return CURLE_COULDNT_CONNECT;
00441   }
00442 
00443   /* ignore the first (VER) byte */
00444   if(socksreq[1] == 255) { /* status / message type */
00445     failf(data, "User was rejected by the SOCKS5 server (%d %d).",
00446           socksreq[0], socksreq[1]);
00447     gss_delete_sec_context(&gss_status, &gss_context, NULL);
00448     return CURLE_COULDNT_CONNECT;
00449   }
00450 
00451   if(socksreq[1] != 2) { /* status / messgae type */
00452     failf(data, "Invalid GSS-API encryption response type (%d %d).",
00453           socksreq[0], socksreq[1]);
00454     gss_delete_sec_context(&gss_status, &gss_context, NULL);
00455     return CURLE_COULDNT_CONNECT;
00456   }
00457 
00458   memcpy(&us_length, socksreq+2, sizeof(short));
00459   us_length = ntohs(us_length);
00460 
00461   gss_recv_token.length= us_length;
00462   gss_recv_token.value=malloc(gss_recv_token.length);
00463   if(!gss_recv_token.value) {
00464     gss_delete_sec_context(&gss_status, &gss_context, NULL);
00465     return CURLE_OUT_OF_MEMORY;
00466   }
00467   result=Curl_blockread_all(conn, sock, (char *)gss_recv_token.value,
00468                             gss_recv_token.length, &actualread);
00469 
00470   if(result || (actualread != us_length)) {
00471     failf(data, "Failed to receive GSS-API encryptrion type.");
00472     gss_release_buffer(&gss_status, &gss_recv_token);
00473     gss_delete_sec_context(&gss_status, &gss_context, NULL);
00474     return CURLE_COULDNT_CONNECT;
00475   }
00476 
00477   if(!data->set.socks5_gssapi_nec) {
00478     gss_major_status = gss_unwrap(&gss_minor_status, gss_context,
00479                                   &gss_recv_token, &gss_w_token,
00480                                   0, GSS_C_QOP_DEFAULT);
00481 
00482     if(check_gss_err(data, gss_major_status, gss_minor_status, "gss_unwrap")) {
00483       gss_release_buffer(&gss_status, &gss_recv_token);
00484       gss_release_buffer(&gss_status, &gss_w_token);
00485       gss_delete_sec_context(&gss_status, &gss_context, NULL);
00486       failf(data, "Failed to unwrap GSS-API encryption value into token.");
00487       return CURLE_COULDNT_CONNECT;
00488     }
00489     gss_release_buffer(&gss_status, &gss_recv_token);
00490 
00491     if(gss_w_token.length != 1) {
00492       failf(data, "Invalid GSS-API encryption response length (%d).",
00493             gss_w_token.length);
00494       gss_release_buffer(&gss_status, &gss_w_token);
00495       gss_delete_sec_context(&gss_status, &gss_context, NULL);
00496       return CURLE_COULDNT_CONNECT;
00497     }
00498 
00499     memcpy(socksreq, gss_w_token.value, gss_w_token.length);
00500     gss_release_buffer(&gss_status, &gss_w_token);
00501   }
00502   else {
00503     if(gss_recv_token.length != 1) {
00504       failf(data, "Invalid GSS-API encryption response length (%d).",
00505             gss_recv_token.length);
00506       gss_release_buffer(&gss_status, &gss_recv_token);
00507       gss_delete_sec_context(&gss_status, &gss_context, NULL);
00508       return CURLE_COULDNT_CONNECT;
00509     }
00510 
00511     memcpy(socksreq, gss_recv_token.value, gss_recv_token.length);
00512     gss_release_buffer(&gss_status, &gss_recv_token);
00513   }
00514 
00515   infof(data, "SOCKS5 access with%s protection granted.\n",
00516         (socksreq[0]==0)?"out GSS-API data":
00517         ((socksreq[0]==1)?" GSS-API integrity":" GSS-API confidentiality"));
00518 
00519   conn->socks5_gssapi_enctype = socksreq[0];
00520   if(socksreq[0] == 0)
00521     gss_delete_sec_context(&gss_status, &gss_context, NULL);
00522 
00523   return CURLE_OK;
00524 }
00525 
00526 #endif /* HAVE_GSSAPI && !CURL_DISABLE_PROXY */


rc_visard_driver
Author(s): Heiko Hirschmueller , Christian Emmerich , Felix Ruess
autogenerated on Thu Jun 6 2019 20:43:06