]> git.ipfire.org Git - thirdparty/cups.git/blobdiff - cups/tls-sspi.c
Don't generate certificates that expire on Feb 29th (Issue #5643)
[thirdparty/cups.git] / cups / tls-sspi.c
index 33926b7542c6a52d2211137e23540fc37ae485ab..ccbdf8aaf0648d94b0d9019c4885f20fcd58cea0 100644 (file)
@@ -1,19 +1,14 @@
 /*
- * "$Id$"
+ * TLS support for CUPS on Windows using the Security Support Provider
+ * Interface (SSPI).
  *
- * TLS support for CUPS on Windows using SSPI.
+ * Copyright 2010-2018 by Apple Inc.
  *
- * Copyright 2010-2014 by Apple Inc.
- *
- * These coded instructions, statements, and computer programs are the
- * property of Apple Inc. and are protected by Federal copyright
- * law.  Distribution and use rights are outlined in the file "LICENSE.txt"
- * which should have been included with this file.  If this file is
- * file is missing or damaged, see the license at "http://www.cups.org/".
- *
- * This file is subject to the Apple OS-Developed Software exception.
+ * Licensed under Apache License v2.0.  See the file "LICENSE" for more information.
  */
 
+/**** This file is included from tls.c ****/
+
 /*
  * Include necessary headers...
  */
 #  define SECURITY_FLAG_IGNORE_UNKNOWN_CA         0x00000100 /* Untrusted root */
 #endif /* SECURITY_FLAG_IGNORE_UNKNOWN_CA */
 
+#ifndef SECURITY_FLAG_IGNORE_CERT_CN_INVALID
+#  define SECURITY_FLAG_IGNORE_CERT_CN_INVALID   0x00001000 /* Common name does not match */
+#endif /* !SECURITY_FLAG_IGNORE_CERT_CN_INVALID */
+
 #ifndef SECURITY_FLAG_IGNORE_CERT_DATE_INVALID
 #  define SECURITY_FLAG_IGNORE_CERT_DATE_INVALID  0x00002000 /* Expired X509 Cert. */
 #endif /* !SECURITY_FLAG_IGNORE_CERT_DATE_INVALID */
 
+
+/*
+ * Local globals...
+ */
+
+static int             tls_options = -1,/* Options for TLS connections */
+                       tls_min_version = _HTTP_TLS_1_0,
+                       tls_max_version = _HTTP_TLS_MAX;
+
+
 /*
  * Local functions...
  */
 
 static _http_sspi_t *http_sspi_alloc(void);
 static int     http_sspi_client(http_t *http, const char *hostname);
-static BOOL    http_sspi_credentials(http_t *http, const LPWSTR containerName, const char *common_name, BOOL server);
-static void    http_sspi_free(_http_sspi_t *conn);
+static PCCERT_CONTEXT http_sspi_create_credential(http_credential_t *cred);
+static BOOL    http_sspi_find_credentials(http_t *http, const LPWSTR containerName, const char *common_name);
+static void    http_sspi_free(_http_sspi_t *sspi);
+static BOOL    http_sspi_make_credentials(_http_sspi_t *sspi, const LPWSTR containerName, const char *common_name, _http_mode_t mode, int years);
 static int     http_sspi_server(http_t *http, const char *hostname);
-static void    http_sspi_set_allows_any_root(_http_sspi_t *conn, BOOL allow);
-static void    http_sspi_set_allows_expired_certs(_http_sspi_t *conn, BOOL allow);
+static void    http_sspi_set_allows_any_root(_http_sspi_t *sspi, BOOL allow);
+static void    http_sspi_set_allows_expired_certs(_http_sspi_t *sspi, BOOL allow);
+static const char *http_sspi_strerror(char *buffer, size_t bufsize, DWORD code);
 static DWORD   http_sspi_verify(PCCERT_CONTEXT cert, const char *common_name, DWORD dwCertFlags);
 
 
 /*
  * 'cupsMakeServerCredentials()' - Make a self-signed certificate and private key pair.
  *
- * @since CUPS 2.0@
+ * @since CUPS 2.0/OS 10.10@
  */
 
 int                                    /* O - 1 on success, 0 on failure */
@@ -70,15 +82,22 @@ cupsMakeServerCredentials(
     const char **alt_names,            /* I - Subject Alternate Names */
     time_t     expiration_date)                /* I - Expiration date */
 {
+  _http_sspi_t *sspi;                  /* SSPI data */
+  int          ret;                    /* Return value */
+
+
   DEBUG_printf(("cupsMakeServerCredentials(path=\"%s\", common_name=\"%s\", num_alt_names=%d, alt_names=%p, expiration_date=%d)", path, common_name, num_alt_names, alt_names, (int)expiration_date));
 
   (void)path;
-  (void)common_name;
   (void)num_alt_names;
   (void)alt_names;
-  (void)expiration_date;
 
-  return (0);
+  sspi = http_sspi_alloc();
+  ret  = http_sspi_make_credentials(sspi, L"ServerContainer", common_name, _HTTP_MODE_SERVER, (int)((expiration_date - time(NULL) + 86399) / 86400 / 365));
+
+  http_sspi_free(sspi);
+
+  return (ret);
 }
 
 
@@ -88,7 +107,7 @@ cupsMakeServerCredentials(
  * Note: The server credentials are used by all threads in the running process.
  * This function is threadsafe.
  *
- * @since CUPS 2.0@
+ * @since CUPS 2.0/OS 10.10@
  */
 
 int                                    /* O - 1 on success, 0 on failure */
@@ -111,7 +130,7 @@ cupsSetServerCredentials(
  * 'httpCopyCredentials()' - Copy the credentials associated with the peer in
  *                           an encrypted connection.
  *
- * @since CUPS 1.5/OS X 10.7@
+ * @since CUPS 1.5/macOS 10.7@
  */
 
 int                                    /* O - Status of call (0 = success) */
@@ -121,12 +140,18 @@ httpCopyCredentials(
 {
   DEBUG_printf(("httpCopyCredentials(http=%p, credentials=%p)", http, credentials));
 
-  (void)http;
+  if (!http || !http->tls || !http->tls->remoteCert || !credentials)
+  {
+    if (credentials)
+      *credentials = NULL;
 
-  if (credentials)
-    *credentials = NULL;
+    return (-1);
+  }
+
+  *credentials = cupsArrayNew(NULL, NULL);
+  httpAddCredential(*credentials, http->tls->remoteCert->pbCertEncoded, http->tls->remoteCert->cbCertEncoded);
 
-  return (-1);
+  return (0);
 }
 
 
@@ -138,16 +163,14 @@ http_tls_credentials_t                    /* O - Internal credentials */
 _httpCreateCredentials(
     cups_array_t *credentials)         /* I - Array of credentials */
 {
-  (void)credentials;
-
-  return (NULL);
+  return (http_sspi_create_credential((http_credential_t *)cupsArrayFirst(credentials)));
 }
 
 
 /*
  * 'httpCredentialsAreValidForName()' - Return whether the credentials are valid for the given name.
  *
- * @since CUPS 2.0@
+ * @since CUPS 2.0/OS 10.10@
  */
 
 int                                    /* O - 1 if valid, 0 otherwise */
@@ -155,17 +178,64 @@ httpCredentialsAreValidForName(
     cups_array_t *credentials,         /* I - Credentials */
     const char   *common_name)         /* I - Name to check */
 {
-  (void)credentials;
-  (void)common_name;
+  int          valid = 1;              /* Valid name? */
+  PCCERT_CONTEXT cert = http_sspi_create_credential((http_credential_t *)cupsArrayFirst(credentials));
+                                       /* Certificate */
+  char         cert_name[1024];        /* Name from certificate */
+
+
+  if (cert)
+  {
+    if (CertNameToStrA(X509_ASN_ENCODING, &(cert->pCertInfo->Subject), CERT_SIMPLE_NAME_STR, cert_name, sizeof(cert_name)))
+    {
+     /*
+      * Extract common name at end...
+      */
+
+      char  *ptr = strrchr(cert_name, ',');
+      if (ptr && ptr[1])
+        _cups_strcpy(cert_name, ptr + 2);
+    }
+    else
+      strlcpy(cert_name, "unknown", sizeof(cert_name));
 
-  return (1);
+    CertFreeCertificateContext(cert);
+  }
+  else
+    strlcpy(cert_name, "unknown", sizeof(cert_name));
+
+ /*
+  * Compare the common names...
+  */
+
+  if (_cups_strcasecmp(common_name, cert_name))
+  {
+   /*
+    * Not an exact match for the common name, check for wildcard certs...
+    */
+
+    const char *domain = strchr(common_name, '.');
+                                       /* Domain in common name */
+
+    if (strncmp(cert_name, "*.", 2) || !domain || _cups_strcasecmp(domain, cert_name + 1))
+    {
+     /*
+      * Not a wildcard match.
+      */
+
+      /* TODO: Check subject alternate names */
+      valid = 0;
+    }
+  }
+
+  return (valid);
 }
 
 
 /*
  * 'httpCredentialsGetTrust()' - Return the trust of credentials.
  *
- * @since CUPS 2.0@
+ * @since CUPS 2.0/OS 10.10@
  */
 
 http_trust_t                           /* O - Level of trust */
@@ -173,68 +243,35 @@ httpCredentialsGetTrust(
     cups_array_t *credentials,         /* I - Credentials */
     const char   *common_name)         /* I - Common name for trust lookup */
 {
-  http_trust_t         trust = HTTP_TRUST_OK;
-                                       /* Trusted? */
-  cups_array_t         *tcreds = NULL; /* Trusted credentials */
-  _cups_globals_t      *cg = _cupsGlobals();
-                                       /* Per-thread globals */
+  http_trust_t trust = HTTP_TRUST_OK;  /* Level of trust */
+  PCCERT_CONTEXT cert = NULL;          /* Certificate to validate */
+  DWORD                certFlags = 0;          /* Cert verification flags */
+  _cups_globals_t *cg = _cupsGlobals();        /* Per-thread global data */
 
 
   if (!common_name)
     return (HTTP_TRUST_UNKNOWN);
 
- /*
-  * Look this common name up in the default keychains...
-  */
-
-  httpLoadCredentials(NULL, &tcreds, common_name);
-
-  if (tcreds)
-  {
-    char       credentials_str[1024],  /* String for incoming credentials */
-               tcreds_str[1024];       /* String for saved credentials */
-
-    httpCredentialsString(credentials, credentials_str, sizeof(credentials_str));
-    httpCredentialsString(tcreds, tcreds_str, sizeof(tcreds_str));
-
-    if (strcmp(credentials_str, tcreds_str))
-    {
-     /*
-      * Credentials don't match, let's look at the expiration date of the new
-      * credentials and allow if the new ones have a later expiration...
-      */
+  cert = http_sspi_create_credential((http_credential_t *)cupsArrayFirst(credentials));
+  if (!cert)
+    return (HTTP_TRUST_UNKNOWN);
 
-      if (httpCredentialsGetExpiration(credentials) <= httpCredentialsGetExpiration(tcreds) ||
-          !httpCredentialsAreValidForName(credentials, common_name))
-      {
-       /*
-        * Either the new credentials are not newly issued, or the common name
-       * does not match the issued certificate...
-       */
+  if (cg->any_root < 0)
+    _cupsSetDefaults();
 
-        trust = HTTP_TRUST_INVALID;
-      }
-      else if (httpCredentialsGetExpiration(tcreds) < time(NULL))
-      {
-       /*
-        * Save the renewed credentials...
-       */
+  if (cg->any_root)
+    certFlags |= SECURITY_FLAG_IGNORE_UNKNOWN_CA;
 
-       trust = HTTP_TRUST_RENEWED;
+  if (cg->expired_certs)
+    certFlags |= SECURITY_FLAG_IGNORE_CERT_DATE_INVALID;
 
-        httpSaveCredentials(NULL, credentials, common_name);
-      }
-    }
+  if (!cg->validate_certs)
+    certFlags |= SECURITY_FLAG_IGNORE_CERT_CN_INVALID;
 
-    httpFreeCredentials(tcreds);
-  }
-  else if (cg->validate_certs && !httpCredentialsAreValidForName(credentials, common_name))
+  if (http_sspi_verify(cert, common_name, certFlags) != SEC_E_OK)
     trust = HTTP_TRUST_INVALID;
 
-  if (!cg->expired_certs && time(NULL) > httpCredentialsGetExpiration(credentials))
-    trust = HTTP_TRUST_EXPIRED;
-  else if (!cg->any_root && cupsArrayCount(credentials) == 1)
-    trust = HTTP_TRUST_INVALID;
+  CertFreeCertificateContext(cert);
 
   return (trust);
 }
@@ -243,23 +280,44 @@ httpCredentialsGetTrust(
 /*
  * 'httpCredentialsGetExpiration()' - Return the expiration date of the credentials.
  *
- * @since CUPS 2.0@
+ * @since CUPS 2.0/OS 10.10@
  */
 
 time_t                                 /* O - Expiration date of credentials */
 httpCredentialsGetExpiration(
     cups_array_t *credentials)         /* I - Credentials */
 {
-  (void)credentials;
+  time_t       expiration_date = 0;    /* Expiration data of credentials */
+  PCCERT_CONTEXT cert = http_sspi_create_credential((http_credential_t *)cupsArrayFirst(credentials));
+                                       /* Certificate */
+
+  if (cert)
+  {
+    SYSTEMTIME systime;                /* System time */
+    struct tm  tm;                     /* UNIX date/time */
+
+    FileTimeToSystemTime(&(cert->pCertInfo->NotAfter), &systime);
+
+    tm.tm_year = systime.wYear - 1900;
+    tm.tm_mon  = systime.wMonth - 1;
+    tm.tm_mday = systime.wDay;
+    tm.tm_hour = systime.wHour;
+    tm.tm_min  = systime.wMinute;
+    tm.tm_sec  = systime.wSecond;
+
+    expiration_date = mktime(&tm);
 
-  return (INT_MAX);
+    CertFreeCertificateContext(cert);
+  }
+
+  return (expiration_date);
 }
 
 
 /*
  * 'httpCredentialsString()' - Return a string representing the credentials.
  *
- * @since CUPS 2.0@
+ * @since CUPS 2.0/OS 10.10@
  */
 
 size_t                                 /* O - Total size of credentials string */
@@ -268,6 +326,11 @@ httpCredentialsString(
     char         *buffer,              /* I - Buffer or @code NULL@ */
     size_t       bufsize)              /* I - Size of buffer */
 {
+  http_credential_t    *first = (http_credential_t *)cupsArrayFirst(credentials);
+                                       /* First certificate */
+  PCCERT_CONTEXT       cert;           /* Certificate */
+
+
   DEBUG_printf(("httpCredentialsString(credentials=%p, buffer=%p, bufsize=" CUPS_LLFMT ")", credentials, buffer, CUPS_LLCAST bufsize));
 
   if (!buffer)
@@ -276,39 +339,46 @@ httpCredentialsString(
   if (buffer && bufsize > 0)
     *buffer = '\0';
 
-#if 0
-  http_credential_t    *first;         /* First certificate */
-  SecCertificateRef    secCert;        /* Certificate reference */
+  cert = http_sspi_create_credential(first);
 
-
-  if ((first = (http_credential_t *)cupsArrayFirst(credentials)) != NULL &&
-      (secCert = http_cdsa_create_credential(first)) != NULL)
+  if (cert)
   {
-    CFStringRef                cf_name;        /* CF common name string */
-    char               name[256];      /* Common name associated with cert */
+    char               cert_name[256]; /* Common name */
+    SYSTEMTIME         systime;        /* System time */
+    struct tm          tm;             /* UNIX date/time */
     time_t             expiration;     /* Expiration date of cert */
-    _cups_md5_state_t  md5_state;      /* MD5 state */
     unsigned char      md5_digest[16]; /* MD5 result */
 
-    if ((cf_name = SecCertificateCopySubjectSummary(secCert)) != NULL)
+    FileTimeToSystemTime(&(cert->pCertInfo->NotAfter), &systime);
+
+    tm.tm_year = systime.wYear - 1900;
+    tm.tm_mon  = systime.wMonth - 1;
+    tm.tm_mday = systime.wDay;
+    tm.tm_hour = systime.wHour;
+    tm.tm_min  = systime.wMinute;
+    tm.tm_sec  = systime.wSecond;
+
+    expiration = mktime(&tm);
+
+    if (CertNameToStrA(X509_ASN_ENCODING, &(cert->pCertInfo->Subject), CERT_SIMPLE_NAME_STR, cert_name, sizeof(cert_name)))
     {
-      CFStringGetCString(cf_name, name, (CFIndex)sizeof(name), kCFStringEncodingUTF8);
-      CFRelease(cf_name);
+     /*
+      * Extract common name at end...
+      */
+
+      char  *ptr = strrchr(cert_name, ',');
+      if (ptr && ptr[1])
+        _cups_strcpy(cert_name, ptr + 2);
     }
     else
-      strlcpy(name, "unknown", sizeof(name));
+      strlcpy(cert_name, "unknown", sizeof(cert_name));
 
-    expiration = (time_t)(SecCertificateNotValidAfter(secCert) + kCFAbsoluteTimeIntervalSince1970);
+    cupsHashData("md5", first->data, first->datalen, md5_digest, sizeof(md5_digest));
 
-    _cupsMD5Init(&md5_state);
-    _cupsMD5Append(&md5_state, first->data, (int)first->datalen);
-    _cupsMD5Finish(&md5_state, md5_digest);
+    snprintf(buffer, bufsize, "%s / %s / %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X", cert_name, httpGetDateString(expiration), md5_digest[0], md5_digest[1], md5_digest[2], md5_digest[3], md5_digest[4], md5_digest[5], md5_digest[6], md5_digest[7], md5_digest[8], md5_digest[9], md5_digest[10], md5_digest[11], md5_digest[12], md5_digest[13], md5_digest[14], md5_digest[15]);
 
-    snprintf(buffer, bufsize, "%s / %s / %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X", name, httpGetDateString(expiration), md5_digest[0], md5_digest[1], md5_digest[2], md5_digest[3], md5_digest[4], md5_digest[5], md5_digest[6], md5_digest[7], md5_digest[8], md5_digest[9], md5_digest[10], md5_digest[11], md5_digest[12], md5_digest[13], md5_digest[14], md5_digest[15]);
-
-    CFRelease(secCert);
+    CertFreeCertificateContext(cert);
   }
-#endif /* 0 */
 
   DEBUG_printf(("1httpCredentialsString: Returning \"%s\".", buffer));
 
@@ -326,13 +396,15 @@ _httpFreeCredentials(
 {
   if (!credentials)
     return;
+
+  CertFreeCertificateContext(credentials);
 }
 
 
 /*
  * 'httpLoadCredentials()' - Load X.509 credentials from a keychain file.
  *
- * @since CUPS 2.0@
+ * @since CUPS 2.0/OS 10.10@
  */
 
 int                                    /* O - 0 on success, -1 on error */
@@ -341,13 +413,111 @@ httpLoadCredentials(
     cups_array_t **credentials,                /* IO - Credentials */
     const char   *common_name)         /* I  - Common name for credentials */
 {
+  HCERTSTORE   store = NULL;           /* Certificate store */
+  PCCERT_CONTEXT storedContext = NULL; /* Context created from the store */
+  DWORD                dwSize = 0;             /* 32 bit size */
+  PBYTE                p = NULL;               /* Temporary storage */
+  HCRYPTPROV   hProv = (HCRYPTPROV)NULL;
+                                       /* Handle to a CSP */
+  CERT_NAME_BLOB sib;                  /* Arbitrary array of bytes */
+#ifdef DEBUG
+  char         error[1024];            /* Error message buffer */
+#endif /* DEBUG */
+
+
   DEBUG_printf(("httpLoadCredentials(path=\"%s\", credentials=%p, common_name=\"%s\")", path, credentials, common_name));
 
   (void)path;
-  (void)common_name;
 
   if (credentials)
+  {
     *credentials = NULL;
+  }
+  else
+  {
+    DEBUG_puts("1httpLoadCredentials: NULL credentials pointer, returning -1.");
+    return (-1);
+  }
+
+  if (!common_name)
+  {
+    DEBUG_puts("1httpLoadCredentials: Bad common name, returning -1.");
+    return (-1);
+  }
+
+  if (!CryptAcquireContextW(&hProv, L"RememberedContainer", MS_DEF_PROV_W, PROV_RSA_FULL, CRYPT_NEWKEYSET | CRYPT_MACHINE_KEYSET))
+  {
+    if (GetLastError() == NTE_EXISTS)
+    {
+      if (!CryptAcquireContextW(&hProv, L"RememberedContainer", MS_DEF_PROV_W, PROV_RSA_FULL, CRYPT_MACHINE_KEYSET))
+      {
+        DEBUG_printf(("1httpLoadCredentials: CryptAcquireContext failed: %s", http_sspi_strerror(error, sizeof(error), GetLastError())));
+        goto cleanup;
+      }
+    }
+  }
+
+  store = CertOpenStore(CERT_STORE_PROV_SYSTEM, X509_ASN_ENCODING|PKCS_7_ASN_ENCODING, hProv, CERT_SYSTEM_STORE_LOCAL_MACHINE | CERT_STORE_NO_CRYPT_RELEASE_FLAG | CERT_STORE_OPEN_EXISTING_FLAG, L"MY");
+
+  if (!store)
+  {
+    DEBUG_printf(("1httpLoadCredentials: CertOpenSystemStore failed: %s", http_sspi_strerror(error, sizeof(error), GetLastError())));
+    goto cleanup;
+  }
+
+  dwSize = 0;
+
+  if (!CertStrToNameA(X509_ASN_ENCODING, common_name, CERT_OID_NAME_STR, NULL, NULL, &dwSize, NULL))
+  {
+    DEBUG_printf(("1httpLoadCredentials: CertStrToName failed: %s", http_sspi_strerror(error, sizeof(error), GetLastError())));
+    goto cleanup;
+  }
+
+  p = (PBYTE)malloc(dwSize);
+
+  if (!p)
+  {
+    DEBUG_printf(("1httpLoadCredentials: malloc failed for %d bytes.", dwSize));
+    goto cleanup;
+  }
+
+  if (!CertStrToNameA(X509_ASN_ENCODING, common_name, CERT_OID_NAME_STR, NULL, p, &dwSize, NULL))
+  {
+    DEBUG_printf(("1httpLoadCredentials: CertStrToName failed: %s", http_sspi_strerror(error, sizeof(error), GetLastError())));
+    goto cleanup;
+  }
+
+  sib.cbData = dwSize;
+  sib.pbData = p;
+
+  storedContext = CertFindCertificateInStore(store, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, CERT_FIND_SUBJECT_NAME, &sib, NULL);
+
+  if (!storedContext)
+  {
+    DEBUG_printf(("1httpLoadCredentials: Unable to find credentials for \"%s\".", common_name));
+    goto cleanup;
+  }
+
+  *credentials = cupsArrayNew(NULL, NULL);
+  httpAddCredential(*credentials, storedContext->pbCertEncoded, storedContext->cbCertEncoded);
+
+cleanup:
+
+ /*
+  * Cleanup
+  */
+
+  if (storedContext)
+    CertFreeCertificateContext(storedContext);
+
+  if (p)
+    free(p);
+
+  if (store)
+    CertCloseStore(store, 0);
+
+  if (hProv)
+    CryptReleaseContext(hProv, 0);
 
   DEBUG_printf(("1httpLoadCredentials: Returning %d.", *credentials ? 0 : -1));
 
@@ -358,7 +528,7 @@ httpLoadCredentials(
 /*
  * 'httpSaveCredentials()' - Save X.509 credentials to a keychain file.
  *
- * @since CUPS 2.0@
+ * @since CUPS 2.0/OS 10.10@
  */
 
 int                                    /* O - -1 on error, 0 on success */
@@ -367,14 +537,128 @@ httpSaveCredentials(
     cups_array_t *credentials,         /* I - Credentials */
     const char   *common_name)         /* I - Common name for credentials */
 {
+  HCERTSTORE   store = NULL;           /* Certificate store */
+  PCCERT_CONTEXT storedContext = NULL; /* Context created from the store */
+  PCCERT_CONTEXT createdContext = NULL;        /* Context created by us */
+  DWORD                dwSize = 0;             /* 32 bit size */
+  PBYTE                p = NULL;               /* Temporary storage */
+  HCRYPTPROV   hProv = (HCRYPTPROV)NULL;
+                                       /* Handle to a CSP */
+  CRYPT_KEY_PROV_INFO ckp;             /* Handle to crypto key */
+  int          ret = -1;               /* Return value */
+#ifdef DEBUG
+  char         error[1024];            /* Error message buffer */
+#endif /* DEBUG */
+
+
   DEBUG_printf(("httpSaveCredentials(path=\"%s\", credentials=%p, common_name=\"%s\")", path, credentials, common_name));
 
   (void)path;
-  (void)credentials;
-  (void)common_name;
 
-  DEBUG_printf(("1httpSaveCredentials: Returning %d.", -1));
-  return (-1);
+  if (!common_name)
+  {
+    DEBUG_puts("1httpSaveCredentials: Bad common name, returning -1.");
+    return (-1);
+  }
+
+  createdContext = http_sspi_create_credential((http_credential_t *)cupsArrayFirst(credentials));
+  if (!createdContext)
+  {
+    DEBUG_puts("1httpSaveCredentials: Bad credentials, returning -1.");
+    return (-1);
+  }
+
+  if (!CryptAcquireContextW(&hProv, L"RememberedContainer", MS_DEF_PROV_W, PROV_RSA_FULL, CRYPT_NEWKEYSET | CRYPT_MACHINE_KEYSET))
+  {
+    if (GetLastError() == NTE_EXISTS)
+    {
+      if (!CryptAcquireContextW(&hProv, L"RememberedContainer", MS_DEF_PROV_W, PROV_RSA_FULL, CRYPT_MACHINE_KEYSET))
+      {
+        DEBUG_printf(("1httpSaveCredentials: CryptAcquireContext failed: %s", http_sspi_strerror(error, sizeof(error), GetLastError())));
+        goto cleanup;
+      }
+    }
+  }
+
+  store = CertOpenStore(CERT_STORE_PROV_SYSTEM, X509_ASN_ENCODING|PKCS_7_ASN_ENCODING, hProv, CERT_SYSTEM_STORE_LOCAL_MACHINE | CERT_STORE_NO_CRYPT_RELEASE_FLAG | CERT_STORE_OPEN_EXISTING_FLAG, L"MY");
+
+  if (!store)
+  {
+    DEBUG_printf(("1httpSaveCredentials: CertOpenSystemStore failed: %s", http_sspi_strerror(error, sizeof(error), GetLastError())));
+    goto cleanup;
+  }
+
+  dwSize = 0;
+
+  if (!CertStrToNameA(X509_ASN_ENCODING, common_name, CERT_OID_NAME_STR, NULL, NULL, &dwSize, NULL))
+  {
+    DEBUG_printf(("1httpSaveCredentials: CertStrToName failed: %s", http_sspi_strerror(error, sizeof(error), GetLastError())));
+    goto cleanup;
+  }
+
+  p = (PBYTE)malloc(dwSize);
+
+  if (!p)
+  {
+    DEBUG_printf(("1httpSaveCredentials: malloc failed for %d bytes.", dwSize));
+    goto cleanup;
+  }
+
+  if (!CertStrToNameA(X509_ASN_ENCODING, common_name, CERT_OID_NAME_STR, NULL, p, &dwSize, NULL))
+  {
+    DEBUG_printf(("1httpSaveCredentials: CertStrToName failed: %s", http_sspi_strerror(error, sizeof(error), GetLastError())));
+    goto cleanup;
+  }
+
+ /*
+  * Add the created context to the named store, and associate it with the named
+  * container...
+  */
+
+  if (!CertAddCertificateContextToStore(store, createdContext, CERT_STORE_ADD_REPLACE_EXISTING, &storedContext))
+  {
+    DEBUG_printf(("1httpSaveCredentials: CertAddCertificateContextToStore failed: %s", http_sspi_strerror(error, sizeof(error), GetLastError())));
+    goto cleanup;
+  }
+
+  ZeroMemory(&ckp, sizeof(ckp));
+  ckp.pwszContainerName = L"RememberedContainer";
+  ckp.pwszProvName      = MS_DEF_PROV_W;
+  ckp.dwProvType        = PROV_RSA_FULL;
+  ckp.dwFlags           = CRYPT_MACHINE_KEYSET;
+  ckp.dwKeySpec         = AT_KEYEXCHANGE;
+
+  if (!CertSetCertificateContextProperty(storedContext, CERT_KEY_PROV_INFO_PROP_ID, 0, &ckp))
+  {
+    DEBUG_printf(("1httpSaveCredentials: CertSetCertificateContextProperty failed: %s", http_sspi_strerror(error, sizeof(error), GetLastError())));
+    goto cleanup;
+  }
+
+  ret = 0;
+
+cleanup:
+
+ /*
+  * Cleanup
+  */
+
+  if (createdContext)
+    CertFreeCertificateContext(createdContext);
+
+  if (storedContext)
+    CertFreeCertificateContext(storedContext);
+
+  if (p)
+    free(p);
+
+  if (store)
+    CertCloseStore(store, 0);
+
+  if (hProv)
+    CryptReleaseContext(hProv, 0);
+
+  DEBUG_printf(("1httpSaveCredentials: Returning %d.", ret));
+  return (ret);
 }
 
 
@@ -415,7 +699,7 @@ _httpTLSRead(http_t *http,          /* I - HTTP connection */
             int    len)                /* I - Length of buffer */
 {
   int          i;                      /* Looping var */
-  _http_sspi_t *conn = http->tls;      /* SSPI data */
+  _http_sspi_t *sspi = http->tls;      /* SSPI data */
   SecBufferDesc        message;                /* Array of SecBuffer struct */
   SecBuffer    buffers[4] = { 0 };     /* Security package buffer */
   int          num = 0;                /* Return value */
@@ -426,28 +710,21 @@ _httpTLSRead(http_t *http,                /* I - HTTP connection */
 
   DEBUG_printf(("4_httpTLSRead(http=%p, buf=%p, len=%d)", http, buf, len));
 
-  if (!conn)
-  {
-    DEBUG_puts("5_httpTLSRead: No TLS context, returning -1.");
-    WSASetLastError(WSAEINVAL);
-    return (-1);
-  }
-
  /*
   * If there are bytes that have already been decrypted and have not yet been
   * read, return those...
   */
 
-  if (conn->readBufferUsed > 0)
+  if (sspi->readBufferUsed > 0)
   {
-    int bytesToCopy = min(conn->readBufferUsed, len);
+    int bytesToCopy = min(sspi->readBufferUsed, len);
                                        /* Number of bytes to copy */
 
-    memcpy(buf, conn->readBuffer, bytesToCopy);
-    conn->readBufferUsed -= bytesToCopy;
+    memcpy(buf, sspi->readBuffer, bytesToCopy);
+    sspi->readBufferUsed -= bytesToCopy;
 
-    if (conn->readBufferUsed > 0)
-      memmove(conn->readBuffer, conn->readBuffer + bytesToCopy, conn->readBufferUsed);
+    if (sspi->readBufferUsed > 0)
+      memmove(sspi->readBuffer, sspi->readBuffer + bytesToCopy, sspi->readBufferUsed);
 
     DEBUG_printf(("5_httpTLSRead: Returning %d bytes previously decrypted.", bytesToCopy));
 
@@ -468,44 +745,44 @@ _httpTLSRead(http_t *http,                /* I - HTTP connection */
     * If there is not enough space in the buffer, then increase its size...
     */
 
-    if (conn->decryptBufferLength <= conn->decryptBufferUsed)
+    if (sspi->decryptBufferLength <= sspi->decryptBufferUsed)
     {
       BYTE *temp;                      /* New buffer */
 
-      if (conn->decryptBufferLength >= 262144)
+      if (sspi->decryptBufferLength >= 262144)
       {
        WSASetLastError(E_OUTOFMEMORY);
         DEBUG_puts("_httpTLSRead: Decryption buffer too large (>256k)");
        return (-1);
       }
 
-      if ((temp = realloc(conn->decryptBuffer, conn->decryptBufferLength + 4096)) == NULL)
+      if ((temp = realloc(sspi->decryptBuffer, sspi->decryptBufferLength + 4096)) == NULL)
       {
-       DEBUG_printf(("_httpTLSRead: Unable to allocate %d byte decryption buffer.", conn->decryptBufferLength + 4096));
+       DEBUG_printf(("_httpTLSRead: Unable to allocate %d byte decryption buffer.", sspi->decryptBufferLength + 4096));
        WSASetLastError(E_OUTOFMEMORY);
        return (-1);
       }
 
-      conn->decryptBufferLength += 4096;
-      conn->decryptBuffer       = temp;
+      sspi->decryptBufferLength += 4096;
+      sspi->decryptBuffer       = temp;
 
-      DEBUG_printf(("_httpTLSRead: Resized decryption buffer to %d bytes.", conn->decryptBufferLength));
+      DEBUG_printf(("_httpTLSRead: Resized decryption buffer to %d bytes.", sspi->decryptBufferLength));
     }
 
-    buffers[0].pvBuffer          = conn->decryptBuffer;
-    buffers[0].cbBuffer          = (unsigned long)conn->decryptBufferUsed;
+    buffers[0].pvBuffer          = sspi->decryptBuffer;
+    buffers[0].cbBuffer          = (unsigned long)sspi->decryptBufferUsed;
     buffers[0].BufferType = SECBUFFER_DATA;
     buffers[1].BufferType = SECBUFFER_EMPTY;
     buffers[2].BufferType = SECBUFFER_EMPTY;
     buffers[3].BufferType = SECBUFFER_EMPTY;
 
-    DEBUG_printf(("5_httpTLSRead: decryptBufferUsed=%d", conn->decryptBufferUsed));
+    DEBUG_printf(("5_httpTLSRead: decryptBufferUsed=%d", sspi->decryptBufferUsed));
 
-    scRet = DecryptMessage(&conn->context, &message, 0, NULL);
+    scRet = DecryptMessage(&sspi->context, &message, 0, NULL);
 
     if (scRet == SEC_E_INCOMPLETE_MESSAGE)
     {
-      num = recv(http->fd, conn->decryptBuffer + conn->decryptBufferUsed, (int)(conn->decryptBufferLength - conn->decryptBufferUsed), 0);
+      num = recv(http->fd, sspi->decryptBuffer + sspi->decryptBufferUsed, (int)(sspi->decryptBufferLength - sspi->decryptBufferUsed), 0);
       if (num < 0)
       {
        DEBUG_printf(("5_httpTLSRead: recv failed: %d", WSAGetLastError()));
@@ -519,7 +796,7 @@ _httpTLSRead(http_t *http,          /* I - HTTP connection */
 
       DEBUG_printf(("5_httpTLSRead: Read %d bytes into decryption buffer.", num));
 
-      conn->decryptBufferUsed += num;
+      sspi->decryptBufferUsed += num;
     }
   }
   while (scRet == SEC_E_INCOMPLETE_MESSAGE);
@@ -532,9 +809,7 @@ _httpTLSRead(http_t *http,          /* I - HTTP connection */
   }
   else if (scRet != SEC_E_OK)
   {
-    char error[1024] = "";
-    FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, scRet, 0, error, sizeof(error), NULL);
-    DEBUG_printf(("5_httpTLSRead: DecryptMessage failed: %lx (%s)", scRet, error));
+    DEBUG_printf(("5_httpTLSRead: DecryptMessage failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), scRet)));
     WSASetLastError(WSASYSCALLFAILURE);
     return (-1);
   }
@@ -576,27 +851,27 @@ _httpTLSRead(http_t *http,                /* I - HTTP connection */
 
     if (bytesToSave)
     {
-      if ((conn->readBufferLength - conn->readBufferUsed) < bytesToSave)
+      if ((sspi->readBufferLength - sspi->readBufferUsed) < bytesToSave)
       {
         BYTE *temp;                    /* New buffer pointer */
 
-        if ((temp = realloc(conn->readBuffer, conn->readBufferUsed + bytesToSave)) == NULL)
+        if ((temp = realloc(sspi->readBuffer, sspi->readBufferUsed + bytesToSave)) == NULL)
        {
-         DEBUG_printf(("_httpTLSRead: Unable to allocate %d bytes.", conn->readBufferUsed + bytesToSave));
+         DEBUG_printf(("_httpTLSRead: Unable to allocate %d bytes.", sspi->readBufferUsed + bytesToSave));
          WSASetLastError(E_OUTOFMEMORY);
          return (-1);
        }
 
-       conn->readBufferLength = conn->readBufferUsed + bytesToSave;
-       conn->readBuffer       = temp;
+       sspi->readBufferLength = sspi->readBufferUsed + bytesToSave;
+       sspi->readBuffer       = temp;
       }
 
-      memcpy(((BYTE *)conn->readBuffer) + conn->readBufferUsed, ((BYTE *)pDataBuffer->pvBuffer) + bytesToCopy, bytesToSave);
+      memcpy(((BYTE *)sspi->readBuffer) + sspi->readBufferUsed, ((BYTE *)pDataBuffer->pvBuffer) + bytesToCopy, bytesToSave);
 
-      conn->readBufferUsed += bytesToSave;
+      sspi->readBufferUsed += bytesToSave;
     }
 
-    return (bytesToCopy);
+    num = bytesToCopy;
   }
   else
   {
@@ -612,15 +887,33 @@ _httpTLSRead(http_t *http,                /* I - HTTP connection */
 
   if (pExtraBuffer)
   {
-    memmove(conn->decryptBuffer, pExtraBuffer->pvBuffer, pExtraBuffer->cbBuffer);
-    conn->decryptBufferUsed = pExtraBuffer->cbBuffer;
+    memmove(sspi->decryptBuffer, pExtraBuffer->pvBuffer, pExtraBuffer->cbBuffer);
+    sspi->decryptBufferUsed = pExtraBuffer->cbBuffer;
   }
   else
   {
-    conn->decryptBufferUsed = 0;
+    sspi->decryptBufferUsed = 0;
   }
 
-  return (0);
+  return (num);
+}
+
+
+/*
+ * '_httpTLSSetOptions()' - Set TLS protocol and cipher suite options.
+ */
+
+void
+_httpTLSSetOptions(int options,                /* I - Options */
+                   int min_version,    /* I - Minimum TLS version */
+                   int max_version)    /* I - Maximum TLS version */
+{
+  if (!(options & _HTTP_TLS_SET_DEFAULT) || tls_options < 0)
+  {
+    tls_options     = options;
+    tls_min_version = min_version;
+    tls_max_version = max_version;
+  }
 }
 
 
@@ -635,7 +928,14 @@ _httpTLSStart(http_t *http)                /* I - HTTP connection */
        *hostptr;                       /* Pointer into hostname */
 
 
-  DEBUG_printf(("7_httpTLSStart(http=%p)", http));
+  DEBUG_printf(("3_httpTLSStart(http=%p)", http));
+
+  if (tls_options < 0)
+  {
+    DEBUG_puts("4_httpTLSStart: Setting defaults.");
+    _cupsSetDefaults();
+    DEBUG_printf(("4_httpTLSStart: tls_options=%x", tls_options));
+  }
 
   if ((http->tls = http_sspi_alloc()) == NULL)
     return (-1);
@@ -670,7 +970,7 @@ _httpTLSStart(http_t *http)         /* I - HTTP connection */
     * Server: determine hostname to use...
     */
 
-    if (http->fields[HTTP_FIELD_HOST][0])
+    if (http->fields[HTTP_FIELD_HOST])
     {
      /*
       * Use hostname for TLS upgrade...
@@ -714,10 +1014,10 @@ _httpTLSStart(http_t *http)              /* I - HTTP connection */
 void
 _httpTLSStop(http_t *http)             /* I - HTTP connection */
 {
-  _http_sspi_t *conn = http->tls;      /* SSPI data */
+  _http_sspi_t *sspi = http->tls;      /* SSPI data */
 
 
-  if (conn->contextInitialized && http->fd >= 0)
+  if (sspi->contextInitialized && http->fd >= 0)
   {
     SecBufferDesc      message;        /* Array of SecBuffer struct */
     SecBuffer          buffers[1] = { 0 };
@@ -739,7 +1039,7 @@ _httpTLSStop(http_t *http)         /* I - HTTP connection */
    message.pBuffers  = buffers;
    message.ulVersion = SECBUFFER_VERSION;
 
-   status = ApplyControlToken(&conn->context, &message);
+   status = ApplyControlToken(&sspi->context, &message);
 
    if (SUCCEEDED(status))
    {
@@ -765,7 +1065,7 @@ _httpTLSStop(http_t *http)         /* I - HTTP connection */
      message.pBuffers  = buffers;
      message.ulVersion = SECBUFFER_VERSION;
 
-     status = AcceptSecurityContext(&conn->creds, &conn->context, NULL,
+     status = AcceptSecurityContext(&sspi->creds, &sspi->context, NULL,
                                     dwSSPIFlags, SECURITY_NATIVE_DREP, NULL,
                                     &message, &dwSSPIOutFlags, &tsExpiry);
 
@@ -794,16 +1094,16 @@ _httpTLSStop(http_t *http)               /* I - HTTP connection */
       }
       else
       {
-        DEBUG_printf(("_httpTLSStop: AcceptSecurityContext failed: %x", status));
+        DEBUG_printf(("_httpTLSStop: AcceptSecurityContext failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), status)));
       }
     }
     else
     {
-      DEBUG_printf(("_httpTLSStop: ApplyControlToken failed: %x", status));
+      DEBUG_printf(("_httpTLSStop: ApplyControlToken failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), status)));
     }
   }
 
-  http_sspi_free(conn);
+  http_sspi_free(sspi);
 
   http->tls = NULL;
 }
@@ -818,7 +1118,7 @@ _httpTLSWrite(http_t     *http,            /* I - HTTP connection */
              const char *buf,          /* I - Buffer holding data */
              int        len)           /* I - Length of buffer */
 {
-  _http_sspi_t *conn = http->tls;      /* SSPI data */
+  _http_sspi_t *sspi = http->tls;      /* SSPI data */
   SecBufferDesc        message;                /* Array of SecBuffer struct */
   SecBuffer    buffers[4] = { 0 };     /* Security package buffer */
   int          bufferLen;              /* Buffer length */
@@ -827,21 +1127,21 @@ _httpTLSWrite(http_t     *http,          /* I - HTTP connection */
   int          num = 0;                /* Return value */
 
 
-  bufferLen = conn->streamSizes.cbMaximumMessage + conn->streamSizes.cbHeader + conn->streamSizes.cbTrailer;
+  bufferLen = sspi->streamSizes.cbMaximumMessage + sspi->streamSizes.cbHeader + sspi->streamSizes.cbTrailer;
 
-  if (bufferLen > conn->writeBufferLength)
+  if (bufferLen > sspi->writeBufferLength)
   {
     BYTE *temp;                                /* New buffer pointer */
 
-    if ((temp = (BYTE *)realloc(conn->writeBuffer, bufferLen)) == NULL)
+    if ((temp = (BYTE *)realloc(sspi->writeBuffer, bufferLen)) == NULL)
     {
       DEBUG_printf(("_httpTLSWrite: Unable to allocate buffer of %d bytes.", bufferLen));
       WSASetLastError(E_OUTOFMEMORY);
       return (-1);
     }
 
-    conn->writeBuffer       = temp;
-    conn->writeBufferLength = bufferLen;
+    sspi->writeBuffer       = temp;
+    sspi->writeBufferLength = bufferLen;
   }
 
   bytesLeft = len;
@@ -849,7 +1149,7 @@ _httpTLSWrite(http_t     *http,            /* I - HTTP connection */
 
   while (bytesLeft)
   {
-    int chunk = min((int)conn->streamSizes.cbMaximumMessage, bytesLeft);
+    int chunk = min((int)sspi->streamSizes.cbMaximumMessage, bytesLeft);
                                        /* Size of data to write */
     SECURITY_STATUS scRet;             /* SSPI status */
 
@@ -857,7 +1157,7 @@ _httpTLSWrite(http_t     *http,            /* I - HTTP connection */
     * Copy user data into the buffer, starting just past the header...
     */
 
-    memcpy(conn->writeBuffer + conn->streamSizes.cbHeader, bufptr, chunk);
+    memcpy(sspi->writeBuffer + sspi->streamSizes.cbHeader, bufptr, chunk);
 
    /*
     * Setup the SSPI buffers
@@ -867,14 +1167,14 @@ _httpTLSWrite(http_t     *http,          /* I - HTTP connection */
     message.cBuffers  = 4;
     message.pBuffers  = buffers;
 
-    buffers[0].pvBuffer   = conn->writeBuffer;
-    buffers[0].cbBuffer   = conn->streamSizes.cbHeader;
+    buffers[0].pvBuffer   = sspi->writeBuffer;
+    buffers[0].cbBuffer   = sspi->streamSizes.cbHeader;
     buffers[0].BufferType = SECBUFFER_STREAM_HEADER;
-    buffers[1].pvBuffer   = conn->writeBuffer + conn->streamSizes.cbHeader;
+    buffers[1].pvBuffer   = sspi->writeBuffer + sspi->streamSizes.cbHeader;
     buffers[1].cbBuffer   = (unsigned long) chunk;
     buffers[1].BufferType = SECBUFFER_DATA;
-    buffers[2].pvBuffer   = conn->writeBuffer + conn->streamSizes.cbHeader + chunk;
-    buffers[2].cbBuffer   = conn->streamSizes.cbTrailer;
+    buffers[2].pvBuffer   = sspi->writeBuffer + sspi->streamSizes.cbHeader + chunk;
+    buffers[2].cbBuffer   = sspi->streamSizes.cbTrailer;
     buffers[2].BufferType = SECBUFFER_STREAM_TRAILER;
     buffers[3].BufferType = SECBUFFER_EMPTY;
 
@@ -882,11 +1182,11 @@ _httpTLSWrite(http_t     *http,          /* I - HTTP connection */
     * Encrypt the data
     */
 
-    scRet = EncryptMessage(&conn->context, 0, &message, 0);
+    scRet = EncryptMessage(&sspi->context, 0, &message, 0);
 
     if (FAILED(scRet))
     {
-      DEBUG_printf(("_httpTLSWrite: EncryptMessage failed: %x", scRet));
+      DEBUG_printf(("_httpTLSWrite: EncryptMessage failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), scRet)));
       WSASetLastError(WSASYSCALLFAILURE);
       return (-1);
     }
@@ -897,7 +1197,7 @@ _httpTLSWrite(http_t     *http,            /* I - HTTP connection */
     * of the trailer...
     */
 
-    num = send(http->fd, conn->writeBuffer, buffers[0].cbBuffer + buffers[1].cbBuffer + buffers[2].cbBuffer, 0);
+    num = send(http->fd, sspi->writeBuffer, buffers[0].cbBuffer + buffers[1].cbBuffer + buffers[2].cbBuffer, 0);
 
     if (num <= 0)
     {
@@ -1008,9 +1308,7 @@ http_setup_ssl(http_t *http)              /* I - Connection to server */
 static _http_sspi_t *                  /* O  - New SSPI/SSL object */
 http_sspi_alloc(void)
 {
-  _http_sspi_t *conn = calloc(sizeof(_http_sspi_t), 1);
-
-  return (conn);
+  return ((_http_sspi_t *)calloc(sizeof(_http_sspi_t), 1));
 }
 
 
@@ -1022,7 +1320,7 @@ static int                                /* O - 0 on success, -1 on failure */
 http_sspi_client(http_t     *http,     /* I - Client connection */
                  const char *hostname) /* I - Server hostname */
 {
-  _http_sspi_t *conn = http->tls;      /* SSPI data */
+  _http_sspi_t *sspi = http->tls;      /* SSPI data */
   DWORD                dwSize;                 /* Size for buffer */
   DWORD                dwSSPIFlags;            /* SSL connection attributes we want */
   DWORD                dwSSPIOutFlags;         /* SSL connection attributes we got */
@@ -1038,7 +1336,7 @@ http_sspi_client(http_t     *http,        /* I - Client connection */
                common_name[1024];      /* CN=username */
 
 
-  DEBUG_printf(("http_sspi_client(http=%p, hostname=\"%s\")", http, hostname));
+  DEBUG_printf(("4http_sspi_client(http=%p, hostname=\"%s\")", http, hostname));
 
   dwSSPIFlags = ISC_REQ_SEQUENCE_DETECT   |
                 ISC_REQ_REPLAY_DETECT     |
@@ -1052,14 +1350,15 @@ http_sspi_client(http_t     *http,      /* I - Client connection */
   */
 
   dwSize = sizeof(username);
-  GetUserName(username, &dwSize);
+  GetUserNameA(username, &dwSize);
   snprintf(common_name, sizeof(common_name), "CN=%s", username);
 
-  if (!http_sspi_credentials(http, L"ClientContainer", common_name, FALSE))
-  {
-    DEBUG_puts("http_sspi_client: Unable to get client credentials.");
-    return (-1);
-  }
+  if (!http_sspi_find_credentials(http, L"ClientContainer", common_name))
+    if (!http_sspi_make_credentials(http->tls, L"ClientContainer", common_name, _HTTP_MODE_CLIENT, 10))
+    {
+      DEBUG_puts("5http_sspi_client: Unable to get client credentials.");
+      return (-1);
+    }
 
  /*
   * Initiate a ClientHello message and generate a token.
@@ -1073,11 +1372,11 @@ http_sspi_client(http_t     *http,      /* I - Client connection */
   outBuffer.pBuffers  = outBuffers;
   outBuffer.ulVersion = SECBUFFER_VERSION;
 
-  scRet = InitializeSecurityContext(&conn->creds, NULL, TEXT(""), dwSSPIFlags, 0, SECURITY_NATIVE_DREP, NULL, 0, &conn->context, &outBuffer, &dwSSPIOutFlags, &tsExpiry);
+  scRet = InitializeSecurityContext(&sspi->creds, NULL, TEXT(""), dwSSPIFlags, 0, SECURITY_NATIVE_DREP, NULL, 0, &sspi->context, &outBuffer, &dwSSPIOutFlags, &tsExpiry);
 
   if (scRet != SEC_I_CONTINUE_NEEDED)
   {
-    DEBUG_printf(("http_sspi_client: InitializeSecurityContext(1) failed: %x", scRet));
+    DEBUG_printf(("5http_sspi_client: InitializeSecurityContext(1) failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), scRet)));
     return (-1);
   }
 
@@ -1089,13 +1388,13 @@ http_sspi_client(http_t     *http,      /* I - Client connection */
   {
     if ((cbData = send(http->fd, outBuffers[0].pvBuffer, outBuffers[0].cbBuffer, 0)) <= 0)
     {
-      DEBUG_printf(("http_sspi_client: send failed: %d", WSAGetLastError()));
+      DEBUG_printf(("5http_sspi_client: send failed: %d", WSAGetLastError()));
       FreeContextBuffer(outBuffers[0].pvBuffer);
-      DeleteSecurityContext(&conn->context);
+      DeleteSecurityContext(&sspi->context);
       return (-1);
     }
 
-    DEBUG_printf(("http_sspi_client: %d bytes of handshake data sent.", cbData));
+    DEBUG_printf(("5http_sspi_client: %d bytes of handshake data sent.", cbData));
 
     FreeContextBuffer(outBuffers[0].pvBuffer);
     outBuffers[0].pvBuffer = NULL;
@@ -1109,7 +1408,7 @@ http_sspi_client(http_t     *http,        /* I - Client connection */
                 ISC_REQ_ALLOCATE_MEMORY        |
                 ISC_REQ_STREAM;
 
-  conn->decryptBufferUsed = 0;
+  sspi->decryptBufferUsed = 0;
 
  /*
   * Loop until the handshake is finished or an error occurs.
@@ -1121,46 +1420,46 @@ http_sspi_client(http_t     *http,      /* I - Client connection */
         scRet == SEC_E_INCOMPLETE_MESSAGE     ||
         scRet == SEC_I_INCOMPLETE_CREDENTIALS)
   {
-    if (conn->decryptBufferUsed == 0 || scRet == SEC_E_INCOMPLETE_MESSAGE)
+    if (sspi->decryptBufferUsed == 0 || scRet == SEC_E_INCOMPLETE_MESSAGE)
     {
-      if (conn->decryptBufferLength <= conn->decryptBufferUsed)
+      if (sspi->decryptBufferLength <= sspi->decryptBufferUsed)
       {
        BYTE *temp;                     /* New buffer */
 
-       if (conn->decryptBufferLength >= 262144)
+       if (sspi->decryptBufferLength >= 262144)
        {
          WSASetLastError(E_OUTOFMEMORY);
-         DEBUG_puts("http_sspi_client: Decryption buffer too large (>256k)");
+         DEBUG_puts("5http_sspi_client: Decryption buffer too large (>256k)");
          return (-1);
        }
 
-       if ((temp = realloc(conn->decryptBuffer, conn->decryptBufferLength + 4096)) == NULL)
+       if ((temp = realloc(sspi->decryptBuffer, sspi->decryptBufferLength + 4096)) == NULL)
        {
-         DEBUG_printf(("http_sspi_client: Unable to allocate %d byte buffer.", conn->decryptBufferLength + 4096));
+         DEBUG_printf(("5http_sspi_client: Unable to allocate %d byte buffer.", sspi->decryptBufferLength + 4096));
          WSASetLastError(E_OUTOFMEMORY);
          return (-1);
        }
 
-       conn->decryptBufferLength += 4096;
-       conn->decryptBuffer       = temp;
+       sspi->decryptBufferLength += 4096;
+       sspi->decryptBuffer       = temp;
       }
 
-      cbData = recv(http->fd, conn->decryptBuffer + conn->decryptBufferUsed, (int)(conn->decryptBufferLength - conn->decryptBufferUsed), 0);
+      cbData = recv(http->fd, sspi->decryptBuffer + sspi->decryptBufferUsed, (int)(sspi->decryptBufferLength - sspi->decryptBufferUsed), 0);
 
       if (cbData < 0)
       {
-        DEBUG_printf(("http_sspi_client: recv failed: %d", WSAGetLastError()));
+        DEBUG_printf(("5http_sspi_client: recv failed: %d", WSAGetLastError()));
         return (-1);
       }
       else if (cbData == 0)
       {
-        DEBUG_printf(("http_sspi_client: Server unexpectedly disconnected."));
+        DEBUG_printf(("5http_sspi_client: Server unexpectedly disconnected."));
         return (-1);
       }
 
-      DEBUG_printf(("http_sspi_client: %d bytes of handshake data received", cbData));
+      DEBUG_printf(("5http_sspi_client: %d bytes of handshake data received", cbData));
 
-      conn->decryptBufferUsed += cbData;
+      sspi->decryptBufferUsed += cbData;
     }
 
    /*
@@ -1170,8 +1469,8 @@ http_sspi_client(http_t     *http,        /* I - Client connection */
     * SECBUFFER_EXTRA.
     */
 
-    inBuffers[0].pvBuffer   = conn->decryptBuffer;
-    inBuffers[0].cbBuffer   = (unsigned long)conn->decryptBufferUsed;
+    inBuffers[0].pvBuffer   = sspi->decryptBuffer;
+    inBuffers[0].cbBuffer   = (unsigned long)sspi->decryptBufferUsed;
     inBuffers[0].BufferType = SECBUFFER_TOKEN;
 
     inBuffers[1].pvBuffer   = NULL;
@@ -1199,7 +1498,7 @@ http_sspi_client(http_t     *http,        /* I - Client connection */
     * Call InitializeSecurityContext.
     */
 
-    scRet = InitializeSecurityContext(&conn->creds, &conn->context, NULL, dwSSPIFlags, 0, SECURITY_NATIVE_DREP, &inBuffer, 0, NULL, &outBuffer, &dwSSPIOutFlags, &tsExpiry);
+    scRet = InitializeSecurityContext(&sspi->creds, &sspi->context, NULL, dwSSPIFlags, 0, SECURITY_NATIVE_DREP, &inBuffer, 0, NULL, &outBuffer, &dwSSPIOutFlags, &tsExpiry);
 
    /*
     * If InitializeSecurityContext was successful (or if the error was one of
@@ -1217,13 +1516,13 @@ http_sspi_client(http_t     *http,      /* I - Client connection */
 
         if (cbData <= 0)
         {
-          DEBUG_printf(("http_sspi_client: send failed: %d", WSAGetLastError()));
+          DEBUG_printf(("5http_sspi_client: send failed: %d", WSAGetLastError()));
           FreeContextBuffer(outBuffers[0].pvBuffer);
-          DeleteSecurityContext(&conn->context);
+          DeleteSecurityContext(&sspi->context);
           return (-1);
         }
 
-        DEBUG_printf(("http_sspi_client: %d bytes of handshake data sent.", cbData));
+        DEBUG_printf(("5http_sspi_client: %d bytes of handshake data sent.", cbData));
 
        /*
         * Free output buffer.
@@ -1255,18 +1554,18 @@ http_sspi_client(http_t     *http,      /* I - Client connection */
       * later decrypt it with DecryptMessage.
       */
 
-      DEBUG_puts("http_sspi_client: Handshake was successful.");
+      DEBUG_puts("5http_sspi_client: Handshake was successful.");
 
       if (inBuffers[1].BufferType == SECBUFFER_EXTRA)
       {
-        memmove(conn->decryptBuffer, conn->decryptBuffer + conn->decryptBufferUsed - inBuffers[1].cbBuffer, inBuffers[1].cbBuffer);
+        memmove(sspi->decryptBuffer, sspi->decryptBuffer + sspi->decryptBufferUsed - inBuffers[1].cbBuffer, inBuffers[1].cbBuffer);
 
-        conn->decryptBufferUsed = inBuffers[1].cbBuffer;
+        sspi->decryptBufferUsed = inBuffers[1].cbBuffer;
 
-        DEBUG_printf(("http_sspi_client: %d bytes of app data was bundled with handshake data", conn->decryptBufferUsed));
+        DEBUG_printf(("5http_sspi_client: %d bytes of app data was bundled with handshake data", sspi->decryptBufferUsed));
       }
       else
-        conn->decryptBufferUsed = 0;
+        sspi->decryptBufferUsed = 0;
 
      /*
       * Bail out to quit
@@ -1281,7 +1580,7 @@ http_sspi_client(http_t     *http,        /* I - Client connection */
 
     if (FAILED(scRet))
     {
-      DEBUG_printf(("http_sspi_client: InitializeSecurityContext(2) failed: %x", scRet));
+      DEBUG_printf(("5http_sspi_client: InitializeSecurityContext(2) failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), scRet)));
       ret = -1;
       break;
     }
@@ -1297,7 +1596,7 @@ http_sspi_client(http_t     *http,        /* I - Client connection */
       * Unimplemented
       */
 
-      DEBUG_printf(("http_sspi_client: server requested client credentials."));
+      DEBUG_printf(("5http_sspi_client: server requested client credentials."));
       ret = -1;
       break;
     }
@@ -1308,13 +1607,13 @@ http_sspi_client(http_t     *http,      /* I - Client connection */
 
     if (inBuffers[1].BufferType == SECBUFFER_EXTRA)
     {
-      memmove(conn->decryptBuffer, conn->decryptBuffer + conn->decryptBufferUsed - inBuffers[1].cbBuffer, inBuffers[1].cbBuffer);
+      memmove(sspi->decryptBuffer, sspi->decryptBuffer + sspi->decryptBufferUsed - inBuffers[1].cbBuffer, inBuffers[1].cbBuffer);
 
-      conn->decryptBufferUsed = inBuffers[1].cbBuffer;
+      sspi->decryptBufferUsed = inBuffers[1].cbBuffer;
     }
     else
     {
-      conn->decryptBufferUsed = 0;
+      sspi->decryptBufferUsed = 0;
     }
   }
 
@@ -1324,37 +1623,25 @@ http_sspi_client(http_t     *http,      /* I - Client connection */
     * Success!  Get the server cert
     */
 
-    conn->contextInitialized = TRUE;
+    sspi->contextInitialized = TRUE;
 
-    scRet = QueryContextAttributes(&conn->context, SECPKG_ATTR_REMOTE_CERT_CONTEXT, (VOID *)&(conn->remoteCert));
+    scRet = QueryContextAttributes(&sspi->context, SECPKG_ATTR_REMOTE_CERT_CONTEXT, (VOID *)&(sspi->remoteCert));
 
     if (scRet != SEC_E_OK)
     {
-      DEBUG_printf(("http_sspi_client: QueryContextAttributes failed(SECPKG_ATTR_REMOTE_CERT_CONTEXT): %x", scRet));
+      DEBUG_printf(("5http_sspi_client: QueryContextAttributes failed(SECPKG_ATTR_REMOTE_CERT_CONTEXT): %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), scRet)));
       return (-1);
     }
 
-#if 0
-    /* TODO: Move this out for opt-in server cert validation, like other platforms. */
-    scRet = http_sspi_verify(conn->remoteCert, hostname, conn->certFlags);
-
-    if (scRet != SEC_E_OK)
-    {
-      DEBUG_printf(("http_sspi_client: sspi_verify_certificate failed: %x", scRet));
-      ret = -1;
-      goto cleanup;
-    }
-#endif // 0
-
    /*
     * Find out how big the header/trailer will be:
     */
 
-    scRet = QueryContextAttributes(&conn->context, SECPKG_ATTR_STREAM_SIZES, &conn->streamSizes);
+    scRet = QueryContextAttributes(&sspi->context, SECPKG_ATTR_STREAM_SIZES, &sspi->streamSizes);
 
     if (scRet != SEC_E_OK)
     {
-      DEBUG_printf(("http_sspi_client: QueryContextAttributes failed(SECPKG_ATTR_STREAM_SIZES): %x", scRet));
+      DEBUG_printf(("5http_sspi_client: QueryContextAttributes failed(SECPKG_ATTR_STREAM_SIZES): %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), scRet)));
       ret = -1;
     }
   }
@@ -1364,20 +1651,33 @@ http_sspi_client(http_t     *http,      /* I - Client connection */
 
 
 /*
- * 'http_sspi_credentials()' - Retrieve a TLS certificate from the system store.
+ * 'http_sspi_create_credential()' - Create an SSPI certificate context.
+ */
+
+static PCCERT_CONTEXT                  /* O - Certificate context */
+http_sspi_create_credential(
+    http_credential_t *cred)           /* I - Credential */
+{
+  if (cred)
+    return (CertCreateCertificateContext(X509_ASN_ENCODING, cred->data, cred->datalen));
+  else
+    return (NULL);
+}
+
+
+/*
+ * 'http_sspi_find_credentials()' - Retrieve a TLS certificate from the system store.
  */
 
 static BOOL                            /* O - 1 on success, 0 on failure */
-http_sspi_credentials(
+http_sspi_find_credentials(
     http_t       *http,                        /* I - HTTP connection */
     const LPWSTR container,            /* I - Cert container name */
-    const char   *common_name,         /* I - Common name of certificate */
-    BOOL         isServer)             /* I - Is caller a server? */
+    const char   *common_name)         /* I - Common name of certificate */
 {
-  _http_sspi_t *conn = http->tls;      /* SSPI data */
+  _http_sspi_t *sspi = http->tls;      /* SSPI data */
   HCERTSTORE   store = NULL;           /* Certificate store */
   PCCERT_CONTEXT storedContext = NULL; /* Context created from the store */
-  PCCERT_CONTEXT createdContext = NULL;        /* Context created by us */
   DWORD                dwSize = 0;             /* 32 bit size */
   PBYTE                p = NULL;               /* Temporary storage */
   HCRYPTPROV   hProv = (HCRYPTPROV)NULL;
@@ -1386,11 +1686,6 @@ http_sspi_credentials(
   SCHANNEL_CRED        SchannelCred;           /* Schannel credential data */
   TimeStamp    tsExpiry;               /* Time stamp */
   SECURITY_STATUS Status;              /* Status */
-  HCRYPTKEY    hKey = (HCRYPTKEY)NULL; /* Handle to crypto key */
-  CRYPT_KEY_PROV_INFO kpi;             /* Key container info */
-  SYSTEMTIME   et;                     /* System time */
-  CERT_EXTENSIONS exts;                        /* Array of cert extensions */
-  CRYPT_KEY_PROV_INFO ckp;             /* Handle to crypto key */
   BOOL         ok = TRUE;              /* Return value */
 
 
@@ -1400,7 +1695,7 @@ http_sspi_credentials(
     {
       if (!CryptAcquireContextW(&hProv, (LPWSTR)container, MS_DEF_PROV_W, PROV_RSA_FULL, CRYPT_MACHINE_KEYSET))
       {
-        DEBUG_printf(("http_sspi_credentials: CryptAcquireContext failed: %x", GetLastError()));
+        DEBUG_printf(("5http_sspi_find_credentials: CryptAcquireContext failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), GetLastError())));
         ok = FALSE;
         goto cleanup;
       }
@@ -1411,16 +1706,16 @@ http_sspi_credentials(
 
   if (!store)
   {
-    DEBUG_printf(("http_sspi_credentials: CertOpenSystemStore failed: %x", GetLastError()));
+    DEBUG_printf(("5http_sspi_find_credentials: CertOpenSystemStore failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), GetLastError())));
     ok = FALSE;
     goto cleanup;
   }
 
   dwSize = 0;
 
-  if (!CertStrToName(X509_ASN_ENCODING, common_name, CERT_OID_NAME_STR, NULL, NULL, &dwSize, NULL))
+  if (!CertStrToNameA(X509_ASN_ENCODING, common_name, CERT_OID_NAME_STR, NULL, NULL, &dwSize, NULL))
   {
-    DEBUG_printf(("http_sspi_credentials: CertStrToName failed: %x", GetLastError()));
+    DEBUG_printf(("5http_sspi_find_credentials: CertStrToName failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), GetLastError())));
     ok = FALSE;
     goto cleanup;
   }
@@ -1429,14 +1724,14 @@ http_sspi_credentials(
 
   if (!p)
   {
-    DEBUG_printf(("http_sspi_credentials: malloc failed for %d bytes", dwSize));
+    DEBUG_printf(("5http_sspi_find_credentials: malloc failed for %d bytes.", dwSize));
     ok = FALSE;
     goto cleanup;
   }
 
-  if (!CertStrToName(X509_ASN_ENCODING, common_name, CERT_OID_NAME_STR, NULL, p, &dwSize, NULL))
+  if (!CertStrToNameA(X509_ASN_ENCODING, common_name, CERT_OID_NAME_STR, NULL, p, &dwSize, NULL))
   {
-    DEBUG_printf(("_sspiGetCredentials: CertStrToName failed: %x", GetLastError()));
+    DEBUG_printf(("5http_sspi_find_credentials: CertStrToName failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), GetLastError())));
     ok = FALSE;
     goto cleanup;
   }
@@ -1448,60 +1743,274 @@ http_sspi_credentials(
 
   if (!storedContext)
   {
-   /*
-    * If we couldn't find the context, then we'll create a new one...
-    */
+    DEBUG_printf(("5http_sspi_find_credentials: Unable to find credentials for \"%s\".", common_name));
+    ok = FALSE;
+    goto cleanup;
+  }
 
-    if (!CryptGenKey(hProv, AT_KEYEXCHANGE, CRYPT_EXPORTABLE, &hKey))
-    {
-      DEBUG_printf(("_sspiGetCredentials: CryptGenKey failed: %x", GetLastError()));
-      ok = FALSE;
-      goto cleanup;
-    }
+  ZeroMemory(&SchannelCred, sizeof(SchannelCred));
 
-    ZeroMemory(&kpi, sizeof(kpi));
-    kpi.pwszContainerName = (LPWSTR)container;
-    kpi.pwszProvName      = MS_DEF_PROV_W;
-    kpi.dwProvType        = PROV_RSA_FULL;
-    kpi.dwFlags           = CERT_SET_KEY_CONTEXT_PROP_ID;
-    kpi.dwKeySpec         = AT_KEYEXCHANGE;
+  SchannelCred.dwVersion = SCHANNEL_CRED_VERSION;
+  SchannelCred.cCreds    = 1;
+  SchannelCred.paCred    = &storedContext;
 
-    GetSystemTime(&et);
-    et.wYear += 10;
+ /*
+  * Set supported protocols (can also be overriden in the registry...)
+  */
 
-    ZeroMemory(&exts, sizeof(exts));
+#ifdef SP_PROT_TLS1_2_SERVER
+  if (http->mode == _HTTP_MODE_SERVER)
+  {
+    if (tls_min_version > _HTTP_TLS_1_1)
+      SchannelCred.grbitEnabledProtocols = SP_PROT_TLS1_2_SERVER;
+    else if (tls_min_version > _HTTP_TLS_1_0)
+      SchannelCred.grbitEnabledProtocols = SP_PROT_TLS1_2_SERVER | SP_PROT_TLS1_1_SERVER;
+    else if (tls_min_version == _HTTP_TLS_SSL3)
+      SchannelCred.grbitEnabledProtocols = SP_PROT_TLS1_2_SERVER | SP_PROT_TLS1_1_SERVER | SP_PROT_TLS1_0_SERVER | SP_PROT_SSL3_SERVER;
+    else
+      SchannelCred.grbitEnabledProtocols = SP_PROT_TLS1_2_SERVER | SP_PROT_TLS1_1_SERVER | SP_PROT_TLS1_0_SERVER;
+  }
+  else
+  {
+    if (tls_min_version > _HTTP_TLS_1_1)
+      SchannelCred.grbitEnabledProtocols = SP_PROT_TLS1_2_CLIENT;
+    else if (tls_min_version > _HTTP_TLS_1_0)
+      SchannelCred.grbitEnabledProtocols = SP_PROT_TLS1_2_CLIENT | SP_PROT_TLS1_1_CLIENT;
+    else if (tls_min_version == _HTTP_TLS_SSL3)
+      SchannelCred.grbitEnabledProtocols = SP_PROT_TLS1_2_CLIENT | SP_PROT_TLS1_1_CLIENT | SP_PROT_TLS1_0_CLIENT | SP_PROT_SSL3_CLIENT;
+    else
+      SchannelCred.grbitEnabledProtocols = SP_PROT_TLS1_2_CLIENT | SP_PROT_TLS1_1_CLIENT | SP_PROT_TLS1_0_CLIENT;
+  }
 
-    createdContext = CertCreateSelfSignCertificate(hProv, &sib, 0, &kpi, NULL, NULL, &et, &exts);
+#else
+  if (http->mode == _HTTP_MODE_SERVER)
+  {
+    if (tls_min_version == _HTTP_TLS_SSL3)
+      SchannelCred.grbitEnabledProtocols = SP_PROT_TLS1_SERVER | SP_PROT_SSL3_SERVER;
+    else
+      SchannelCred.grbitEnabledProtocols = SP_PROT_TLS1_SERVER;
+  }
+  else
+  {
+    if (tls_min_version == _HTTP_TLS_SSL3)
+      SchannelCred.grbitEnabledProtocols = SP_PROT_TLS1_CLIENT | SP_PROT_SSL3_CLIENT;
+    else
+      SchannelCred.grbitEnabledProtocols = SP_PROT_TLS1_CLIENT;
+  }
+#endif /* SP_PROT_TLS1_2_SERVER */
 
-    if (!createdContext)
-    {
-      DEBUG_printf(("_sspiGetCredentials: CertCreateSelfSignCertificate failed: %x", GetLastError()));
-      ok = FALSE;
-      goto cleanup;
-    }
+  /* TODO: Support _HTTP_TLS_ALLOW_RC4, _HTTP_TLS_ALLOW_DH, and _HTTP_TLS_DENY_CBC options; right now we'll rely on Windows registry to enable/disable RC4/DH/CBC... */
 
-    if (!CertAddCertificateContextToStore(store, createdContext, CERT_STORE_ADD_REPLACE_EXISTING, &storedContext))
-    {
-      DEBUG_printf(("_sspiGetCredentials: CertAddCertificateContextToStore failed: %x", GetLastError()));
-      ok = FALSE;
-      goto cleanup;
-    }
+ /*
+  * Create an SSPI credential.
+  */
+
+  Status = AcquireCredentialsHandle(NULL, UNISP_NAME, http->mode == _HTTP_MODE_SERVER ? SECPKG_CRED_INBOUND : SECPKG_CRED_OUTBOUND, NULL, &SchannelCred, NULL, NULL, &sspi->creds, &tsExpiry);
+  if (Status != SEC_E_OK)
+  {
+    DEBUG_printf(("5http_sspi_find_credentials: AcquireCredentialsHandle failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), Status)));
+    ok = FALSE;
+    goto cleanup;
+  }
+
+cleanup:
+
+ /*
+  * Cleanup
+  */
+
+  if (storedContext)
+    CertFreeCertificateContext(storedContext);
+
+  if (p)
+    free(p);
+
+  if (store)
+    CertCloseStore(store, 0);
+
+  if (hProv)
+    CryptReleaseContext(hProv, 0);
+
+  return (ok);
+}
+
+
+/*
+ * 'http_sspi_free()' - Close a connection and free resources.
+ */
+
+static void
+http_sspi_free(_http_sspi_t *sspi)     /* I - SSPI data */
+{
+  if (!sspi)
+    return;
+
+  if (sspi->contextInitialized)
+    DeleteSecurityContext(&sspi->context);
+
+  if (sspi->decryptBuffer)
+    free(sspi->decryptBuffer);
+
+  if (sspi->readBuffer)
+    free(sspi->readBuffer);
+
+  if (sspi->writeBuffer)
+    free(sspi->writeBuffer);
+
+  if (sspi->localCert)
+    CertFreeCertificateContext(sspi->localCert);
+
+  if (sspi->remoteCert)
+    CertFreeCertificateContext(sspi->remoteCert);
+
+  free(sspi);
+}
+
+
+/*
+ * 'http_sspi_make_credentials()' - Create a TLS certificate in the system store.
+ */
+
+static BOOL                            /* O - 1 on success, 0 on failure */
+http_sspi_make_credentials(
+    _http_sspi_t *sspi,                        /* I - SSPI data */
+    const LPWSTR container,            /* I - Cert container name */
+    const char   *common_name,         /* I - Common name of certificate */
+    _http_mode_t mode,                 /* I - Client or server? */
+    int          years)                        /* I - Years until expiration */
+{
+  HCERTSTORE   store = NULL;           /* Certificate store */
+  PCCERT_CONTEXT storedContext = NULL; /* Context created from the store */
+  PCCERT_CONTEXT createdContext = NULL;        /* Context created by us */
+  DWORD                dwSize = 0;             /* 32 bit size */
+  PBYTE                p = NULL;               /* Temporary storage */
+  HCRYPTPROV   hProv = (HCRYPTPROV)NULL;
+                                       /* Handle to a CSP */
+  CERT_NAME_BLOB sib;                  /* Arbitrary array of bytes */
+  SCHANNEL_CRED        SchannelCred;           /* Schannel credential data */
+  TimeStamp    tsExpiry;               /* Time stamp */
+  SECURITY_STATUS Status;              /* Status */
+  HCRYPTKEY    hKey = (HCRYPTKEY)NULL; /* Handle to crypto key */
+  CRYPT_KEY_PROV_INFO kpi;             /* Key container info */
+  SYSTEMTIME   et;                     /* System time */
+  CERT_EXTENSIONS exts;                        /* Array of cert extensions */
+  CRYPT_KEY_PROV_INFO ckp;             /* Handle to crypto key */
+  BOOL         ok = TRUE;              /* Return value */
 
-    ZeroMemory(&ckp, sizeof(ckp));
-    ckp.pwszContainerName = (LPWSTR) container;
-    ckp.pwszProvName      = MS_DEF_PROV_W;
-    ckp.dwProvType        = PROV_RSA_FULL;
-    ckp.dwFlags           = CRYPT_MACHINE_KEYSET;
-    ckp.dwKeySpec         = AT_KEYEXCHANGE;
 
-    if (!CertSetCertificateContextProperty(storedContext, CERT_KEY_PROV_INFO_PROP_ID, 0, &ckp))
+  DEBUG_printf(("4http_sspi_make_credentials(sspi=%p, container=%p, common_name=\"%s\", mode=%d, years=%d)", sspi, container, common_name, mode, years));
+
+  if (!CryptAcquireContextW(&hProv, (LPWSTR)container, MS_DEF_PROV_W, PROV_RSA_FULL, CRYPT_NEWKEYSET | CRYPT_MACHINE_KEYSET))
+  {
+    if (GetLastError() == NTE_EXISTS)
     {
-      DEBUG_printf(("_sspiGetCredentials: CertSetCertificateContextProperty failed: %x", GetLastError()));
-      ok = FALSE;
-      goto cleanup;
+      if (!CryptAcquireContextW(&hProv, (LPWSTR)container, MS_DEF_PROV_W, PROV_RSA_FULL, CRYPT_MACHINE_KEYSET))
+      {
+        DEBUG_printf(("5http_sspi_make_credentials: CryptAcquireContext failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), GetLastError())));
+        ok = FALSE;
+        goto cleanup;
+      }
     }
   }
 
+  store = CertOpenStore(CERT_STORE_PROV_SYSTEM, X509_ASN_ENCODING|PKCS_7_ASN_ENCODING, hProv, CERT_SYSTEM_STORE_LOCAL_MACHINE | CERT_STORE_NO_CRYPT_RELEASE_FLAG | CERT_STORE_OPEN_EXISTING_FLAG, L"MY");
+
+  if (!store)
+  {
+    DEBUG_printf(("5http_sspi_make_credentials: CertOpenSystemStore failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), GetLastError())));
+    ok = FALSE;
+    goto cleanup;
+  }
+
+  dwSize = 0;
+
+  if (!CertStrToNameA(X509_ASN_ENCODING, common_name, CERT_OID_NAME_STR, NULL, NULL, &dwSize, NULL))
+  {
+    DEBUG_printf(("5http_sspi_make_credentials: CertStrToName failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), GetLastError())));
+    ok = FALSE;
+    goto cleanup;
+  }
+
+  p = (PBYTE)malloc(dwSize);
+
+  if (!p)
+  {
+    DEBUG_printf(("5http_sspi_make_credentials: malloc failed for %d bytes", dwSize));
+    ok = FALSE;
+    goto cleanup;
+  }
+
+  if (!CertStrToNameA(X509_ASN_ENCODING, common_name, CERT_OID_NAME_STR, NULL, p, &dwSize, NULL))
+  {
+    DEBUG_printf(("5http_sspi_make_credentials: CertStrToName failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), GetLastError())));
+    ok = FALSE;
+    goto cleanup;
+  }
+
+ /*
+  * Create a private key and self-signed certificate...
+  */
+
+  if (!CryptGenKey(hProv, AT_KEYEXCHANGE, CRYPT_EXPORTABLE, &hKey))
+  {
+    DEBUG_printf(("5http_sspi_make_credentials: CryptGenKey failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), GetLastError())));
+    ok = FALSE;
+    goto cleanup;
+  }
+
+  ZeroMemory(&kpi, sizeof(kpi));
+  kpi.pwszContainerName = (LPWSTR)container;
+  kpi.pwszProvName      = MS_DEF_PROV_W;
+  kpi.dwProvType        = PROV_RSA_FULL;
+  kpi.dwFlags           = CERT_SET_KEY_CONTEXT_PROP_ID;
+  kpi.dwKeySpec         = AT_KEYEXCHANGE;
+
+  GetSystemTime(&et);
+  et.wYear += years;
+  if (et.wMonth == 2 && et.wDay == 29)
+    et.wDay = 28;                      /* Avoid Feb 29th due to leap years */
+
+  ZeroMemory(&exts, sizeof(exts));
+
+  createdContext = CertCreateSelfSignCertificate(hProv, &sib, 0, &kpi, NULL, NULL, &et, &exts);
+
+  if (!createdContext)
+  {
+    DEBUG_printf(("5http_sspi_make_credentials: CertCreateSelfSignCertificate failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), GetLastError())));
+    ok = FALSE;
+    goto cleanup;
+  }
+
+ /*
+  * Add the created context to the named store, and associate it with the named
+  * container...
+  */
+
+  if (!CertAddCertificateContextToStore(store, createdContext, CERT_STORE_ADD_REPLACE_EXISTING, &storedContext))
+  {
+    DEBUG_printf(("5http_sspi_make_credentials: CertAddCertificateContextToStore failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), GetLastError())));
+    ok = FALSE;
+    goto cleanup;
+  }
+
+  ZeroMemory(&ckp, sizeof(ckp));
+  ckp.pwszContainerName = (LPWSTR) container;
+  ckp.pwszProvName      = MS_DEF_PROV_W;
+  ckp.dwProvType        = PROV_RSA_FULL;
+  ckp.dwFlags           = CRYPT_MACHINE_KEYSET;
+  ckp.dwKeySpec         = AT_KEYEXCHANGE;
+
+  if (!CertSetCertificateContextProperty(storedContext, CERT_KEY_PROV_INFO_PROP_ID, 0, &ckp))
+  {
+    DEBUG_printf(("5http_sspi_make_credentials: CertSetCertificateContextProperty failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), GetLastError())));
+    ok = FALSE;
+    goto cleanup;
+  }
+
+ /*
+  * Get a handle to use the certificate...
+  */
+
   ZeroMemory(&SchannelCred, sizeof(SchannelCred));
 
   SchannelCred.dwVersion = SCHANNEL_CRED_VERSION;
@@ -1512,17 +2021,17 @@ http_sspi_credentials(
   * SSPI doesn't seem to like it if grbitEnabledProtocols is set for a client.
   */
 
-  if (isServer)
+  if (mode == _HTTP_MODE_SERVER)
     SchannelCred.grbitEnabledProtocols = SP_PROT_SSL3TLS1;
 
  /*
   * Create an SSPI credential.
   */
 
-  Status = AcquireCredentialsHandle(NULL, UNISP_NAME, isServer ? SECPKG_CRED_INBOUND : SECPKG_CRED_OUTBOUND, NULL, &SchannelCred, NULL, NULL, &conn->creds, &tsExpiry);
+  Status = AcquireCredentialsHandle(NULL, UNISP_NAME, mode == _HTTP_MODE_SERVER ? SECPKG_CRED_INBOUND : SECPKG_CRED_OUTBOUND, NULL, &SchannelCred, NULL, NULL, &sspi->creds, &tsExpiry);
   if (Status != SEC_E_OK)
   {
-    DEBUG_printf(("_sspiGetCredentials: AcquireCredentialsHandle failed: %x", Status));
+    DEBUG_printf(("5http_sspi_make_credentials: AcquireCredentialsHandle failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), Status)));
     ok = FALSE;
     goto cleanup;
   }
@@ -1555,38 +2064,6 @@ cleanup:
 }
 
 
-/*
- * 'http_sspi_free()' - Close a connection and free resources.
- */
-
-static void
-http_sspi_free(_http_sspi_t *conn)     /* I  - Client connection */
-{
-  if (!conn)
-    return;
-
-  if (conn->contextInitialized)
-    DeleteSecurityContext(&conn->context);
-
-  if (conn->decryptBuffer)
-    free(conn->decryptBuffer);
-
-  if (conn->readBuffer)
-    free(conn->readBuffer);
-
-  if (conn->writeBuffer)
-    free(conn->writeBuffer);
-
-  if (conn->localCert)
-    CertFreeCertificateContext(conn->localCert);
-
-  if (conn->remoteCert)
-    CertFreeCertificateContext(conn->remoteCert);
-
-  free(conn);
-}
-
-
 /*
  * 'http_sspi_server()' - Negotiate a TLS connection as a server.
  */
@@ -1595,7 +2072,8 @@ static int                                /* O - 0 on success, -1 on failure */
 http_sspi_server(http_t     *http,     /* I - HTTP connection */
                  const char *hostname) /* I - Hostname of server */
 {
-  _http_sspi_t *conn = http->tls;      /* I - SSPI data */
+  _http_sspi_t *sspi = http->tls;      /* I - SSPI data */
+  char         common_name[512];       /* Common name for cert */
   DWORD                dwSSPIFlags;            /* SSL connection attributes we want */
   DWORD                dwSSPIOutFlags;         /* SSL connection attributes we got */
   TimeStamp    tsExpiry;               /* Time stamp */
@@ -1609,7 +2087,7 @@ http_sspi_server(http_t     *http,        /* I - HTTP connection */
   int          ret = 0;                /* Return value */
 
 
-  DEBUG_printf(("http_sspi_server(http=%p, hostname=\"%s\")", http, hostname));
+  DEBUG_printf(("4http_sspi_server(http=%p, hostname=\"%s\")", http, hostname));
 
   dwSSPIFlags = ASC_REQ_SEQUENCE_DETECT  |
                 ASC_REQ_REPLAY_DETECT    |
@@ -1618,7 +2096,20 @@ http_sspi_server(http_t     *http,       /* I - HTTP connection */
                 ASC_REQ_ALLOCATE_MEMORY  |
                 ASC_REQ_STREAM;
 
-  conn->decryptBufferUsed = 0;
+  sspi->decryptBufferUsed = 0;
+
+ /*
+  * Lookup the server certificate...
+  */
+
+  snprintf(common_name, sizeof(common_name), "CN=%s", hostname);
+
+  if (!http_sspi_find_credentials(http, L"ServerContainer", common_name))
+    if (!http_sspi_make_credentials(http->tls, L"ServerContainer", common_name, _HTTP_MODE_SERVER, 10))
+    {
+      DEBUG_puts("5http_sspi_server: Unable to get server credentials.");
+      return (-1);
+    }
 
  /*
   * Set OutBuffer for AcceptSecurityContext call
@@ -1634,33 +2125,33 @@ http_sspi_server(http_t     *http,      /* I - HTTP connection */
          scRet == SEC_E_INCOMPLETE_MESSAGE ||
          scRet == SEC_I_INCOMPLETE_CREDENTIALS)
   {
-    if (conn->decryptBufferUsed == 0 || scRet == SEC_E_INCOMPLETE_MESSAGE)
+    if (sspi->decryptBufferUsed == 0 || scRet == SEC_E_INCOMPLETE_MESSAGE)
     {
-      if (conn->decryptBufferLength <= conn->decryptBufferUsed)
+      if (sspi->decryptBufferLength <= sspi->decryptBufferUsed)
       {
        BYTE *temp;                     /* New buffer */
 
-       if (conn->decryptBufferLength >= 262144)
+       if (sspi->decryptBufferLength >= 262144)
        {
          WSASetLastError(E_OUTOFMEMORY);
-         DEBUG_puts("http_sspi_server: Decryption buffer too large (>256k)");
+         DEBUG_puts("5http_sspi_server: Decryption buffer too large (>256k)");
          return (-1);
        }
 
-       if ((temp = realloc(conn->decryptBuffer, conn->decryptBufferLength + 4096)) == NULL)
+       if ((temp = realloc(sspi->decryptBuffer, sspi->decryptBufferLength + 4096)) == NULL)
        {
-         DEBUG_printf(("http_sspi_server: Unable to allocate %d byte buffer.", conn->decryptBufferLength + 4096));
+         DEBUG_printf(("5http_sspi_server: Unable to allocate %d byte buffer.", sspi->decryptBufferLength + 4096));
          WSASetLastError(E_OUTOFMEMORY);
          return (-1);
        }
 
-       conn->decryptBufferLength += 4096;
-       conn->decryptBuffer       = temp;
+       sspi->decryptBufferLength += 4096;
+       sspi->decryptBuffer       = temp;
       }
 
       for (;;)
       {
-        num = recv(http->fd, conn->decryptBuffer + conn->decryptBufferUsed, (int)(conn->decryptBufferLength - conn->decryptBufferUsed), 0);
+        num = recv(http->fd, sspi->decryptBuffer + sspi->decryptBufferUsed, (int)(sspi->decryptBufferLength - sspi->decryptBufferUsed), 0);
 
         if (num == -1 && WSAGetLastError() == WSAEWOULDBLOCK)
           Sleep(1);
@@ -1670,17 +2161,17 @@ http_sspi_server(http_t     *http,      /* I - HTTP connection */
 
       if (num < 0)
       {
-        DEBUG_printf(("http_sspi_server: recv failed: %d", WSAGetLastError()));
+        DEBUG_printf(("5http_sspi_server: recv failed: %d", WSAGetLastError()));
         return (-1);
       }
       else if (num == 0)
       {
-        DEBUG_puts("http_sspi_server: client disconnected");
+        DEBUG_puts("5http_sspi_server: client disconnected");
         return (-1);
       }
 
-      DEBUG_printf(("http_sspi_server: received %d (handshake) bytes from client.", num));
-      conn->decryptBufferUsed += num;
+      DEBUG_printf(("5http_sspi_server: received %d (handshake) bytes from client.", num));
+      sspi->decryptBufferUsed += num;
     }
 
    /*
@@ -1688,8 +2179,8 @@ http_sspi_server(http_t     *http,        /* I - HTTP connection */
     * on this run around the loop.
     */
 
-    inBuffers[0].pvBuffer   = conn->decryptBuffer;
-    inBuffers[0].cbBuffer   = (unsigned long)conn->decryptBufferUsed;
+    inBuffers[0].pvBuffer   = sspi->decryptBuffer;
+    inBuffers[0].cbBuffer   = (unsigned long)sspi->decryptBufferUsed;
     inBuffers[0].BufferType = SECBUFFER_TOKEN;
 
     inBuffers[1].pvBuffer   = NULL;
@@ -1709,7 +2200,7 @@ http_sspi_server(http_t     *http,        /* I - HTTP connection */
     outBuffers[0].BufferType = SECBUFFER_TOKEN;
     outBuffers[0].cbBuffer   = 0;
 
-    scRet = AcceptSecurityContext(&conn->creds, (fInitContext?NULL:&conn->context), &inBuffer, dwSSPIFlags, SECURITY_NATIVE_DREP, (fInitContext?&conn->context:NULL), &outBuffer, &dwSSPIOutFlags, &tsExpiry);
+    scRet = AcceptSecurityContext(&sspi->creds, (fInitContext?NULL:&sspi->context), &inBuffer, dwSSPIFlags, SECURITY_NATIVE_DREP, (fInitContext?&sspi->context:NULL), &outBuffer, &dwSSPIOutFlags, &tsExpiry);
 
     fInitContext = FALSE;
 
@@ -1727,11 +2218,11 @@ http_sspi_server(http_t     *http,      /* I - HTTP connection */
 
         if (num <= 0)
         {
-          DEBUG_printf(("http_sspi_server: handshake send failed: %d", WSAGetLastError()));
+          DEBUG_printf(("5http_sspi_server: handshake send failed: %d", WSAGetLastError()));
          return (-1);
         }
 
-        DEBUG_printf(("http_sspi_server: sent %d handshake bytes to client.", outBuffers[0].cbBuffer));
+        DEBUG_printf(("5http_sspi_server: sent %d handshake bytes to client.", outBuffers[0].cbBuffer));
 
         FreeContextBuffer(outBuffers[0].pvBuffer);
         outBuffers[0].pvBuffer = NULL;
@@ -1746,18 +2237,18 @@ http_sspi_server(http_t     *http,      /* I - HTTP connection */
 
       if (inBuffers[1].BufferType == SECBUFFER_EXTRA)
       {
-        memcpy(conn->decryptBuffer, (LPBYTE)(conn->decryptBuffer + conn->decryptBufferUsed - inBuffers[1].cbBuffer), inBuffers[1].cbBuffer);
-        conn->decryptBufferUsed = inBuffers[1].cbBuffer;
+        memcpy(sspi->decryptBuffer, (LPBYTE)(sspi->decryptBuffer + sspi->decryptBufferUsed - inBuffers[1].cbBuffer), inBuffers[1].cbBuffer);
+        sspi->decryptBufferUsed = inBuffers[1].cbBuffer;
       }
       else
       {
-        conn->decryptBufferUsed = 0;
+        sspi->decryptBufferUsed = 0;
       }
       break;
     }
     else if (FAILED(scRet) && scRet != SEC_E_INCOMPLETE_MESSAGE)
     {
-      DEBUG_printf(("http_sspi_server: AcceptSecurityContext failed: %x", scRet));
+      DEBUG_printf(("5http_sspi_server: AcceptSecurityContext failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), scRet)));
       ret = -1;
       break;
     }
@@ -1767,29 +2258,29 @@ http_sspi_server(http_t     *http,      /* I - HTTP connection */
     {
       if (inBuffers[1].BufferType == SECBUFFER_EXTRA)
       {
-        memcpy(conn->decryptBuffer, (LPBYTE)(conn->decryptBuffer + conn->decryptBufferUsed - inBuffers[1].cbBuffer), inBuffers[1].cbBuffer);
-        conn->decryptBufferUsed = inBuffers[1].cbBuffer;
+        memcpy(sspi->decryptBuffer, (LPBYTE)(sspi->decryptBuffer + sspi->decryptBufferUsed - inBuffers[1].cbBuffer), inBuffers[1].cbBuffer);
+        sspi->decryptBufferUsed = inBuffers[1].cbBuffer;
       }
       else
       {
-        conn->decryptBufferUsed = 0;
+        sspi->decryptBufferUsed = 0;
       }
     }
   }
 
   if (!ret)
   {
-    conn->contextInitialized = TRUE;
+    sspi->contextInitialized = TRUE;
 
    /*
     * Find out how big the header will be:
     */
 
-    scRet = QueryContextAttributes(&conn->context, SECPKG_ATTR_STREAM_SIZES, &conn->streamSizes);
+    scRet = QueryContextAttributes(&sspi->context, SECPKG_ATTR_STREAM_SIZES, &sspi->streamSizes);
 
     if (scRet != SEC_E_OK)
     {
-      DEBUG_printf(("http_sspi_server: QueryContextAttributes failed: %x", scRet));
+      DEBUG_printf(("5http_sspi_server: QueryContextAttributes failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), scRet)));
       ret = -1;
     }
   }
@@ -1798,32 +2289,33 @@ http_sspi_server(http_t     *http,      /* I - HTTP connection */
 }
 
 
-#if 0
 /*
- * '_sspiSetAllowsAnyRoot()' - Set the client cert policy for untrusted root certs
+ * 'http_sspi_strerror()' - Return a string for the specified error code.
  */
-void
-_sspiSetAllowsAnyRoot(_http_sspi_t *conn,
-                                       /* I  - Client connection */
-                      BOOL           allow)
-                                       /* I  - Allow any root */
+
+static const char *                    /* O - String for error */
+http_sspi_strerror(char   *buffer,     /* I - Error message buffer */
+                   size_t bufsize,     /* I - Size of buffer */
+                   DWORD  code)                /* I - Error code */
 {
-  conn->certFlags = (allow) ? conn->certFlags | SECURITY_FLAG_IGNORE_UNKNOWN_CA :
-                              conn->certFlags & ~SECURITY_FLAG_IGNORE_UNKNOWN_CA;
-}
+  if (FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, code, 0, buffer, bufsize, NULL))
+  {
+   /*
+    * Strip trailing CR + LF...
+    */
 
+    char       *ptr;                   /* Pointer into error message */
 
-/*
- * '_sspiSetAllowsExpiredCerts()' - Set the client cert policy for expired root certs
- */
-void
-_sspiSetAllowsExpiredCerts(_http_sspi_t *conn,
-                                       /* I  - Client connection */
-                           BOOL           allow)
-                                       /* I  - Allow expired certs */
-{
-  conn->certFlags = (allow) ? conn->certFlags | SECURITY_FLAG_IGNORE_CERT_DATE_INVALID :
-                              conn->certFlags & ~SECURITY_FLAG_IGNORE_CERT_DATE_INVALID;
+    for (ptr = buffer + strlen(buffer) - 1; ptr >= buffer; ptr --)
+      if (*ptr == '\n' || *ptr == '\r')
+        *ptr = '\0';
+      else
+        break;
+  }
+  else
+    snprintf(buffer, bufsize, "Unknown error %x", code);
+
+  return (buffer);
 }
 
 
@@ -1853,6 +2345,9 @@ http_sspi_verify(
                                        /* Number of ites in rgszUsages */
   DWORD                        count;          /* 32 bit count variable */
   DWORD                        status;         /* Return value */
+#ifdef DEBUG
+  char                 error[1024];    /* Error message string */
+#endif /* DEBUG */
 
 
   if (!cert)
@@ -1890,7 +2385,8 @@ http_sspi_verify(
   if (!CertGetCertificateChain(NULL, cert, NULL, cert->hCertStore, &chainPara, 0, NULL, &chainContext))
   {
     status = GetLastError();
-    DEBUG_printf(("CertGetCertificateChain returned 0x%x\n", status));
+
+    DEBUG_printf(("CertGetCertificateChain returned: %s", http_sspi_strerror(error, sizeof(error), status)));
 
     LocalFree(commonNameUnicode);
     return (status);
@@ -1916,7 +2412,8 @@ http_sspi_verify(
   if (!CertVerifyCertificateChainPolicy(CERT_CHAIN_POLICY_SSL, chainContext, &policyPara, &policyStatus))
   {
     status = GetLastError();
-    DEBUG_printf(("CertVerifyCertificateChainPolicy returned %d", status));
+
+    DEBUG_printf(("CertVerifyCertificateChainPolicy returned %s", http_sspi_strerror(error, sizeof(error), status)));
   }
   else if (policyStatus.dwError)
     status = policyStatus.dwError;
@@ -1931,9 +2428,3 @@ http_sspi_verify(
 
   return (status);
 }
-#endif // 0
-
-
-/*
- * End of "$Id$".
- */