Annotation of embedaddon/curl/lib/vauth/krb5_gssapi.c, revision 1.1.1.1

1.1       misho       1: /***************************************************************************
                      2:  *                                  _   _ ____  _
                      3:  *  Project                     ___| | | |  _ \| |
                      4:  *                             / __| | | | |_) | |
                      5:  *                            | (__| |_| |  _ <| |___
                      6:  *                             \___|\___/|_| \_\_____|
                      7:  *
                      8:  * Copyright (C) 2014 - 2019, Steve Holme, <steve_holme@hotmail.com>.
                      9:  * Copyright (C) 2015, Daniel Stenberg, <daniel@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 https://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:  * RFC4752 The Kerberos V5 ("GSSAPI") SASL Mechanism
                     23:  *
                     24:  ***************************************************************************/
                     25: 
                     26: #include "curl_setup.h"
                     27: 
                     28: #if defined(HAVE_GSSAPI) && defined(USE_KERBEROS5)
                     29: 
                     30: #include <curl/curl.h>
                     31: 
                     32: #include "vauth/vauth.h"
                     33: #include "curl_sasl.h"
                     34: #include "urldata.h"
                     35: #include "curl_base64.h"
                     36: #include "curl_gssapi.h"
                     37: #include "sendf.h"
                     38: #include "curl_printf.h"
                     39: 
                     40: /* The last #include files should be: */
                     41: #include "curl_memory.h"
                     42: #include "memdebug.h"
                     43: 
                     44: /*
                     45:  * Curl_auth_is_gssapi_supported()
                     46:  *
                     47:  * This is used to evaluate if GSSAPI (Kerberos V5) is supported.
                     48:  *
                     49:  * Parameters: None
                     50:  *
                     51:  * Returns TRUE if Kerberos V5 is supported by the GSS-API library.
                     52:  */
                     53: bool Curl_auth_is_gssapi_supported(void)
                     54: {
                     55:   return TRUE;
                     56: }
                     57: 
                     58: /*
                     59:  * Curl_auth_create_gssapi_user_message()
                     60:  *
                     61:  * This is used to generate an already encoded GSSAPI (Kerberos V5) user token
                     62:  * message ready for sending to the recipient.
                     63:  *
                     64:  * Parameters:
                     65:  *
                     66:  * data        [in]     - The session handle.
                     67:  * userp       [in]     - The user name.
                     68:  * passwdp     [in]     - The user's password.
                     69:  * service     [in]     - The service type such as http, smtp, pop or imap.
                     70:  * host        [in[     - The host name.
                     71:  * mutual_auth [in]     - Flag specifying whether or not mutual authentication
                     72:  *                        is enabled.
                     73:  * chlg64      [in]     - Pointer to the optional base64 encoded challenge
                     74:  *                        message.
                     75:  * krb5        [in/out] - The Kerberos 5 data struct being used and modified.
                     76:  * outptr      [in/out] - The address where a pointer to newly allocated memory
                     77:  *                        holding the result will be stored upon completion.
                     78:  * outlen      [out]    - The length of the output message.
                     79:  *
                     80:  * Returns CURLE_OK on success.
                     81:  */
                     82: CURLcode Curl_auth_create_gssapi_user_message(struct Curl_easy *data,
                     83:                                               const char *userp,
                     84:                                               const char *passwdp,
                     85:                                               const char *service,
                     86:                                               const char *host,
                     87:                                               const bool mutual_auth,
                     88:                                               const char *chlg64,
                     89:                                               struct kerberos5data *krb5,
                     90:                                               char **outptr, size_t *outlen)
                     91: {
                     92:   CURLcode result = CURLE_OK;
                     93:   size_t chlglen = 0;
                     94:   unsigned char *chlg = NULL;
                     95:   OM_uint32 major_status;
                     96:   OM_uint32 minor_status;
                     97:   OM_uint32 unused_status;
                     98:   gss_buffer_desc spn_token = GSS_C_EMPTY_BUFFER;
                     99:   gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
                    100:   gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
                    101: 
                    102:   (void) userp;
                    103:   (void) passwdp;
                    104: 
                    105:   if(!krb5->spn) {
                    106:     /* Generate our SPN */
                    107:     char *spn = Curl_auth_build_spn(service, NULL, host);
                    108:     if(!spn)
                    109:       return CURLE_OUT_OF_MEMORY;
                    110: 
                    111:     /* Populate the SPN structure */
                    112:     spn_token.value = spn;
                    113:     spn_token.length = strlen(spn);
                    114: 
                    115:     /* Import the SPN */
                    116:     major_status = gss_import_name(&minor_status, &spn_token,
                    117:                                    GSS_C_NT_HOSTBASED_SERVICE, &krb5->spn);
                    118:     if(GSS_ERROR(major_status)) {
                    119:       Curl_gss_log_error(data, "gss_import_name() failed: ",
                    120:                          major_status, minor_status);
                    121: 
                    122:       free(spn);
                    123: 
                    124:       return CURLE_AUTH_ERROR;
                    125:     }
                    126: 
                    127:     free(spn);
                    128:   }
                    129: 
                    130:   if(chlg64 && *chlg64) {
                    131:     /* Decode the base-64 encoded challenge message */
                    132:     if(*chlg64 != '=') {
                    133:       result = Curl_base64_decode(chlg64, &chlg, &chlglen);
                    134:       if(result)
                    135:         return result;
                    136:     }
                    137: 
                    138:     /* Ensure we have a valid challenge message */
                    139:     if(!chlg) {
                    140:       infof(data, "GSSAPI handshake failure (empty challenge message)\n");
                    141: 
                    142:       return CURLE_BAD_CONTENT_ENCODING;
                    143:     }
                    144: 
                    145:     /* Setup the challenge "input" security buffer */
                    146:     input_token.value = chlg;
                    147:     input_token.length = chlglen;
                    148:   }
                    149: 
                    150:   major_status = Curl_gss_init_sec_context(data,
                    151:                                            &minor_status,
                    152:                                            &krb5->context,
                    153:                                            krb5->spn,
                    154:                                            &Curl_krb5_mech_oid,
                    155:                                            GSS_C_NO_CHANNEL_BINDINGS,
                    156:                                            &input_token,
                    157:                                            &output_token,
                    158:                                            mutual_auth,
                    159:                                            NULL);
                    160: 
                    161:   /* Free the decoded challenge as it is not required anymore */
                    162:   free(input_token.value);
                    163: 
                    164:   if(GSS_ERROR(major_status)) {
                    165:     if(output_token.value)
                    166:       gss_release_buffer(&unused_status, &output_token);
                    167: 
                    168:     Curl_gss_log_error(data, "gss_init_sec_context() failed: ",
                    169:                        major_status, minor_status);
                    170: 
                    171:     return CURLE_AUTH_ERROR;
                    172:   }
                    173: 
                    174:   if(output_token.value && output_token.length) {
                    175:     /* Base64 encode the response */
                    176:     result = Curl_base64_encode(data, (char *) output_token.value,
                    177:                                 output_token.length, outptr, outlen);
                    178: 
                    179:     gss_release_buffer(&unused_status, &output_token);
                    180:   }
                    181:   else if(mutual_auth) {
                    182:     *outptr = strdup("");
                    183:     if(!*outptr)
                    184:       result = CURLE_OUT_OF_MEMORY;
                    185:   }
                    186: 
                    187:   return result;
                    188: }
                    189: 
                    190: /*
                    191:  * Curl_auth_create_gssapi_security_message()
                    192:  *
                    193:  * This is used to generate an already encoded GSSAPI (Kerberos V5) security
                    194:  * token message ready for sending to the recipient.
                    195:  *
                    196:  * Parameters:
                    197:  *
                    198:  * data    [in]     - The session handle.
                    199:  * chlg64  [in]     - Pointer to the optional base64 encoded challenge message.
                    200:  * krb5    [in/out] - The Kerberos 5 data struct being used and modified.
                    201:  * outptr  [in/out] - The address where a pointer to newly allocated memory
                    202:  *                    holding the result will be stored upon completion.
                    203:  * outlen  [out]    - The length of the output message.
                    204:  *
                    205:  * Returns CURLE_OK on success.
                    206:  */
                    207: CURLcode Curl_auth_create_gssapi_security_message(struct Curl_easy *data,
                    208:                                                   const char *chlg64,
                    209:                                                   struct kerberos5data *krb5,
                    210:                                                   char **outptr,
                    211:                                                   size_t *outlen)
                    212: {
                    213:   CURLcode result = CURLE_OK;
                    214:   size_t chlglen = 0;
                    215:   size_t messagelen = 0;
                    216:   unsigned char *chlg = NULL;
                    217:   unsigned char *message = NULL;
                    218:   OM_uint32 major_status;
                    219:   OM_uint32 minor_status;
                    220:   OM_uint32 unused_status;
                    221:   gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
                    222:   gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
                    223:   unsigned int indata = 0;
                    224:   unsigned int outdata = 0;
                    225:   gss_qop_t qop = GSS_C_QOP_DEFAULT;
                    226:   unsigned int sec_layer = 0;
                    227:   unsigned int max_size = 0;
                    228:   gss_name_t username = GSS_C_NO_NAME;
                    229:   gss_buffer_desc username_token;
                    230: 
                    231:   /* Decode the base-64 encoded input message */
                    232:   if(strlen(chlg64) && *chlg64 != '=') {
                    233:     result = Curl_base64_decode(chlg64, &chlg, &chlglen);
                    234:     if(result)
                    235:       return result;
                    236:   }
                    237: 
                    238:   /* Ensure we have a valid challenge message */
                    239:   if(!chlg) {
                    240:     infof(data, "GSSAPI handshake failure (empty security message)\n");
                    241: 
                    242:     return CURLE_BAD_CONTENT_ENCODING;
                    243:   }
                    244: 
                    245:   /* Get the fully qualified username back from the context */
                    246:   major_status = gss_inquire_context(&minor_status, krb5->context,
                    247:                                      &username, NULL, NULL, NULL, NULL,
                    248:                                      NULL, NULL);
                    249:   if(GSS_ERROR(major_status)) {
                    250:     Curl_gss_log_error(data, "gss_inquire_context() failed: ",
                    251:                        major_status, minor_status);
                    252: 
                    253:     free(chlg);
                    254: 
                    255:     return CURLE_AUTH_ERROR;
                    256:   }
                    257: 
                    258:   /* Convert the username from internal format to a displayable token */
                    259:   major_status = gss_display_name(&minor_status, username,
                    260:                                   &username_token, NULL);
                    261:   if(GSS_ERROR(major_status)) {
                    262:     Curl_gss_log_error(data, "gss_display_name() failed: ",
                    263:                        major_status, minor_status);
                    264: 
                    265:     free(chlg);
                    266: 
                    267:     return CURLE_AUTH_ERROR;
                    268:   }
                    269: 
                    270:   /* Setup the challenge "input" security buffer */
                    271:   input_token.value = chlg;
                    272:   input_token.length = chlglen;
                    273: 
                    274:   /* Decrypt the inbound challenge and obtain the qop */
                    275:   major_status = gss_unwrap(&minor_status, krb5->context, &input_token,
                    276:                             &output_token, NULL, &qop);
                    277:   if(GSS_ERROR(major_status)) {
                    278:     Curl_gss_log_error(data, "gss_unwrap() failed: ",
                    279:                        major_status, minor_status);
                    280: 
                    281:     gss_release_buffer(&unused_status, &username_token);
                    282:     free(chlg);
                    283: 
                    284:     return CURLE_BAD_CONTENT_ENCODING;
                    285:   }
                    286: 
                    287:   /* Not 4 octets long so fail as per RFC4752 Section 3.1 */
                    288:   if(output_token.length != 4) {
                    289:     infof(data, "GSSAPI handshake failure (invalid security data)\n");
                    290: 
                    291:     gss_release_buffer(&unused_status, &username_token);
                    292:     free(chlg);
                    293: 
                    294:     return CURLE_BAD_CONTENT_ENCODING;
                    295:   }
                    296: 
                    297:   /* Copy the data out and free the challenge as it is not required anymore */
                    298:   memcpy(&indata, output_token.value, 4);
                    299:   gss_release_buffer(&unused_status, &output_token);
                    300:   free(chlg);
                    301: 
                    302:   /* Extract the security layer */
                    303:   sec_layer = indata & 0x000000FF;
                    304:   if(!(sec_layer & GSSAUTH_P_NONE)) {
                    305:     infof(data, "GSSAPI handshake failure (invalid security layer)\n");
                    306: 
                    307:     gss_release_buffer(&unused_status, &username_token);
                    308: 
                    309:     return CURLE_BAD_CONTENT_ENCODING;
                    310:   }
                    311: 
                    312:   /* Extract the maximum message size the server can receive */
                    313:   max_size = ntohl(indata & 0xFFFFFF00);
                    314:   if(max_size > 0) {
                    315:     /* The server has told us it supports a maximum receive buffer, however, as
                    316:        we don't require one unless we are encrypting data, we tell the server
                    317:        our receive buffer is zero. */
                    318:     max_size = 0;
                    319:   }
                    320: 
                    321:   /* Allocate our message */
                    322:   messagelen = sizeof(outdata) + username_token.length + 1;
                    323:   message = malloc(messagelen);
                    324:   if(!message) {
                    325:     gss_release_buffer(&unused_status, &username_token);
                    326: 
                    327:     return CURLE_OUT_OF_MEMORY;
                    328:   }
                    329: 
                    330:   /* Populate the message with the security layer, client supported receive
                    331:      message size and authorization identity including the 0x00 based
                    332:      terminator. Note: Despite RFC4752 Section 3.1 stating "The authorization
                    333:      identity is not terminated with the zero-valued (%x00) octet." it seems
                    334:      necessary to include it. */
                    335:   outdata = htonl(max_size) | sec_layer;
                    336:   memcpy(message, &outdata, sizeof(outdata));
                    337:   memcpy(message + sizeof(outdata), username_token.value,
                    338:          username_token.length);
                    339:   message[messagelen - 1] = '\0';
                    340: 
                    341:   /* Free the username token as it is not required anymore */
                    342:   gss_release_buffer(&unused_status, &username_token);
                    343: 
                    344:   /* Setup the "authentication data" security buffer */
                    345:   input_token.value = message;
                    346:   input_token.length = messagelen;
                    347: 
                    348:   /* Encrypt the data */
                    349:   major_status = gss_wrap(&minor_status, krb5->context, 0,
                    350:                           GSS_C_QOP_DEFAULT, &input_token, NULL,
                    351:                           &output_token);
                    352:   if(GSS_ERROR(major_status)) {
                    353:     Curl_gss_log_error(data, "gss_wrap() failed: ",
                    354:                        major_status, minor_status);
                    355: 
                    356:     free(message);
                    357: 
                    358:     return CURLE_AUTH_ERROR;
                    359:   }
                    360: 
                    361:   /* Base64 encode the response */
                    362:   result = Curl_base64_encode(data, (char *) output_token.value,
                    363:                               output_token.length, outptr, outlen);
                    364: 
                    365:   /* Free the output buffer */
                    366:   gss_release_buffer(&unused_status, &output_token);
                    367: 
                    368:   /* Free the message buffer */
                    369:   free(message);
                    370: 
                    371:   return result;
                    372: }
                    373: 
                    374: /*
                    375:  * Curl_auth_cleanup_gssapi()
                    376:  *
                    377:  * This is used to clean up the GSSAPI (Kerberos V5) specific data.
                    378:  *
                    379:  * Parameters:
                    380:  *
                    381:  * krb5     [in/out] - The Kerberos 5 data struct being cleaned up.
                    382:  *
                    383:  */
                    384: void Curl_auth_cleanup_gssapi(struct kerberos5data *krb5)
                    385: {
                    386:   OM_uint32 minor_status;
                    387: 
                    388:   /* Free our security context */
                    389:   if(krb5->context != GSS_C_NO_CONTEXT) {
                    390:     gss_delete_sec_context(&minor_status, &krb5->context, GSS_C_NO_BUFFER);
                    391:     krb5->context = GSS_C_NO_CONTEXT;
                    392:   }
                    393: 
                    394:   /* Free the SPN */
                    395:   if(krb5->spn != GSS_C_NO_NAME) {
                    396:     gss_release_name(&minor_status, &krb5->spn);
                    397:     krb5->spn = GSS_C_NO_NAME;
                    398:   }
                    399: }
                    400: 
                    401: #endif /* HAVE_GSSAPI && USE_KERBEROS5 */

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