Annotation of embedaddon/curl/lib/vauth/krb5_sspi.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:  *
                     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:  * RFC4752 The Kerberos V5 ("GSSAPI") SASL Mechanism
                     22:  *
                     23:  ***************************************************************************/
                     24: 
                     25: #include "curl_setup.h"
                     26: 
                     27: #if defined(USE_WINDOWS_SSPI) && defined(USE_KERBEROS5)
                     28: 
                     29: #include <curl/curl.h>
                     30: 
                     31: #include "vauth/vauth.h"
                     32: #include "urldata.h"
                     33: #include "curl_base64.h"
                     34: #include "warnless.h"
                     35: #include "curl_multibyte.h"
                     36: #include "sendf.h"
                     37: 
                     38: /* The last #include files should be: */
                     39: #include "curl_memory.h"
                     40: #include "memdebug.h"
                     41: 
                     42: /*
                     43:  * Curl_auth_is_gssapi_supported()
                     44:  *
                     45:  * This is used to evaluate if GSSAPI (Kerberos V5) is supported.
                     46:  *
                     47:  * Parameters: None
                     48:  *
                     49:  * Returns TRUE if Kerberos V5 is supported by Windows SSPI.
                     50:  */
                     51: bool Curl_auth_is_gssapi_supported(void)
                     52: {
                     53:   PSecPkgInfo SecurityPackage;
                     54:   SECURITY_STATUS status;
                     55: 
                     56:   /* Query the security package for Kerberos */
                     57:   status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *)
                     58:                                               TEXT(SP_NAME_KERBEROS),
                     59:                                               &SecurityPackage);
                     60: 
                     61:   /* Release the package buffer as it is not required anymore */
                     62:   if(status == SEC_E_OK) {
                     63:     s_pSecFn->FreeContextBuffer(SecurityPackage);
                     64:   }
                     65: 
                     66:   return (status == SEC_E_OK ? TRUE : FALSE);
                     67: }
                     68: 
                     69: /*
                     70:  * Curl_auth_create_gssapi_user_message()
                     71:  *
                     72:  * This is used to generate an already encoded GSSAPI (Kerberos V5) user token
                     73:  * message ready for sending to the recipient.
                     74:  *
                     75:  * Parameters:
                     76:  *
                     77:  * data        [in]     - The session handle.
                     78:  * userp       [in]     - The user name in the format User or Domain\User.
                     79:  * passwdp     [in]     - The user's password.
                     80:  * service     [in]     - The service type such as http, smtp, pop or imap.
                     81:  * host        [in]     - The host name.
                     82:  * mutual_auth [in]     - Flag specifying whether or not mutual authentication
                     83:  *                        is enabled.
                     84:  * chlg64      [in]     - The optional base64 encoded challenge message.
                     85:  * krb5        [in/out] - The Kerberos 5 data struct being used and modified.
                     86:  * outptr      [in/out] - The address where a pointer to newly allocated memory
                     87:  *                        holding the result will be stored upon completion.
                     88:  * outlen      [out]    - The length of the output message.
                     89:  *
                     90:  * Returns CURLE_OK on success.
                     91:  */
                     92: CURLcode Curl_auth_create_gssapi_user_message(struct Curl_easy *data,
                     93:                                               const char *userp,
                     94:                                               const char *passwdp,
                     95:                                               const char *service,
                     96:                                               const char *host,
                     97:                                               const bool mutual_auth,
                     98:                                               const char *chlg64,
                     99:                                               struct kerberos5data *krb5,
                    100:                                               char **outptr, size_t *outlen)
                    101: {
                    102:   CURLcode result = CURLE_OK;
                    103:   size_t chlglen = 0;
                    104:   unsigned char *chlg = NULL;
                    105:   CtxtHandle context;
                    106:   PSecPkgInfo SecurityPackage;
                    107:   SecBuffer chlg_buf;
                    108:   SecBuffer resp_buf;
                    109:   SecBufferDesc chlg_desc;
                    110:   SecBufferDesc resp_desc;
                    111:   SECURITY_STATUS status;
                    112:   unsigned long attrs;
                    113:   TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */
                    114: 
                    115:   if(!krb5->spn) {
                    116:     /* Generate our SPN */
                    117:     krb5->spn = Curl_auth_build_spn(service, host, NULL);
                    118:     if(!krb5->spn)
                    119:       return CURLE_OUT_OF_MEMORY;
                    120:   }
                    121: 
                    122:   if(!krb5->output_token) {
                    123:     /* Query the security package for Kerberos */
                    124:     status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *)
                    125:                                                 TEXT(SP_NAME_KERBEROS),
                    126:                                                 &SecurityPackage);
                    127:     if(status != SEC_E_OK) {
                    128:       return CURLE_NOT_BUILT_IN;
                    129:     }
                    130: 
                    131:     krb5->token_max = SecurityPackage->cbMaxToken;
                    132: 
                    133:     /* Release the package buffer as it is not required anymore */
                    134:     s_pSecFn->FreeContextBuffer(SecurityPackage);
                    135: 
                    136:     /* Allocate our response buffer */
                    137:     krb5->output_token = malloc(krb5->token_max);
                    138:     if(!krb5->output_token)
                    139:       return CURLE_OUT_OF_MEMORY;
                    140:   }
                    141: 
                    142:   if(!krb5->credentials) {
                    143:     /* Do we have credentials to use or are we using single sign-on? */
                    144:     if(userp && *userp) {
                    145:       /* Populate our identity structure */
                    146:       result = Curl_create_sspi_identity(userp, passwdp, &krb5->identity);
                    147:       if(result)
                    148:         return result;
                    149: 
                    150:       /* Allow proper cleanup of the identity structure */
                    151:       krb5->p_identity = &krb5->identity;
                    152:     }
                    153:     else
                    154:       /* Use the current Windows user */
                    155:       krb5->p_identity = NULL;
                    156: 
                    157:     /* Allocate our credentials handle */
                    158:     krb5->credentials = calloc(1, sizeof(CredHandle));
                    159:     if(!krb5->credentials)
                    160:       return CURLE_OUT_OF_MEMORY;
                    161: 
                    162:     /* Acquire our credentials handle */
                    163:     status = s_pSecFn->AcquireCredentialsHandle(NULL,
                    164:                                                 (TCHAR *)
                    165:                                                 TEXT(SP_NAME_KERBEROS),
                    166:                                                 SECPKG_CRED_OUTBOUND, NULL,
                    167:                                                 krb5->p_identity, NULL, NULL,
                    168:                                                 krb5->credentials, &expiry);
                    169:     if(status != SEC_E_OK)
                    170:       return CURLE_LOGIN_DENIED;
                    171: 
                    172:     /* Allocate our new context handle */
                    173:     krb5->context = calloc(1, sizeof(CtxtHandle));
                    174:     if(!krb5->context)
                    175:       return CURLE_OUT_OF_MEMORY;
                    176:   }
                    177: 
                    178:   if(chlg64 && *chlg64) {
                    179:     /* Decode the base-64 encoded challenge message */
                    180:     if(*chlg64 != '=') {
                    181:       result = Curl_base64_decode(chlg64, &chlg, &chlglen);
                    182:       if(result)
                    183:         return result;
                    184:     }
                    185: 
                    186:     /* Ensure we have a valid challenge message */
                    187:     if(!chlg) {
                    188:       infof(data, "GSSAPI handshake failure (empty challenge message)\n");
                    189: 
                    190:       return CURLE_BAD_CONTENT_ENCODING;
                    191:     }
                    192: 
                    193:     /* Setup the challenge "input" security buffer */
                    194:     chlg_desc.ulVersion = SECBUFFER_VERSION;
                    195:     chlg_desc.cBuffers  = 1;
                    196:     chlg_desc.pBuffers  = &chlg_buf;
                    197:     chlg_buf.BufferType = SECBUFFER_TOKEN;
                    198:     chlg_buf.pvBuffer   = chlg;
                    199:     chlg_buf.cbBuffer   = curlx_uztoul(chlglen);
                    200:   }
                    201: 
                    202:   /* Setup the response "output" security buffer */
                    203:   resp_desc.ulVersion = SECBUFFER_VERSION;
                    204:   resp_desc.cBuffers  = 1;
                    205:   resp_desc.pBuffers  = &resp_buf;
                    206:   resp_buf.BufferType = SECBUFFER_TOKEN;
                    207:   resp_buf.pvBuffer   = krb5->output_token;
                    208:   resp_buf.cbBuffer   = curlx_uztoul(krb5->token_max);
                    209: 
                    210:   /* Generate our challenge-response message */
                    211:   status = s_pSecFn->InitializeSecurityContext(krb5->credentials,
                    212:                                                chlg ? krb5->context : NULL,
                    213:                                                krb5->spn,
                    214:                                                (mutual_auth ?
                    215:                                                 ISC_REQ_MUTUAL_AUTH : 0),
                    216:                                                0, SECURITY_NATIVE_DREP,
                    217:                                                chlg ? &chlg_desc : NULL, 0,
                    218:                                                &context,
                    219:                                                &resp_desc, &attrs,
                    220:                                                &expiry);
                    221: 
                    222:   /* Free the decoded challenge as it is not required anymore */
                    223:   free(chlg);
                    224: 
                    225:   if(status == SEC_E_INSUFFICIENT_MEMORY) {
                    226:     return CURLE_OUT_OF_MEMORY;
                    227:   }
                    228: 
                    229:   if(status != SEC_E_OK && status != SEC_I_CONTINUE_NEEDED) {
                    230:     return CURLE_AUTH_ERROR;
                    231:   }
                    232: 
                    233:   if(memcmp(&context, krb5->context, sizeof(context))) {
                    234:     s_pSecFn->DeleteSecurityContext(krb5->context);
                    235: 
                    236:     memcpy(krb5->context, &context, sizeof(context));
                    237:   }
                    238: 
                    239:   if(resp_buf.cbBuffer) {
                    240:     /* Base64 encode the response */
                    241:     result = Curl_base64_encode(data, (char *) resp_buf.pvBuffer,
                    242:                                 resp_buf.cbBuffer, outptr, outlen);
                    243:   }
                    244:   else if(mutual_auth) {
                    245:     *outptr = strdup("");
                    246:     if(!*outptr)
                    247:       result = CURLE_OUT_OF_MEMORY;
                    248:   }
                    249: 
                    250:   return result;
                    251: }
                    252: 
                    253: /*
                    254:  * Curl_auth_create_gssapi_security_message()
                    255:  *
                    256:  * This is used to generate an already encoded GSSAPI (Kerberos V5) security
                    257:  * token message ready for sending to the recipient.
                    258:  *
                    259:  * Parameters:
                    260:  *
                    261:  * data    [in]     - The session handle.
                    262:  * chlg64  [in]     - The optional base64 encoded challenge message.
                    263:  * krb5    [in/out] - The Kerberos 5 data struct being used and modified.
                    264:  * outptr  [in/out] - The address where a pointer to newly allocated memory
                    265:  *                    holding the result will be stored upon completion.
                    266:  * outlen  [out]    - The length of the output message.
                    267:  *
                    268:  * Returns CURLE_OK on success.
                    269:  */
                    270: CURLcode Curl_auth_create_gssapi_security_message(struct Curl_easy *data,
                    271:                                                   const char *chlg64,
                    272:                                                   struct kerberos5data *krb5,
                    273:                                                   char **outptr,
                    274:                                                   size_t *outlen)
                    275: {
                    276:   CURLcode result = CURLE_OK;
                    277:   size_t offset = 0;
                    278:   size_t chlglen = 0;
                    279:   size_t messagelen = 0;
                    280:   size_t appdatalen = 0;
                    281:   unsigned char *chlg = NULL;
                    282:   unsigned char *trailer = NULL;
                    283:   unsigned char *message = NULL;
                    284:   unsigned char *padding = NULL;
                    285:   unsigned char *appdata = NULL;
                    286:   SecBuffer input_buf[2];
                    287:   SecBuffer wrap_buf[3];
                    288:   SecBufferDesc input_desc;
                    289:   SecBufferDesc wrap_desc;
                    290:   unsigned long indata = 0;
                    291:   unsigned long outdata = 0;
                    292:   unsigned long qop = 0;
                    293:   unsigned long sec_layer = 0;
                    294:   unsigned long max_size = 0;
                    295:   SecPkgContext_Sizes sizes;
                    296:   SecPkgCredentials_Names names;
                    297:   SECURITY_STATUS status;
                    298:   char *user_name;
                    299: 
                    300:   /* Decode the base-64 encoded input message */
                    301:   if(strlen(chlg64) && *chlg64 != '=') {
                    302:     result = Curl_base64_decode(chlg64, &chlg, &chlglen);
                    303:     if(result)
                    304:       return result;
                    305:   }
                    306: 
                    307:   /* Ensure we have a valid challenge message */
                    308:   if(!chlg) {
                    309:     infof(data, "GSSAPI handshake failure (empty security message)\n");
                    310: 
                    311:     return CURLE_BAD_CONTENT_ENCODING;
                    312:   }
                    313: 
                    314:   /* Get our response size information */
                    315:   status = s_pSecFn->QueryContextAttributes(krb5->context,
                    316:                                             SECPKG_ATTR_SIZES,
                    317:                                             &sizes);
                    318:   if(status != SEC_E_OK) {
                    319:     free(chlg);
                    320: 
                    321:     if(status == SEC_E_INSUFFICIENT_MEMORY)
                    322:       return CURLE_OUT_OF_MEMORY;
                    323: 
                    324:     return CURLE_AUTH_ERROR;
                    325:   }
                    326: 
                    327:   /* Get the fully qualified username back from the context */
                    328:   status = s_pSecFn->QueryCredentialsAttributes(krb5->credentials,
                    329:                                                 SECPKG_CRED_ATTR_NAMES,
                    330:                                                 &names);
                    331:   if(status != SEC_E_OK) {
                    332:     free(chlg);
                    333: 
                    334:     if(status == SEC_E_INSUFFICIENT_MEMORY)
                    335:       return CURLE_OUT_OF_MEMORY;
                    336: 
                    337:     return CURLE_AUTH_ERROR;
                    338:   }
                    339: 
                    340:   /* Setup the "input" security buffer */
                    341:   input_desc.ulVersion = SECBUFFER_VERSION;
                    342:   input_desc.cBuffers = 2;
                    343:   input_desc.pBuffers = input_buf;
                    344:   input_buf[0].BufferType = SECBUFFER_STREAM;
                    345:   input_buf[0].pvBuffer = chlg;
                    346:   input_buf[0].cbBuffer = curlx_uztoul(chlglen);
                    347:   input_buf[1].BufferType = SECBUFFER_DATA;
                    348:   input_buf[1].pvBuffer = NULL;
                    349:   input_buf[1].cbBuffer = 0;
                    350: 
                    351:   /* Decrypt the inbound challenge and obtain the qop */
                    352:   status = s_pSecFn->DecryptMessage(krb5->context, &input_desc, 0, &qop);
                    353:   if(status != SEC_E_OK) {
                    354:     infof(data, "GSSAPI handshake failure (empty security message)\n");
                    355: 
                    356:     free(chlg);
                    357: 
                    358:     return CURLE_BAD_CONTENT_ENCODING;
                    359:   }
                    360: 
                    361:   /* Not 4 octets long so fail as per RFC4752 Section 3.1 */
                    362:   if(input_buf[1].cbBuffer != 4) {
                    363:     infof(data, "GSSAPI handshake failure (invalid security data)\n");
                    364: 
                    365:     free(chlg);
                    366: 
                    367:     return CURLE_BAD_CONTENT_ENCODING;
                    368:   }
                    369: 
                    370:   /* Copy the data out and free the challenge as it is not required anymore */
                    371:   memcpy(&indata, input_buf[1].pvBuffer, 4);
                    372:   s_pSecFn->FreeContextBuffer(input_buf[1].pvBuffer);
                    373:   free(chlg);
                    374: 
                    375:   /* Extract the security layer */
                    376:   sec_layer = indata & 0x000000FF;
                    377:   if(!(sec_layer & KERB_WRAP_NO_ENCRYPT)) {
                    378:     infof(data, "GSSAPI handshake failure (invalid security layer)\n");
                    379: 
                    380:     return CURLE_BAD_CONTENT_ENCODING;
                    381:   }
                    382: 
                    383:   /* Extract the maximum message size the server can receive */
                    384:   max_size = ntohl(indata & 0xFFFFFF00);
                    385:   if(max_size > 0) {
                    386:     /* The server has told us it supports a maximum receive buffer, however, as
                    387:        we don't require one unless we are encrypting data, we tell the server
                    388:        our receive buffer is zero. */
                    389:     max_size = 0;
                    390:   }
                    391: 
                    392:   /* Allocate the trailer */
                    393:   trailer = malloc(sizes.cbSecurityTrailer);
                    394:   if(!trailer)
                    395:     return CURLE_OUT_OF_MEMORY;
                    396: 
                    397:   /* Convert the user name to UTF8 when operating with Unicode */
                    398:   user_name = Curl_convert_tchar_to_UTF8(names.sUserName);
                    399:   if(!user_name) {
                    400:     free(trailer);
                    401: 
                    402:     return CURLE_OUT_OF_MEMORY;
                    403:   }
                    404: 
                    405:   /* Allocate our message */
                    406:   messagelen = sizeof(outdata) + strlen(user_name) + 1;
                    407:   message = malloc(messagelen);
                    408:   if(!message) {
                    409:     free(trailer);
                    410:     Curl_unicodefree(user_name);
                    411: 
                    412:     return CURLE_OUT_OF_MEMORY;
                    413:   }
                    414: 
                    415:   /* Populate the message with the security layer, client supported receive
                    416:      message size and authorization identity including the 0x00 based
                    417:      terminator. Note: Despite RFC4752 Section 3.1 stating "The authorization
                    418:      identity is not terminated with the zero-valued (%x00) octet." it seems
                    419:      necessary to include it. */
                    420:   outdata = htonl(max_size) | sec_layer;
                    421:   memcpy(message, &outdata, sizeof(outdata));
                    422:   strcpy((char *) message + sizeof(outdata), user_name);
                    423:   Curl_unicodefree(user_name);
                    424: 
                    425:   /* Allocate the padding */
                    426:   padding = malloc(sizes.cbBlockSize);
                    427:   if(!padding) {
                    428:     free(message);
                    429:     free(trailer);
                    430: 
                    431:     return CURLE_OUT_OF_MEMORY;
                    432:   }
                    433: 
                    434:   /* Setup the "authentication data" security buffer */
                    435:   wrap_desc.ulVersion    = SECBUFFER_VERSION;
                    436:   wrap_desc.cBuffers     = 3;
                    437:   wrap_desc.pBuffers     = wrap_buf;
                    438:   wrap_buf[0].BufferType = SECBUFFER_TOKEN;
                    439:   wrap_buf[0].pvBuffer   = trailer;
                    440:   wrap_buf[0].cbBuffer   = sizes.cbSecurityTrailer;
                    441:   wrap_buf[1].BufferType = SECBUFFER_DATA;
                    442:   wrap_buf[1].pvBuffer   = message;
                    443:   wrap_buf[1].cbBuffer   = curlx_uztoul(messagelen);
                    444:   wrap_buf[2].BufferType = SECBUFFER_PADDING;
                    445:   wrap_buf[2].pvBuffer   = padding;
                    446:   wrap_buf[2].cbBuffer   = sizes.cbBlockSize;
                    447: 
                    448:   /* Encrypt the data */
                    449:   status = s_pSecFn->EncryptMessage(krb5->context, KERB_WRAP_NO_ENCRYPT,
                    450:                                     &wrap_desc, 0);
                    451:   if(status != SEC_E_OK) {
                    452:     free(padding);
                    453:     free(message);
                    454:     free(trailer);
                    455: 
                    456:     if(status == SEC_E_INSUFFICIENT_MEMORY)
                    457:       return CURLE_OUT_OF_MEMORY;
                    458: 
                    459:     return CURLE_AUTH_ERROR;
                    460:   }
                    461: 
                    462:   /* Allocate the encryption (wrap) buffer */
                    463:   appdatalen = wrap_buf[0].cbBuffer + wrap_buf[1].cbBuffer +
                    464:                wrap_buf[2].cbBuffer;
                    465:   appdata = malloc(appdatalen);
                    466:   if(!appdata) {
                    467:     free(padding);
                    468:     free(message);
                    469:     free(trailer);
                    470: 
                    471:     return CURLE_OUT_OF_MEMORY;
                    472:   }
                    473: 
                    474:   /* Populate the encryption buffer */
                    475:   memcpy(appdata, wrap_buf[0].pvBuffer, wrap_buf[0].cbBuffer);
                    476:   offset += wrap_buf[0].cbBuffer;
                    477:   memcpy(appdata + offset, wrap_buf[1].pvBuffer, wrap_buf[1].cbBuffer);
                    478:   offset += wrap_buf[1].cbBuffer;
                    479:   memcpy(appdata + offset, wrap_buf[2].pvBuffer, wrap_buf[2].cbBuffer);
                    480: 
                    481:   /* Base64 encode the response */
                    482:   result = Curl_base64_encode(data, (char *) appdata, appdatalen, outptr,
                    483:                               outlen);
                    484: 
                    485:   /* Free all of our local buffers */
                    486:   free(appdata);
                    487:   free(padding);
                    488:   free(message);
                    489:   free(trailer);
                    490: 
                    491:   return result;
                    492: }
                    493: 
                    494: /*
                    495:  * Curl_auth_cleanup_gssapi()
                    496:  *
                    497:  * This is used to clean up the GSSAPI (Kerberos V5) specific data.
                    498:  *
                    499:  * Parameters:
                    500:  *
                    501:  * krb5     [in/out] - The Kerberos 5 data struct being cleaned up.
                    502:  *
                    503:  */
                    504: void Curl_auth_cleanup_gssapi(struct kerberos5data *krb5)
                    505: {
                    506:   /* Free our security context */
                    507:   if(krb5->context) {
                    508:     s_pSecFn->DeleteSecurityContext(krb5->context);
                    509:     free(krb5->context);
                    510:     krb5->context = NULL;
                    511:   }
                    512: 
                    513:   /* Free our credentials handle */
                    514:   if(krb5->credentials) {
                    515:     s_pSecFn->FreeCredentialsHandle(krb5->credentials);
                    516:     free(krb5->credentials);
                    517:     krb5->credentials = NULL;
                    518:   }
                    519: 
                    520:   /* Free our identity */
                    521:   Curl_sspi_free_identity(krb5->p_identity);
                    522:   krb5->p_identity = NULL;
                    523: 
                    524:   /* Free the SPN and output token */
                    525:   Curl_safefree(krb5->spn);
                    526:   Curl_safefree(krb5->output_token);
                    527: 
                    528:   /* Reset any variables */
                    529:   krb5->token_max = 0;
                    530: }
                    531: 
                    532: #endif /* USE_WINDOWS_SSPI && USE_KERBEROS5*/

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