krb5_sspi.c
Go to the documentation of this file.
00001 /***************************************************************************
00002  *                                  _   _ ____  _
00003  *  Project                     ___| | | |  _ \| |
00004  *                             / __| | | | |_) | |
00005  *                            | (__| |_| |  _ <| |___
00006  *                             \___|\___/|_| \_\_____|
00007  *
00008  * Copyright (C) 2014 - 2016, Steve Holme, <steve_holme@hotmail.com>.
00009  *
00010  * This software is licensed as described in the file COPYING, which
00011  * you should have received as part of this distribution. The terms
00012  * are also available at https://curl.haxx.se/docs/copyright.html.
00013  *
00014  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
00015  * copies of the Software, and permit persons to whom the Software is
00016  * furnished to do so, under the terms of the COPYING file.
00017  *
00018  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
00019  * KIND, either express or implied.
00020  *
00021  * RFC4752 The Kerberos V5 ("GSSAPI") SASL Mechanism
00022  *
00023  ***************************************************************************/
00024 
00025 #include "curl_setup.h"
00026 
00027 #if defined(USE_WINDOWS_SSPI) && defined(USE_KERBEROS5)
00028 
00029 #include <curl/curl.h>
00030 
00031 #include "vauth/vauth.h"
00032 #include "urldata.h"
00033 #include "curl_base64.h"
00034 #include "warnless.h"
00035 #include "curl_multibyte.h"
00036 #include "sendf.h"
00037 
00038 /* The last #include files should be: */
00039 #include "curl_memory.h"
00040 #include "memdebug.h"
00041 
00042 /*
00043  * Curl_auth_is_gssapi_supported()
00044  *
00045  * This is used to evaluate if GSSAPI (Kerberos V5) is supported.
00046  *
00047  * Parameters: None
00048  *
00049  * Returns TRUE if Kerberos V5 is supported by Windows SSPI.
00050  */
00051 bool Curl_auth_is_gssapi_supported(void)
00052 {
00053   PSecPkgInfo SecurityPackage;
00054   SECURITY_STATUS status;
00055 
00056   /* Query the security package for Kerberos */
00057   status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *)
00058                                               TEXT(SP_NAME_KERBEROS),
00059                                               &SecurityPackage);
00060 
00061   return (status == SEC_E_OK ? TRUE : FALSE);
00062 }
00063 
00064 /*
00065  * Curl_auth_create_gssapi_user_message()
00066  *
00067  * This is used to generate an already encoded GSSAPI (Kerberos V5) user token
00068  * message ready for sending to the recipient.
00069  *
00070  * Parameters:
00071  *
00072  * data        [in]     - The session handle.
00073  * userp       [in]     - The user name in the format User or Domain\User.
00074  * passdwp     [in]     - The user's password.
00075  * service     [in]     - The service type such as http, smtp, pop or imap.
00076  * host        [in]     - The host name.
00077  * mutual_auth [in]     - Flag specifing whether or not mutual authentication
00078  *                        is enabled.
00079  * chlg64      [in]     - The optional base64 encoded challenge message.
00080  * krb5        [in/out] - The Kerberos 5 data struct being used and modified.
00081  * outptr      [in/out] - The address where a pointer to newly allocated memory
00082  *                        holding the result will be stored upon completion.
00083  * outlen      [out]    - The length of the output message.
00084  *
00085  * Returns CURLE_OK on success.
00086  */
00087 CURLcode Curl_auth_create_gssapi_user_message(struct Curl_easy *data,
00088                                               const char *userp,
00089                                               const char *passwdp,
00090                                               const char *service,
00091                                               const char *host,
00092                                               const bool mutual_auth,
00093                                               const char *chlg64,
00094                                               struct kerberos5data *krb5,
00095                                               char **outptr, size_t *outlen)
00096 {
00097   CURLcode result = CURLE_OK;
00098   size_t chlglen = 0;
00099   unsigned char *chlg = NULL;
00100   CtxtHandle context;
00101   PSecPkgInfo SecurityPackage;
00102   SecBuffer chlg_buf;
00103   SecBuffer resp_buf;
00104   SecBufferDesc chlg_desc;
00105   SecBufferDesc resp_desc;
00106   SECURITY_STATUS status;
00107   unsigned long attrs;
00108   TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */
00109 
00110   if(!krb5->spn) {
00111     /* Generate our SPN */
00112     krb5->spn = Curl_auth_build_spn(service, host, NULL);
00113     if(!krb5->spn)
00114       return CURLE_OUT_OF_MEMORY;
00115   }
00116 
00117   if(!krb5->output_token) {
00118     /* Query the security package for Kerberos */
00119     status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *)
00120                                                 TEXT(SP_NAME_KERBEROS),
00121                                                 &SecurityPackage);
00122     if(status != SEC_E_OK) {
00123       return CURLE_NOT_BUILT_IN;
00124     }
00125 
00126     krb5->token_max = SecurityPackage->cbMaxToken;
00127 
00128     /* Release the package buffer as it is not required anymore */
00129     s_pSecFn->FreeContextBuffer(SecurityPackage);
00130 
00131     /* Allocate our response buffer */
00132     krb5->output_token = malloc(krb5->token_max);
00133     if(!krb5->output_token)
00134       return CURLE_OUT_OF_MEMORY;
00135   }
00136 
00137   if(!krb5->credentials) {
00138     /* Do we have credientials to use or are we using single sign-on? */
00139     if(userp && *userp) {
00140       /* Populate our identity structure */
00141       result = Curl_create_sspi_identity(userp, passwdp, &krb5->identity);
00142       if(result)
00143         return result;
00144 
00145       /* Allow proper cleanup of the identity structure */
00146       krb5->p_identity = &krb5->identity;
00147     }
00148     else
00149       /* Use the current Windows user */
00150       krb5->p_identity = NULL;
00151 
00152     /* Allocate our credentials handle */
00153     krb5->credentials = malloc(sizeof(CredHandle));
00154     if(!krb5->credentials)
00155       return CURLE_OUT_OF_MEMORY;
00156 
00157     memset(krb5->credentials, 0, sizeof(CredHandle));
00158 
00159     /* Acquire our credentials handle */
00160     status = s_pSecFn->AcquireCredentialsHandle(NULL,
00161                                                 (TCHAR *)
00162                                                 TEXT(SP_NAME_KERBEROS),
00163                                                 SECPKG_CRED_OUTBOUND, NULL,
00164                                                 krb5->p_identity, NULL, NULL,
00165                                                 krb5->credentials, &expiry);
00166     if(status != SEC_E_OK)
00167       return CURLE_LOGIN_DENIED;
00168 
00169     /* Allocate our new context handle */
00170     krb5->context = malloc(sizeof(CtxtHandle));
00171     if(!krb5->context)
00172       return CURLE_OUT_OF_MEMORY;
00173 
00174     memset(krb5->context, 0, sizeof(CtxtHandle));
00175   }
00176 
00177   if(chlg64 && *chlg64) {
00178     /* Decode the base-64 encoded challenge message */
00179     if(*chlg64 != '=') {
00180       result = Curl_base64_decode(chlg64, &chlg, &chlglen);
00181       if(result)
00182         return result;
00183     }
00184 
00185     /* Ensure we have a valid challenge message */
00186     if(!chlg) {
00187       infof(data, "GSSAPI handshake failure (empty challenge message)\n");
00188 
00189       return CURLE_BAD_CONTENT_ENCODING;
00190     }
00191 
00192     /* Setup the challenge "input" security buffer */
00193     chlg_desc.ulVersion = SECBUFFER_VERSION;
00194     chlg_desc.cBuffers  = 1;
00195     chlg_desc.pBuffers  = &chlg_buf;
00196     chlg_buf.BufferType = SECBUFFER_TOKEN;
00197     chlg_buf.pvBuffer   = chlg;
00198     chlg_buf.cbBuffer   = curlx_uztoul(chlglen);
00199   }
00200 
00201   /* Setup the response "output" security buffer */
00202   resp_desc.ulVersion = SECBUFFER_VERSION;
00203   resp_desc.cBuffers  = 1;
00204   resp_desc.pBuffers  = &resp_buf;
00205   resp_buf.BufferType = SECBUFFER_TOKEN;
00206   resp_buf.pvBuffer   = krb5->output_token;
00207   resp_buf.cbBuffer   = curlx_uztoul(krb5->token_max);
00208 
00209   /* Generate our challenge-response message */
00210   status = s_pSecFn->InitializeSecurityContext(krb5->credentials,
00211                                                chlg ? krb5->context : NULL,
00212                                                krb5->spn,
00213                                                (mutual_auth ?
00214                                                 ISC_REQ_MUTUAL_AUTH : 0),
00215                                                0, SECURITY_NATIVE_DREP,
00216                                                chlg ? &chlg_desc : NULL, 0,
00217                                                &context,
00218                                                &resp_desc, &attrs,
00219                                                &expiry);
00220 
00221   /* Free the decoded challenge as it is not required anymore */
00222   free(chlg);
00223 
00224   if(status != SEC_E_OK && status != SEC_I_CONTINUE_NEEDED) {
00225     return CURLE_RECV_ERROR;
00226   }
00227 
00228   if(memcmp(&context, krb5->context, sizeof(context))) {
00229     s_pSecFn->DeleteSecurityContext(krb5->context);
00230 
00231     memcpy(krb5->context, &context, sizeof(context));
00232   }
00233 
00234   if(resp_buf.cbBuffer) {
00235     /* Base64 encode the response */
00236     result = Curl_base64_encode(data, (char *) resp_buf.pvBuffer,
00237                                 resp_buf.cbBuffer, outptr, outlen);
00238   }
00239   else if(mutual_auth) {
00240     *outptr = strdup("");
00241     if(!*outptr)
00242       result = CURLE_OUT_OF_MEMORY;
00243   }
00244 
00245   return result;
00246 }
00247 
00248 /*
00249  * Curl_auth_create_gssapi_security_message()
00250  *
00251  * This is used to generate an already encoded GSSAPI (Kerberos V5) security
00252  * token message ready for sending to the recipient.
00253  *
00254  * Parameters:
00255  *
00256  * data    [in]     - The session handle.
00257  * chlg64  [in]     - The optional base64 encoded challenge message.
00258  * krb5    [in/out] - The Kerberos 5 data struct being used and modified.
00259  * outptr  [in/out] - The address where a pointer to newly allocated memory
00260  *                    holding the result will be stored upon completion.
00261  * outlen  [out]    - The length of the output message.
00262  *
00263  * Returns CURLE_OK on success.
00264  */
00265 CURLcode Curl_auth_create_gssapi_security_message(struct Curl_easy *data,
00266                                                   const char *chlg64,
00267                                                   struct kerberos5data *krb5,
00268                                                   char **outptr,
00269                                                   size_t *outlen)
00270 {
00271   CURLcode result = CURLE_OK;
00272   size_t offset = 0;
00273   size_t chlglen = 0;
00274   size_t messagelen = 0;
00275   size_t appdatalen = 0;
00276   unsigned char *chlg = NULL;
00277   unsigned char *trailer = NULL;
00278   unsigned char *message = NULL;
00279   unsigned char *padding = NULL;
00280   unsigned char *appdata = NULL;
00281   SecBuffer input_buf[2];
00282   SecBuffer wrap_buf[3];
00283   SecBufferDesc input_desc;
00284   SecBufferDesc wrap_desc;
00285   unsigned long indata = 0;
00286   unsigned long outdata = 0;
00287   unsigned long qop = 0;
00288   unsigned long sec_layer = 0;
00289   unsigned long max_size = 0;
00290   SecPkgContext_Sizes sizes;
00291   SecPkgCredentials_Names names;
00292   SECURITY_STATUS status;
00293   char *user_name;
00294 
00295   /* Decode the base-64 encoded input message */
00296   if(strlen(chlg64) && *chlg64 != '=') {
00297     result = Curl_base64_decode(chlg64, &chlg, &chlglen);
00298     if(result)
00299       return result;
00300   }
00301 
00302   /* Ensure we have a valid challenge message */
00303   if(!chlg) {
00304     infof(data, "GSSAPI handshake failure (empty security message)\n");
00305 
00306     return CURLE_BAD_CONTENT_ENCODING;
00307   }
00308 
00309   /* Get our response size information */
00310   status = s_pSecFn->QueryContextAttributes(krb5->context,
00311                                             SECPKG_ATTR_SIZES,
00312                                             &sizes);
00313   if(status != SEC_E_OK) {
00314     free(chlg);
00315 
00316     return CURLE_OUT_OF_MEMORY;
00317   }
00318 
00319   /* Get the fully qualified username back from the context */
00320   status = s_pSecFn->QueryCredentialsAttributes(krb5->credentials,
00321                                                 SECPKG_CRED_ATTR_NAMES,
00322                                                 &names);
00323   if(status != SEC_E_OK) {
00324     free(chlg);
00325 
00326     return CURLE_RECV_ERROR;
00327   }
00328 
00329   /* Setup the "input" security buffer */
00330   input_desc.ulVersion = SECBUFFER_VERSION;
00331   input_desc.cBuffers = 2;
00332   input_desc.pBuffers = input_buf;
00333   input_buf[0].BufferType = SECBUFFER_STREAM;
00334   input_buf[0].pvBuffer = chlg;
00335   input_buf[0].cbBuffer = curlx_uztoul(chlglen);
00336   input_buf[1].BufferType = SECBUFFER_DATA;
00337   input_buf[1].pvBuffer = NULL;
00338   input_buf[1].cbBuffer = 0;
00339 
00340   /* Decrypt the inbound challenge and obtain the qop */
00341   status = s_pSecFn->DecryptMessage(krb5->context, &input_desc, 0, &qop);
00342   if(status != SEC_E_OK) {
00343     infof(data, "GSSAPI handshake failure (empty security message)\n");
00344 
00345     free(chlg);
00346 
00347     return CURLE_BAD_CONTENT_ENCODING;
00348   }
00349 
00350   /* Not 4 octets long so fail as per RFC4752 Section 3.1 */
00351   if(input_buf[1].cbBuffer != 4) {
00352     infof(data, "GSSAPI handshake failure (invalid security data)\n");
00353 
00354     free(chlg);
00355 
00356     return CURLE_BAD_CONTENT_ENCODING;
00357   }
00358 
00359   /* Copy the data out and free the challenge as it is not required anymore */
00360   memcpy(&indata, input_buf[1].pvBuffer, 4);
00361   s_pSecFn->FreeContextBuffer(input_buf[1].pvBuffer);
00362   free(chlg);
00363 
00364   /* Extract the security layer */
00365   sec_layer = indata & 0x000000FF;
00366   if(!(sec_layer & KERB_WRAP_NO_ENCRYPT)) {
00367     infof(data, "GSSAPI handshake failure (invalid security layer)\n");
00368 
00369     return CURLE_BAD_CONTENT_ENCODING;
00370   }
00371 
00372   /* Extract the maximum message size the server can receive */
00373   max_size = ntohl(indata & 0xFFFFFF00);
00374   if(max_size > 0) {
00375     /* The server has told us it supports a maximum receive buffer, however, as
00376        we don't require one unless we are encrypting data, we tell the server
00377        our receive buffer is zero. */
00378     max_size = 0;
00379   }
00380 
00381   /* Allocate the trailer */
00382   trailer = malloc(sizes.cbSecurityTrailer);
00383   if(!trailer)
00384     return CURLE_OUT_OF_MEMORY;
00385 
00386   /* Convert the user name to UTF8 when operating with Unicode */
00387   user_name = Curl_convert_tchar_to_UTF8(names.sUserName);
00388   if(!user_name) {
00389     free(trailer);
00390 
00391     return CURLE_OUT_OF_MEMORY;
00392   }
00393 
00394   /* Allocate our message */
00395   messagelen = sizeof(outdata) + strlen(user_name) + 1;
00396   message = malloc(messagelen);
00397   if(!message) {
00398     free(trailer);
00399     Curl_unicodefree(user_name);
00400 
00401     return CURLE_OUT_OF_MEMORY;
00402   }
00403 
00404   /* Populate the message with the security layer, client supported receive
00405      message size and authorization identity including the 0x00 based
00406      terminator. Note: Despite RFC4752 Section 3.1 stating "The authorization
00407      identity is not terminated with the zero-valued (%x00) octet." it seems
00408      necessary to include it. */
00409   outdata = htonl(max_size) | sec_layer;
00410   memcpy(message, &outdata, sizeof(outdata));
00411   strcpy((char *) message + sizeof(outdata), user_name);
00412   Curl_unicodefree(user_name);
00413 
00414   /* Allocate the padding */
00415   padding = malloc(sizes.cbBlockSize);
00416   if(!padding) {
00417     free(message);
00418     free(trailer);
00419 
00420     return CURLE_OUT_OF_MEMORY;
00421   }
00422 
00423   /* Setup the "authentication data" security buffer */
00424   wrap_desc.ulVersion    = SECBUFFER_VERSION;
00425   wrap_desc.cBuffers     = 3;
00426   wrap_desc.pBuffers     = wrap_buf;
00427   wrap_buf[0].BufferType = SECBUFFER_TOKEN;
00428   wrap_buf[0].pvBuffer   = trailer;
00429   wrap_buf[0].cbBuffer   = sizes.cbSecurityTrailer;
00430   wrap_buf[1].BufferType = SECBUFFER_DATA;
00431   wrap_buf[1].pvBuffer   = message;
00432   wrap_buf[1].cbBuffer   = curlx_uztoul(messagelen);
00433   wrap_buf[2].BufferType = SECBUFFER_PADDING;
00434   wrap_buf[2].pvBuffer   = padding;
00435   wrap_buf[2].cbBuffer   = sizes.cbBlockSize;
00436 
00437   /* Encrypt the data */
00438   status = s_pSecFn->EncryptMessage(krb5->context, KERB_WRAP_NO_ENCRYPT,
00439                                     &wrap_desc, 0);
00440   if(status != SEC_E_OK) {
00441     free(padding);
00442     free(message);
00443     free(trailer);
00444 
00445     return CURLE_OUT_OF_MEMORY;
00446   }
00447 
00448   /* Allocate the encryption (wrap) buffer */
00449   appdatalen = wrap_buf[0].cbBuffer + wrap_buf[1].cbBuffer +
00450                wrap_buf[2].cbBuffer;
00451   appdata = malloc(appdatalen);
00452   if(!appdata) {
00453     free(padding);
00454     free(message);
00455     free(trailer);
00456 
00457     return CURLE_OUT_OF_MEMORY;
00458   }
00459 
00460   /* Populate the encryption buffer */
00461   memcpy(appdata, wrap_buf[0].pvBuffer, wrap_buf[0].cbBuffer);
00462   offset += wrap_buf[0].cbBuffer;
00463   memcpy(appdata + offset, wrap_buf[1].pvBuffer, wrap_buf[1].cbBuffer);
00464   offset += wrap_buf[1].cbBuffer;
00465   memcpy(appdata + offset, wrap_buf[2].pvBuffer, wrap_buf[2].cbBuffer);
00466 
00467   /* Base64 encode the response */
00468   result = Curl_base64_encode(data, (char *) appdata, appdatalen, outptr,
00469                               outlen);
00470 
00471   /* Free all of our local buffers */
00472   free(appdata);
00473   free(padding);
00474   free(message);
00475   free(trailer);
00476 
00477   return result;
00478 }
00479 
00480 /*
00481  * Curl_auth_gssapi_cleanup()
00482  *
00483  * This is used to clean up the GSSAPI (Kerberos V5) specific data.
00484  *
00485  * Parameters:
00486  *
00487  * krb5     [in/out] - The Kerberos 5 data struct being cleaned up.
00488  *
00489  */
00490 void Curl_auth_gssapi_cleanup(struct kerberos5data *krb5)
00491 {
00492   /* Free our security context */
00493   if(krb5->context) {
00494     s_pSecFn->DeleteSecurityContext(krb5->context);
00495     free(krb5->context);
00496     krb5->context = NULL;
00497   }
00498 
00499   /* Free our credentials handle */
00500   if(krb5->credentials) {
00501     s_pSecFn->FreeCredentialsHandle(krb5->credentials);
00502     free(krb5->credentials);
00503     krb5->credentials = NULL;
00504   }
00505 
00506   /* Free our identity */
00507   Curl_sspi_free_identity(krb5->p_identity);
00508   krb5->p_identity = NULL;
00509 
00510   /* Free the SPN and output token */
00511   Curl_safefree(krb5->spn);
00512   Curl_safefree(krb5->output_token);
00513 
00514   /* Reset any variables */
00515   krb5->token_max = 0;
00516 }
00517 
00518 #endif /* USE_WINDOWS_SSPI && USE_KERBEROS5*/


rc_visard_driver
Author(s): Heiko Hirschmueller , Christian Emmerich , Felix Ruess
autogenerated on Thu Jun 6 2019 20:43:05