Annotation of embedaddon/curl/lib/vauth/krb5_sspi.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:  *
        !            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>