Home | History | Annotate | Download | only in vauth
      1 /***************************************************************************
      2  *                                  _   _ ____  _
      3  *  Project                     ___| | | |  _ \| |
      4  *                             / __| | | | |_) | |
      5  *                            | (__| |_| |  _ <| |___
      6  *                             \___|\___/|_| \_\_____|
      7  *
      8  * Copyright (C) 1998 - 2016, Daniel Stenberg, <daniel (at) haxx.se>, et al.
      9  *
     10  * This software is licensed as described in the file COPYING, which
     11  * you should have received as part of this distribution. The terms
     12  * are also available at https://curl.haxx.se/docs/copyright.html.
     13  *
     14  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
     15  * copies of the Software, and permit persons to whom the Software is
     16  * furnished to do so, under the terms of the COPYING file.
     17  *
     18  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
     19  * KIND, either express or implied.
     20  *
     21  * RFC4178 Simple and Protected GSS-API Negotiation Mechanism
     22  *
     23  ***************************************************************************/
     24 
     25 #include "curl_setup.h"
     26 
     27 #if defined(HAVE_GSSAPI) && defined(USE_SPNEGO)
     28 
     29 #include <curl/curl.h>
     30 
     31 #include "vauth/vauth.h"
     32 #include "urldata.h"
     33 #include "curl_base64.h"
     34 #include "curl_gssapi.h"
     35 #include "warnless.h"
     36 #include "curl_multibyte.h"
     37 #include "sendf.h"
     38 
     39 /* The last #include files should be: */
     40 #include "curl_memory.h"
     41 #include "memdebug.h"
     42 
     43 /*
     44  * Curl_auth_decode_spnego_message()
     45  *
     46  * This is used to decode an already encoded SPNEGO (Negotiate) challenge
     47  * message.
     48  *
     49  * Parameters:
     50  *
     51  * data        [in]     - The session handle.
     52  * userp       [in]     - The user name in the format User or Domain\User.
     53  * passdwp     [in]     - The user's password.
     54  * service     [in]     - The service type such as http, smtp, pop or imap.
     55  * host        [in]     - The host name.
     56  * chlg64      [in]     - The optional base64 encoded challenge message.
     57  * nego        [in/out] - The Negotiate data struct being used and modified.
     58  *
     59  * Returns CURLE_OK on success.
     60  */
     61 CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data,
     62                                          const char *user,
     63                                          const char *password,
     64                                          const char *service,
     65                                          const char *host,
     66                                          const char *chlg64,
     67                                          struct negotiatedata *nego)
     68 {
     69   CURLcode result = CURLE_OK;
     70   size_t chlglen = 0;
     71   unsigned char *chlg = NULL;
     72   OM_uint32 major_status;
     73   OM_uint32 minor_status;
     74   OM_uint32 unused_status;
     75   gss_buffer_desc spn_token = GSS_C_EMPTY_BUFFER;
     76   gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
     77   gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
     78 
     79   (void) user;
     80   (void) password;
     81 
     82   if(nego->context && nego->status == GSS_S_COMPLETE) {
     83     /* We finished successfully our part of authentication, but server
     84      * rejected it (since we're again here). Exit with an error since we
     85      * can't invent anything better */
     86     Curl_auth_spnego_cleanup(nego);
     87     return CURLE_LOGIN_DENIED;
     88   }
     89 
     90   if(!nego->spn) {
     91     /* Generate our SPN */
     92     char *spn = Curl_auth_build_spn(service, NULL, host);
     93     if(!spn)
     94       return CURLE_OUT_OF_MEMORY;
     95 
     96     /* Populate the SPN structure */
     97     spn_token.value = spn;
     98     spn_token.length = strlen(spn);
     99 
    100     /* Import the SPN */
    101     major_status = gss_import_name(&minor_status, &spn_token,
    102                                    GSS_C_NT_HOSTBASED_SERVICE,
    103                                    &nego->spn);
    104     if(GSS_ERROR(major_status)) {
    105       Curl_gss_log_error(data, "gss_import_name() failed: ",
    106                          major_status, minor_status);
    107 
    108       free(spn);
    109 
    110       return CURLE_OUT_OF_MEMORY;
    111     }
    112 
    113     free(spn);
    114   }
    115 
    116   if(chlg64 && *chlg64) {
    117     /* Decode the base-64 encoded challenge message */
    118     if(*chlg64 != '=') {
    119       result = Curl_base64_decode(chlg64, &chlg, &chlglen);
    120       if(result)
    121         return result;
    122     }
    123 
    124     /* Ensure we have a valid challenge message */
    125     if(!chlg) {
    126       infof(data, "SPNEGO handshake failure (empty challenge message)\n");
    127 
    128       return CURLE_BAD_CONTENT_ENCODING;
    129     }
    130 
    131     /* Setup the challenge "input" security buffer */
    132     input_token.value = chlg;
    133     input_token.length = chlglen;
    134   }
    135 
    136   /* Generate our challenge-response message */
    137   major_status = Curl_gss_init_sec_context(data,
    138                                            &minor_status,
    139                                            &nego->context,
    140                                            nego->spn,
    141                                            &Curl_spnego_mech_oid,
    142                                            GSS_C_NO_CHANNEL_BINDINGS,
    143                                            &input_token,
    144                                            &output_token,
    145                                            TRUE,
    146                                            NULL);
    147 
    148   /* Free the decoded challenge as it is not required anymore */
    149   Curl_safefree(input_token.value);
    150 
    151   nego->status = major_status;
    152   if(GSS_ERROR(major_status)) {
    153     if(output_token.value)
    154       gss_release_buffer(&unused_status, &output_token);
    155 
    156     Curl_gss_log_error(data, "gss_init_sec_context() failed: ",
    157                        major_status, minor_status);
    158 
    159     return CURLE_OUT_OF_MEMORY;
    160   }
    161 
    162   if(!output_token.value || !output_token.length) {
    163     if(output_token.value)
    164       gss_release_buffer(&unused_status, &output_token);
    165 
    166     return CURLE_OUT_OF_MEMORY;
    167   }
    168 
    169   nego->output_token = output_token;
    170 
    171   return CURLE_OK;
    172 }
    173 
    174 /*
    175  * Curl_auth_create_spnego_message()
    176  *
    177  * This is used to generate an already encoded SPNEGO (Negotiate) response
    178  * message ready for sending to the recipient.
    179  *
    180  * Parameters:
    181  *
    182  * data        [in]     - The session handle.
    183  * nego        [in/out] - The Negotiate data struct being used and modified.
    184  * outptr      [in/out] - The address where a pointer to newly allocated memory
    185  *                        holding the result will be stored upon completion.
    186  * outlen      [out]    - The length of the output message.
    187  *
    188  * Returns CURLE_OK on success.
    189  */
    190 CURLcode Curl_auth_create_spnego_message(struct Curl_easy *data,
    191                                          struct negotiatedata *nego,
    192                                          char **outptr, size_t *outlen)
    193 {
    194   CURLcode result;
    195   OM_uint32 minor_status;
    196 
    197   /* Base64 encode the already generated response */
    198   result = Curl_base64_encode(data,
    199                               nego->output_token.value,
    200                               nego->output_token.length,
    201                               outptr, outlen);
    202 
    203   if(result) {
    204     gss_release_buffer(&minor_status, &nego->output_token);
    205     nego->output_token.value = NULL;
    206     nego->output_token.length = 0;
    207 
    208     return result;
    209   }
    210 
    211   if(!*outptr || !*outlen) {
    212     gss_release_buffer(&minor_status, &nego->output_token);
    213     nego->output_token.value = NULL;
    214     nego->output_token.length = 0;
    215 
    216     return CURLE_REMOTE_ACCESS_DENIED;
    217   }
    218 
    219   return CURLE_OK;
    220 }
    221 
    222 /*
    223  * Curl_auth_spnego_cleanup()
    224  *
    225  * This is used to clean up the SPNEGO (Negotiate) specific data.
    226  *
    227  * Parameters:
    228  *
    229  * nego     [in/out] - The Negotiate data struct being cleaned up.
    230  *
    231  */
    232 void Curl_auth_spnego_cleanup(struct negotiatedata *nego)
    233 {
    234   OM_uint32 minor_status;
    235 
    236   /* Free our security context */
    237   if(nego->context != GSS_C_NO_CONTEXT) {
    238     gss_delete_sec_context(&minor_status, &nego->context, GSS_C_NO_BUFFER);
    239     nego->context = GSS_C_NO_CONTEXT;
    240   }
    241 
    242   /* Free the output token */
    243   if(nego->output_token.value) {
    244     gss_release_buffer(&minor_status, &nego->output_token);
    245     nego->output_token.value = NULL;
    246     nego->output_token.length = 0;
    247 
    248   }
    249 
    250   /* Free the SPN */
    251   if(nego->spn != GSS_C_NO_NAME) {
    252     gss_release_name(&minor_status, &nego->spn);
    253     nego->spn = GSS_C_NO_NAME;
    254   }
    255 
    256   /* Reset any variables */
    257   nego->status = 0;
    258 }
    259 
    260 #endif /* HAVE_GSSAPI && USE_SPNEGO */
    261