sasl: Added support for DIGEST-MD5 via Windows SSPI
authorSteve Holme <steve_holme@hotmail.com>
Sun, 6 Apr 2014 11:08:11 +0000 (12:08 +0100)
committerSteve Holme <steve_holme@hotmail.com>
Sun, 6 Apr 2014 11:49:32 +0000 (12:49 +0100)
lib/curl_sasl_sspi.c

index 675118ede6b046c15c572d59b582ede0de97f9d1..e9eaf32300df71bf630e717e983769be75b4aa73 100644 (file)
 
 #include <curl/curl.h>
 
+#include "urldata.h"
+#include "curl_base64.h"
+#include "warnless.h"
+
+#define _MPRINTF_REPLACE /* use our functions only */
+#include <curl/mprintf.h>
+
 /* The last #include file should be: */
 #include "memdebug.h"
 
@@ -58,14 +65,128 @@ CURLcode Curl_sasl_create_digest_md5_message(struct SessionHandle *data,
                                              const char *service,
                                              char **outptr, size_t *outlen)
 {
-  (void)data;
-  (void)chlg64;
-  (void)userp;
-  (void)passwdp;
-  (void)outptr;
-  (void)outlen;
-
-  return CURLE_NOT_BUILT_IN;
+  CURLcode result = CURLE_OK;
+  char *spn = NULL;
+  size_t chlglen = 0;
+  unsigned char *chlg = NULL;
+  unsigned char resp[1024];
+  CredHandle handle;
+  CtxtHandle ctx;
+  PSecPkgInfo SecurityPackage;
+  SEC_WINNT_AUTH_IDENTITY identity;
+  SEC_WINNT_AUTH_IDENTITY *identityp = NULL;
+  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 */
+
+  /* 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;
+
+  /* Ensure we have some login credientials as DigestSSP cannot use the current
+     Windows user like NTLMSSP can */
+  if(!userp || !*userp)
+    return CURLE_LOGIN_DENIED;
+
+  /* Query the security package for DigestSSP */
+  status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT("WDigest"),
+                                              &SecurityPackage);
+  if(status != SEC_E_OK)
+    return CURLE_NOT_BUILT_IN;
+
+  /* Calculate our SPN */
+  spn = aprintf("%s/%s", service, data->easy_conn->host);
+  if(!spn)
+    return CURLE_OUT_OF_MEMORY;
+
+  /* Populate our identity structure */
+  result = Curl_create_sspi_identity(userp, passwdp, &identity);
+  if(result) {
+    Curl_safefree(spn);
+
+    return result;
+  }
+
+  /* Allow proper cleanup of the identity structure */
+  identityp = &identity;
+
+  /* Acquire our credientials handle */
+  status = s_pSecFn->AcquireCredentialsHandle(NULL,
+                                              (TCHAR *) TEXT("WDigest"),
+                                              SECPKG_CRED_OUTBOUND, NULL,
+                                              identityp, NULL, NULL,
+                                              &handle, &tsDummy);
+
+  if(status != SEC_E_OK) {
+    Curl_sspi_free_identity(identityp);
+    Curl_safefree(spn);
+
+    return CURLE_OUT_OF_MEMORY;
+  }
+
+  /* Setup the challenge 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 security buffer */
+  resp_desc.ulVersion = SECBUFFER_VERSION;
+  resp_desc.cBuffers  = 1;
+  resp_desc.pBuffers  = &resp_buf;
+  resp_buf.BufferType = SECBUFFER_TOKEN;
+  resp_buf.pvBuffer   = resp;
+  resp_buf.cbBuffer   = sizeof(resp);
+
+  /* Generate our challenge-response */
+  status = s_pSecFn->InitializeSecurityContext(&handle,
+                                               NULL,
+                                               (TCHAR *) spn,
+                                               0, 0, 0,
+                                               &chlg_desc,
+                                               0, &ctx,
+                                               &resp_desc,
+                                               &attrs, &tsDummy);
+
+  if(status == SEC_I_COMPLETE_AND_CONTINUE ||
+     status == SEC_I_CONTINUE_NEEDED)
+    s_pSecFn->CompleteAuthToken(&handle, &resp_desc);
+  else if(status != SEC_E_OK) {
+    s_pSecFn->FreeCredentialsHandle(&handle);
+    Curl_sspi_free_identity(identityp);
+    Curl_safefree(spn);
+
+    return CURLE_RECV_ERROR;
+  }
+
+  /* Base64 encode the response */
+  result = Curl_base64_encode(data, (char *)resp, resp_buf.cbBuffer, outptr,
+                              outlen);
+
+  /* Free our handles */
+  s_pSecFn->DeleteSecurityContext(&ctx);
+  s_pSecFn->FreeCredentialsHandle(&handle);
+
+  /* Free the identity structure */
+  Curl_sspi_free_identity(identityp);
+
+  /* Free the SPN */
+  Curl_safefree(spn);
+
+  return result;
 }
 
 #endif /* USE_WINDOWS_SSPI */