]> git.ipfire.org Git - thirdparty/cups.git/blobdiff - cups/tls-sspi.c
Fix source file header text duplication text duplication.
[thirdparty/cups.git] / cups / tls-sspi.c
index fcebb7ee4ca837324b7f3e41907cbeba237ec944..46f6e79095c37422ce3a8b7fcc78a7b6ea2f6e06 100644 (file)
@@ -1,19 +1,20 @@
 /*
- * "$Id$"
+ * TLS support for CUPS on Windows using the Security Support Provider
+ * Interface (SSPI).
  *
- * TLS support for CUPS on Windows using SSPI.
- *
- * Copyright 2010-2014 by Apple Inc.
+ * Copyright 2010-2015 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/".
+ * missing or damaged, see the license at "http://www.cups.org/".
  *
  * This file is subject to the Apple OS-Developed Software exception.
  */
 
+/**** 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 */
+
+
 /*
  * Local functions...
  */
@@ -55,14 +68,14 @@ static BOOL http_sspi_make_credentials(_http_sspi_t *sspi, const LPWSTR containe
 static int     http_sspi_server(http_t *http, const char *hostname);
 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(_http_sspi_t *sspi, DWORD code);
+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 */
@@ -98,7 +111,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 */
@@ -121,7 +134,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) */
@@ -131,12 +144,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);
+  }
 
-  return (-1);
+  *credentials = cupsArrayNew(NULL, NULL);
+  httpAddCredential(*credentials, http->tls->remoteCert->pbCertEncoded, http->tls->remoteCert->cbCertEncoded);
+
+  return (0);
 }
 
 
@@ -155,7 +174,7 @@ _httpCreateCredentials(
 /*
  * '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 */
@@ -171,7 +190,17 @@ httpCredentialsAreValidForName(
 
   if (cert)
   {
-    if (!CertNameToStr(X509_ASN_ENCODING, cert->pCertInfo->Subject, CERT_SIMPLE_NAME_STR, cert_name, sizeof(cert_name)))
+    if (CertNameToStr(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));
 
     CertFreeCertificateContext(cert);
@@ -210,7 +239,7 @@ httpCredentialsAreValidForName(
 /*
  * 'httpCredentialsGetTrust()' - Return the trust of credentials.
  *
- * @since CUPS 2.0@
+ * @since CUPS 2.0/OS 10.10@
  */
 
 http_trust_t                           /* O - Level of trust */
@@ -231,6 +260,9 @@ httpCredentialsGetTrust(
   if (!cert)
     return (HTTP_TRUST_UNKNOWN);
 
+  if (cg->any_root < 0)
+    _cupsSetDefaults();
+
   if (cg->any_root)
     certFlags |= SECURITY_FLAG_IGNORE_UNKNOWN_CA;
 
@@ -252,7 +284,7 @@ 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 */
@@ -268,7 +300,7 @@ httpCredentialsGetExpiration(
     SYSTEMTIME systime;                /* System time */
     struct tm  tm;                     /* UNIX date/time */
 
-    FileTimeToSystemTime(cert->pCertInfo->NotAfter, &systime);
+    FileTimeToSystemTime(&(cert->pCertInfo->NotAfter), &systime);
 
     tm.tm_year = systime.wYear - 1900;
     tm.tm_mon  = systime.wMonth - 1;
@@ -289,7 +321,7 @@ httpCredentialsGetExpiration(
 /*
  * '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 */
@@ -322,7 +354,7 @@ httpCredentialsString(
     _cups_md5_state_t  md5_state;      /* MD5 state */
     unsigned char      md5_digest[16]; /* MD5 result */
 
-    FileTimeToSystemTime(cert->pCertInfo->NotAfter, &systime);
+    FileTimeToSystemTime(&(cert->pCertInfo->NotAfter), &systime);
 
     tm.tm_year = systime.wYear - 1900;
     tm.tm_mon  = systime.wMonth - 1;
@@ -333,7 +365,17 @@ httpCredentialsString(
 
     expiration = mktime(&tm);
 
-    if (!CertNameToStr(X509_ASN_ENCODING, cert->pCertInfo->Subject, CERT_SIMPLE_NAME_STR, cert_name, sizeof(cert_name)))
+    if (CertNameToStr(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));
 
     _cupsMD5Init(&md5_state);
@@ -369,7 +411,7 @@ _httpFreeCredentials(
 /*
  * '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 */
@@ -378,13 +420,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 (!CertStrToName(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 (!CertStrToName(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));
 
@@ -395,7 +535,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 */
@@ -404,14 +544,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 (!CertStrToName(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 (!CertStrToName(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);
 }
 
 
@@ -562,7 +816,7 @@ _httpTLSRead(http_t *http,          /* I - HTTP connection */
   }
   else if (scRet != SEC_E_OK)
   {
-    DEBUG_printf(("5_httpTLSRead: DecryptMessage failed: %s", http_sspi_strerror(sspi, scRet)));
+    DEBUG_printf(("5_httpTLSRead: DecryptMessage failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), scRet)));
     WSASetLastError(WSASYSCALLFAILURE);
     return (-1);
   }
@@ -652,6 +906,17 @@ _httpTLSRead(http_t *http,         /* I - HTTP connection */
 }
 
 
+/*
+ * '_httpTLSSetOptions()' - Set TLS protocol and cipher suite options.
+ */
+
+void
+_httpTLSSetOptions(int options)                /* I - Options */
+{
+  tls_options = options;
+}
+
+
 /*
  * '_httpTLSStart()' - Set up SSL/TLS support on a connection.
  */
@@ -663,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);
@@ -822,12 +1094,12 @@ _httpTLSStop(http_t *http)               /* I - HTTP connection */
       }
       else
       {
-        DEBUG_printf(("_httpTLSStop: AcceptSecurityContext failed: %s", http_sspi_strerror(sspi, status)));
+        DEBUG_printf(("_httpTLSStop: AcceptSecurityContext failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), status)));
       }
     }
     else
     {
-      DEBUG_printf(("_httpTLSStop: ApplyControlToken failed: %s", http_sspi_strerror(sspi, status)));
+      DEBUG_printf(("_httpTLSStop: ApplyControlToken failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), status)));
     }
   }
 
@@ -914,7 +1186,7 @@ _httpTLSWrite(http_t     *http,            /* I - HTTP connection */
 
     if (FAILED(scRet))
     {
-      DEBUG_printf(("_httpTLSWrite: EncryptMessage failed: %s", http_sspi_strerror(sspi, scRet)));
+      DEBUG_printf(("_httpTLSWrite: EncryptMessage failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), scRet)));
       WSASetLastError(WSASYSCALLFAILURE);
       return (-1);
     }
@@ -1064,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     |
@@ -1084,7 +1356,7 @@ http_sspi_client(http_t     *http,        /* I - Client connection */
   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("http_sspi_client: Unable to get client credentials.");
+      DEBUG_puts("5http_sspi_client: Unable to get client credentials.");
       return (-1);
     }
 
@@ -1104,7 +1376,7 @@ http_sspi_client(http_t     *http,        /* I - Client connection */
 
   if (scRet != SEC_I_CONTINUE_NEEDED)
   {
-    DEBUG_printf(("http_sspi_client: InitializeSecurityContext(1) failed: %s", http_sspi_strerror(sspi, scRet)));
+    DEBUG_printf(("5http_sspi_client: InitializeSecurityContext(1) failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), scRet)));
     return (-1);
   }
 
@@ -1116,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(&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;
@@ -1157,13 +1429,13 @@ http_sspi_client(http_t     *http,      /* I - Client connection */
        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(sspi->decryptBuffer, sspi->decryptBufferLength + 4096)) == NULL)
        {
-         DEBUG_printf(("http_sspi_client: Unable to allocate %d byte buffer.", sspi->decryptBufferLength + 4096));
+         DEBUG_printf(("5http_sspi_client: Unable to allocate %d byte buffer.", sspi->decryptBufferLength + 4096));
          WSASetLastError(E_OUTOFMEMORY);
          return (-1);
        }
@@ -1176,16 +1448,16 @@ http_sspi_client(http_t     *http,      /* I - Client connection */
 
       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));
 
       sspi->decryptBufferUsed += cbData;
     }
@@ -1244,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(&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.
@@ -1282,7 +1554,7 @@ 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)
       {
@@ -1290,7 +1562,7 @@ http_sspi_client(http_t     *http,        /* I - Client connection */
 
         sspi->decryptBufferUsed = inBuffers[1].cbBuffer;
 
-        DEBUG_printf(("http_sspi_client: %d bytes of app data was bundled with handshake data", sspi->decryptBufferUsed));
+        DEBUG_printf(("5http_sspi_client: %d bytes of app data was bundled with handshake data", sspi->decryptBufferUsed));
       }
       else
         sspi->decryptBufferUsed = 0;
@@ -1308,7 +1580,7 @@ http_sspi_client(http_t     *http,        /* I - Client connection */
 
     if (FAILED(scRet))
     {
-      DEBUG_printf(("http_sspi_client: InitializeSecurityContext(2) failed: %s", http_sspi_strerror(sspi, scRet)));
+      DEBUG_printf(("5http_sspi_client: InitializeSecurityContext(2) failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), scRet)));
       ret = -1;
       break;
     }
@@ -1324,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;
     }
@@ -1357,7 +1629,7 @@ http_sspi_client(http_t     *http,        /* I - Client connection */
 
     if (scRet != SEC_E_OK)
     {
-      DEBUG_printf(("http_sspi_client: QueryContextAttributes failed(SECPKG_ATTR_REMOTE_CERT_CONTEXT): %s", http_sspi_strerror(sspi, 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);
     }
 
@@ -1369,7 +1641,7 @@ http_sspi_client(http_t     *http,        /* I - Client connection */
 
     if (scRet != SEC_E_OK)
     {
-      DEBUG_printf(("http_sspi_client: QueryContextAttributes failed(SECPKG_ATTR_STREAM_SIZES): %s", http_sspi_strerror(sspi, scRet)));
+      DEBUG_printf(("5http_sspi_client: QueryContextAttributes failed(SECPKG_ATTR_STREAM_SIZES): %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), scRet)));
       ret = -1;
     }
   }
@@ -1423,7 +1695,7 @@ http_sspi_find_credentials(
     {
       if (!CryptAcquireContextW(&hProv, (LPWSTR)container, MS_DEF_PROV_W, PROV_RSA_FULL, CRYPT_MACHINE_KEYSET))
       {
-        DEBUG_printf(("http_sspi_find_credentials: CryptAcquireContext failed: %s", http_sspi_strerror(sspi, GetLastError())));
+        DEBUG_printf(("5http_sspi_find_credentials: CryptAcquireContext failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), GetLastError())));
         ok = FALSE;
         goto cleanup;
       }
@@ -1434,7 +1706,7 @@ http_sspi_find_credentials(
 
   if (!store)
   {
-    DEBUG_printf(("http_sspi_find_credentials: CertOpenSystemStore failed: %s", http_sspi_strerror(sspi, GetLastError())));
+    DEBUG_printf(("5http_sspi_find_credentials: CertOpenSystemStore failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), GetLastError())));
     ok = FALSE;
     goto cleanup;
   }
@@ -1443,7 +1715,7 @@ http_sspi_find_credentials(
 
   if (!CertStrToName(X509_ASN_ENCODING, common_name, CERT_OID_NAME_STR, NULL, NULL, &dwSize, NULL))
   {
-    DEBUG_printf(("http_sspi_find_credentials: CertStrToName failed: %s", http_sspi_strerror(sspi, GetLastError())));
+    DEBUG_printf(("5http_sspi_find_credentials: CertStrToName failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), GetLastError())));
     ok = FALSE;
     goto cleanup;
   }
@@ -1452,14 +1724,14 @@ http_sspi_find_credentials(
 
   if (!p)
   {
-    DEBUG_printf(("http_sspi_find_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))
   {
-    DEBUG_printf(("http_sspi_find_credentials: CertStrToName failed: %s", http_sspi_strerror(sspi, GetLastError())));
+    DEBUG_printf(("5http_sspi_find_credentials: CertStrToName failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), GetLastError())));
     ok = FALSE;
     goto cleanup;
   }
@@ -1471,7 +1743,7 @@ http_sspi_find_credentials(
 
   if (!storedContext)
   {
-    DEBUG_printf(("http_sspi_find_credentials: Unable to find credentials for \"%s\".", common_name));
+    DEBUG_printf(("5http_sspi_find_credentials: Unable to find credentials for \"%s\".", common_name));
     ok = FALSE;
     goto cleanup;
   }
@@ -1483,11 +1755,47 @@ http_sspi_find_credentials(
   SchannelCred.paCred    = &storedContext;
 
  /*
-  * SSPI doesn't seem to like it if grbitEnabledProtocols is set for a client.
+  * Set supported protocols (can also be overriden in the registry...)
   */
 
+#ifdef SP_PROT_TLS1_2_SERVER
   if (http->mode == _HTTP_MODE_SERVER)
-    SchannelCred.grbitEnabledProtocols = SP_PROT_SSL3TLS1;
+  {
+    if (tls_options & _HTTP_TLS_DENY_TLS10)
+      SchannelCred.grbitEnabledProtocols = SP_PROT_TLS1_2_SERVER | SP_PROT_TLS1_1_SERVER;
+    else if (tls_options & _HTTP_TLS_ALLOW_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_options & _HTTP_TLS_DENY_TLS10)
+      SchannelCred.grbitEnabledProtocols = SP_PROT_TLS1_2_CLIENT | SP_PROT_TLS1_1_CLIENT;
+    else if (tls_options & _HTTP_TLS_ALLOW_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;
+  }
+
+#else
+  if (http->mode == _HTTP_MODE_SERVER)
+  {
+    if (tls_options & _HTTP_TLS_ALLOW_SSL3)
+      SchannelCred.grbitEnabledProtocols = SP_PROT_TLS1_SERVER | SP_PROT_SSL3_SERVER;
+    else
+      SchannelCred.grbitEnabledProtocols = SP_PROT_TLS1_SERVER;
+  }
+  else
+  {
+    if (tls_options & _HTTP_TLS_ALLOW_SSL3)
+      SchannelCred.grbitEnabledProtocols = SP_PROT_TLS1_CLIENT | SP_PROT_SSL3_CLIENT;
+    else
+      SchannelCred.grbitEnabledProtocols = SP_PROT_TLS1_CLIENT;
+  }
+#endif /* SP_PROT_TLS1_2_SERVER */
+
+  /* TODO: Support _HTTP_TLS_ALLOW_RC4 and _HTTP_TLS_ALLOW_DH options; right now we'll rely on Windows registry to enable/disable RC4/DH... */
 
  /*
   * Create an SSPI credential.
@@ -1496,7 +1804,7 @@ http_sspi_find_credentials(
   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(("http_sspi_find_credentials: AcquireCredentialsHandle failed: %s", http_sspi_strerror(sspi, Status)));
+    DEBUG_printf(("5http_sspi_find_credentials: AcquireCredentialsHandle failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), Status)));
     ok = FALSE;
     goto cleanup;
   }
@@ -1594,7 +1902,7 @@ http_sspi_make_credentials(
     {
       if (!CryptAcquireContextW(&hProv, (LPWSTR)container, MS_DEF_PROV_W, PROV_RSA_FULL, CRYPT_MACHINE_KEYSET))
       {
-        DEBUG_printf(("http_sspi_make_credentials: CryptAcquireContext failed: %s", http_sspi_strerror(sspi, GetLastError())));
+        DEBUG_printf(("5http_sspi_make_credentials: CryptAcquireContext failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), GetLastError())));
         ok = FALSE;
         goto cleanup;
       }
@@ -1605,7 +1913,7 @@ http_sspi_make_credentials(
 
   if (!store)
   {
-    DEBUG_printf(("http_sspi_make_credentials: CertOpenSystemStore failed: %s", http_sspi_strerror(sspi, GetLastError())));
+    DEBUG_printf(("5http_sspi_make_credentials: CertOpenSystemStore failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), GetLastError())));
     ok = FALSE;
     goto cleanup;
   }
@@ -1614,7 +1922,7 @@ http_sspi_make_credentials(
 
   if (!CertStrToName(X509_ASN_ENCODING, common_name, CERT_OID_NAME_STR, NULL, NULL, &dwSize, NULL))
   {
-    DEBUG_printf(("http_sspi_make_credentials: CertStrToName failed: %s", http_sspi_strerror(sspi, GetLastError())));
+    DEBUG_printf(("5http_sspi_make_credentials: CertStrToName failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), GetLastError())));
     ok = FALSE;
     goto cleanup;
   }
@@ -1623,25 +1931,25 @@ http_sspi_make_credentials(
 
   if (!p)
   {
-    DEBUG_printf(("http_sspi_make_credentials: malloc failed for %d bytes", dwSize));
+    DEBUG_printf(("5http_sspi_make_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))
   {
-    DEBUG_printf(("http_sspi_make_credentials: CertStrToName failed: %s", http_sspi_strerror(sspi, GetLastError())));
+    DEBUG_printf(("5http_sspi_make_credentials: CertStrToName failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), GetLastError())));
     ok = FALSE;
     goto cleanup;
   }
 
  /*
-  * If we couldn't find the context, then we'll create a new one...
+  * Create a private key and self-signed certificate...
   */
 
   if (!CryptGenKey(hProv, AT_KEYEXCHANGE, CRYPT_EXPORTABLE, &hKey))
   {
-    DEBUG_printf(("http_sspi_make_credentials: CryptGenKey failed: %s", http_sspi_strerror(sspi, GetLastError())));
+    DEBUG_printf(("5http_sspi_make_credentials: CryptGenKey failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), GetLastError())));
     ok = FALSE;
     goto cleanup;
   }
@@ -1662,14 +1970,19 @@ http_sspi_make_credentials(
 
   if (!createdContext)
   {
-    DEBUG_printf(("http_sspi_make_credentials: CertCreateSelfSignCertificate failed: %s", http_sspi_strerror(sspi, GetLastError())));
+    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(("http_sspi_make_credentials: CertAddCertificateContextToStore failed: %s", http_sspi_strerror(sspi, GetLastError())));
+    DEBUG_printf(("5http_sspi_make_credentials: CertAddCertificateContextToStore failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), GetLastError())));
     ok = FALSE;
     goto cleanup;
   }
@@ -1683,11 +1996,14 @@ http_sspi_make_credentials(
 
   if (!CertSetCertificateContextProperty(storedContext, CERT_KEY_PROV_INFO_PROP_ID, 0, &ckp))
   {
-    DEBUG_printf(("http_sspi_make_credentials: CertSetCertificateContextProperty failed: %s", http_sspi_strerror(sspi, GetLastError())));
+    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));
 
@@ -1709,7 +2025,7 @@ http_sspi_make_credentials(
   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(("http_sspi_make_credentials: AcquireCredentialsHandle failed: %s", http_sspi_strerror(sspi, Status)));
+    DEBUG_printf(("5http_sspi_make_credentials: AcquireCredentialsHandle failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), Status)));
     ok = FALSE;
     goto cleanup;
   }
@@ -1765,7 +2081,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    |
@@ -1785,7 +2101,7 @@ http_sspi_server(http_t     *http,        /* I - HTTP connection */
   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("http_sspi_server: Unable to get server credentials.");
+      DEBUG_puts("5http_sspi_server: Unable to get server credentials.");
       return (-1);
     }
 
@@ -1812,13 +2128,13 @@ http_sspi_server(http_t     *http,      /* I - HTTP connection */
        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(sspi->decryptBuffer, sspi->decryptBufferLength + 4096)) == NULL)
        {
-         DEBUG_printf(("http_sspi_server: Unable to allocate %d byte buffer.", sspi->decryptBufferLength + 4096));
+         DEBUG_printf(("5http_sspi_server: Unable to allocate %d byte buffer.", sspi->decryptBufferLength + 4096));
          WSASetLastError(E_OUTOFMEMORY);
          return (-1);
        }
@@ -1839,16 +2155,16 @@ 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));
+      DEBUG_printf(("5http_sspi_server: received %d (handshake) bytes from client.", num));
       sspi->decryptBufferUsed += num;
     }
 
@@ -1896,11 +2212,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;
@@ -1926,7 +2242,7 @@ http_sspi_server(http_t     *http,        /* I - HTTP connection */
     }
     else if (FAILED(scRet) && scRet != SEC_E_INCOMPLETE_MESSAGE)
     {
-      DEBUG_printf(("http_sspi_server: AcceptSecurityContext failed: %s", http_sspi_strerror(sspi, scRet)));
+      DEBUG_printf(("5http_sspi_server: AcceptSecurityContext failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), scRet)));
       ret = -1;
       break;
     }
@@ -1958,7 +2274,7 @@ http_sspi_server(http_t     *http,        /* I - HTTP connection */
 
     if (scRet != SEC_E_OK)
     {
-      DEBUG_printf(("http_sspi_server: QueryContextAttributes failed: %s", http_sspi_strerror(sspi, scRet)));
+      DEBUG_printf(("5http_sspi_server: QueryContextAttributes failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), scRet)));
       ret = -1;
     }
   }
@@ -1972,10 +2288,11 @@ http_sspi_server(http_t     *http,      /* I - HTTP connection */
  */
 
 static const char *                    /* O - String for error */
-http_sspi_strerror(_http_sspi_t *sspi, /* I - SSPI data */
-                   DWORD        code)  /* I - Error code */
+http_sspi_strerror(char   *buffer,     /* I - Error message buffer */
+                   size_t bufsize,     /* I - Size of buffer */
+                   DWORD  code)                /* I - Error code */
 {
-  if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, code, 0, sspi->error, sizeof(sspi->error), NULL))
+  if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, code, 0, buffer, bufsize, NULL))
   {
    /*
     * Strip trailing CR + LF...
@@ -1983,16 +2300,16 @@ http_sspi_strerror(_http_sspi_t *sspi,  /* I - SSPI data */
 
     char       *ptr;                   /* Pointer into error message */
 
-    for (ptr = sspi->error + strlen(sspi->error) - 1; ptr >= sspi->error; ptr --)
+    for (ptr = buffer + strlen(buffer) - 1; ptr >= buffer; ptr --)
       if (*ptr == '\n' || *ptr == '\r')
         *ptr = '\0';
       else
         break;
   }
   else
-    snprintf(sspi->error, sizeof(sspi->error), "Unknown error %x", code);
+    snprintf(buffer, bufsize, "Unknown error %x", code);
 
-  return (sspi->error);
+  return (buffer);
 }
 
 
@@ -2022,6 +2339,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)
@@ -2059,7 +2379,8 @@ http_sspi_verify(
   if (!CertGetCertificateChain(NULL, cert, NULL, cert->hCertStore, &chainPara, 0, NULL, &chainContext))
   {
     status = GetLastError();
-    DEBUG_printf(("CertGetCertificateChain returned: %x", status));
+
+    DEBUG_printf(("CertGetCertificateChain returned: %s", http_sspi_strerror(error, sizeof(error), status)));
 
     LocalFree(commonNameUnicode);
     return (status);
@@ -2085,7 +2406,8 @@ http_sspi_verify(
   if (!CertVerifyCertificateChainPolicy(CERT_CHAIN_POLICY_SSL, chainContext, &policyPara, &policyStatus))
   {
     status = GetLastError();
-    DEBUG_printf(("CertVerifyCertificateChainPolicy returned %x", status));
+
+    DEBUG_printf(("CertVerifyCertificateChainPolicy returned %s", http_sspi_strerror(error, sizeof(error), status)));
   }
   else if (policyStatus.dwError)
     status = policyStatus.dwError;
@@ -2100,8 +2422,3 @@ http_sspi_verify(
 
   return (status);
 }
-
-
-/*
- * End of "$Id$".
- */