Annotation of embedaddon/curl/lib/vauth/krb5_gssapi.c, revision 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>