diff options
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/curl_sasl.c | 13 | ||||
| -rw-r--r-- | lib/curl_sasl.h | 21 | ||||
| -rw-r--r-- | lib/curl_sasl_sspi.c | 411 | 
3 files changed, 443 insertions, 2 deletions
diff --git a/lib/curl_sasl.c b/lib/curl_sasl.c index 1068dc8fe..8f6aab2e8 100644 --- a/lib/curl_sasl.c +++ b/lib/curl_sasl.c @@ -53,6 +53,10 @@  /* The last #include file should be: */  #include "memdebug.h" +#if defined(USE_WINDOWS_SSPI) +extern void Curl_sasl_gssapi_cleanup(struct kerberos5data *krb5); +#endif +  #if !defined(CURL_DISABLE_CRYPTO_AUTH) && !defined(USE_WINDOWS_SSPI)  #define DIGEST_QOP_VALUE_AUTH             (1 << 0)  #define DIGEST_QOP_VALUE_AUTH_INT         (1 << 1) @@ -718,12 +722,17 @@ CURLcode Curl_sasl_create_xoauth2_message(struct SessionHandle *data,   */  void Curl_sasl_cleanup(struct connectdata *conn, unsigned int authused)  { +#if defined(USE_WINDOWS_SSPI) +  /* Cleanup the gssapi structure */ +  if(authused == SASL_MECH_GSSAPI) { +    Curl_sasl_gssapi_cleanup(&conn->krb5); +  }  #ifdef USE_NTLM    /* Cleanup the ntlm structure */ -  if(authused == SASL_MECH_NTLM) { +  else if(authused == SASL_MECH_NTLM) {      Curl_ntlm_sspi_cleanup(&conn->ntlm);    } -  (void)conn; +#endif  #else    /* Reserved for future use */    (void)conn; diff --git a/lib/curl_sasl.h b/lib/curl_sasl.h index fe7c471ce..6957ee134 100644 --- a/lib/curl_sasl.h +++ b/lib/curl_sasl.h @@ -119,6 +119,27 @@ CURLcode Curl_sasl_create_ntlm_type3_message(struct SessionHandle *data,  #endif /* USE_NTLM */ +#if defined(USE_WINDOWS_SSPI) +/* This is used to generate a base64 encoded GSSAPI (Kerberos V5) user token +   message */ +CURLcode Curl_sasl_create_gssapi_user_message(struct SessionHandle *data, +                                              const char *userp, +                                              const char *passwdp, +                                              const char *service, +                                              const bool mutual, +                                              const char *chlg64, +                                              struct kerberos5data *krb5, +                                              char **outptr, size_t *outlen); + +/* This is used to generate a base64 encoded GSSAPI (Kerberos V5) security +   token message */ +CURLcode Curl_sasl_create_gssapi_security_message(struct SessionHandle *data, +                                                  const char *input, +                                                  struct kerberos5data *krb5, +                                                  char **outptr, +                                                  size_t *outlen); +#endif +  /* This is used to generate a base64 encoded XOAUTH2 authentication message     containing the user name and bearer token */  CURLcode Curl_sasl_create_xoauth2_message(struct SessionHandle *data, diff --git a/lib/curl_sasl_sspi.c b/lib/curl_sasl_sspi.c index d25aabf97..5620a5b49 100644 --- a/lib/curl_sasl_sspi.c +++ b/lib/curl_sasl_sspi.c @@ -5,6 +5,7 @@   *                            | (__| |_| |  _ <| |___   *                             \___|\___/|_| \_\_____|   * + * Copyright (C) 2014, Steve Holme, <steve_holme@hotmail.com>.   * Copyright (C) 2014, Daniel Stenberg, <daniel@haxx.se>, et al.   *   * This software is licensed as described in the file COPYING, which @@ -20,6 +21,7 @@   *   * RFC2831 DIGEST-MD5 authentication   * RFC4422 Simple Authentication and Security Layer (SASL) + * RFC4752 The Kerberos V5 ("GSSAPI") SASL Mechanism   *   ***************************************************************************/ @@ -267,4 +269,413 @@ CURLcode Curl_sasl_create_digest_md5_message(struct SessionHandle *data,  #endif /* !CURL_DISABLE_CRYPTO_AUTH */ +/* + * Curl_sasl_create_gssapi_user_message() + * + * This is used to generate an already encoded GSSAPI (Kerberos V5) user token + * message ready for sending to the recipient. + * + * Parameters: + * + * data    [in]     - The session handle. + * userp   [in]     - The user name. + * passdwp [in]     - The user's password. + * service [in]     - The service type such as www, smtp, pop or imap. + * mutual  [in]     - Flag specifing whether or not mutual authentication is + *                    enabled. + * chlg64  [in]     - Pointer to the optional base64 encoded challenge message. + * krb5    [in/out] - The gssapi data struct being used and modified. + * outptr  [in/out] - The address where a pointer to newly allocated memory + *                    holding the result will be stored upon completion. + * outlen  [out]    - The length of the output message. + * + * Returns CURLE_OK on success. + */ +CURLcode Curl_sasl_create_gssapi_user_message(struct SessionHandle *data, +                                              const char *userp, +                                              const char *passwdp, +                                              const char *service, +                                              const bool mutual, +                                              const char *chlg64, +                                              struct kerberos5data *krb5, +                                              char **outptr, size_t *outlen) +{ +  CURLcode result = CURLE_OK; +  size_t chlglen = 0; +  unsigned char *chlg = NULL; +  unsigned char *resp = NULL; +  CtxtHandle context; +  PSecPkgInfo SecurityPackage; +  SecBuffer chlg_buf; +  SecBuffer resp_buf; +  SecBufferDesc chlg_desc; +  SecBufferDesc resp_desc; +  SECURITY_STATUS status; +  unsigned long attrs; +  TimeStamp tsDummy; /* For Windows 9x compatibility of SSPI calls */ + +  if(!krb5->credentials) { +    /* Query the security package for Kerberos */ +    status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT("Kerberos"), +                                                &SecurityPackage); +    if(status != SEC_E_OK) { +      return CURLE_NOT_BUILT_IN; +    } + +    krb5->token_max = SecurityPackage->cbMaxToken; + +    /* Release the package buffer as it is not required anymore */ +    s_pSecFn->FreeContextBuffer(SecurityPackage); + +    /* Generate our SPN */ +    krb5->spn = Curl_sasl_build_spn(service, data->easy_conn->host.name); +    if(!krb5->spn) +      return CURLE_OUT_OF_MEMORY; + +    if(userp && *userp) { +      CURLcode result; + +      /* Populate our identity structure */ +      result = Curl_create_sspi_identity(userp, passwdp, &krb5->identity); +      if(result) +        return result; + +      /* Allow proper cleanup of the identity structure */ +      krb5->p_identity = &krb5->identity; + +      /* Allocate our response buffer */ +      krb5->output_token = malloc(krb5->token_max); +      if(!krb5->output_token) +        return CURLE_OUT_OF_MEMORY; +    } +    else +      /* Use the current Windows user */ +      krb5->p_identity = NULL; + +    /* Allocate our credentials handle */ +    krb5->credentials = malloc(sizeof(CredHandle)); +    if(!krb5->credentials) +      return CURLE_OUT_OF_MEMORY; + +    memset(krb5->credentials, 0, sizeof(CredHandle)); + +    /* Acquire our credientials handle */ +    status = s_pSecFn->AcquireCredentialsHandle(NULL, +                                                (TCHAR *) TEXT("Kerberos"), +                                                SECPKG_CRED_OUTBOUND, NULL, +                                                krb5->p_identity, NULL, NULL, +                                                krb5->credentials, &tsDummy); +    if(status != SEC_E_OK) +      return CURLE_OUT_OF_MEMORY; + +    /* Allocate our new context handle */ +    krb5->context = malloc(sizeof(CtxtHandle)); +    if(!krb5->context) +      return CURLE_OUT_OF_MEMORY; + +    memset(krb5->context, 0, sizeof(CtxtHandle)); +  } +  else { +    /* Decode the base-64 encoded challenge message */ +    if(strlen(chlg64) && *chlg64 != '=') { +      result = Curl_base64_decode(chlg64, &chlg, &chlglen); +      if(result) +        return result; +    } + +    /* Ensure we have a valid challenge message */ +    if(!chlg) +      return CURLE_BAD_CONTENT_ENCODING; + +    /* Setup the challenge "input" security buffer */ +    chlg_desc.ulVersion = SECBUFFER_VERSION; +    chlg_desc.cBuffers  = 1; +    chlg_desc.pBuffers  = &chlg_buf; +    chlg_buf.BufferType = SECBUFFER_TOKEN; +    chlg_buf.pvBuffer   = chlg; +    chlg_buf.cbBuffer   = curlx_uztoul(chlglen); +  } + +  /* Setup the response "output" security buffer */ +  resp_desc.ulVersion = SECBUFFER_VERSION; +  resp_desc.cBuffers  = 1; +  resp_desc.pBuffers  = &resp_buf; +  resp_buf.BufferType = SECBUFFER_TOKEN; +  resp_buf.pvBuffer   = krb5->output_token; +  resp_buf.cbBuffer   = curlx_uztoul(krb5->token_max); + +  /* Generate our challenge-response message */ +  status = s_pSecFn->InitializeSecurityContext(krb5->credentials, +                                               chlg ? krb5->context : NULL, +                                               krb5->spn, +                                               (mutual ? +                                                 ISC_REQ_MUTUAL_AUTH : 0), +                                               0, SECURITY_NATIVE_DREP, +                                               chlg ? &chlg_desc : NULL, 0, +                                               &context, +                                               &resp_desc, &attrs, +                                               &tsDummy); + +  if(status != SEC_E_OK && status != SEC_I_CONTINUE_NEEDED) +    return CURLE_RECV_ERROR; + +  if(memcmp(&context, krb5->context, sizeof(context))) { +    s_pSecFn->DeleteSecurityContext(krb5->context); + +    memcpy(krb5->context, &context, sizeof(context)); +  } + +  if(resp_buf.cbBuffer) { +    /* Base64 encode the response */ +    result = Curl_base64_encode(data, (char *)resp_buf.pvBuffer, +                                resp_buf.cbBuffer, outptr, outlen); +  } + +  return result; +} + +/* + * Curl_sasl_create_gssapi_security_message() + * + * This is used to generate an already encoded GSSAPI (Kerberos V5) security + * token message ready for sending to the recipient. + * + * Parameters: + * + * data    [in]     - The session handle. + * chlg64  [in]     - Pointer to the optional base64 encoded challenge message. + * krb5    [in/out] - The gssapi data struct being used and modified. + * outptr  [in/out] - The address where a pointer to newly allocated memory + *                    holding the result will be stored upon completion. + * outlen  [out]    - The length of the output message. + * + * Returns CURLE_OK on success. + */ +CURLcode Curl_sasl_create_gssapi_security_message(struct SessionHandle *data, +                                                  const char *chlg64, +                                                  struct kerberos5data *krb5, +                                                  char **outptr, +                                                  size_t *outlen) +{ +  CURLcode result = CURLE_OK; +  size_t offset = 0; +  size_t chlglen = 0; +  size_t messagelen = 0; +  size_t appdatalen = 0; +  unsigned char *chlg = NULL; +  unsigned char *trailer = NULL; +  unsigned char *message = NULL; +  unsigned char *padding = NULL; +  unsigned char *appdata = NULL; +  SecBuffer input_buf[2]; +  SecBuffer wrap_buf[3]; +  SecBufferDesc input_desc; +  SecBufferDesc wrap_desc; +  unsigned long indata = 0; +  unsigned long qop = 0; +  unsigned long sec_layer = 0; +  unsigned long max_size = 0; +  SecPkgContext_Sizes sizes; +  SecPkgCredentials_Names names; +  SECURITY_STATUS status; + +  /* TODO: Verify the unicodeness of this function */ + +  /* Decode the base-64 encoded input message */ +  if(strlen(chlg64) && *chlg64 != '=') { +    result = Curl_base64_decode(chlg64, &chlg, &chlglen); +    if(result) +      return result; +  } + +  /* Ensure we have a valid challenge message */ +  if(!chlg) +    return CURLE_BAD_CONTENT_ENCODING; + +  /* Get our response size information */ +  status = s_pSecFn->QueryContextAttributes(krb5->context, +                                            SECPKG_ATTR_SIZES, +                                            &sizes); +  if(status != SEC_E_OK) { +    Curl_safefree(chlg); + +    return CURLE_OUT_OF_MEMORY; +  } + +  /* Get the fully qualified username back from the context */ +  status = s_pSecFn->QueryCredentialsAttributes(krb5->credentials, +                                                SECPKG_CRED_ATTR_NAMES, +                                                &names); +  if(status != SEC_E_OK) { +    Curl_safefree(chlg); + +    return CURLE_RECV_ERROR; +  } + +  /* Setup the "input" security buffer */ +  input_desc.ulVersion = SECBUFFER_VERSION; +  input_desc.cBuffers = 2; +  input_desc.pBuffers = input_buf; +  input_buf[0].BufferType = SECBUFFER_STREAM; +  input_buf[0].pvBuffer = chlg; +  input_buf[0].cbBuffer = curlx_uztoul(chlglen); +  input_buf[1].BufferType = SECBUFFER_DATA; +  input_buf[1].pvBuffer = NULL; +  input_buf[1].cbBuffer = 0; + +  /* Decrypt in the inbound challenge obtaining the qop */ +  status = s_pSecFn->DecryptMessage(krb5->context, &input_desc, 0, &qop); +  if(status != SEC_E_OK) { +    Curl_safefree(chlg); + +    return CURLE_RECV_ERROR; +  } + +  /* Not 4 octets long to fail as per RFC4752 Section 3.1 */ +  if(input_buf[1].cbBuffer != 4) { +    Curl_safefree(chlg); + +    return CURLE_RECV_ERROR; +  } + +  /* Copy the data out into a coinput_bufnvenient variable and free the SSPI +     allocated buffer as it is not required anymore */ +  memcpy(&indata, input_buf[1].pvBuffer, 4); +  s_pSecFn->FreeContextBuffer(input_buf[1].pvBuffer); + +  /* Extract the security layer */ +  sec_layer = indata & 0x000000FF; +  if(!(sec_layer & KERB_WRAP_NO_ENCRYPT)) { +    Curl_safefree(chlg); + +    return CURLE_RECV_ERROR; +  } + +  /* Extract the maximum message size the server can receive */ +  max_size = ntohl(indata & 0xFFFFFF00); + +  /* Allocate the trailer */ +  trailer = malloc(sizes.cbSecurityTrailer); +  if(!trailer) { +    Curl_safefree(chlg); + +    return CURLE_OUT_OF_MEMORY; +  } + +  /* Allocate our message */ +  messagelen = 4 + strlen(names.sUserName) + 1; +  message = malloc(messagelen); +  if(!message) { +    Curl_safefree(trailer); +    Curl_safefree(chlg); + +    return CURLE_OUT_OF_MEMORY; +  } + +  /* Populate the message with the security layer, client supported receive +     message size (lets claim to support the same as the server) and +     authorization identity including the 0x00 based terminator. Note: Dispite +     RFC4752 Section 3.1 stating "The authorization identity is not terminated +     with the zero-valued (%x00) octet." it seems necessary to include it. */ +  memcpy(message, &indata, 4); +  strcpy((char *)message + 4, names.sUserName); + +  /* Allocate the padding */ +  padding = malloc(sizes.cbBlockSize); +  if(!padding) { +    Curl_safefree(message); +    Curl_safefree(trailer); +    Curl_safefree(chlg); + +    return CURLE_OUT_OF_MEMORY; +  } + +  /* Setup the "authentication data" security buffer */ +  wrap_desc.ulVersion    = SECBUFFER_VERSION; +  wrap_desc.cBuffers     = 3; +  wrap_desc.pBuffers     = wrap_buf; +  wrap_buf[0].BufferType = SECBUFFER_TOKEN; +  wrap_buf[0].pvBuffer   = trailer; +  wrap_buf[0].cbBuffer   = sizes.cbSecurityTrailer; +  wrap_buf[1].BufferType = SECBUFFER_DATA; +  wrap_buf[1].pvBuffer   = message; +  wrap_buf[1].cbBuffer   = curlx_uztoul(messagelen); +  wrap_buf[2].BufferType = SECBUFFER_PADDING; +  wrap_buf[2].pvBuffer   = padding; +  wrap_buf[2].cbBuffer   = sizes.cbBlockSize; + +  /* Encrypt the data */ +  status = s_pSecFn->EncryptMessage(krb5->context, KERB_WRAP_NO_ENCRYPT, +                                    &wrap_desc, 0); +  if(status != SEC_E_OK) { +    Curl_safefree(padding); +    Curl_safefree(message); +    Curl_safefree(trailer); +    Curl_safefree(chlg); + +    return CURLE_RECV_ERROR; +  } + +  /* Allocate the encryption (wrap) buffer */ +  appdatalen = wrap_buf[0].cbBuffer + wrap_buf[1].cbBuffer + +               wrap_buf[2].cbBuffer; +  appdata = malloc(appdatalen); +  if(!appdata) { +    Curl_safefree(padding); +    Curl_safefree(message); +    Curl_safefree(trailer); +    Curl_safefree(chlg); + +    return CURLE_OUT_OF_MEMORY; +  } + +  /* Populate the encryption buffer */ +  memcpy(appdata, wrap_buf[0].pvBuffer, wrap_buf[0].cbBuffer); +  offset += wrap_buf[0].cbBuffer; +  memcpy(appdata + offset, wrap_buf[1].pvBuffer, wrap_buf[1].cbBuffer); +  offset += wrap_buf[1].cbBuffer; +  memcpy(appdata + offset, wrap_buf[2].pvBuffer, wrap_buf[2].cbBuffer); + +  /* Base64 encode the response */ +  result = Curl_base64_encode(data, (char *)appdata, appdatalen, outptr, +                              outlen); + +  /* Free all of our local buffers */ +  Curl_safefree(appdata); +  Curl_safefree(padding); +  Curl_safefree(message); +  Curl_safefree(trailer); +  Curl_safefree(chlg); + +  return result; +} + +void Curl_sasl_gssapi_cleanup(struct kerberos5data *krb5) +{ +  /* Free  the context */ +  if(krb5->context) { +    s_pSecFn->DeleteSecurityContext(krb5->context); +    free(krb5->context); +    krb5->context = NULL; +  } + +  /* Free the credientials handle */ +  if(krb5->credentials) { +    s_pSecFn->FreeCredentialsHandle(krb5->credentials); +    free(krb5->credentials); +    krb5->credentials = NULL; +  } + +  /* Free our identity */ +  Curl_sspi_free_identity(krb5->p_identity); +  krb5->p_identity = NULL; + +  /* Free the SPN and output token */ +  Curl_safefree(krb5->spn); +  Curl_safefree(krb5->output_token); + +  /* Reset any variables */ +  krb5->token_max = 0; +} +  #endif /* USE_WINDOWS_SSPI */  | 
