File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / curl / lib / vauth / spnego_sspi.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Wed Jun 3 10:01:15 2020 UTC (4 years, 1 month ago) by misho
Branches: curl, MAIN
CVS tags: v7_70_0p4, HEAD
curl

    1: /***************************************************************************
    2:  *                                  _   _ ____  _
    3:  *  Project                     ___| | | |  _ \| |
    4:  *                             / __| | | | |_) | |
    5:  *                            | (__| |_| |  _ <| |___
    6:  *                             \___|\___/|_| \_\_____|
    7:  *
    8:  * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
    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:  * RFC4178 Simple and Protected GSS-API Negotiation Mechanism
   22:  *
   23:  ***************************************************************************/
   24: 
   25: #include "curl_setup.h"
   26: 
   27: #if defined(USE_WINDOWS_SSPI) && defined(USE_SPNEGO)
   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: #include "strerror.h"
   38: 
   39: /* The last #include files should be: */
   40: #include "curl_memory.h"
   41: #include "memdebug.h"
   42: 
   43: /*
   44:  * Curl_auth_is_spnego_supported()
   45:  *
   46:  * This is used to evaluate if SPNEGO (Negotiate) is supported.
   47:  *
   48:  * Parameters: None
   49:  *
   50:  * Returns TRUE if Negotiate is supported by Windows SSPI.
   51:  */
   52: bool Curl_auth_is_spnego_supported(void)
   53: {
   54:   PSecPkgInfo SecurityPackage;
   55:   SECURITY_STATUS status;
   56: 
   57:   /* Query the security package for Negotiate */
   58:   status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *)
   59:                                               TEXT(SP_NAME_NEGOTIATE),
   60:                                               &SecurityPackage);
   61: 
   62:   /* Release the package buffer as it is not required anymore */
   63:   if(status == SEC_E_OK) {
   64:     s_pSecFn->FreeContextBuffer(SecurityPackage);
   65:   }
   66: 
   67: 
   68:   return (status == SEC_E_OK ? TRUE : FALSE);
   69: }
   70: 
   71: /*
   72:  * Curl_auth_decode_spnego_message()
   73:  *
   74:  * This is used to decode an already encoded SPNEGO (Negotiate) challenge
   75:  * message.
   76:  *
   77:  * Parameters:
   78:  *
   79:  * data        [in]     - The session handle.
   80:  * user        [in]     - The user name in the format User or Domain\User.
   81:  * password    [in]     - The user's password.
   82:  * service     [in]     - The service type such as http, smtp, pop or imap.
   83:  * host        [in]     - The host name.
   84:  * chlg64      [in]     - The optional base64 encoded challenge message.
   85:  * nego        [in/out] - The Negotiate data struct being used and modified.
   86:  *
   87:  * Returns CURLE_OK on success.
   88:  */
   89: CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data,
   90:                                          const char *user,
   91:                                          const char *password,
   92:                                          const char *service,
   93:                                          const char *host,
   94:                                          const char *chlg64,
   95:                                          struct negotiatedata *nego)
   96: {
   97:   CURLcode result = CURLE_OK;
   98:   size_t chlglen = 0;
   99:   unsigned char *chlg = NULL;
  100:   PSecPkgInfo SecurityPackage;
  101:   SecBuffer chlg_buf[2];
  102:   SecBuffer resp_buf;
  103:   SecBufferDesc chlg_desc;
  104:   SecBufferDesc resp_desc;
  105:   unsigned long attrs;
  106:   TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */
  107: 
  108: #if defined(CURL_DISABLE_VERBOSE_STRINGS)
  109:   (void) data;
  110: #endif
  111: 
  112:   if(nego->context && nego->status == SEC_E_OK) {
  113:     /* We finished successfully our part of authentication, but server
  114:      * rejected it (since we're again here). Exit with an error since we
  115:      * can't invent anything better */
  116:     Curl_auth_cleanup_spnego(nego);
  117:     return CURLE_LOGIN_DENIED;
  118:   }
  119: 
  120:   if(!nego->spn) {
  121:     /* Generate our SPN */
  122:     nego->spn = Curl_auth_build_spn(service, host, NULL);
  123:     if(!nego->spn)
  124:       return CURLE_OUT_OF_MEMORY;
  125:   }
  126: 
  127:   if(!nego->output_token) {
  128:     /* Query the security package for Negotiate */
  129:     nego->status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *)
  130:                                                       TEXT(SP_NAME_NEGOTIATE),
  131:                                                       &SecurityPackage);
  132:     if(nego->status != SEC_E_OK)
  133:       return CURLE_NOT_BUILT_IN;
  134: 
  135:     nego->token_max = SecurityPackage->cbMaxToken;
  136: 
  137:     /* Release the package buffer as it is not required anymore */
  138:     s_pSecFn->FreeContextBuffer(SecurityPackage);
  139: 
  140:     /* Allocate our output buffer */
  141:     nego->output_token = malloc(nego->token_max);
  142:     if(!nego->output_token)
  143:       return CURLE_OUT_OF_MEMORY;
  144:  }
  145: 
  146:   if(!nego->credentials) {
  147:     /* Do we have credentials to use or are we using single sign-on? */
  148:     if(user && *user) {
  149:       /* Populate our identity structure */
  150:       result = Curl_create_sspi_identity(user, password, &nego->identity);
  151:       if(result)
  152:         return result;
  153: 
  154:       /* Allow proper cleanup of the identity structure */
  155:       nego->p_identity = &nego->identity;
  156:     }
  157:     else
  158:       /* Use the current Windows user */
  159:       nego->p_identity = NULL;
  160: 
  161:     /* Allocate our credentials handle */
  162:     nego->credentials = calloc(1, sizeof(CredHandle));
  163:     if(!nego->credentials)
  164:       return CURLE_OUT_OF_MEMORY;
  165: 
  166:     /* Acquire our credentials handle */
  167:     nego->status =
  168:       s_pSecFn->AcquireCredentialsHandle(NULL,
  169:                                          (TCHAR *)TEXT(SP_NAME_NEGOTIATE),
  170:                                          SECPKG_CRED_OUTBOUND, NULL,
  171:                                          nego->p_identity, NULL, NULL,
  172:                                          nego->credentials, &expiry);
  173:     if(nego->status != SEC_E_OK)
  174:       return CURLE_AUTH_ERROR;
  175: 
  176:     /* Allocate our new context handle */
  177:     nego->context = calloc(1, sizeof(CtxtHandle));
  178:     if(!nego->context)
  179:       return CURLE_OUT_OF_MEMORY;
  180:   }
  181: 
  182:   if(chlg64 && *chlg64) {
  183:     /* Decode the base-64 encoded challenge message */
  184:     if(*chlg64 != '=') {
  185:       result = Curl_base64_decode(chlg64, &chlg, &chlglen);
  186:       if(result)
  187:         return result;
  188:     }
  189: 
  190:     /* Ensure we have a valid challenge message */
  191:     if(!chlg) {
  192:       infof(data, "SPNEGO handshake failure (empty challenge message)\n");
  193: 
  194:       return CURLE_BAD_CONTENT_ENCODING;
  195:     }
  196: 
  197:     /* Setup the challenge "input" security buffer */
  198:     chlg_desc.ulVersion    = SECBUFFER_VERSION;
  199:     chlg_desc.cBuffers     = 1;
  200:     chlg_desc.pBuffers     = &chlg_buf[0];
  201:     chlg_buf[0].BufferType = SECBUFFER_TOKEN;
  202:     chlg_buf[0].pvBuffer   = chlg;
  203:     chlg_buf[0].cbBuffer   = curlx_uztoul(chlglen);
  204: 
  205: #ifdef SECPKG_ATTR_ENDPOINT_BINDINGS
  206:     /* ssl context comes from Schannel.
  207:     * When extended protection is used in IIS server,
  208:     * we have to pass a second SecBuffer to the SecBufferDesc
  209:     * otherwise IIS will not pass the authentication (401 response).
  210:     * Minimum supported version is Windows 7.
  211:     * https://docs.microsoft.com/en-us/security-updates
  212:     * /SecurityAdvisories/2009/973811
  213:     */
  214:     if(nego->sslContext) {
  215:       SEC_CHANNEL_BINDINGS channelBindings;
  216:       SecPkgContext_Bindings pkgBindings;
  217:       pkgBindings.Bindings = &channelBindings;
  218:       nego->status = s_pSecFn->QueryContextAttributes(
  219:           nego->sslContext,
  220:           SECPKG_ATTR_ENDPOINT_BINDINGS,
  221:           &pkgBindings
  222:       );
  223:       if(nego->status == SEC_E_OK) {
  224:         chlg_desc.cBuffers++;
  225:         chlg_buf[1].BufferType = SECBUFFER_CHANNEL_BINDINGS;
  226:         chlg_buf[1].cbBuffer   = pkgBindings.BindingsLength;
  227:         chlg_buf[1].pvBuffer   = pkgBindings.Bindings;
  228:       }
  229:     }
  230: #endif
  231:   }
  232: 
  233:   /* Setup the response "output" security buffer */
  234:   resp_desc.ulVersion = SECBUFFER_VERSION;
  235:   resp_desc.cBuffers  = 1;
  236:   resp_desc.pBuffers  = &resp_buf;
  237:   resp_buf.BufferType = SECBUFFER_TOKEN;
  238:   resp_buf.pvBuffer   = nego->output_token;
  239:   resp_buf.cbBuffer   = curlx_uztoul(nego->token_max);
  240: 
  241:   /* Generate our challenge-response message */
  242:   nego->status = s_pSecFn->InitializeSecurityContext(nego->credentials,
  243:                                                      chlg ? nego->context :
  244:                                                             NULL,
  245:                                                      nego->spn,
  246:                                                      ISC_REQ_CONFIDENTIALITY,
  247:                                                      0, SECURITY_NATIVE_DREP,
  248:                                                      chlg ? &chlg_desc : NULL,
  249:                                                      0, nego->context,
  250:                                                      &resp_desc, &attrs,
  251:                                                      &expiry);
  252: 
  253:   /* Free the decoded challenge as it is not required anymore */
  254:   free(chlg);
  255: 
  256:   if(GSS_ERROR(nego->status)) {
  257:     char buffer[STRERROR_LEN];
  258:     failf(data, "InitializeSecurityContext failed: %s",
  259:           Curl_sspi_strerror(nego->status, buffer, sizeof(buffer)));
  260: 
  261:     if(nego->status == (DWORD)SEC_E_INSUFFICIENT_MEMORY)
  262:       return CURLE_OUT_OF_MEMORY;
  263: 
  264:     return CURLE_AUTH_ERROR;
  265:   }
  266: 
  267:   if(nego->status == SEC_I_COMPLETE_NEEDED ||
  268:      nego->status == SEC_I_COMPLETE_AND_CONTINUE) {
  269:     nego->status = s_pSecFn->CompleteAuthToken(nego->context, &resp_desc);
  270:     if(GSS_ERROR(nego->status)) {
  271:       char buffer[STRERROR_LEN];
  272:       failf(data, "CompleteAuthToken failed: %s",
  273:             Curl_sspi_strerror(nego->status, buffer, sizeof(buffer)));
  274: 
  275:       if(nego->status == (DWORD)SEC_E_INSUFFICIENT_MEMORY)
  276:         return CURLE_OUT_OF_MEMORY;
  277: 
  278:       return CURLE_AUTH_ERROR;
  279:     }
  280:   }
  281: 
  282:   nego->output_token_length = resp_buf.cbBuffer;
  283: 
  284:   return result;
  285: }
  286: 
  287: /*
  288:  * Curl_auth_create_spnego_message()
  289:  *
  290:  * This is used to generate an already encoded SPNEGO (Negotiate) response
  291:  * message ready for sending to the recipient.
  292:  *
  293:  * Parameters:
  294:  *
  295:  * data        [in]     - The session handle.
  296:  * nego        [in/out] - The Negotiate data struct being used and modified.
  297:  * outptr      [in/out] - The address where a pointer to newly allocated memory
  298:  *                        holding the result will be stored upon completion.
  299:  * outlen      [out]    - The length of the output message.
  300:  *
  301:  * Returns CURLE_OK on success.
  302:  */
  303: CURLcode Curl_auth_create_spnego_message(struct Curl_easy *data,
  304:                                          struct negotiatedata *nego,
  305:                                          char **outptr, size_t *outlen)
  306: {
  307:   CURLcode result;
  308: 
  309:   /* Base64 encode the already generated response */
  310:   result = Curl_base64_encode(data,
  311:                               (const char *) nego->output_token,
  312:                               nego->output_token_length,
  313:                               outptr, outlen);
  314: 
  315:   if(result)
  316:     return result;
  317: 
  318:   if(!*outptr || !*outlen) {
  319:     free(*outptr);
  320:     return CURLE_REMOTE_ACCESS_DENIED;
  321:   }
  322: 
  323:   return CURLE_OK;
  324: }
  325: 
  326: /*
  327:  * Curl_auth_cleanup_spnego()
  328:  *
  329:  * This is used to clean up the SPNEGO (Negotiate) specific data.
  330:  *
  331:  * Parameters:
  332:  *
  333:  * nego     [in/out] - The Negotiate data struct being cleaned up.
  334:  *
  335:  */
  336: void Curl_auth_cleanup_spnego(struct negotiatedata *nego)
  337: {
  338:   /* Free our security context */
  339:   if(nego->context) {
  340:     s_pSecFn->DeleteSecurityContext(nego->context);
  341:     free(nego->context);
  342:     nego->context = NULL;
  343:   }
  344: 
  345:   /* Free our credentials handle */
  346:   if(nego->credentials) {
  347:     s_pSecFn->FreeCredentialsHandle(nego->credentials);
  348:     free(nego->credentials);
  349:     nego->credentials = NULL;
  350:   }
  351: 
  352:   /* Free our identity */
  353:   Curl_sspi_free_identity(nego->p_identity);
  354:   nego->p_identity = NULL;
  355: 
  356:   /* Free the SPN and output token */
  357:   Curl_safefree(nego->spn);
  358:   Curl_safefree(nego->output_token);
  359: 
  360:   /* Reset any variables */
  361:   nego->status = 0;
  362:   nego->token_max = 0;
  363:   nego->noauthpersist = FALSE;
  364:   nego->havenoauthpersist = FALSE;
  365:   nego->havenegdata = FALSE;
  366:   nego->havemultiplerequests = FALSE;
  367: }
  368: 
  369: #endif /* USE_WINDOWS_SSPI && USE_SPNEGO */

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