quickjs-tart

quickjs-based runtime for wallet-core logic
Log | Files | Refs | README | LICENSE

curl_gssapi.c (12412B)


      1 /***************************************************************************
      2  *                                  _   _ ____  _
      3  *  Project                     ___| | | |  _ \| |
      4  *                             / __| | | | |_) | |
      5  *                            | (__| |_| |  _ <| |___
      6  *                             \___|\___/|_| \_\_____|
      7  *
      8  * Copyright (C) Daniel Stenberg, <daniel@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.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  * SPDX-License-Identifier: curl
     22  *
     23  ***************************************************************************/
     24 
     25 #include "curl_setup.h"
     26 
     27 #ifdef HAVE_GSSAPI
     28 
     29 #include "curl_gssapi.h"
     30 #include "sendf.h"
     31 
     32 /* The last 3 #include files should be in this order */
     33 #include "curl_printf.h"
     34 #include "curl_memory.h"
     35 #include "memdebug.h"
     36 
     37 #if defined(__GNUC__)
     38 #define CURL_ALIGN8  __attribute__((aligned(8)))
     39 #else
     40 #define CURL_ALIGN8
     41 #endif
     42 
     43 #if defined(__GNUC__) && defined(__APPLE__)
     44 #pragma GCC diagnostic push
     45 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
     46 #endif
     47 
     48 gss_OID_desc Curl_spnego_mech_oid CURL_ALIGN8 = {
     49   6, CURL_UNCONST("\x2b\x06\x01\x05\x05\x02")
     50 };
     51 gss_OID_desc Curl_krb5_mech_oid CURL_ALIGN8 = {
     52   9, CURL_UNCONST("\x2a\x86\x48\x86\xf7\x12\x01\x02\x02")
     53 };
     54 
     55 #ifdef DEBUGBUILD
     56 enum min_err_code {
     57   STUB_GSS_OK = 0,
     58   STUB_GSS_NO_MEMORY,
     59   STUB_GSS_INVALID_ARGS,
     60   STUB_GSS_INVALID_CREDS,
     61   STUB_GSS_INVALID_CTX,
     62   STUB_GSS_SERVER_ERR,
     63   STUB_GSS_NO_MECH,
     64   STUB_GSS_LAST
     65 };
     66 
     67 /* libcurl is also passing this struct to these functions, which are not yet
     68  * stubbed:
     69  *   gss_inquire_context()
     70  *   gss_unwrap()
     71  *   gss_wrap()
     72  */
     73 struct stub_gss_ctx_id_t_desc {
     74   enum { STUB_GSS_NONE, STUB_GSS_KRB5, STUB_GSS_NTLM1, STUB_GSS_NTLM3 } sent;
     75   int have_krb5;
     76   int have_ntlm;
     77   OM_uint32 flags;
     78   char creds[250];
     79 };
     80 
     81 static OM_uint32
     82 stub_gss_init_sec_context(OM_uint32 *min,
     83                           gss_cred_id_t initiator_cred_handle,
     84                           struct stub_gss_ctx_id_t_desc **context,
     85                           gss_name_t target_name,
     86                           const gss_OID mech_type,
     87                           OM_uint32 req_flags,
     88                           OM_uint32 time_req,
     89                           const gss_channel_bindings_t input_chan_bindings,
     90                           gss_buffer_desc *input_token,
     91                           gss_OID *actual_mech_type,
     92                           gss_buffer_desc *output_token,
     93                           OM_uint32 *ret_flags,
     94                           OM_uint32 *time_rec)
     95 {
     96   struct stub_gss_ctx_id_t_desc *ctx = NULL;
     97 
     98   /* The token will be encoded in base64 */
     99   size_t length = sizeof(ctx->creds) * 3 / 4;
    100   size_t used = 0;
    101   char *token = NULL;
    102   const char *creds = NULL;
    103 
    104   (void)initiator_cred_handle;
    105   (void)mech_type;
    106   (void)time_req;
    107   (void)input_chan_bindings;
    108   (void)actual_mech_type;
    109 
    110   if(!min)
    111     return GSS_S_FAILURE;
    112 
    113   *min = 0;
    114 
    115   if(!context || !target_name || !output_token) {
    116     *min = STUB_GSS_INVALID_ARGS;
    117     return GSS_S_FAILURE;
    118   }
    119 
    120   creds = getenv("CURL_STUB_GSS_CREDS");
    121   if(!creds || strlen(creds) >= sizeof(ctx->creds)) {
    122     *min = STUB_GSS_INVALID_CREDS;
    123     return GSS_S_FAILURE;
    124   }
    125 
    126   ctx = *context;
    127   if(ctx && strcmp(ctx->creds, creds)) {
    128     *min = STUB_GSS_INVALID_CREDS;
    129     return GSS_S_FAILURE;
    130   }
    131 
    132   output_token->length = 0;
    133   output_token->value = NULL;
    134 
    135   if(input_token && input_token->length) {
    136     if(!ctx) {
    137       *min = STUB_GSS_INVALID_CTX;
    138       return GSS_S_FAILURE;
    139     }
    140 
    141     /* Server response, either D (RA==) or C (Qw==) */
    142     if(((char *) input_token->value)[0] == 'D') {
    143       /* Done */
    144       switch(ctx->sent) {
    145       case STUB_GSS_KRB5:
    146       case STUB_GSS_NTLM3:
    147         if(ret_flags)
    148           *ret_flags = ctx->flags;
    149         if(time_rec)
    150           *time_rec = GSS_C_INDEFINITE;
    151         return GSS_S_COMPLETE;
    152       default:
    153         *min = STUB_GSS_SERVER_ERR;
    154         return GSS_S_FAILURE;
    155       }
    156     }
    157 
    158     if(((char *) input_token->value)[0] != 'C') {
    159       /* We only support Done or Continue */
    160       *min = STUB_GSS_SERVER_ERR;
    161       return GSS_S_FAILURE;
    162     }
    163 
    164     /* Continue */
    165     switch(ctx->sent) {
    166     case STUB_GSS_KRB5:
    167       /* We sent KRB5 and it failed, let's try NTLM */
    168       if(ctx->have_ntlm) {
    169         ctx->sent = STUB_GSS_NTLM1;
    170         break;
    171       }
    172       else {
    173         *min = STUB_GSS_SERVER_ERR;
    174         return GSS_S_FAILURE;
    175       }
    176     case STUB_GSS_NTLM1:
    177       ctx->sent = STUB_GSS_NTLM3;
    178       break;
    179     default:
    180       *min = STUB_GSS_SERVER_ERR;
    181       return GSS_S_FAILURE;
    182     }
    183   }
    184   else {
    185     if(ctx) {
    186       *min = STUB_GSS_INVALID_CTX;
    187       return GSS_S_FAILURE;
    188     }
    189 
    190     ctx = calloc(1, sizeof(*ctx));
    191     if(!ctx) {
    192       *min = STUB_GSS_NO_MEMORY;
    193       return GSS_S_FAILURE;
    194     }
    195 
    196     if(strstr(creds, "KRB5"))
    197       ctx->have_krb5 = 1;
    198 
    199     if(strstr(creds, "NTLM"))
    200       ctx->have_ntlm = 1;
    201 
    202     if(ctx->have_krb5)
    203       ctx->sent = STUB_GSS_KRB5;
    204     else if(ctx->have_ntlm)
    205       ctx->sent = STUB_GSS_NTLM1;
    206     else {
    207       free(ctx);
    208       *min = STUB_GSS_NO_MECH;
    209       return GSS_S_FAILURE;
    210     }
    211 
    212     strcpy(ctx->creds, creds);
    213     ctx->flags = req_flags;
    214   }
    215 
    216   /* To avoid memdebug macro replacement, wrap the name in parentheses to call
    217      the original version. It is freed via the GSS API gss_release_buffer(). */
    218   token = (malloc)(length);
    219   if(!token) {
    220     free(ctx);
    221     *min = STUB_GSS_NO_MEMORY;
    222     return GSS_S_FAILURE;
    223   }
    224 
    225   {
    226     gss_buffer_desc target_desc;
    227     gss_OID name_type = GSS_C_NO_OID;
    228     OM_uint32 minor_status;
    229     OM_uint32 major_status;
    230     major_status = gss_display_name(&minor_status, target_name,
    231                                     &target_desc, &name_type);
    232     if(GSS_ERROR(major_status)) {
    233       (free)(token);
    234       free(ctx);
    235       *min = STUB_GSS_NO_MEMORY;
    236       return GSS_S_FAILURE;
    237     }
    238 
    239     if(strlen(creds) + target_desc.length + 5 >= sizeof(ctx->creds)) {
    240       (free)(token);
    241       free(ctx);
    242       *min = STUB_GSS_NO_MEMORY;
    243       return GSS_S_FAILURE;
    244     }
    245 
    246     /* Token format: creds:target:type:padding */
    247     used = msnprintf(token, length, "%s:%.*s:%d:", creds,
    248                      (int)target_desc.length, (const char *)target_desc.value,
    249                      ctx->sent);
    250 
    251     gss_release_buffer(&minor_status, &target_desc);
    252   }
    253 
    254   if(used >= length) {
    255     (free)(token);
    256     free(ctx);
    257     *min = STUB_GSS_NO_MEMORY;
    258     return GSS_S_FAILURE;
    259   }
    260 
    261   /* Overwrite null-terminator */
    262   memset(token + used, 'A', length - used);
    263 
    264   *context = ctx;
    265 
    266   output_token->value = token;
    267   output_token->length = length;
    268 
    269   return GSS_S_CONTINUE_NEEDED;
    270 }
    271 
    272 static OM_uint32
    273 stub_gss_delete_sec_context(OM_uint32 *min,
    274                             struct stub_gss_ctx_id_t_desc **context,
    275                             gss_buffer_t output_token)
    276 {
    277   (void)output_token;
    278 
    279   if(!min)
    280     return GSS_S_FAILURE;
    281 
    282   if(!context) {
    283     *min = STUB_GSS_INVALID_CTX;
    284     return GSS_S_FAILURE;
    285   }
    286   if(!*context) {
    287     *min = STUB_GSS_INVALID_CTX;
    288     return GSS_S_FAILURE;
    289   }
    290 
    291   free(*context);
    292   *context = NULL;
    293   *min = 0;
    294 
    295   return GSS_S_COMPLETE;
    296 }
    297 #endif /* DEBUGBUILD */
    298 
    299 OM_uint32 Curl_gss_init_sec_context(struct Curl_easy *data,
    300                                     OM_uint32 *minor_status,
    301                                     gss_ctx_id_t *context,
    302                                     gss_name_t target_name,
    303                                     gss_OID mech_type,
    304                                     gss_channel_bindings_t input_chan_bindings,
    305                                     gss_buffer_t input_token,
    306                                     gss_buffer_t output_token,
    307                                     const bool mutual_auth,
    308                                     OM_uint32 *ret_flags)
    309 {
    310   OM_uint32 req_flags = GSS_C_REPLAY_FLAG;
    311 
    312   if(mutual_auth)
    313     req_flags |= GSS_C_MUTUAL_FLAG;
    314 
    315   if(data->set.gssapi_delegation & CURLGSSAPI_DELEGATION_POLICY_FLAG) {
    316 #ifdef GSS_C_DELEG_POLICY_FLAG
    317     req_flags |= GSS_C_DELEG_POLICY_FLAG;
    318 #else
    319     infof(data, "WARNING: support for CURLGSSAPI_DELEGATION_POLICY_FLAG not "
    320           "compiled in");
    321 #endif
    322   }
    323 
    324   if(data->set.gssapi_delegation & CURLGSSAPI_DELEGATION_FLAG)
    325     req_flags |= GSS_C_DELEG_FLAG;
    326 
    327 #ifdef DEBUGBUILD
    328   if(getenv("CURL_STUB_GSS_CREDS"))
    329     return stub_gss_init_sec_context(minor_status,
    330                                      GSS_C_NO_CREDENTIAL, /* cred_handle */
    331                                      (struct stub_gss_ctx_id_t_desc **)context,
    332                                      target_name,
    333                                      mech_type,
    334                                      req_flags,
    335                                      0, /* time_req */
    336                                      input_chan_bindings,
    337                                      input_token,
    338                                      NULL, /* actual_mech_type */
    339                                      output_token,
    340                                      ret_flags,
    341                                      NULL /* time_rec */);
    342 #endif /* DEBUGBUILD */
    343 
    344   return gss_init_sec_context(minor_status,
    345                               GSS_C_NO_CREDENTIAL, /* cred_handle */
    346                               context,
    347                               target_name,
    348                               mech_type,
    349                               req_flags,
    350                               0, /* time_req */
    351                               input_chan_bindings,
    352                               input_token,
    353                               NULL, /* actual_mech_type */
    354                               output_token,
    355                               ret_flags,
    356                               NULL /* time_rec */);
    357 }
    358 
    359 OM_uint32 Curl_gss_delete_sec_context(OM_uint32 *min,
    360                                       gss_ctx_id_t *context,
    361                                       gss_buffer_t output_token)
    362 {
    363 #ifdef DEBUGBUILD
    364   if(getenv("CURL_STUB_GSS_CREDS"))
    365     return stub_gss_delete_sec_context(min,
    366                                      (struct stub_gss_ctx_id_t_desc **)context,
    367                                      output_token);
    368 #endif /* DEBUGBUILD */
    369 
    370   return gss_delete_sec_context(min, context, output_token);
    371 }
    372 
    373 #define GSS_LOG_BUFFER_LEN 1024
    374 static size_t display_gss_error(OM_uint32 status, int type,
    375                                 char *buf, size_t len) {
    376   OM_uint32 maj_stat;
    377   OM_uint32 min_stat;
    378   OM_uint32 msg_ctx = 0;
    379   gss_buffer_desc status_string = GSS_C_EMPTY_BUFFER;
    380 
    381   do {
    382     maj_stat = gss_display_status(&min_stat,
    383                                   status,
    384                                   type,
    385                                   GSS_C_NO_OID,
    386                                   &msg_ctx,
    387                                   &status_string);
    388     if(maj_stat == GSS_S_COMPLETE && status_string.length > 0) {
    389       if(GSS_LOG_BUFFER_LEN > len + status_string.length + 3) {
    390         len += msnprintf(buf + len, GSS_LOG_BUFFER_LEN - len,
    391                          "%.*s. ", (int)status_string.length,
    392                          (char *)status_string.value);
    393       }
    394     }
    395     gss_release_buffer(&min_stat, &status_string);
    396   } while(!GSS_ERROR(maj_stat) && msg_ctx);
    397 
    398   return len;
    399 }
    400 
    401 /*
    402  * Curl_gss_log_error()
    403  *
    404  * This is used to log a GSS-API error status.
    405  *
    406  * Parameters:
    407  *
    408  * data    [in] - The session handle.
    409  * prefix  [in] - The prefix of the log message.
    410  * major   [in] - The major status code.
    411  * minor   [in] - The minor status code.
    412  */
    413 void Curl_gss_log_error(struct Curl_easy *data, const char *prefix,
    414                         OM_uint32 major, OM_uint32 minor)
    415 {
    416   char buf[GSS_LOG_BUFFER_LEN];
    417   size_t len = 0;
    418 
    419   if(major != GSS_S_FAILURE)
    420     len = display_gss_error(major, GSS_C_GSS_CODE, buf, len);
    421 
    422   display_gss_error(minor, GSS_C_MECH_CODE, buf, len);
    423 
    424   infof(data, "%s%s", prefix, buf);
    425 #ifdef CURL_DISABLE_VERBOSE_STRINGS
    426   (void)data;
    427   (void)prefix;
    428 #endif
    429 }
    430 
    431 #if defined(__GNUC__) && defined(__APPLE__)
    432 #pragma GCC diagnostic pop
    433 #endif
    434 
    435 #endif /* HAVE_GSSAPI */