]> git.ipfire.org Git - thirdparty/cups.git/commitdiff
Initial work on SSPI support in 2.0.
authormsweet <msweet@a1ca3aef-8c08-0410-bb20-df032aa958be>
Tue, 15 Jul 2014 18:59:26 +0000 (18:59 +0000)
committermsweet <msweet@a1ca3aef-8c08-0410-bb20-df032aa958be>
Tue, 15 Jul 2014 18:59:26 +0000 (18:59 +0000)
git-svn-id: svn+ssh://src.apple.com/svn/cups/cups.org/trunk@12029 a1ca3aef-8c08-0410-bb20-df032aa958be

cups/http-private.h
cups/tls-darwin.c
cups/tls-sspi.c
scheduler/tls-darwin.c [deleted file]
scheduler/tls-gnutls.c [deleted file]
xcode/CUPS.xcodeproj/project.pbxproj

index 693e4cf81c93d6dec27be8de3b12396a5f1f8339..e1b5d4a19de76ef11554bcb2108480b3e205c2e5 100644 (file)
@@ -117,7 +117,12 @@ extern CFAbsoluteTime SecCertificateNotValidAfter(SecCertificateRef certificate)
 #      include <Security/SecPolicyPriv.h>
 #    endif /* HAVE_SECPOLICYPRIV_H */
 #  elif defined(HAVE_SSPISSL)
-#    include "sspi-private.h"
+#    include <wincrypt.h>
+#    include <wintrust.h>
+#    include <schannel.h>
+#    define SECURITY_WIN32
+#    include <security.h>
+#    include <sspi.h>
 #  endif /* HAVE_GNUTLS */
 
 #  ifndef WIN32
@@ -203,7 +208,25 @@ typedef CFArrayRef http_tls_credentials_t;
  * Windows' SSPI library gets a CUPS wrapper...
  */
 
-typedef _sspi_struct_t * http_tls_t;
+typedef struct _http_sspi_s            /**** SSPI/SSL data structure ****/
+{
+  CredHandle   creds;                  /* Credentials */
+  CtxtHandle   context;                /* SSL context */
+  BOOL         contextInitialized;     /* Is context init'd? */
+  SecPkgContext_StreamSizes streamSizes;/* SSL data stream sizes */
+  BYTE         *decryptBuffer;         /* Data pre-decryption*/
+  size_t       decryptBufferLength;    /* Length of decrypt buffer */
+  size_t       decryptBufferUsed;      /* Bytes used in buffer */
+  BYTE         *readBuffer;            /* Data post-decryption */
+  int          readBufferLength;       /* Length of read buffer */
+  int          readBufferUsed;         /* Bytes used in buffer */
+  BYTE         *writeBuffer;           /* Data pre-encryption */
+  int          writeBufferLength;      /* Length of write buffer */
+  DWORD                certFlags;              /* Cert verification flags */
+  PCCERT_CONTEXT localCert,            /* Local certificate */
+               remoteCert;             /* Remote (peer's) certificate */
+} _http_sspi_t;
+typedef _http_sspi_t *http_tls_t;
 typedef void *http_tls_credentials_t;
 
 #  else
index 7daa291d2ba9a1591c7c584833316f250ead294c..fc202852351411e700bd7f7b34c5cb95c688f67e 100644 (file)
@@ -51,6 +51,7 @@ static _cups_mutex_t  tls_mutex = _CUPS_MUTEX_INITIALIZER;
 #ifdef HAVE_SECKEYCHAINOPEN
 static CFArrayRef      http_cdsa_copy_server(const char *common_name);
 #endif /* HAVE_SECKEYCHAINOPEN */
+static SecCertificateRef http_cdsa_create_credential(http_credential_t *credential);
 static const char      *http_cdsa_default_path(char *buffer, size_t bufsize);
 static OSStatus                http_cdsa_read(SSLConnectionRef connection, void *data, size_t *dataLength);
 static int             http_cdsa_set_credentials(http_t *http);
@@ -412,21 +413,6 @@ httpCopyCredentials(
 }
 
 
-/*
- * 'http_cdsa_create_credential()' - Create a single credential in the internal format.
- */
-
-static SecCertificateRef                       /* O - Certificate */
-http_cdsa_create_credential(
-    http_credential_t *credential)             /* I - Credential */
-{
-  if (!credential)
-    return (NULL);
-
-  return (SecCertificateCreateWithBytes(kCFAllocatorDefault, credential->data, (CFIndex)credential->datalen));
-}
-
-
 /*
  * '_httpCreateCredentials()' - Create credentials in the internal format.
  */
@@ -1491,6 +1477,21 @@ http_cdsa_copy_server(
 #endif /* HAVE_SECKEYCHAINOPEN */
 
 
+/*
+ * 'http_cdsa_create_credential()' - Create a single credential in the internal format.
+ */
+
+static SecCertificateRef                       /* O - Certificate */
+http_cdsa_create_credential(
+    http_credential_t *credential)             /* I - Credential */
+{
+  if (!credential)
+    return (NULL);
+
+  return (SecCertificateCreateWithBytes(kCFAllocatorDefault, credential->data, (CFIndex)credential->datalen));
+}
+
+
 /*
  * 'http_cdsa_default_path()' - Get the default keychain path.
  */
index 00242f9e997b9763c0f10b117013dd890b3e6b58..0f08c5a862e1b3ae3d406b3e2f357d27e396b699 100644 (file)
@@ -3,7 +3,7 @@
  *
  * TLS support for CUPS on Windows using SSPI.
  *
- * Copyright 2010-2013 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
  * Include necessary headers...
  */
 
-#include "sspi-private.h"
 #include "debug-private.h"
 
 
-/* required to link this library for certificate functions */
+/*
+ * Include necessary libraries...
+ */
+
 #pragma comment(lib, "Crypt32.lib")
 #pragma comment(lib, "Secur32.lib")
 #pragma comment(lib, "Ws2_32.lib")
 
 
-#if !defined(SECURITY_FLAG_IGNORE_UNKNOWN_CA)
+/*
+ * Constants...
+ */
+
+#ifndef SECURITY_FLAG_IGNORE_UNKNOWN_CA
 #  define SECURITY_FLAG_IGNORE_UNKNOWN_CA         0x00000100 /* Untrusted root */
-#endif
+#endif /* SECURITY_FLAG_IGNORE_UNKNOWN_CA */
 
-#if !defined(SECURITY_FLAG_IGNORE_CERT_DATE_INVALID)
+#ifndef SECURITY_FLAG_IGNORE_CERT_DATE_INVALID
 #  define SECURITY_FLAG_IGNORE_CERT_DATE_INVALID  0x00002000 /* Expired X509 Cert. */
-#endif
+#endif /* !SECURITY_FLAG_IGNORE_CERT_DATE_INVALID */
+
+/*
+ * Local functions...
+ */
 
-static DWORD sspi_verify_certificate(PCCERT_CONTEXT  serverCert,
-                                     const CHAR      *serverName,
-                                     DWORD           dwCertFlags);
+static _http_sspi_t *http_sspi_alloc(void);
+static int     http_sspi_client(http_t *http, const char *hostname);
+static void    http_sspi_free(_http_sspi_t *conn);
+static BOOL    http_sspi_get_credentials(_http_sspi_t *conn, const LPWSTR containerName, const TCHAR  *commonName, BOOL server);
+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 DWORD   http_sspi_verify(PCCERT_CONTEXT cert, const char *common_name, DWORD dwCertFlags);
 
 
 /*
- * 'http_tls_initialize()' - Initialize the TLS stack.
+ * 'cupsMakeServerCredentials()' - Make a self-signed certificate and private key pair.
+ *
+ * @since CUPS 2.0@
  */
 
-static void
-http_tls_initialize(void)
+int                                    /* O - 1 on success, 0 on failure */
+cupsMakeServerCredentials(
+    const char *path,                  /* I - Keychain path or @code NULL@ for default */
+    const char *common_name,           /* I - Common name */
+    int        num_alt_names,          /* I - Number of subject alternate names */
+    const char **alt_names,            /* I - Subject Alternate Names */
+    time_t     expiration_date)                /* I - Expiration date */
 {
-#ifdef HAVE_GNUTLS
- /*
-  * Initialize GNU TLS...
-  */
+  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));
 
-  gnutls_global_init();
+  (void)path;
+  (void)common_name;
+  (void)num_alt_names;
+  (void)alt_names;
+  (void)expiration_date;
 
-#elif defined(HAVE_LIBSSL)
- /*
-  * Initialize OpenSSL...
-  */
+  return (0);
+}
 
-  SSL_load_error_strings();
-  SSL_library_init();
 
- /*
-  * Using the current time is a dubious random seed, but on some systems
-  * it is the best we can do (on others, this seed isn't even used...)
-  */
+/*
+ * 'cupsSetServerCredentials()' - Set the default server credentials.
+ *
+ * Note: The server credentials are used by all threads in the running process.
+ * This function is threadsafe.
+ *
+ * @since CUPS 2.0@
+ */
 
-  CUPS_SRAND(time(NULL));
+int                                    /* O - 1 on success, 0 on failure */
+cupsSetServerCredentials(
+    const char *path,                  /* I - Keychain path or @code NULL@ for default */
+    const char *common_name,           /* I - Default common name for server */
+    int        auto_create)            /* I - 1 = automatically create self-signed certificates */
+{
+  DEBUG_printf(("cupsSetServerCredentials(path=\"%s\", common_name=\"%s\", auto_create=%d)", path, common_name, auto_create));
 
-  for (i = 0; i < sizeof(data); i ++)
-    data[i] = CUPS_RAND();
+  (void)path;
+  (void)common_name;
+  (void)auto_create;
 
-  RAND_seed(data, sizeof(data));
-#endif /* HAVE_GNUTLS */
+  return (0);
 }
 
 
-#ifdef HAVE_SSL
 /*
- * 'http_tls_read()' - Read from a SSL/TLS connection.
+ * 'httpCopyCredentials()' - Copy the credentials associated with the peer in
+ *                           an encrypted connection.
+ *
+ * @since CUPS 1.5/OS X 10.7@
  */
 
-static int                             /* O - Bytes read */
-http_tls_read(http_t *http,            /* I - Connection to server */
-             char   *buf,              /* I - Buffer to store data */
-             int    len)               /* I - Length of buffer */
+int                                    /* O - Status of call (0 = success) */
+httpCopyCredentials(
+    http_t      *http,                 /* I - Connection to server */
+    cups_array_t **credentials)                /* O - Array of credentials */
 {
-#  if defined(HAVE_LIBSSL)
-  return (SSL_read((SSL *)(http->tls), buf, len));
-
-#  elif defined(HAVE_GNUTLS)
-  ssize_t      result;                 /* Return value */
-
-
-  result = gnutls_record_recv(http->tls, buf, len);
-
-  if (result < 0 && !errno)
-  {
-   /*
-    * Convert GNU TLS error to errno value...
-    */
+  DEBUG_printf(("httpCopyCredentials(http=%p, credentials=%p)", http, credentials));
 
-    switch (result)
-    {
-      case GNUTLS_E_INTERRUPTED :
-         errno = EINTR;
-         break;
+  (void)http;
 
-      case GNUTLS_E_AGAIN :
-          errno = EAGAIN;
-          break;
+  if (credentials)
+    *credentials = NULL;
 
-      default :
-          errno = EPIPE;
-          break;
-    }
+  return (-1);
+}
 
-    result = -1;
-  }
 
-  return ((int)result);
+/*
+ * '_httpCreateCredentials()' - Create credentials in the internal format.
+ */
 
-#  elif defined(HAVE_CDSASSL)
-  int          result;                 /* Return value */
-  OSStatus     error;                  /* Error info */
-  size_t       processed;              /* Number of bytes processed */
+http_tls_credentials_t                 /* O - Internal credentials */
+_httpCreateCredentials(
+    cups_array_t *credentials)         /* I - Array of credentials */
+{
+  (void)credentials;
 
+  return (NULL);
+}
 
-  error = SSLRead(http->tls, buf, len, &processed);
-  DEBUG_printf(("6http_tls_read: error=%d, processed=%d", (int)error,
-                (int)processed));
-  switch (error)
-  {
-    case 0 :
-       result = (int)processed;
-       break;
-
-    case errSSLWouldBlock :
-       if (processed)
-         result = (int)processed;
-       else
-       {
-         result = -1;
-         errno  = EINTR;
-       }
-       break;
 
-    case errSSLClosedGraceful :
-    default :
-       if (processed)
-         result = (int)processed;
-       else
-       {
-         result = -1;
-         errno  = EPIPE;
-       }
-       break;
-  }
+/*
+ * 'httpCredentialsAreValidForName()' - Return whether the credentials are valid for the given name.
+ *
+ * @since CUPS 2.0@
+ */
 
-  return (result);
+int                                    /* O - 1 if valid, 0 otherwise */
+httpCredentialsAreValidForName(
+    cups_array_t *credentials,         /* I - Credentials */
+    const char   *common_name)         /* I - Name to check */
+{
+  (void)credentials;
+  (void)common_name;
 
-#  elif defined(HAVE_SSPISSL)
-  return _sspiRead((_sspi_struct_t*) http->tls, buf, len);
-#  endif /* HAVE_LIBSSL */
+  return (1);
 }
-#endif /* HAVE_SSL */
 
 
-#ifdef HAVE_SSL
 /*
- * 'http_setup_ssl()' - Set up SSL/TLS support on a connection.
+ * 'httpCredentialsGetTrust()' - Return the trust of credentials.
+ *
+ * @since CUPS 2.0@
  */
 
-static int                             /* O - 0 on success, -1 on failure */
-http_setup_ssl(http_t *http)           /* I - Connection to server */
+http_trust_t                           /* O - Level of trust */
+httpCredentialsGetTrust(
+    cups_array_t *credentials,         /* I - Credentials */
+    const char   *common_name)         /* I - Common name for trust lookup */
 {
-  char                 hostname[256],  /* Hostname */
-                       *hostptr;       /* Pointer into hostname */
-
-#  ifdef HAVE_LIBSSL
-  SSL_CTX              *context;       /* Context for encryption */
-  BIO                  *bio;           /* BIO data */
-  const char           *message = NULL;/* Error message */
-#  elif defined(HAVE_GNUTLS)
-  int                  status;         /* Status of handshake */
-  gnutls_certificate_client_credentials *credentials;
-                                       /* TLS credentials */
-#  elif defined(HAVE_CDSASSL)
+  http_trust_t         trust = HTTP_TRUST_OK;
+                                       /* Trusted? */
+  cups_array_t         *tcreds = NULL; /* Trusted credentials */
   _cups_globals_t      *cg = _cupsGlobals();
-                                       /* Pointer to library globals */
-  OSStatus             error;          /* Error code */
-  const char           *message = NULL;/* Error message */
-  cups_array_t         *credentials;   /* Credentials array */
-  cups_array_t         *names;         /* CUPS distinguished names */
-  CFArrayRef           dn_array;       /* CF distinguished names array */
-  CFIndex              count;          /* Number of credentials */
-  CFDataRef            data;           /* Certificate data */
-  int                  i;              /* Looping var */
-  http_credential_t    *credential;    /* Credential data */
-#  elif defined(HAVE_SSPISSL)
-  TCHAR                        username[256];  /* Username returned from GetUserName() */
-  TCHAR                        commonName[256];/* Common name for certificate */
-  DWORD                        dwSize;         /* 32 bit size */
-#  endif /* HAVE_LIBSSL */
+                                       /* Per-thread globals */
 
 
-  DEBUG_printf(("7http_setup_ssl(http=%p)", http));
+  if (!common_name)
+    return (HTTP_TRUST_UNKNOWN);
 
  /*
-  * Get the hostname to use for SSL...
+  * Look this common name up in the default keychains...
   */
 
-  if (httpAddrLocalhost(http->hostaddr))
-  {
-    strlcpy(hostname, "localhost", sizeof(hostname));
-  }
-  else
-  {
-   /*
-    * Otherwise make sure the hostname we have does not end in a trailing dot.
-    */
-
-    strlcpy(hostname, http->hostname, sizeof(hostname));
-    if ((hostptr = hostname + strlen(hostname) - 1) >= hostname &&
-        *hostptr == '.')
-      *hostptr = '\0';
-  }
-
-#  ifdef HAVE_LIBSSL
-  context = SSL_CTX_new(SSLv23_client_method());
-
-  SSL_CTX_set_options(context, SSL_OP_NO_SSLv2); /* Only use SSLv3 or TLS */
+  httpLoadCredentials(NULL, &tcreds, common_name);
 
-  bio = BIO_new(_httpBIOMethods());
-  BIO_ctrl(bio, BIO_C_SET_FILE_PTR, 0, (char *)http);
-
-  http->tls = SSL_new(context);
-  SSL_set_bio(http->tls, bio, bio);
-
-#   ifdef HAVE_SSL_SET_TLSEXT_HOST_NAME
-  SSL_set_tlsext_host_name(http->tls, hostname);
-#   endif /* HAVE_SSL_SET_TLSEXT_HOST_NAME */
-
-  if (SSL_connect(http->tls) != 1)
+  if (tcreds)
   {
-    unsigned long      error;  /* Error code */
+    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));
 
-    while ((error = ERR_get_error()) != 0)
+    if (strcmp(credentials_str, tcreds_str))
     {
-      message = ERR_error_string(error, NULL);
-      DEBUG_printf(("8http_setup_ssl: %s", message));
-    }
+     /*
+      * 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...
+      */
 
-    SSL_CTX_free(context);
-    SSL_free(http->tls);
-    http->tls = NULL;
+      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...
+       */
 
-#    ifdef WIN32
-    http->error  = WSAGetLastError();
-#    else
-    http->error  = errno;
-#    endif /* WIN32 */
-    http->status = HTTP_STATUS_ERROR;
+        trust = HTTP_TRUST_INVALID;
+      }
+      else if (httpCredentialsGetExpiration(tcreds) < time(NULL))
+      {
+       /*
+        * Save the renewed credentials...
+       */
 
-    if (!message)
-      message = _("Unable to establish a secure connection to host.");
+       trust = HTTP_TRUST_RENEWED;
 
-    _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI, message, 1);
+        httpSaveCredentials(NULL, credentials, common_name);
+      }
+    }
 
-    return (-1);
+    httpFreeCredentials(tcreds);
   }
+  else if (cg->validate_certs && !httpCredentialsAreValidForName(credentials, common_name))
+    trust = HTTP_TRUST_INVALID;
 
-#  elif defined(HAVE_GNUTLS)
-  credentials = (gnutls_certificate_client_credentials *)
-                    malloc(sizeof(gnutls_certificate_client_credentials));
-  if (credentials == NULL)
-  {
-    DEBUG_printf(("8http_setup_ssl: Unable to allocate credentials: %s",
-                  strerror(errno)));
-    http->error  = errno;
-    http->status = HTTP_STATUS_ERROR;
-    _cupsSetHTTPError(HTTP_STATUS_ERROR);
-
-    return (-1);
-  }
+  if (!cg->expired_certs && time(NULL) > httpCredentialsGetExpiration(credentials))
+    trust = HTTP_TRUST_EXPIRED;
+  else if (!cg->any_root && cupsArrayCount(credentials) == 1)
+    trust = HTTP_TRUST_INVALID;
 
-  gnutls_certificate_allocate_credentials(credentials);
+  return (trust);
+}
 
-  gnutls_init(&http->tls, GNUTLS_CLIENT);
-  gnutls_set_default_priority(http->tls);
-  gnutls_server_name_set(http->tls, GNUTLS_NAME_DNS, hostname,
-                         strlen(hostname));
-  gnutls_credentials_set(http->tls, GNUTLS_CRD_CERTIFICATE, *credentials);
-  gnutls_transport_set_ptr(http->tls, (gnutls_transport_ptr)http);
-  gnutls_transport_set_pull_function(http->tls, _httpReadGNUTLS);
-  gnutls_transport_set_push_function(http->tls, _httpWriteGNUTLS);
 
-  while ((status = gnutls_handshake(http->tls)) != GNUTLS_E_SUCCESS)
-  {
-    DEBUG_printf(("8http_setup_ssl: gnutls_handshake returned %d (%s)",
-                  status, gnutls_strerror(status)));
+/*
+ * 'httpCredentialsGetExpiration()' - Return the expiration date of the credentials.
+ *
+ * @since CUPS 2.0@
+ */
 
-    if (gnutls_error_is_fatal(status))
-    {
-      http->error  = EIO;
-      http->status = HTTP_STATUS_ERROR;
+time_t                                 /* O - Expiration date of credentials */
+httpCredentialsGetExpiration(
+    cups_array_t *credentials)         /* I - Credentials */
+{
+  (void)credentials;
 
-      _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI, gnutls_strerror(status), 0);
+  return (INT_MAX);
+}
 
-      gnutls_deinit(http->tls);
-      gnutls_certificate_free_credentials(*credentials);
-      free(credentials);
-      http->tls = NULL;
 
-      return (-1);
-    }
-  }
+/*
+ * 'httpCredentialsString()' - Return a string representing the credentials.
+ *
+ * @since CUPS 2.0@
+ */
 
-  http->tls_credentials = credentials;
+size_t                                 /* O - Total size of credentials string */
+httpCredentialsString(
+    cups_array_t *credentials,         /* I - Credentials */
+    char         *buffer,              /* I - Buffer or @code NULL@ */
+    size_t       bufsize)              /* I - Size of buffer */
+{
+  DEBUG_printf(("httpCredentialsString(credentials=%p, buffer=%p, bufsize=" CUPS_LLFMT ")", credentials, buffer, CUPS_LLCAST bufsize));
 
-#  elif defined(HAVE_CDSASSL)
-  if ((http->tls = SSLCreateContext(kCFAllocatorDefault, kSSLClientSide,
-                                    kSSLStreamType)) == NULL)
-  {
-    DEBUG_puts("4http_setup_ssl: SSLCreateContext failed.");
-    http->error  = errno = ENOMEM;
-    http->status = HTTP_STATUS_ERROR;
-    _cupsSetHTTPError(HTTP_STATUS_ERROR);
+  if (!buffer)
+    return (0);
 
-    return (-1);
-  }
+  if (buffer && bufsize > 0)
+    *buffer = '\0';
 
-  error = SSLSetConnection(http->tls, http);
-  DEBUG_printf(("4http_setup_ssl: SSLSetConnection, error=%d", (int)error));
+#if 0
+  http_credential_t    *first;         /* First certificate */
+  SecCertificateRef    secCert;        /* Certificate reference */
 
-  if (!error)
-  {
-    error = SSLSetIOFuncs(http->tls, _httpReadCDSA, _httpWriteCDSA);
-    DEBUG_printf(("4http_setup_ssl: SSLSetIOFuncs, error=%d", (int)error));
-  }
 
-  if (!error)
+  if ((first = (http_credential_t *)cupsArrayFirst(credentials)) != NULL &&
+      (secCert = http_cdsa_create_credential(first)) != NULL)
   {
-    error = SSLSetSessionOption(http->tls, kSSLSessionOptionBreakOnServerAuth,
-                                true);
-    DEBUG_printf(("4http_setup_ssl: SSLSetSessionOption, error=%d",
-                  (int)error));
-  }
+    CFStringRef                cf_name;        /* CF common name string */
+    char               name[256];      /* Common name associated with cert */
+    time_t             expiration;     /* Expiration date of cert */
+    _cups_md5_state_t  md5_state;      /* MD5 state */
+    unsigned char      md5_digest[16]; /* MD5 result */
 
-  if (!error)
-  {
-    if (cg->client_cert_cb)
+    if ((cf_name = SecCertificateCopySubjectSummary(secCert)) != NULL)
     {
-      error = SSLSetSessionOption(http->tls,
-                                 kSSLSessionOptionBreakOnCertRequested, true);
-      DEBUG_printf(("4http_setup_ssl: kSSLSessionOptionBreakOnCertRequested, "
-                    "error=%d", (int)error));
+      CFStringGetCString(cf_name, name, (CFIndex)sizeof(name), kCFStringEncodingUTF8);
+      CFRelease(cf_name);
     }
     else
-    {
-      error = http_set_credentials(http);
-      DEBUG_printf(("4http_setup_ssl: http_set_credentials, error=%d",
-                    (int)error));
-    }
-  }
-
- /*
-  * Let the server know which hostname/domain we are trying to connect to
-  * in case it wants to serve up a certificate with a matching common name.
-  */
-
-  if (!error)
-  {
-    error = SSLSetPeerDomainName(http->tls, hostname, strlen(hostname));
+      strlcpy(name, "unknown", sizeof(name));
 
-    DEBUG_printf(("4http_setup_ssl: SSLSetPeerDomainName, error=%d",
-                  (int)error));
-  }
-
-  if (!error)
-  {
-    int done = 0;                      /* Are we done yet? */
+    expiration = (time_t)(SecCertificateNotValidAfter(secCert) + kCFAbsoluteTimeIntervalSince1970);
 
-    while (!error && !done)
-    {
-      error = SSLHandshake(http->tls);
+    _cupsMD5Init(&md5_state);
+    _cupsMD5Append(&md5_state, first->data, (int)first->datalen);
+    _cupsMD5Finish(&md5_state, md5_digest);
 
-      DEBUG_printf(("4http_setup_ssl: SSLHandshake returned %d.", (int)error));
+    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]);
 
-      switch (error)
-      {
-       case noErr :
-           done = 1;
-           break;
-
-       case errSSLWouldBlock :
-           error = noErr;              /* Force a retry */
-           usleep(1000);               /* in 1 millisecond */
-           break;
-
-       case errSSLServerAuthCompleted :
-           error = 0;
-           if (cg->server_cert_cb)
-           {
-             error = httpCopyCredentials(http, &credentials);
-             if (!error)
-             {
-               error = (cg->server_cert_cb)(http, http->tls, credentials,
-                                            cg->server_cert_data);
-               httpFreeCredentials(credentials);
-             }
-
-             DEBUG_printf(("4http_setup_ssl: Server certificate callback "
-                           "returned %d.", (int)error));
-           }
-           break;
-
-       case errSSLClientCertRequested :
-           error = 0;
-
-           if (cg->client_cert_cb)
-           {
-             names = NULL;
-             if (!(error = SSLCopyDistinguishedNames(http->tls, &dn_array)) &&
-                 dn_array)
-             {
-               if ((names = cupsArrayNew(NULL, NULL)) != NULL)
-               {
-                 for (i = 0, count = CFArrayGetCount(dn_array); i < count; i++)
-                 {
-                   data = (CFDataRef)CFArrayGetValueAtIndex(dn_array, i);
-
-                   if ((credential = malloc(sizeof(*credential))) != NULL)
-                   {
-                     credential->datalen = CFDataGetLength(data);
-                     if ((credential->data = malloc(credential->datalen)))
-                     {
-                       memcpy((void *)credential->data, CFDataGetBytePtr(data),
-                              credential->datalen);
-                       cupsArrayAdd(names, credential);
-                     }
-                     else
-                       free(credential);
-                   }
-                 }
-               }
-
-               CFRelease(dn_array);
-             }
-
-             if (!error)
-             {
-               error = (cg->client_cert_cb)(http, http->tls, names,
-                                            cg->client_cert_data);
-
-               DEBUG_printf(("4http_setup_ssl: Client certificate callback "
-                             "returned %d.", (int)error));
-             }
-
-             httpFreeCredentials(names);
-           }
-           break;
-
-       case errSSLUnknownRootCert :
-           message = _("Unable to establish a secure connection to host "
-                       "(untrusted certificate).");
-           break;
-
-       case errSSLNoRootCert :
-           message = _("Unable to establish a secure connection to host "
-                       "(self-signed certificate).");
-           break;
-
-       case errSSLCertExpired :
-           message = _("Unable to establish a secure connection to host "
-                       "(expired certificate).");
-           break;
-
-       case errSSLCertNotYetValid :
-           message = _("Unable to establish a secure connection to host "
-                       "(certificate not yet valid).");
-           break;
-
-       case errSSLHostNameMismatch :
-           message = _("Unable to establish a secure connection to host "
-                       "(host name mismatch).");
-           break;
-
-       case errSSLXCertChainInvalid :
-           message = _("Unable to establish a secure connection to host "
-                       "(certificate chain invalid).");
-           break;
-
-       case errSSLConnectionRefused :
-           message = _("Unable to establish a secure connection to host "
-                       "(peer dropped connection before responding).");
-           break;
-
-       default :
-           break;
-      }
-    }
+    CFRelease(secCert);
   }
+#endif /* 0 */
 
-  if (error)
-  {
-    http->error  = error;
-    http->status = HTTP_STATUS_ERROR;
-    errno        = ECONNREFUSED;
-
-    CFRelease(http->tls);
-    http->tls = NULL;
-
-   /*
-    * If an error string wasn't set by the callbacks use a generic one...
-    */
+  DEBUG_printf(("1httpCredentialsString: Returning \"%s\".", buffer));
 
-    if (!message)
-#ifdef HAVE_CSSMERRORSTRING
-      message = cssmErrorString(error);
-#else
-      message = _("Unable to establish a secure connection to host.");
-#endif /* HAVE_CSSMERRORSTRING */
+  return (strlen(buffer));
+}
 
-    _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI, message, 1);
 
-    return (-1);
-  }
+/*
+ * '_httpFreeCredentials()' - Free internal credentials.
+ */
 
-#  elif defined(HAVE_SSPISSL)
-  http->tls = _sspiAlloc();
+void
+_httpFreeCredentials(
+    http_tls_credentials_t credentials)        /* I - Internal credentials */
+{
+  if (!credentials)
+    return;
+}
 
-  if (!http->tls)
-  {
-    _cupsSetHTTPError(HTTP_STATUS_ERROR);
-    return (-1);
-  }
 
-  http->tls->sock = http->fd;
-  dwSize          = sizeof(username) / sizeof(TCHAR);
-  GetUserName(username, &dwSize);
-  _sntprintf_s(commonName, sizeof(commonName) / sizeof(TCHAR),
-               sizeof(commonName) / sizeof(TCHAR), TEXT("CN=%s"), username);
+/*
+ * 'httpLoadCredentials()' - Load X.509 credentials from a keychain file.
+ *
+ * @since CUPS 2.0@
+ */
 
-  if (!_sspiGetCredentials(http->tls, L"ClientContainer",
-                           commonName, FALSE))
-  {
-    _sspiFree(http->tls);
-    http->tls = NULL;
+int                                    /* O - 0 on success, -1 on error */
+httpLoadCredentials(
+    const char   *path,                        /* I  - Keychain path or @code NULL@ for default */
+    cups_array_t **credentials,                /* IO - Credentials */
+    const char   *common_name)         /* I  - Common name for credentials */
+{
+  DEBUG_printf(("httpLoadCredentials(path=\"%s\", credentials=%p, common_name=\"%s\")", path, credentials, common_name));
 
-    http->error  = EIO;
-    http->status = HTTP_STATUS_ERROR;
+  (void)path;
+  (void)common_name;
 
-    _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI,
-                  _("Unable to establish a secure connection to host."), 1);
+  if (credentials)
+    *credentials = NULL;
 
-    return (-1);
-  }
+  DEBUG_printf(("1httpLoadCredentials: Returning %d.", *credentials ? 0 : -1));
 
-  _sspiSetAllowsAnyRoot(http->tls, TRUE);
-  _sspiSetAllowsExpiredCerts(http->tls, TRUE);
+  return (*credentials ? 0 : -1);
+}
 
-  if (!_sspiConnect(http->tls, hostname))
-  {
-    _sspiFree(http->tls);
-    http->tls = NULL;
 
-    http->error  = EIO;
-    http->status = HTTP_STATUS_ERROR;
+/*
+ * 'httpSaveCredentials()' - Save X.509 credentials to a keychain file.
+ *
+ * @since CUPS 2.0@
+ */
 
-    _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI,
-                  _("Unable to establish a secure connection to host."), 1);
+int                                    /* O - -1 on error, 0 on success */
+httpSaveCredentials(
+    const char   *path,                        /* I - Keychain path or @code NULL@ for default */
+    cups_array_t *credentials,         /* I - Credentials */
+    const char   *common_name)         /* I - Common name for credentials */
+{
+  DEBUG_printf(("httpSaveCredentials(path=\"%s\", credentials=%p, common_name=\"%s\")", path, credentials, common_name));
 
-    return (-1);
-  }
-#  endif /* HAVE_CDSASSL */
+  (void)path;
+  (void)credentials;
+  (void)common_name;
 
-  return (0);
+  DEBUG_printf(("1httpSaveCredentials: Returning %d.", -1));
+  return (-1);
 }
 
 
 /*
- * 'http_shutdown_ssl()' - Shut down SSL/TLS on a connection.
+ * '_httpTLSInitialize()' - Initialize the TLS stack.
  */
 
-static void
-http_shutdown_ssl(http_t *http)                /* I - Connection to server */
+void
+_httpTLSInitialize(void)
 {
-#  ifdef HAVE_LIBSSL
-  SSL_CTX      *context;               /* Context for encryption */
-
-  context = SSL_get_SSL_CTX(http->tls);
-
-  SSL_shutdown(http->tls);
-  SSL_CTX_free(context);
-  SSL_free(http->tls);
+ /*
+  * Nothing to do...
+  */
+}
 
-#  elif defined(HAVE_GNUTLS)
-  gnutls_certificate_client_credentials *credentials;
-                                       /* TLS credentials */
 
-  credentials = (gnutls_certificate_client_credentials *)(http->tls_credentials);
+/*
+ * '_httpTLSPending()' - Return the number of pending TLS-encrypted bytes.
+ */
 
-  gnutls_bye(http->tls, GNUTLS_SHUT_RDWR);
-  gnutls_deinit(http->tls);
-  gnutls_certificate_free_credentials(*credentials);
-  free(credentials);
+size_t                                 /* O - Bytes available */
+_httpTLSPending(http_t *http)          /* I - HTTP connection */
+{
+  if (http->tls)
+    return (http->tls->readBufferUsed);
+  else
+    return (0);
+}
 
-#  elif defined(HAVE_CDSASSL)
-  while (SSLClose(http->tls) == errSSLWouldBlock)
-    usleep(1000);
 
-  CFRelease(http->tls);
+/*
+ * '_httpTLSRead()' - Read from a SSL/TLS connection.
+ */
 
-  if (http->tls_credentials)
-    CFRelease(http->tls_credentials);
+int                                    /* O - Bytes read */
+_httpTLSRead(http_t *http,             /* I - HTTP connection */
+            char   *buf,               /* I - Buffer to store data */
+            int    len)                /* I - Length of buffer */
+{
+  int          i;                      /* Looping var */
+  _http_sspi_t *conn = http->tls;      /* SSPI data */
+  SecBufferDesc        message;                /* Array of SecBuffer struct */
+  SecBuffer    buffers[4] = { 0 };     /* Security package buffer */
+  int          num = 0;                /* Return value */
+  PSecBuffer   pDataBuffer;            /* Data buffer */
+  PSecBuffer   pExtraBuffer;           /* Excess data buffer */
+  SECURITY_STATUS scRet;               /* SSPI status */
 
-#  elif defined(HAVE_SSPISSL)
-  _sspiFree(http->tls);
-#  endif /* HAVE_LIBSSL */
 
-  http->tls             = NULL;
-  http->tls_credentials = NULL;
-}
-#endif /* HAVE_SSL */
+  if (!conn)
+  {
+    WSASetLastError(WSAEINVAL);
+    return (-1);
+  }
 
+ /*
+  * If there are bytes that have already been decrypted and have not yet been
+  * read, return those...
+  */
 
-#ifdef HAVE_SSL
-/*
- * 'http_write_ssl()' - Write to a SSL/TLS connection.
- */
+  if (conn->readBufferUsed > 0)
+  {
+    int bytesToCopy = min(conn->readBufferUsed, len);
                                      /* Number of bytes to copy */
 
-static int                             /* O - Bytes written */
-http_write_ssl(http_t     *http,       /* I - Connection to server */
-              const char *buf,         /* I - Buffer holding data */
-              int        len)          /* I - Length of buffer */
-{
-  ssize_t      result;                 /* Return value */
+    memcpy(buf, conn->readBuffer, bytesToCopy);
+    conn->readBufferUsed -= bytesToCopy;
 
+    if (conn->readBufferUsed > 0)
+      memmove(conn->readBuffer, conn->readBuffer + bytesToCopy, conn->readBufferUsed);
 
-  DEBUG_printf(("2http_write_ssl(http=%p, buf=%p, len=%d)", http, buf, len));
+    return (bytesToCopy);
+  }
 
-#  if defined(HAVE_LIBSSL)
-  result = SSL_write((SSL *)(http->tls), buf, len);
+ /*
+  * Initialize security buffer structs
+  */
 
-#  elif defined(HAVE_GNUTLS)
-  result = gnutls_record_send(http->tls, buf, len);
+  message.ulVersion = SECBUFFER_VERSION;
+  message.cBuffers = 4;
+  message.pBuffers = buffers;
 
-  if (result < 0 && !errno)
+  do
   {
    /*
-    * Convert GNU TLS error to errno value...
+    * If there is not enough space in the buffer, then increase its size...
     */
 
-    switch (result)
+    if (conn->decryptBufferLength <= conn->decryptBufferUsed)
     {
-      case GNUTLS_E_INTERRUPTED :
-         errno = EINTR;
-         break;
+      BYTE *temp;                      /* New buffer */
 
-      case GNUTLS_E_AGAIN :
-          errno = EAGAIN;
-          break;
-
-      default :
-          errno = EPIPE;
-          break;
-    }
+      if (conn->decryptBufferLength >= 262144)
+      {
+       WSASetLastError(E_OUTOFMEMORY);
+        DEBUG_puts("_httpTLSRead: Decryption buffer too large (>256k)");
+       return (-1);
+      }
 
-    result = -1;
-  }
+      if ((temp = realloc(conn->decryptBuffer, conn->decryptBufferLength + 4096)) == NULL)
+      {
+       DEBUG_printf(("_httpTLSRead: Unable to allocate %d byte buffer.", conn->decryptBufferLength + 4096));
+       WSASetLastError(E_OUTOFMEMORY);
+       return (-1);
+      }
 
-#  elif defined(HAVE_CDSASSL)
-  OSStatus     error;                  /* Error info */
-  size_t       processed;              /* Number of bytes processed */
+      conn->decryptBufferLength += 4096;
+      conn->decryptBuffer       = temp;
+    }
 
+    buffers[0].pvBuffer          = conn->decryptBuffer;
+    buffers[0].cbBuffer          = (unsigned long) conn->decryptBufferUsed;
+    buffers[0].BufferType = SECBUFFER_DATA;
+    buffers[1].BufferType = SECBUFFER_EMPTY;
+    buffers[2].BufferType = SECBUFFER_EMPTY;
+    buffers[3].BufferType = SECBUFFER_EMPTY;
 
-  error = SSLWrite(http->tls, buf, len, &processed);
+    scRet = DecryptMessage(&conn->context, &message, 0, NULL);
 
-  switch (error)
-  {
-    case 0 :
-       result = (int)processed;
-       break;
-
-    case errSSLWouldBlock :
-       if (processed)
-         result = (int)processed;
-       else
-       {
-         result = -1;
-         errno  = EINTR;
-       }
-       break;
+    if (scRet == SEC_E_INCOMPLETE_MESSAGE)
+    {
+      num = recv(http->fd, conn->decryptBuffer + conn->decryptBufferUsed, (int)(conn->decryptBufferLength - conn->decryptBufferUsed), 0);
+      if (num < 0)
+      {
+       DEBUG_printf(("_httpTLSRead: recv failed: %d", WSAGetLastError()));
+       return (-1);
+      }
+      else if (num == 0)
+      {
+       DEBUG_puts("_httpTLSRead: Server disconnected.");
+       return (0);
+      }
 
-    case errSSLClosedGraceful :
-    default :
-       if (processed)
-         result = (int)processed;
-       else
-       {
-         result = -1;
-         errno  = EPIPE;
-       }
-       break;
+      conn->decryptBufferUsed += num;
+    }
   }
-#  elif defined(HAVE_SSPISSL)
-  return _sspiWrite((_sspi_struct_t *)http->tls, (void *)buf, len);
-#  endif /* HAVE_LIBSSL */
-
-  DEBUG_printf(("3http_write_ssl: Returning %d.", (int)result));
-
-  return ((int)result);
-}
-#endif /* HAVE_SSL */
+  while (scRet == SEC_E_INCOMPLETE_MESSAGE);
 
+  if (scRet == SEC_I_CONTEXT_EXPIRED)
+  {
+    DEBUG_puts("_httpTLSRead: Context expired.");
+    WSASetLastError(WSAECONNRESET);
+    return (-1);
+  }
+  else if (scRet != SEC_E_OK)
+  {
+    DEBUG_printf(("_httpTLSRead: DecryptMessage failed: %lx", scRet));
+    WSASetLastError(WSASYSCALLFAILURE);
+    return (-1);
+  }
 
-#ifdef HAVE_SSL
-/*
- * 'http_write_ssl()' - Write to a SSL/TLS connection.
- */
+ /*
+  * The decryption worked.  Now, locate data buffer.
+  */
 
-static int                             /* O - Bytes written */
-http_write_ssl(http_t     *http,       /* I - Connection to server */
-              const char *buf,         /* I - Buffer holding data */
-              int        len)          /* I - Length of buffer */
-{
-  ssize_t      result;                 /* Return value */
+  pDataBuffer  = NULL;
+  pExtraBuffer = NULL;
 
+  for (i = 1; i < 4; i++)
+  {
+    if (buffers[i].BufferType == SECBUFFER_DATA)
+      pDataBuffer = &buffers[i];
+    else if (!pExtraBuffer && (buffers[i].BufferType == SECBUFFER_EXTRA))
+      pExtraBuffer = &buffers[i];
+  }
 
-  DEBUG_printf(("2http_write_ssl(http=%p, buf=%p, len=%d)", http, buf, len));
+ /*
+  * If a data buffer is found, then copy the decrypted bytes to the passed-in
+  * buffer...
+  */
 
-#  if defined(HAVE_LIBSSL)
-  result = SSL_write((SSL *)(http->tls), buf, len);
+  if (pDataBuffer)
+  {
+    int bytesToCopy = min(pDataBuffer->cbBuffer, len);
+                                     /* Number of bytes to copy into buf */
+    int bytesToSave = pDataBuffer->cbBuffer - bytesToCopy;
+                                     /* Number of bytes to save in our read buffer */
 
-#  elif defined(HAVE_GNUTLS)
-  result = gnutls_record_send(http->tls, buf, len);
+    if (bytesToCopy)
+      memcpy(buf, pDataBuffer->pvBuffer, bytesToCopy);
 
-  if (result < 0 && !errno)
-  {
    /*
-    * Convert GNU TLS error to errno value...
+    * If there are more decrypted bytes than can be copied to the passed in
+    * buffer, then save them...
     */
 
-    switch (result)
+    if (bytesToSave)
     {
-      case GNUTLS_E_INTERRUPTED :
-         errno = EINTR;
-         break;
+      if ((conn->readBufferLength - conn->readBufferUsed) < bytesToSave)
+      {
+        BYTE *temp;                    /* New buffer pointer */
 
-      case GNUTLS_E_AGAIN :
-          errno = EAGAIN;
-          break;
+        if ((temp = realloc(conn->readBuffer, conn->readBufferUsed + bytesToSave)) == NULL)
+       {
+         DEBUG_printf(("_httpTLSRead: Unable to allocate %d bytes.", conn->readBufferUsed + bytesToSave));
+         WSASetLastError(E_OUTOFMEMORY);
+         return (-1);
+       }
 
-      default :
-          errno = EPIPE;
-          break;
-    }
+       conn->readBufferLength = conn->readBufferUsed + bytesToSave;
+       conn->readBuffer       = temp;
+      }
 
-    result = -1;
-  }
+      memcpy(((BYTE *)conn->readBuffer) + conn->readBufferUsed, ((BYTE *)pDataBuffer->pvBuffer) + bytesToCopy, bytesToSave);
 
-#  elif defined(HAVE_CDSASSL)
-  OSStatus     error;                  /* Error info */
-  size_t       processed;              /* Number of bytes processed */
+      conn->readBufferUsed += bytesToSave;
+    }
 
+    return (bytesToCopy);
+  }
+  else
+  {
+    DEBUG_puts("_httpTLSRead: Unable to find data buffer."));
+    WSASetLastError(WSASYSCALLFAILURE);
+    return (-1);
+  }
 
-  error = SSLWrite(http->tls, buf, len, &processed);
+ /*
+  * If the decryption process left extra bytes, then save those back in
+  * decryptBuffer.  They will be processed the next time through the loop.
+  */
 
-  switch (error)
+  if (pExtraBuffer)
   {
-    case 0 :
-       result = (int)processed;
-       break;
-
-    case errSSLWouldBlock :
-       if (processed)
-         result = (int)processed;
-       else
-       {
-         result = -1;
-         errno  = EINTR;
-       }
-       break;
-
-    case errSSLClosedGraceful :
-    default :
-       if (processed)
-         result = (int)processed;
-       else
-       {
-         result = -1;
-         errno  = EPIPE;
-       }
-       break;
+    memmove(conn->decryptBuffer, pExtraBuffer->pvBuffer, pExtraBuffer->cbBuffer);
+    conn->decryptBufferUsed = pExtraBuffer->cbBuffer;
+  }
+  else
+  {
+    conn->decryptBufferUsed = 0;
   }
-#  elif defined(HAVE_SSPISSL)
-  return _sspiWrite((_sspi_struct_t *)http->tls, (void *)buf, len);
-#  endif /* HAVE_LIBSSL */
-
-  DEBUG_printf(("3http_write_ssl: Returning %d.", (int)result));
 
-  return ((int)result);
+  return (0);
 }
-#endif /* HAVE_SSL */
 
 
 /*
- * 'sspi_alloc()' - Allocate SSPI ssl object
+ * '_httpTLSStart()' - Set up SSL/TLS support on a connection.
  */
-_sspi_struct_t*                                /* O  - New SSPI/SSL object */
-_sspiAlloc(void)
+
+int                                    /* O - 0 on success, -1 on failure */
+_httpTLSStart(http_t *http)            /* I - HTTP connection */
 {
-  _sspi_struct_t *conn = calloc(sizeof(_sspi_struct_t), 1);
+  char hostname[256],                  /* Hostname */
+       *hostptr;                       /* Pointer into hostname */
 
-  if (conn)
-    conn->sock = INVALID_SOCKET;
 
-  return (conn);
-}
+  DEBUG_printf(("7_httpTLSStart(http=%p)", http));
+
+  if ((http->tls = http_sspi_alloc()) == NULL)
+    return (-1);
 
+  if (http->mode == _HTTP_MODE_CLIENT)
+  {
+   /*
+    * Client: determine hostname...
+    */
 
-/*
- * '_sspiGetCredentials()' - Retrieve an SSL/TLS certificate from the system store
- *                              If one cannot be found, one is created.
- */
-BOOL                                   /* O - 1 on success, 0 on failure */
-_sspiGetCredentials(_sspi_struct_t *conn,
-                                       /* I - Client connection */
-                    const LPWSTR   container,
-                                       /* I - Cert container name */
-                    const TCHAR    *cn,        /* I - Common name of certificate */
-                    BOOL           isServer)
-                                       /* I - Is caller a server? */
-{
-  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 */
+    if (httpAddrLocalhost(http->hostaddr))
+    {
+      strlcpy(hostname, "localhost", sizeof(hostname));
+    }
+    else
+    {
+     /*
+      * Otherwise make sure the hostname we have does not end in a trailing dot.
+      */
 
-  if (!conn)
-    return (FALSE);
-  if (!cn)
-    return (FALSE);
+      strlcpy(hostname, http->hostname, sizeof(hostname));
+      if ((hostptr = hostname + strlen(hostname) - 1) >= hostname &&
+         *hostptr == '.')
+       *hostptr = '\0';
+    }
 
-  if (!CryptAcquireContextW(&hProv, (LPWSTR) container, MS_DEF_PROV_W,
-                           PROV_RSA_FULL,
-                           CRYPT_NEWKEYSET | CRYPT_MACHINE_KEYSET))
+    return (http_sspi_client(http, hostname));
+  }
+  else
   {
-    if (GetLastError() == NTE_EXISTS)
+   /*
+    * Server: determine hostname to use...
+    */
+
+    if (http->fields[HTTP_FIELD_HOST][0])
     {
-      if (!CryptAcquireContextW(&hProv, (LPWSTR) container, MS_DEF_PROV_W,
-                               PROV_RSA_FULL, CRYPT_MACHINE_KEYSET))
+     /*
+      * Use hostname for TLS upgrade...
+      */
+
+      strlcpy(hostname, http->fields[HTTP_FIELD_HOST], sizeof(hostname));
+    }
+    else
+    {
+     /*
+      * Resolve hostname from connection address...
+      */
+
+      http_addr_t      addr;           /* Connection address */
+      socklen_t                addrlen;        /* Length of address */
+
+      addrlen = sizeof(addr);
+      if (getsockname(http->fd, (struct sockaddr *)&addr, &addrlen))
       {
-        DEBUG_printf(("_sspiGetCredentials: CryptAcquireContext failed: %x\n",
-                      GetLastError()));
-        ok = FALSE;
-        goto cleanup;
+       DEBUG_printf(("4_httpTLSStart: Unable to get socket address: %s", strerror(errno)));
+       hostname[0] = '\0';
+      }
+      else if (httpAddrLocalhost(&addr))
+       hostname[0] = '\0';
+      else
+      {
+       httpAddrLookup(&addr, hostname, sizeof(hostname));
+        DEBUG_printf(("4_httpTLSStart: Resolved socket address to \"%s\".", hostname));
       }
     }
-  }
-
-  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(("_sspiGetCredentials: CertOpenSystemStore failed: %x\n",
-                  GetLastError()));
-    ok = FALSE;
-    goto cleanup;
+    return (http_sspi_server(http, hostname));
   }
+}
 
-  dwSize = 0;
 
-  if (!CertStrToName(X509_ASN_ENCODING, cn, CERT_OID_NAME_STR,
-                     NULL, NULL, &dwSize, NULL))
-  {
-    DEBUG_printf(("_sspiGetCredentials: CertStrToName failed: %x\n",
-                   GetLastError()));
-    ok = FALSE;
-    goto cleanup;
-  }
+/*
+ * '_httpTLSStop()' - Shut down SSL/TLS on a connection.
+ */
 
-  p = (PBYTE) malloc(dwSize);
+void
+_httpTLSStop(http_t *http)             /* I - HTTP connection */
+{
+  _http_sspi_t *conn = http->tls;      /* SSPI data */
 
-  if (!p)
-  {
-    DEBUG_printf(("_sspiGetCredentials: malloc failed for %d bytes", dwSize));
-    ok = FALSE;
-    goto cleanup;
-  }
 
-  if (!CertStrToName(X509_ASN_ENCODING, cn, CERT_OID_NAME_STR, NULL,
-                     p, &dwSize, NULL))
+  if (conn->contextInitialized && http->fd >= 0)
   {
-    DEBUG_printf(("_sspiGetCredentials: CertStrToName failed: %x",
-                 GetLastError()));
-    ok = FALSE;
-    goto cleanup;
-  }
+    SecBufferDesc      message;        /* Array of SecBuffer struct */
+    SecBuffer          buffers[1] = { 0 };
+                                       /* Security package buffer */
+    DWORD              dwType;         /* Type */
+    DWORD              status;         /* Status */
 
-  sib.cbData = dwSize;
-  sib.pbData = p;
+  /*
+   * Notify schannel that we are about to close the connection.
+   */
 
-  storedContext = CertFindCertificateInStore(store, X509_ASN_ENCODING|PKCS_7_ASN_ENCODING,
-                                             0, CERT_FIND_SUBJECT_NAME, &sib, NULL);
+   dwType = SCHANNEL_SHUTDOWN;
 
-  if (!storedContext)
-  {
-   /*
-    * If we couldn't find the context, then we'll
-    * create a new one
-    */
-    if (!CryptGenKey(hProv, AT_KEYEXCHANGE, CRYPT_EXPORTABLE, &hKey))
-    {
-      DEBUG_printf(("_sspiGetCredentials: CryptGenKey failed: %x",
-                    GetLastError()));
-      ok = FALSE;
-      goto cleanup;
-    }
+   buffers[0].pvBuffer   = &dwType;
+   buffers[0].BufferType = SECBUFFER_TOKEN;
+   buffers[0].cbBuffer   = sizeof(dwType);
 
-    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;
+   message.cBuffers  = 1;
+   message.pBuffers  = buffers;
+   message.ulVersion = SECBUFFER_VERSION;
 
-    GetSystemTime(&et);
-    et.wYear += 10;
+   status = ApplyControlToken(&conn->context, &message);
 
-    ZeroMemory(&exts, sizeof(exts));
+   if (SUCCEEDED(status))
+   {
+     PBYTE     pbMessage;              /* Message buffer */
+     DWORD     cbMessage;              /* Message buffer count */
+     DWORD     cbData;                 /* Data count */
+     DWORD     dwSSPIFlags;            /* SSL attributes we requested */
+     DWORD     dwSSPIOutFlags;         /* SSL attributes we received */
+     TimeStamp tsExpiry;               /* Time stamp */
 
-    createdContext = CertCreateSelfSignCertificate(hProv, &sib, 0, &kpi, NULL, NULL,
-                                                   &et, &exts);
+     dwSSPIFlags = ASC_REQ_SEQUENCE_DETECT     |
+                   ASC_REQ_REPLAY_DETECT       |
+                   ASC_REQ_CONFIDENTIALITY     |
+                   ASC_REQ_EXTENDED_ERROR      |
+                   ASC_REQ_ALLOCATE_MEMORY     |
+                   ASC_REQ_STREAM;
 
-    if (!createdContext)
-    {
-      DEBUG_printf(("_sspiGetCredentials: CertCreateSelfSignCertificate failed: %x",
-                   GetLastError()));
-      ok = FALSE;
-      goto cleanup;
-    }
+     buffers[0].pvBuffer   = NULL;
+     buffers[0].BufferType = SECBUFFER_TOKEN;
+     buffers[0].cbBuffer   = 0;
 
-    if (!CertAddCertificateContextToStore(store, createdContext,
-                                          CERT_STORE_ADD_REPLACE_EXISTING,
-                                          &storedContext))
-    {
-      DEBUG_printf(("_sspiGetCredentials: CertAddCertificateContextToStore failed: %x",
-                    GetLastError()));
-      ok = FALSE;
-      goto cleanup;
+     message.cBuffers  = 1;
+     message.pBuffers  = buffers;
+     message.ulVersion = SECBUFFER_VERSION;
+
+     status = AcceptSecurityContext(&conn->creds, &conn->context, NULL,
+                                    dwSSPIFlags, SECURITY_NATIVE_DREP, NULL,
+                                    &message, &dwSSPIOutFlags, &tsExpiry);
+
+      if (SUCCEEDED(status))
+      {
+        pbMessage = buffers[0].pvBuffer;
+        cbMessage = buffers[0].cbBuffer;
+
+       /*
+        * Send the close notify message to the client.
+        */
+
+        if (pbMessage && cbMessage)
+        {
+          cbData = send(http->fd, pbMessage, cbMessage, 0);
+          if ((cbData == SOCKET_ERROR) || (cbData == 0))
+          {
+            status = WSAGetLastError();
+            DEBUG_printf(("_httpTLSStop: sending close notify failed: %d", status));
+          }
+          else
+          {
+            FreeContextBuffer(pbMessage);
+          }
+        }
+      }
+      else
+      {
+        DEBUG_printf(("_httpTLSStop: AcceptSecurityContext failed: %x", status));
+      }
+    }
+    else
+    {
+      DEBUG_printf(("_httpTLSStop: ApplyControlToken failed: %x", status));
     }
+  }
 
-    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;
+  http_sspi_free(conn);
 
-    if (!CertSetCertificateContextProperty(storedContext,
-                                           CERT_KEY_PROV_INFO_PROP_ID,
-                                           0, &ckp))
+  http->tls = NULL;
+}
+
+
+/*
+ * '_httpTLSWrite()' - Write to a SSL/TLS connection.
+ */
+
+int                                    /* O - Bytes written */
+_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 */
+  SecBufferDesc        message;                /* Array of SecBuffer struct */
+  SecBuffer    buffers[4] = { 0 };     /* Security package buffer */
+  int          bufferLen;              /* Buffer length */
+  int          bytesLeft;              /* Bytes left to write */
+  const char   *bufptr;                /* Pointer into buffer */
+  int          num = 0;                /* Return value */
+
+
+  bufferLen = conn->streamSizes.cbMaximumMessage + conn->streamSizes.cbHeader + conn->streamSizes.cbTrailer;
+
+  if (bufferLen > conn->writeBufferLen)
+  {
+    BYTE *temp;                                /* New buffer pointer */
+
+    if ((temp = (BYTE *)realloc(conn->writeBuffer, bufferLen)) == NULL)
     {
-      DEBUG_printf(("_sspiGetCredentials: CertSetCertificateContextProperty failed: %x",
-                    GetLastError()));
-      ok = FALSE;
-      goto cleanup;
+      DEBUG_printf(("_httpTLSWrite: Unable to allocate buffer of %d bytes.", bufferLen));
+      WSASetLastError(E_OUTOFMEMORY);
+      return (-1);
     }
+
+    conn->writeBuffer    = temp;
+    conn->writeBufferLen = bufferLen;
   }
 
-  ZeroMemory(&SchannelCred, sizeof(SchannelCred));
+  bytesLeft = len;
+  bufptr    = buf;
 
-  SchannelCred.dwVersion = SCHANNEL_CRED_VERSION;
-  SchannelCred.cCreds = 1;
-  SchannelCred.paCred = &storedContext;
+  while (bytesLeft)
+  {
+    int chunk = min(conn->streamSizes.cbMaximumMessage, bytesLeft);
+                                       /* Size of data to write */
+    SECURITY_STATUS scRet;             /* SSPI status */
+
+   /*
+    * Copy user data into the buffer, starting just past the header...
+    */
+
+    memcpy(conn->writeBuffer + conn->streamSizes.cbHeader, bufptr, chunk);
+
+   /*
+    * Setup the SSPI buffers
+    */
+
+    message.ulVersion = SECBUFFER_VERSION;
+    message.cBuffers  = 4;
+    message.pBuffers  = buffers;
+
+    buffers[0].pvBuffer   = conn->writeBuffer;
+    buffers[0].cbBuffer   = conn->streamSizes.cbHeader;
+    buffers[0].BufferType = SECBUFFER_STREAM_HEADER;
+    buffers[1].pvBuffer   = conn->writeBuffer + conn->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].BufferType = SECBUFFER_STREAM_TRAILER;
+    buffers[3].BufferType = SECBUFFER_EMPTY;
+
+   /*
+    * Encrypt the data
+    */
+
+    scRet = EncryptMessage(&conn->context, 0, &message, 0);
+
+    if (FAILED(scRet))
+    {
+      DEBUG_printf(("_httpTLSWrite: EncryptMessage failed: %x", scRet));
+      WSASetLastError(WSASYSCALLFAILURE);
+      return (-1);
+    }
+
+   /*
+    * Send the data. Remember the size of the total data to send is the size
+    * of the header, the size of the data the caller passed in and the size
+    * of the trailer...
+    */
+
+    num = send(http->fd, conn->writeBuffer, buffers[0].cbBuffer + buffers[1].cbBuffer + buffers[2].cbBuffer, 0);
+
+    if (num <= 0)
+    {
+      DEBUG_printf(("_httpTLSWrite: send failed: %ld", WSAGetLastError()));
+      return (num);
+    }
+
+    bytesLeft -= chunk;
+    bufptr    += chunk;
+  }
+
+  return (len);
+}
+
+
+#ifdef HAVE_SSL
+/*
+ * 'http_setup_ssl()' - Set up SSL/TLS support on a connection.
+ */
+
+static int                             /* O - 0 on success, -1 on failure */
+http_setup_ssl(http_t *http)           /* I - Connection to server */
+{
+  char                 hostname[256],  /* Hostname */
+                       *hostptr;       /* Pointer into hostname */
+
+  TCHAR                        username[256];  /* Username returned from GetUserName() */
+  TCHAR                        commonName[256];/* Common name for certificate */
+  DWORD                        dwSize;         /* 32 bit size */
 
- /*
-  * SSPI doesn't seem to like it if grbitEnabledProtocols
-  * is set for a client
-  */
-  if (isServer)
-    SchannelCred.grbitEnabledProtocols = SP_PROT_SSL3TLS1;
+
+  DEBUG_printf(("7http_setup_ssl(http=%p)", http));
 
  /*
-  * Create an SSPI credential.
+  * Get the hostname to use for SSL...
   */
-  Status = AcquireCredentialsHandle(NULL, UNISP_NAME,
-                                    isServer ? SECPKG_CRED_INBOUND:SECPKG_CRED_OUTBOUND,
-                                    NULL, &SchannelCred, NULL, NULL, &conn->creds,
-                                    &tsExpiry);
-  if (Status != SEC_E_OK)
+
+  if (httpAddrLocalhost(http->hostaddr))
   {
-    DEBUG_printf(("_sspiGetCredentials: AcquireCredentialsHandle failed: %x", Status));
-    ok = FALSE;
-    goto cleanup;
+    strlcpy(hostname, "localhost", sizeof(hostname));
   }
+  else
+  {
+   /*
+    * Otherwise make sure the hostname we have does not end in a trailing dot.
+    */
 
-cleanup:
+    strlcpy(hostname, http->hostname, sizeof(hostname));
+    if ((hostptr = hostname + strlen(hostname) - 1) >= hostname &&
+        *hostptr == '.')
+      *hostptr = '\0';
+  }
 
- /*
-  * Cleanup
-  */
-  if (hKey)
-    CryptDestroyKey(hKey);
+  http->tls = _sspiAlloc();
 
-  if (createdContext)
-    CertFreeCertificateContext(createdContext);
+  if (!http->tls)
+  {
+    _cupsSetHTTPError(HTTP_STATUS_ERROR);
+    return (-1);
+  }
 
-  if (storedContext)
-    CertFreeCertificateContext(storedContext);
+  http->tls->sock = http->fd;
+  dwSize          = sizeof(username) / sizeof(TCHAR);
+  GetUserName(username, &dwSize);
+  _sntprintf_s(commonName, sizeof(commonName) / sizeof(TCHAR),
+               sizeof(commonName) / sizeof(TCHAR), TEXT("CN=%s"), username);
 
-  if (p)
-    free(p);
+  if (!_sspiGetCredentials(http->tls, L"ClientContainer",
+                           commonName, FALSE))
+  {
+    _sspiFree(http->tls);
+    http->tls = NULL;
 
-  if (store)
-    CertCloseStore(store, 0);
+    http->error  = EIO;
+    http->status = HTTP_STATUS_ERROR;
 
-  if (hProv)
-    CryptReleaseContext(hProv, 0);
+    _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI,
+                  _("Unable to establish a secure connection to host."), 1);
 
-  return (ok);
+    return (-1);
+  }
+
+  _sspiSetAllowsAnyRoot(http->tls, TRUE);
+  _sspiSetAllowsExpiredCerts(http->tls, TRUE);
+
+  if (!_sspiConnect(http->tls, hostname))
+  {
+    _sspiFree(http->tls);
+    http->tls = NULL;
+
+    http->error  = EIO;
+    http->status = HTTP_STATUS_ERROR;
+
+    _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI,
+                  _("Unable to establish a secure connection to host."), 1);
+
+    return (-1);
+  }
+
+  return (0);
 }
 
 
 /*
- * '_sspiConnect()' - Make an SSL connection. This function
- *                    assumes a TCP/IP connection has already
- *                    been successfully made
+ * 'http_sspi_alloc()' - Allocate SSPI object.
  */
-BOOL                                   /* O - 1 on success, 0 on failure */
-_sspiConnect(_sspi_struct_t *conn,     /* I - Client connection */
-             const CHAR *hostname)     /* I - Server hostname */
+
+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);
+}
+
+
+/*
+ * 'http_sspi_client()' - Negotiate a TLS connection as a client.
+ */
+
+static int                             /* O - 0 on success, -1 on failure */
+http_sspi_client(http_t     *http,     /* I - Client connection */
+                 const char *hostname) /* I - Server hostname */
 {
-  PCCERT_CONTEXT       serverCert;     /* Server certificate */
+  _http_sspi_t         *conn;          /* SSPI data */
   DWORD                        dwSSPIFlags;    /* SSL connection attributes we want */
   DWORD                        dwSSPIOutFlags; /* SSL connection attributes we got */
   TimeStamp            tsExpiry;       /* Time stamp */
   SECURITY_STATUS      scRet;          /* Status */
-  DWORD                        cbData;         /* Data count */
+  int                  cbData;         /* Data count */
   SecBufferDesc                inBuffer;       /* Array of SecBuffer structs */
   SecBuffer            inBuffers[2];   /* Security package buffer */
   SecBufferDesc                outBuffer;      /* Array of SecBuffer structs */
   SecBuffer            outBuffers[1];  /* Security package buffer */
-  BOOL                 ok = TRUE;      /* Return value */
+  int                  ret = 0;        /* Return value */
 
-  serverCert  = NULL;
 
+  DEBUG_printf(("http_sspi_client(http=%p, hostname=\"%s\")", http, hostname));
+
+  serverCert  = NULL;
   dwSSPIFlags = ISC_REQ_SEQUENCE_DETECT   |
                 ISC_REQ_REPLAY_DETECT     |
                 ISC_REQ_CONFIDENTIALITY   |
@@ -1082,52 +1035,45 @@ _sspiConnect(_sspi_struct_t *conn,      /* I - Client connection */
  /*
   * Initiate a ClientHello message and generate a token.
   */
+
   outBuffers[0].pvBuffer   = NULL;
   outBuffers[0].BufferType = SECBUFFER_TOKEN;
   outBuffers[0].cbBuffer   = 0;
 
-  outBuffer.cBuffers = 1;
-  outBuffer.pBuffers = outBuffers;
+  outBuffer.cBuffers  = 1;
+  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(&conn->creds, NULL, TEXT(""), dwSSPIFlags, 0, SECURITY_NATIVE_DREP, NULL, 0, &conn->context, &outBuffer, &dwSSPIOutFlags, &tsExpiry);
 
   if (scRet != SEC_I_CONTINUE_NEEDED)
   {
-    DEBUG_printf(("_sspiConnect: InitializeSecurityContext(1) failed: %x", scRet));
-    ok = FALSE;
-    goto cleanup;
+    DEBUG_printf(("http_sspi_client: InitializeSecurityContext(1) failed: %x", scRet));
+    return (-1);
   }
 
  /*
   * Send response to server if there is one.
   */
+
   if (outBuffers[0].cbBuffer && outBuffers[0].pvBuffer)
   {
-    cbData = send(conn->sock, outBuffers[0].pvBuffer, outBuffers[0].cbBuffer, 0);
-
-    if ((cbData == SOCKET_ERROR) || !cbData)
+    if ((cbData = send(http->fd, outBuffers[0].pvBuffer, outBuffers[0].cbBuffer, 0)) <= 0)
     {
-      DEBUG_printf(("_sspiConnect: send failed: %d", WSAGetLastError()));
+      DEBUG_printf(("http_sspi_client: send failed: %d", WSAGetLastError()));
       FreeContextBuffer(outBuffers[0].pvBuffer);
       DeleteSecurityContext(&conn->context);
-      ok = FALSE;
-      goto cleanup;
+      return (-1);
     }
 
-    DEBUG_printf(("_sspiConnect: %d bytes of handshake data sent", cbData));
+    DEBUG_printf(("http_sspi_client: %d bytes of handshake data sent.", cbData));
 
-   /*
-    * Free output buffer.
-    */
     FreeContextBuffer(outBuffers[0].pvBuffer);
     outBuffers[0].pvBuffer = NULL;
   }
 
   dwSSPIFlags = ISC_REQ_MANUAL_CRED_VALIDATION |
-                   ISC_REQ_SEQUENCE_DETECT        |
+               ISC_REQ_SEQUENCE_DETECT        |
                 ISC_REQ_REPLAY_DETECT          |
                 ISC_REQ_CONFIDENTIALITY        |
                 ISC_RET_EXTENDED_ERROR         |
@@ -1139,59 +1085,64 @@ _sspiConnect(_sspi_struct_t *conn,      /* I - Client connection */
  /*
   * Loop until the handshake is finished or an error occurs.
   */
+
   scRet = SEC_I_CONTINUE_NEEDED;
 
   while(scRet == SEC_I_CONTINUE_NEEDED        ||
         scRet == SEC_E_INCOMPLETE_MESSAGE     ||
         scRet == SEC_I_INCOMPLETE_CREDENTIALS)
   {
-    if ((conn->decryptBufferUsed == 0) || (scRet == SEC_E_INCOMPLETE_MESSAGE))
+    if (conn->decryptBufferUsed == 0 || scRet == SEC_E_INCOMPLETE_MESSAGE)
     {
       if (conn->decryptBufferLength <= conn->decryptBufferUsed)
       {
-        conn->decryptBufferLength += 4096;
-        conn->decryptBuffer = (BYTE*) realloc(conn->decryptBuffer, conn->decryptBufferLength);
+       BYTE *temp;                     /* New buffer */
 
-        if (!conn->decryptBuffer)
-        {
-          DEBUG_printf(("_sspiConnect: unable to allocate %d byte decrypt buffer",
-                        conn->decryptBufferLength));
-          SetLastError(E_OUTOFMEMORY);
-          ok = FALSE;
-          goto cleanup;
-        }
+       if (conn->decryptBufferLength >= 262144)
+       {
+         WSASetLastError(E_OUTOFMEMORY);
+         DEBUG_puts("http_sspi_client: Decryption buffer too large (>256k)");
+         return (-1);
+       }
+
+       if ((temp = realloc(conn->decryptBuffer, conn->decryptBufferLength + 4096)) == NULL)
+       {
+         DEBUG_printf(("http_sspi_client: Unable to allocate %d byte buffer.", conn->decryptBufferLength + 4096));
+         WSASetLastError(E_OUTOFMEMORY);
+         return (-1);
+       }
+
+       conn->decryptBufferLength += 4096;
+       conn->decryptBuffer       = temp;
       }
 
-      cbData = recv(conn->sock, conn->decryptBuffer + conn->decryptBufferUsed,
-                    (int) (conn->decryptBufferLength - conn->decryptBufferUsed), 0);
+      cbData = recv(http->fd, conn->decryptBuffer + conn->decryptBufferUsed, (int)(conn->decryptBufferLength - conn->decryptBufferUsed), 0);
 
-      if (cbData == SOCKET_ERROR)
+      if (cbData < 0)
       {
-        DEBUG_printf(("_sspiConnect: recv failed: %d", WSAGetLastError()));
-        ok = FALSE;
-        goto cleanup;
+        DEBUG_printf(("http_sspi_client: recv failed: %d", WSAGetLastError()));
+        return (-1);
       }
       else if (cbData == 0)
       {
-        DEBUG_printf(("_sspiConnect: server unexpectedly disconnected"));
-        ok = FALSE;
-        goto cleanup;
+        DEBUG_printf(("http_sspi_client: Server unexpectedly disconnected."));
+        return (-1);
       }
 
-      DEBUG_printf(("_sspiConnect: %d bytes of handshake data received",
-                    cbData));
+      DEBUG_printf(("http_sspi_client: %d bytes of handshake data received", cbData));
 
       conn->decryptBufferUsed += cbData;
     }
 
    /*
-    * Set up the input buffers. Buffer 0 is used to pass in data
-    * received from the server. Schannel will consume some or all
-    * of this. Leftover data (if any) will be placed in buffer 1 and
-    * given a buffer type of SECBUFFER_EXTRA.
+    * Set up the input buffers. Buffer 0 is used to pass in data received from
+    * the server.  Schannel will consume some or all of this.  Leftover data
+    * (if any) will be placed in buffer 1 and given a buffer type of
+    * SECBUFFER_EXTRA.
     */
+
     inBuffers[0].pvBuffer   = conn->decryptBuffer;
-    inBuffers[0].cbBuffer   = (unsigned long) conn->decryptBufferUsed;
+    inBuffers[0].cbBuffer   = (unsigned long)conn->decryptBufferUsed;
     inBuffers[0].BufferType = SECBUFFER_TOKEN;
 
     inBuffers[1].pvBuffer   = NULL;
@@ -1203,101 +1154,87 @@ _sspiConnect(_sspi_struct_t *conn,     /* I - Client connection */
     inBuffer.ulVersion      = SECBUFFER_VERSION;
 
    /*
-    * Set up the output buffers. These are initialized to NULL
-    * so as to make it less likely we'll attempt to free random
-    * garbage later.
+    * Set up the output buffers. These are initialized to NULL so as to make it
+    * less likely we'll attempt to free random garbage later.
     */
-    outBuffers[0].pvBuffer  = NULL;
-    outBuffers[0].BufferType= SECBUFFER_TOKEN;
-    outBuffers[0].cbBuffer  = 0;
 
-    outBuffer.cBuffers      = 1;
-    outBuffer.pBuffers      = outBuffers;
-    outBuffer.ulVersion     = SECBUFFER_VERSION;
+    outBuffers[0].pvBuffer   = NULL;
+    outBuffers[0].BufferType = SECBUFFER_TOKEN;
+    outBuffers[0].cbBuffer   = 0;
+
+    outBuffer.cBuffers       = 1;
+    outBuffer.pBuffers       = outBuffers;
+    outBuffer.ulVersion      = SECBUFFER_VERSION;
 
    /*
     * Call InitializeSecurityContext.
     */
-    scRet = InitializeSecurityContext(&conn->creds, &conn->context, NULL, dwSSPIFlags,
-                                      0, SECURITY_NATIVE_DREP, &inBuffer, 0, NULL,
-                                      &outBuffer, &dwSSPIOutFlags, &tsExpiry);
+
+    scRet = InitializeSecurityContext(&conn->creds, &conn->context, NULL, dwSSPIFlags, 0, SECURITY_NATIVE_DREP, &inBuffer, 0, NULL, &outBuffer, &dwSSPIOutFlags, &tsExpiry);
 
    /*
-    * If InitializeSecurityContext was successful (or if the error was
-    * one of the special extended ones), send the contends of the output
-    * buffer to the server.
+    * If InitializeSecurityContext was successful (or if the error was one of
+    * the special extended ones), send the contents of the output buffer to the
+    * server.
     */
+
     if (scRet == SEC_E_OK                ||
         scRet == SEC_I_CONTINUE_NEEDED   ||
         FAILED(scRet) && (dwSSPIOutFlags & ISC_RET_EXTENDED_ERROR))
     {
       if (outBuffers[0].cbBuffer && outBuffers[0].pvBuffer)
       {
-        cbData = send(conn->sock, outBuffers[0].pvBuffer, outBuffers[0].cbBuffer, 0);
+        cbData = send(http->fd, outBuffers[0].pvBuffer, outBuffers[0].cbBuffer, 0);
 
-        if ((cbData == SOCKET_ERROR) || !cbData)
+        if (cbData <= 0)
         {
-          DEBUG_printf(("_sspiConnect: send failed: %d", WSAGetLastError()));
+          DEBUG_printf(("http_sspi_client: send failed: %d", WSAGetLastError()));
           FreeContextBuffer(outBuffers[0].pvBuffer);
           DeleteSecurityContext(&conn->context);
-          ok = FALSE;
-          goto cleanup;
+          return (-1);
         }
 
-        DEBUG_printf(("_sspiConnect: %d bytes of handshake data sent", cbData));
+        DEBUG_printf(("http_sspi_client: %d bytes of handshake data sent.", cbData));
 
        /*
         * Free output buffer.
         */
+
         FreeContextBuffer(outBuffers[0].pvBuffer);
         outBuffers[0].pvBuffer = NULL;
       }
     }
 
    /*
-    * If InitializeSecurityContext returned SEC_E_INCOMPLETE_MESSAGE,
-    * then we need to read more data from the server and try again.
+    * If InitializeSecurityContext returned SEC_E_INCOMPLETE_MESSAGE, then we
+    * need to read more data from the server and try again.
     */
+
     if (scRet == SEC_E_INCOMPLETE_MESSAGE)
       continue;
 
    /*
-    * If InitializeSecurityContext returned SEC_E_OK, then the
-    * handshake completed successfully.
+    * If InitializeSecurityContext returned SEC_E_OK, then the handshake
+    * completed successfully.
     */
+
     if (scRet == SEC_E_OK)
     {
      /*
       * If the "extra" buffer contains data, this is encrypted application
-      * protocol layer stuff. It needs to be saved. The application layer
-      * will later decrypt it with DecryptMessage.
+      * protocol layer stuff. It needs to be saved. The application layer will
+      * later decrypt it with DecryptMessage.
       */
-      DEBUG_printf(("_sspiConnect: Handshake was successful"));
+
+      DEBUG_puts("http_sspi_client: Handshake was successful.");
 
       if (inBuffers[1].BufferType == SECBUFFER_EXTRA)
       {
-        if (conn->decryptBufferLength < inBuffers[1].cbBuffer)
-        {
-          conn->decryptBuffer = realloc(conn->decryptBuffer, inBuffers[1].cbBuffer);
-
-          if (!conn->decryptBuffer)
-          {
-            DEBUG_printf(("_sspiConnect: unable to allocate %d bytes for decrypt buffer",
-                          inBuffers[1].cbBuffer));
-            SetLastError(E_OUTOFMEMORY);
-            ok = FALSE;
-            goto cleanup;
-          }
-        }
-
-        memmove(conn->decryptBuffer,
-                conn->decryptBuffer + (conn->decryptBufferUsed - inBuffers[1].cbBuffer),
-                inBuffers[1].cbBuffer);
+        memmove(conn->decryptBuffer, conn->decryptBuffer + conn->decryptBufferUsed - inBuffers[1].cbBuffer, inBuffers[1].cbBuffer);
 
         conn->decryptBufferUsed = inBuffers[1].cbBuffer;
 
-        DEBUG_printf(("_sspiConnect: %d bytes of app data was bundled with handshake data",
-                      conn->decryptBufferUsed));
+        DEBUG_printf(("http_sspi_client: %d bytes of app data was bundled with handshake data", conn->decryptBufferUsed));
       }
       else
         conn->decryptBufferUsed = 0;
@@ -1305,16 +1242,18 @@ _sspiConnect(_sspi_struct_t *conn,      /* I - Client connection */
      /*
       * Bail out to quit
       */
+
       break;
     }
 
    /*
     * Check for fatal error.
     */
+
     if (FAILED(scRet))
     {
-      DEBUG_printf(("_sspiConnect: InitializeSecurityContext(2) failed: %x", scRet));
-      ok = FALSE;
+      DEBUG_printf(("http_sspi_client: InitializeSecurityContext(2) failed: %x", scRet));
+      ret = -1;
       break;
     }
 
@@ -1322,25 +1261,25 @@ _sspiConnect(_sspi_struct_t *conn,      /* I - Client connection */
     * If InitializeSecurityContext returned SEC_I_INCOMPLETE_CREDENTIALS,
     * then the server just requested client authentication.
     */
+
     if (scRet == SEC_I_INCOMPLETE_CREDENTIALS)
     {
      /*
       * Unimplemented
       */
-      DEBUG_printf(("_sspiConnect: server requested client credentials"));
-      ok = FALSE;
+
+      DEBUG_printf(("http_sspi_client: server requested client credentials."));
+      ret = -1;
       break;
     }
 
    /*
-    * Copy any leftover data from the "extra" buffer, and go around
-    * again.
+    * Copy any leftover data from the "extra" buffer, and go around again.
     */
+
     if (inBuffers[1].BufferType == SECBUFFER_EXTRA)
     {
-      memmove(conn->decryptBuffer,
-              conn->decryptBuffer + (conn->decryptBufferUsed - inBuffers[1].cbBuffer),
-              inBuffers[1].cbBuffer);
+      memmove(conn->decryptBuffer, conn->decryptBuffer + conn->decryptBufferUsed - inBuffers[1].cbBuffer, inBuffers[1].cbBuffer);
 
       conn->decryptBufferUsed = inBuffers[1].cbBuffer;
     }
@@ -1350,849 +1289,629 @@ _sspiConnect(_sspi_struct_t *conn,    /* I - Client connection */
     }
   }
 
-  if (ok)
+  if (!ret)
   {
-    conn->contextInitialized = TRUE;
-
    /*
-    * Get the server cert
+    * Success!  Get the server cert
     */
-    scRet = QueryContextAttributes(&conn->context, SECPKG_ATTR_REMOTE_CERT_CONTEXT, (VOID*) &serverCert );
+
+    conn->contextInitialized = TRUE;
+
+    scRet = QueryContextAttributes(&conn->context, SECPKG_ATTR_REMOTE_CERT_CONTEXT, (VOID *)&(conn->remoteCert));
 
     if (scRet != SEC_E_OK)
     {
-      DEBUG_printf(("_sspiConnect: QueryContextAttributes failed(SECPKG_ATTR_REMOTE_CERT_CONTEXT): %x", scRet));
-      ok = FALSE;
-      goto cleanup;
+      DEBUG_printf(("http_sspi_client: QueryContextAttributes failed(SECPKG_ATTR_REMOTE_CERT_CONTEXT): %x", scRet));
+      return (-1);
     }
 
-    scRet = sspi_verify_certificate(serverCert, hostname, conn->certFlags);
+#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(("_sspiConnect: sspi_verify_certificate failed: %x", scRet));
-      ok = FALSE;
+      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);
 
     if (scRet != SEC_E_OK)
     {
-      DEBUG_printf(("_sspiConnect: QueryContextAttributes failed(SECPKG_ATTR_STREAM_SIZES): %x", scRet));
-      ok = FALSE;
+      DEBUG_printf(("http_sspi_client: QueryContextAttributes failed(SECPKG_ATTR_STREAM_SIZES): %x", scRet));
+      ret = -1;
     }
   }
 
-cleanup:
+  return (ret);
+}
 
-  if (serverCert)
-    CertFreeCertificateContext(serverCert);
 
-  return (ok);
+/*
+ * '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.pbCertEncoded)
+    CertFreeCertificateContext(conn->localCert);
+
+  if (conn->remoteCert.pbCertEncoded)
+    CertFreeCertificateContext(conn->remoteCert);
+
+  free(conn);
 }
 
 
+#if 0
 /*
- * '_sspiAccept()' - Accept an SSL/TLS connection
+ * '_sspiGetCredentials()' - Retrieve an SSL/TLS certificate from the system store
+ *                              If one cannot be found, one is created.
  */
 BOOL                                   /* O - 1 on success, 0 on failure */
-_sspiAccept(_sspi_struct_t *conn)      /* I  - Client connection */
+_sspiGetCredentials(_http_sspi_t *conn,
+                                       /* I - Client connection */
+                    const LPWSTR   container,
+                                       /* I - Cert container name */
+                    const TCHAR    *cn,        /* I - Common name of certificate */
+                    BOOL           isServer)
+                                       /* I - Is caller a server? */
 {
-  DWORD                        dwSSPIFlags;    /* SSL connection attributes we want */
-  DWORD                        dwSSPIOutFlags; /* SSL connection attributes we got */
+  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      scRet;          /* SSPI Status */
-  SecBufferDesc                inBuffer;       /* Array of SecBuffer structs */
-  SecBuffer            inBuffers[2];   /* Security package buffer */
-  SecBufferDesc                outBuffer;      /* Array of SecBuffer structs */
-  SecBuffer            outBuffers[1];  /* Security package buffer */
-  DWORD                        num = 0;        /* 32 bit status value */
-  BOOL                 fInitContext = TRUE;
-                                       /* Has the context been init'd? */
+  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 */
 
   if (!conn)
     return (FALSE);
+  if (!cn)
+    return (FALSE);
 
-  dwSSPIFlags = ASC_REQ_SEQUENCE_DETECT  |
-                ASC_REQ_REPLAY_DETECT    |
-                ASC_REQ_CONFIDENTIALITY  |
-                ASC_REQ_EXTENDED_ERROR   |
-                ASC_REQ_ALLOCATE_MEMORY  |
-                ASC_REQ_STREAM;
-
-  conn->decryptBufferUsed = 0;
-
- /*
-  * Set OutBuffer for AcceptSecurityContext call
-  */
-  outBuffer.cBuffers = 1;
-  outBuffer.pBuffers = outBuffers;
-  outBuffer.ulVersion = SECBUFFER_VERSION;
-
-  scRet = SEC_I_CONTINUE_NEEDED;
-
-  while (scRet == SEC_I_CONTINUE_NEEDED    ||
-         scRet == SEC_E_INCOMPLETE_MESSAGE ||
-         scRet == SEC_I_INCOMPLETE_CREDENTIALS)
+  if (!CryptAcquireContextW(&hProv, (LPWSTR) container, MS_DEF_PROV_W,
+                           PROV_RSA_FULL,
+                           CRYPT_NEWKEYSET | CRYPT_MACHINE_KEYSET))
   {
-    if ((conn->decryptBufferUsed == 0) || (scRet == SEC_E_INCOMPLETE_MESSAGE))
+    if (GetLastError() == NTE_EXISTS)
     {
-      if (conn->decryptBufferLength <= conn->decryptBufferUsed)
-      {
-        conn->decryptBufferLength += 4096;
-        conn->decryptBuffer = (BYTE*) realloc(conn->decryptBuffer,
-                                              conn->decryptBufferLength);
-
-        if (!conn->decryptBuffer)
-        {
-          DEBUG_printf(("_sspiAccept: unable to allocate %d byte decrypt buffer",
-                        conn->decryptBufferLength));
-          ok = FALSE;
-          goto cleanup;
-        }
-      }
-
-      for (;;)
-      {
-        num = recv(conn->sock,
-                   conn->decryptBuffer + conn->decryptBufferUsed,
-                   (int)(conn->decryptBufferLength - conn->decryptBufferUsed),
-                   0);
-
-        if ((num == SOCKET_ERROR) && (WSAGetLastError() == WSAEWOULDBLOCK))
-          Sleep(1);
-        else
-          break;
-      }
-
-      if (num == SOCKET_ERROR)
-      {
-        DEBUG_printf(("_sspiAccept: recv failed: %d", WSAGetLastError()));
-        ok = FALSE;
-        goto cleanup;
-      }
-      else if (num == 0)
+      if (!CryptAcquireContextW(&hProv, (LPWSTR) container, MS_DEF_PROV_W,
+                               PROV_RSA_FULL, CRYPT_MACHINE_KEYSET))
       {
-        DEBUG_printf(("_sspiAccept: client disconnected"));
+        DEBUG_printf(("_sspiGetCredentials: CryptAcquireContext failed: %x\n",
+                      GetLastError()));
         ok = FALSE;
         goto cleanup;
       }
-
-      DEBUG_printf(("_sspiAccept: received %d (handshake) bytes from client",
-                    num));
-      conn->decryptBufferUsed += num;
     }
+  }
 
-   /*
-    * InBuffers[1] is for getting extra data that
-    * SSPI/SCHANNEL doesn't proccess on this
-    * run around the loop.
-    */
-    inBuffers[0].pvBuffer   = conn->decryptBuffer;
-    inBuffers[0].cbBuffer   = (unsigned long) conn->decryptBufferUsed;
-    inBuffers[0].BufferType = SECBUFFER_TOKEN;
+  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");
 
-    inBuffers[1].pvBuffer   = NULL;
-    inBuffers[1].cbBuffer   = 0;
-    inBuffers[1].BufferType = SECBUFFER_EMPTY;
+  if (!store)
+  {
+    DEBUG_printf(("_sspiGetCredentials: CertOpenSystemStore failed: %x\n",
+                  GetLastError()));
+    ok = FALSE;
+    goto cleanup;
+  }
 
-    inBuffer.cBuffers       = 2;
-    inBuffer.pBuffers       = inBuffers;
-    inBuffer.ulVersion      = SECBUFFER_VERSION;
+  dwSize = 0;
 
-   /*
-    * Initialize these so if we fail, pvBuffer contains NULL,
-    * so we don't try to free random garbage at the quit
-    */
-    outBuffers[0].pvBuffer   = NULL;
-    outBuffers[0].BufferType = SECBUFFER_TOKEN;
-    outBuffers[0].cbBuffer   = 0;
+  if (!CertStrToName(X509_ASN_ENCODING, cn, CERT_OID_NAME_STR,
+                     NULL, NULL, &dwSize, NULL))
+  {
+    DEBUG_printf(("_sspiGetCredentials: CertStrToName failed: %x\n",
+                   GetLastError()));
+    ok = FALSE;
+    goto cleanup;
+  }
 
-    scRet = AcceptSecurityContext(&conn->creds, (fInitContext?NULL:&conn->context),
-                                  &inBuffer, dwSSPIFlags, SECURITY_NATIVE_DREP,
-                                  (fInitContext?&conn->context:NULL), &outBuffer,
-                                  &dwSSPIOutFlags, &tsExpiry);
+  p = (PBYTE) malloc(dwSize);
 
-    fInitContext = FALSE;
+  if (!p)
+  {
+    DEBUG_printf(("_sspiGetCredentials: malloc failed for %d bytes", dwSize));
+    ok = FALSE;
+    goto cleanup;
+  }
 
-    if (scRet == SEC_E_OK              ||
-        scRet == SEC_I_CONTINUE_NEEDED ||
-        (FAILED(scRet) && ((dwSSPIOutFlags & ISC_RET_EXTENDED_ERROR) != 0)))
-    {
-      if (outBuffers[0].cbBuffer && outBuffers[0].pvBuffer)
-      {
-       /*
-        * Send response to server if there is one
-        */
-        num = send(conn->sock, outBuffers[0].pvBuffer, outBuffers[0].cbBuffer, 0);
+  if (!CertStrToName(X509_ASN_ENCODING, cn, CERT_OID_NAME_STR, NULL,
+                     p, &dwSize, NULL))
+  {
+    DEBUG_printf(("_sspiGetCredentials: CertStrToName failed: %x",
+                 GetLastError()));
+    ok = FALSE;
+    goto cleanup;
+  }
 
-        if ((num == SOCKET_ERROR) || (num == 0))
-        {
-          DEBUG_printf(("_sspiAccept: handshake send failed: %d", WSAGetLastError()));
-          ok = FALSE;
-          goto cleanup;
-        }
+  sib.cbData = dwSize;
+  sib.pbData = p;
 
-        DEBUG_printf(("_sspiAccept: send %d handshake bytes to client",
-                     outBuffers[0].cbBuffer));
+  storedContext = CertFindCertificateInStore(store, X509_ASN_ENCODING|PKCS_7_ASN_ENCODING,
+                                             0, CERT_FIND_SUBJECT_NAME, &sib, NULL);
 
-        FreeContextBuffer(outBuffers[0].pvBuffer);
-        outBuffers[0].pvBuffer = NULL;
-      }
+  if (!storedContext)
+  {
+   /*
+    * If we couldn't find the context, then we'll
+    * create a new one
+    */
+    if (!CryptGenKey(hProv, AT_KEYEXCHANGE, CRYPT_EXPORTABLE, &hKey))
+    {
+      DEBUG_printf(("_sspiGetCredentials: CryptGenKey failed: %x",
+                    GetLastError()));
+      ok = FALSE;
+      goto cleanup;
     }
 
-    if (scRet == SEC_E_OK)
-    {
-     /*
-      * If there's extra data then save it for
-      * next time we go to decrypt
-      */
-      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;
-      }
-      else
-      {
-        conn->decryptBufferUsed = 0;
-      }
+    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;
 
-      ok = TRUE;
-      break;
-    }
-    else if (FAILED(scRet) && (scRet != SEC_E_INCOMPLETE_MESSAGE))
+    GetSystemTime(&et);
+    et.wYear += 10;
+
+    ZeroMemory(&exts, sizeof(exts));
+
+    createdContext = CertCreateSelfSignCertificate(hProv, &sib, 0, &kpi, NULL, NULL,
+                                                   &et, &exts);
+
+    if (!createdContext)
     {
-      DEBUG_printf(("_sspiAccept: AcceptSecurityContext failed: %x", scRet));
+      DEBUG_printf(("_sspiGetCredentials: CertCreateSelfSignCertificate failed: %x",
+                   GetLastError()));
       ok = FALSE;
-      break;
+      goto cleanup;
     }
 
-    if (scRet != SEC_E_INCOMPLETE_MESSAGE &&
-        scRet != SEC_I_INCOMPLETE_CREDENTIALS)
+    if (!CertAddCertificateContextToStore(store, createdContext,
+                                          CERT_STORE_ADD_REPLACE_EXISTING,
+                                          &storedContext))
     {
-      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;
-      }
-      else
-      {
-        conn->decryptBufferUsed = 0;
-      }
+      DEBUG_printf(("_sspiGetCredentials: CertAddCertificateContextToStore failed: %x",
+                    GetLastError()));
+      ok = FALSE;
+      goto cleanup;
     }
-  }
 
-  if (ok)
-  {
-    conn->contextInitialized = TRUE;
-
-   /*
-    * Find out how big the header will be:
-    */
-    scRet = QueryContextAttributes(&conn->context, SECPKG_ATTR_STREAM_SIZES, &conn->streamSizes);
+    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 (scRet != SEC_E_OK)
+    if (!CertSetCertificateContextProperty(storedContext,
+                                           CERT_KEY_PROV_INFO_PROP_ID,
+                                           0, &ckp))
     {
-      DEBUG_printf(("_sspiAccept: QueryContextAttributes failed: %x", scRet));
+      DEBUG_printf(("_sspiGetCredentials: CertSetCertificateContextProperty failed: %x",
+                    GetLastError()));
       ok = FALSE;
+      goto cleanup;
     }
   }
 
-cleanup:
-
-  return (ok);
-}
-
-
-/*
- * '_sspiSetAllowsAnyRoot()' - Set the client cert policy for untrusted root certs
- */
-void
-_sspiSetAllowsAnyRoot(_sspi_struct_t *conn,
-                                       /* I  - Client connection */
-                      BOOL           allow)
-                                       /* I  - Allow any root */
-{
-  conn->certFlags = (allow) ? conn->certFlags | SECURITY_FLAG_IGNORE_UNKNOWN_CA :
-                              conn->certFlags & ~SECURITY_FLAG_IGNORE_UNKNOWN_CA;
-}
-
-
-/*
- * '_sspiSetAllowsExpiredCerts()' - Set the client cert policy for expired root certs
- */
-void
-_sspiSetAllowsExpiredCerts(_sspi_struct_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;
-}
-
-
-/*
- * '_sspiWrite()' - Write a buffer to an ssl socket
- */
-int                                    /* O  - Bytes written or SOCKET_ERROR */
-_sspiWrite(_sspi_struct_t *conn,       /* I  - Client connection */
-           void           *buf,                /* I  - Buffer */
-           size_t         len)         /* I  - Buffer length */
-{
-  SecBufferDesc        message;                /* Array of SecBuffer struct */
-  SecBuffer    buffers[4] = { 0 };     /* Security package buffer */
-  BYTE         *buffer = NULL;         /* Scratch buffer */
-  int          bufferLen;              /* Buffer length */
-  size_t       bytesLeft;              /* Bytes left to write */
-  int          index = 0;              /* Index into buffer */
-  int          num = 0;                /* Return value */
-
-  if (!conn || !buf || !len)
-  {
-    WSASetLastError(WSAEINVAL);
-    num = SOCKET_ERROR;
-    goto cleanup;
-  }
+  ZeroMemory(&SchannelCred, sizeof(SchannelCred));
 
-  bufferLen = conn->streamSizes.cbMaximumMessage +
-              conn->streamSizes.cbHeader +
-              conn->streamSizes.cbTrailer;
+  SchannelCred.dwVersion = SCHANNEL_CRED_VERSION;
+  SchannelCred.cCreds = 1;
+  SchannelCred.paCred = &storedContext;
 
-  buffer = (BYTE*) malloc(bufferLen);
+ /*
+  * SSPI doesn't seem to like it if grbitEnabledProtocols
+  * is set for a client
+  */
+  if (isServer)
+    SchannelCred.grbitEnabledProtocols = SP_PROT_SSL3TLS1;
 
-  if (!buffer)
+ /*
+  * Create an SSPI credential.
+  */
+  Status = AcquireCredentialsHandle(NULL, UNISP_NAME,
+                                    isServer ? SECPKG_CRED_INBOUND:SECPKG_CRED_OUTBOUND,
+                                    NULL, &SchannelCred, NULL, NULL, &conn->creds,
+                                    &tsExpiry);
+  if (Status != SEC_E_OK)
   {
-    DEBUG_printf(("_sspiWrite: buffer alloc of %d bytes failed", bufferLen));
-    WSASetLastError(E_OUTOFMEMORY);
-    num = SOCKET_ERROR;
+    DEBUG_printf(("_sspiGetCredentials: AcquireCredentialsHandle failed: %x", Status));
+    ok = FALSE;
     goto cleanup;
   }
 
-  bytesLeft = len;
-
-  while (bytesLeft)
-  {
-    size_t chunk = min(conn->streamSizes.cbMaximumMessage,     /* Size of data to write */
-                       bytesLeft);
-    SECURITY_STATUS scRet;                                     /* SSPI status */
-
-   /*
-    * Copy user data into the buffer, starting
-    * just past the header
-    */
-    memcpy(buffer + conn->streamSizes.cbHeader,
-           ((BYTE*) buf) + index,
-           chunk);
-
-   /*
-    * Setup the SSPI buffers
-    */
-    message.ulVersion = SECBUFFER_VERSION;
-    message.cBuffers = 4;
-    message.pBuffers = buffers;
-    buffers[0].pvBuffer = buffer;
-    buffers[0].cbBuffer = conn->streamSizes.cbHeader;
-    buffers[0].BufferType = SECBUFFER_STREAM_HEADER;
-    buffers[1].pvBuffer = buffer + conn->streamSizes.cbHeader;
-    buffers[1].cbBuffer = (unsigned long) chunk;
-    buffers[1].BufferType = SECBUFFER_DATA;
-    buffers[2].pvBuffer = buffer + conn->streamSizes.cbHeader + chunk;
-    buffers[2].cbBuffer = conn->streamSizes.cbTrailer;
-    buffers[2].BufferType = SECBUFFER_STREAM_TRAILER;
-    buffers[3].BufferType = SECBUFFER_EMPTY;
-
-   /*
-    * Encrypt the data
-    */
-    scRet = EncryptMessage(&conn->context, 0, &message, 0);
-
-    if (FAILED(scRet))
-    {
-      DEBUG_printf(("_sspiWrite: EncryptMessage failed: %x", scRet));
-      WSASetLastError(WSASYSCALLFAILURE);
-      num = SOCKET_ERROR;
-      goto cleanup;
-    }
+cleanup:
 
-   /*
-    * Send the data. Remember the size of
-    * the total data to send is the size
-    * of the header, the size of the data
-    * the caller passed in and the size
-    * of the trailer
-    */
-    num = send(conn->sock,
-               buffer,
-               buffers[0].cbBuffer + buffers[1].cbBuffer + buffers[2].cbBuffer,
-               0);
+ /*
+  * Cleanup
+  */
+  if (hKey)
+    CryptDestroyKey(hKey);
 
-    if ((num == SOCKET_ERROR) || (num == 0))
-    {
-      DEBUG_printf(("_sspiWrite: send failed: %ld", WSAGetLastError()));
-      goto cleanup;
-    }
+  if (createdContext)
+    CertFreeCertificateContext(createdContext);
 
-    bytesLeft -= (int) chunk;
-    index += (int) chunk;
-  }
+  if (storedContext)
+    CertFreeCertificateContext(storedContext);
 
-  num = (int) len;
+  if (p)
+    free(p);
 
-cleanup:
+  if (store)
+    CertCloseStore(store, 0);
 
-  if (buffer)
-    free(buffer);
+  if (hProv)
+    CryptReleaseContext(hProv, 0);
 
-  return (num);
+  return (ok);
 }
+#endif // 0
 
 
 /*
- * '_sspiRead()' - Read a buffer from an ssl socket
+ * 'http_sspi_server()' - Negotiate a TLS connection as a server.
  */
-int                                    /* O  - Bytes read or SOCKET_ERROR */
-_sspiRead(_sspi_struct_t *conn,                /* I  - Client connection */
-          void           *buf,         /* I  - Buffer */
-          size_t         len)          /* I  - Buffer length */
+
+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 */
 {
-  SecBufferDesc        message;                /* Array of SecBuffer struct */
-  SecBuffer    buffers[4] = { 0 };     /* Security package buffer */
-  int          num = 0;                /* Return value */
+  _http_sspi_t *conn = http->tls;      /* I - SSPI data */
+  DWORD                dwSSPIFlags;            /* SSL connection attributes we want */
+  DWORD                dwSSPIOutFlags;         /* SSL connection attributes we got */
+  TimeStamp    tsExpiry;               /* Time stamp */
+  SECURITY_STATUS scRet;               /* SSPI Status */
+  SecBufferDesc        inBuffer;               /* Array of SecBuffer structs */
+  SecBuffer    inBuffers[2];           /* Security package buffer */
+  SecBufferDesc        outBuffer;              /* Array of SecBuffer structs */
+  SecBuffer    outBuffers[1];          /* Security package buffer */
+  int          num = 0;                /* 32 bit status value */
+  BOOL         fInitContext = TRUE;    /* Has the context been init'd? */
+  int          ret = 0;                /* Return value */
+
+
+  DEBUG_printf(("http_sspi_server(http=%p, hostname=\"%s\")", http, hostname));
 
-  if (!conn)
-  {
-    WSASetLastError(WSAEINVAL);
-    num = SOCKET_ERROR;
-    goto cleanup;
-  }
+  dwSSPIFlags = ASC_REQ_SEQUENCE_DETECT  |
+                ASC_REQ_REPLAY_DETECT    |
+                ASC_REQ_CONFIDENTIALITY  |
+                ASC_REQ_EXTENDED_ERROR   |
+                ASC_REQ_ALLOCATE_MEMORY  |
+                ASC_REQ_STREAM;
+
+  conn->decryptBufferUsed = 0;
 
  /*
-  * If there are bytes that have already been
-  * decrypted and have not yet been read, return
-  * those
+  * Set OutBuffer for AcceptSecurityContext call
   */
-  if (buf && (conn->readBufferUsed > 0))
-  {
-    int bytesToCopy = (int) min(conn->readBufferUsed, len);    /* Amount of bytes to copy */
-                                                               /* from read buffer */
 
-    memcpy(buf, conn->readBuffer, bytesToCopy);
-    conn->readBufferUsed -= bytesToCopy;
+  outBuffer.cBuffers  = 1;
+  outBuffer.pBuffers  = outBuffers;
+  outBuffer.ulVersion = SECBUFFER_VERSION;
 
-    if (conn->readBufferUsed > 0)
-     /*
-      * If the caller didn't request all the bytes
-      * we have in the buffer, then move the unread
-      * bytes down
-      */
-      memmove(conn->readBuffer,
-              conn->readBuffer + bytesToCopy,
-              conn->readBufferUsed);
+  scRet = SEC_I_CONTINUE_NEEDED;
 
-    num = bytesToCopy;
-  }
-  else
+  while (scRet == SEC_I_CONTINUE_NEEDED    ||
+         scRet == SEC_E_INCOMPLETE_MESSAGE ||
+         scRet == SEC_I_INCOMPLETE_CREDENTIALS)
   {
-    PSecBuffer         pDataBuffer;    /* Data buffer */
-    PSecBuffer         pExtraBuffer;   /* Excess data buffer */
-    SECURITY_STATUS    scRet;          /* SSPI status */
-    int                        i;              /* Loop control variable */
-
-   /*
-    * Initialize security buffer structs
-    */
-    message.ulVersion = SECBUFFER_VERSION;
-    message.cBuffers = 4;
-    message.pBuffers = buffers;
-
-    do
+    if (conn->decryptBufferUsed == 0 || scRet == SEC_E_INCOMPLETE_MESSAGE)
     {
-     /*
-      * If there is not enough space in the
-      * buffer, then increase it's size
-      */
       if (conn->decryptBufferLength <= conn->decryptBufferUsed)
       {
-        conn->decryptBufferLength += 4096;
-        conn->decryptBuffer = (BYTE*) realloc(conn->decryptBuffer,
-                                              conn->decryptBufferLength);
+       BYTE *temp;                     /* New buffer */
 
-        if (!conn->decryptBuffer)
-        {
-          DEBUG_printf(("_sspiRead: unable to allocate %d byte buffer",
-                        conn->decryptBufferLength));
-          WSASetLastError(E_OUTOFMEMORY);
-          num = SOCKET_ERROR;
-          goto cleanup;
-        }
-      }
+       if (conn->decryptBufferLength >= 262144)
+       {
+         WSASetLastError(E_OUTOFMEMORY);
+         DEBUG_puts("http_sspi_server: Decryption buffer too large (>256k)");
+         return (-1);
+       }
 
-      buffers[0].pvBuffer      = conn->decryptBuffer;
-      buffers[0].cbBuffer      = (unsigned long) conn->decryptBufferUsed;
-      buffers[0].BufferType    = SECBUFFER_DATA;
-      buffers[1].BufferType    = SECBUFFER_EMPTY;
-      buffers[2].BufferType    = SECBUFFER_EMPTY;
-      buffers[3].BufferType    = SECBUFFER_EMPTY;
+       if ((temp = realloc(conn->decryptBuffer, conn->decryptBufferLength + 4096)) == NULL)
+       {
+         DEBUG_printf(("http_sspi_server: Unable to allocate %d byte buffer.", conn->decryptBufferLength + 4096));
+         WSASetLastError(E_OUTOFMEMORY);
+         return (-1);
+       }
 
-      scRet = DecryptMessage(&conn->context, &message, 0, NULL);
+       conn->decryptBufferLength += 4096;
+       conn->decryptBuffer       = temp;
+      }
 
-      if (scRet == SEC_E_INCOMPLETE_MESSAGE)
+      for (;;)
       {
-        if (buf)
-        {
-          num = recv(conn->sock,
-                     conn->decryptBuffer + conn->decryptBufferUsed,
-                     (int)(conn->decryptBufferLength - conn->decryptBufferUsed),
-                     0);
-          if (num == SOCKET_ERROR)
-          {
-            DEBUG_printf(("_sspiRead: recv failed: %d", WSAGetLastError()));
-            goto cleanup;
-          }
-          else if (num == 0)
-          {
-            DEBUG_printf(("_sspiRead: server disconnected"));
-            goto cleanup;
-          }
+        num = recv(http->fd, conn->decryptBuffer + conn->decryptBufferUsed, (int)(conn->decryptBufferLength - conn->decryptBufferUsed), 0);
 
-          conn->decryptBufferUsed += num;
-        }
+        if (num == -1 WSAGetLastError() == WSAEWOULDBLOCK)
+          Sleep(1);
         else
-        {
-          num = (int) conn->readBufferUsed;
-          goto cleanup;
-        }
+          break;
       }
-    }
-    while (scRet == SEC_E_INCOMPLETE_MESSAGE);
 
-    if (scRet == SEC_I_CONTEXT_EXPIRED)
-    {
-      DEBUG_printf(("_sspiRead: context expired"));
-      WSASetLastError(WSAECONNRESET);
-      num = SOCKET_ERROR;
-      goto cleanup;
-    }
-    else if (scRet != SEC_E_OK)
-    {
-      DEBUG_printf(("_sspiRead: DecryptMessage failed: %lx", scRet));
-      WSASetLastError(WSASYSCALLFAILURE);
-      num = SOCKET_ERROR;
-      goto cleanup;
+      if (num < 0)
+      {
+        DEBUG_printf(("http_sspi_server: recv failed: %d", WSAGetLastError()));
+        return (-1);
+      }
+      else if (num == 0)
+      {
+        DEBUG_puts("http_sspi_server: client disconnected");
+        return (-1);
+      }
+
+      DEBUG_printf(("http_sspi_server: received %d (handshake) bytes from client.", num));
+      conn->decryptBufferUsed += num;
     }
 
    /*
-    * The decryption worked.  Now, locate data buffer.
+    * InBuffers[1] is for getting extra data that SSPI/SCHANNEL doesn't process
+    * on this run around the loop.
     */
-    pDataBuffer  = NULL;
-    pExtraBuffer = NULL;
-    for (i = 1; i < 4; i++)
-    {
-      if (buffers[i].BufferType == SECBUFFER_DATA)
-        pDataBuffer = &buffers[i];
-      else if (!pExtraBuffer && (buffers[i].BufferType == SECBUFFER_EXTRA))
-        pExtraBuffer = &buffers[i];
-    }
+
+    inBuffers[0].pvBuffer   = conn->decryptBuffer;
+    inBuffers[0].cbBuffer   = (unsigned long)conn->decryptBufferUsed;
+    inBuffers[0].BufferType = SECBUFFER_TOKEN;
+
+    inBuffers[1].pvBuffer   = NULL;
+    inBuffers[1].cbBuffer   = 0;
+    inBuffers[1].BufferType = SECBUFFER_EMPTY;
+
+    inBuffer.cBuffers       = 2;
+    inBuffer.pBuffers       = inBuffers;
+    inBuffer.ulVersion      = SECBUFFER_VERSION;
 
    /*
-    * If a data buffer is found, then copy
-    * the decrypted bytes to the passed-in
-    * buffer
+    * Initialize these so if we fail, pvBuffer contains NULL, so we don't try to
+    * free random garbage at the quit.
     */
-    if (pDataBuffer)
-    {
-      int bytesToCopy = min(pDataBuffer->cbBuffer, (int) len);
-                                       /* Number of bytes to copy into buf */
-      int bytesToSave = pDataBuffer->cbBuffer - bytesToCopy;
-                                       /* Number of bytes to save in our read buffer */
 
-      if (bytesToCopy)
-        memcpy(buf, pDataBuffer->pvBuffer, bytesToCopy);
+    outBuffers[0].pvBuffer   = NULL;
+    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);
 
-     /*
-      * If there are more decrypted bytes than can be
-      * copied to the passed in buffer, then save them
-      */
-      if (bytesToSave)
+    fInitContext = FALSE;
+
+    if (scRet == SEC_E_OK              ||
+        scRet == SEC_I_CONTINUE_NEEDED ||
+        (FAILED(scRet) && ((dwSSPIOutFlags & ISC_RET_EXTENDED_ERROR) != 0)))
+    {
+      if (outBuffers[0].cbBuffer && outBuffers[0].pvBuffer)
       {
-        if ((int)(conn->readBufferLength - conn->readBufferUsed) < bytesToSave)
-        {
-          conn->readBufferLength = conn->readBufferUsed + bytesToSave;
-          conn->readBuffer = realloc(conn->readBuffer,
-                                     conn->readBufferLength);
+       /*
+        * Send response to server if there is one.
+        */
 
-          if (!conn->readBuffer)
-          {
-            DEBUG_printf(("_sspiRead: unable to allocate %d bytes", conn->readBufferLength));
-            WSASetLastError(E_OUTOFMEMORY);
-            num = SOCKET_ERROR;
-            goto cleanup;
-          }
+        num = send(http->fd, outBuffers[0].pvBuffer, outBuffers[0].cbBuffer, 0);
+
+        if (num <= 0)
+        {
+          DEBUG_printf(("http_sspi_server: handshake send failed: %d", WSAGetLastError()));
+         return (-1);
         }
 
-        memcpy(((BYTE*) conn->readBuffer) + conn->readBufferUsed,
-               ((BYTE*) pDataBuffer->pvBuffer) + bytesToCopy,
-               bytesToSave);
+        DEBUG_printf(("http_sspi_server: sent %d handshake bytes to client.", outBuffers[0].cbBuffer));
 
-        conn->readBufferUsed += bytesToSave;
+        FreeContextBuffer(outBuffers[0].pvBuffer);
+        outBuffers[0].pvBuffer = NULL;
       }
+    }
+
+    if (scRet == SEC_E_OK)
+    {
+     /*
+      * If there's extra data then save it for next time we go to decrypt.
+      */
 
-      num = (buf) ? bytesToCopy : (int) conn->readBufferUsed;
+      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;
+      }
+      else
+      {
+        conn->decryptBufferUsed = 0;
+      }
+      break;
     }
-    else
+    else if (FAILED(scRet) && scRet != SEC_E_INCOMPLETE_MESSAGE)
     {
-      DEBUG_printf(("_sspiRead: unable to find data buffer"));
-      WSASetLastError(WSASYSCALLFAILURE);
-      num = SOCKET_ERROR;
-      goto cleanup;
+      DEBUG_printf(("http_sspi_server: AcceptSecurityContext failed: %x", scRet));
+      ret = -1;
+      break;
     }
 
-   /*
-    * If the decryption process left extra bytes,
-    * then save those back in decryptBuffer. They will
-    * be processed the next time through the loop.
-    */
-    if (pExtraBuffer)
+    if (scRet != SEC_E_INCOMPLETE_MESSAGE &&
+        scRet != SEC_I_INCOMPLETE_CREDENTIALS)
     {
-      memmove(conn->decryptBuffer, pExtraBuffer->pvBuffer, pExtraBuffer->cbBuffer);
-      conn->decryptBufferUsed = pExtraBuffer->cbBuffer;
+      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;
+      }
+      else
+      {
+        conn->decryptBufferUsed = 0;
+      }
     }
-    else
+  }
+
+  if (!ret)
+  {
+    conn->contextInitialized = TRUE;
+
+   /*
+    * Find out how big the header will be:
+    */
+
+    scRet = QueryContextAttributes(&conn->context, SECPKG_ATTR_STREAM_SIZES, &conn->streamSizes);
+
+    if (scRet != SEC_E_OK)
     {
-      conn->decryptBufferUsed = 0;
+      DEBUG_printf(("http_sspi_server: QueryContextAttributes failed: %x", scRet));
+      ret = -1;
     }
   }
 
-cleanup:
-
-  return (num);
+  return (ret);
 }
 
 
+#if 0
 /*
- * '_sspiPending()' - Returns the number of available bytes
+ * '_sspiSetAllowsAnyRoot()' - Set the client cert policy for untrusted root certs
  */
-int                                    /* O  - Number of available bytes */
-_sspiPending(_sspi_struct_t *conn)     /* I  - Client connection */
+void
+_sspiSetAllowsAnyRoot(_http_sspi_t *conn,
+                                       /* I  - Client connection */
+                      BOOL           allow)
+                                       /* I  - Allow any root */
 {
-  return (_sspiRead(conn, NULL, 0));
+  conn->certFlags = (allow) ? conn->certFlags | SECURITY_FLAG_IGNORE_UNKNOWN_CA :
+                              conn->certFlags & ~SECURITY_FLAG_IGNORE_UNKNOWN_CA;
 }
 
 
 /*
- * '_sspiFree()' - Close a connection and free resources
+ * '_sspiSetAllowsExpiredCerts()' - Set the client cert policy for expired root certs
  */
 void
-_sspiFree(_sspi_struct_t *conn)                /* I  - Client connection */
+_sspiSetAllowsExpiredCerts(_http_sspi_t *conn,
+                                       /* I  - Client connection */
+                           BOOL           allow)
+                                       /* I  - Allow expired certs */
 {
-  if (!conn)
-    return;
-
-  if (conn->contextInitialized)
-  {
-    SecBufferDesc      message;        /* Array of SecBuffer struct */
-    SecBuffer          buffers[1] = { 0 };
-                                       /* Security package buffer */
-    DWORD              dwType;         /* Type */
-    DWORD              status;         /* Status */
-
-  /*
-   * Notify schannel that we are about to close the connection.
-   */
-   dwType = SCHANNEL_SHUTDOWN;
-
-   buffers[0].pvBuffer   = &dwType;
-   buffers[0].BufferType = SECBUFFER_TOKEN;
-   buffers[0].cbBuffer   = sizeof(dwType);
-
-   message.cBuffers  = 1;
-   message.pBuffers  = buffers;
-   message.ulVersion = SECBUFFER_VERSION;
-
-   status = ApplyControlToken(&conn->context, &message);
-
-   if (SUCCEEDED(status))
-   {
-     PBYTE     pbMessage;              /* Message buffer */
-     DWORD     cbMessage;              /* Message buffer count */
-     DWORD     cbData;                 /* Data count */
-     DWORD     dwSSPIFlags;            /* SSL attributes we requested */
-     DWORD     dwSSPIOutFlags;         /* SSL attributes we received */
-     TimeStamp tsExpiry;               /* Time stamp */
-
-     dwSSPIFlags = ASC_REQ_SEQUENCE_DETECT     |
-                   ASC_REQ_REPLAY_DETECT       |
-                   ASC_REQ_CONFIDENTIALITY     |
-                   ASC_REQ_EXTENDED_ERROR      |
-                   ASC_REQ_ALLOCATE_MEMORY     |
-                   ASC_REQ_STREAM;
-
-     buffers[0].pvBuffer   = NULL;
-     buffers[0].BufferType = SECBUFFER_TOKEN;
-     buffers[0].cbBuffer   = 0;
-
-     message.cBuffers  = 1;
-     message.pBuffers  = buffers;
-     message.ulVersion = SECBUFFER_VERSION;
-
-     status = AcceptSecurityContext(&conn->creds, &conn->context, NULL,
-                                    dwSSPIFlags, SECURITY_NATIVE_DREP, NULL,
-                                    &message, &dwSSPIOutFlags, &tsExpiry);
-
-      if (SUCCEEDED(status))
-      {
-        pbMessage = buffers[0].pvBuffer;
-        cbMessage = buffers[0].cbBuffer;
-
-       /*
-        * Send the close notify message to the client.
-        */
-        if (pbMessage && cbMessage)
-        {
-          cbData = send(conn->sock, pbMessage, cbMessage, 0);
-          if ((cbData == SOCKET_ERROR) || (cbData == 0))
-          {
-            status = WSAGetLastError();
-            DEBUG_printf(("_sspiFree: sending close notify failed: %d", status));
-          }
-          else
-          {
-            FreeContextBuffer(pbMessage);
-          }
-        }
-      }
-      else
-      {
-        DEBUG_printf(("_sspiFree: AcceptSecurityContext failed: %x", status));
-      }
-    }
-    else
-    {
-      DEBUG_printf(("_sspiFree: ApplyControlToken failed: %x", status));
-    }
-
-    DeleteSecurityContext(&conn->context);
-    conn->contextInitialized = FALSE;
-  }
-
-  if (conn->decryptBuffer)
-  {
-    free(conn->decryptBuffer);
-    conn->decryptBuffer = NULL;
-  }
-
-  if (conn->readBuffer)
-  {
-    free(conn->readBuffer);
-    conn->readBuffer = NULL;
-  }
-
-  if (conn->sock != INVALID_SOCKET)
-  {
-    closesocket(conn->sock);
-    conn->sock = INVALID_SOCKET;
-  }
-
-  free(conn);
+  conn->certFlags = (allow) ? conn->certFlags | SECURITY_FLAG_IGNORE_CERT_DATE_INVALID :
+                              conn->certFlags & ~SECURITY_FLAG_IGNORE_CERT_DATE_INVALID;
 }
 
 
 /*
- * 'sspi_verify_certificate()' - Verify a server certificate
+ * 'http_sspi_verify()' - Verify a certificate.
  */
-static DWORD                           /* 0  - Error code (0 == No error) */
-sspi_verify_certificate(PCCERT_CONTEXT  serverCert,
-                                       /* I  - Server certificate */
-                        const CHAR      *serverName,
-                                       /* I  - Server name */
-                        DWORD           dwCertFlags)
-                                       /* I  - Verification flags */
+
+static DWORD                           /* O - Error code (0 == No error) */
+http_sspi_verify(
+    PCCERT_CONTEXT cert,               /* I - Server certificate */
+    const char     *common_name,       /* I - Common name */
+    DWORD          dwCertFlags)                /* I - Verification flags */
 {
-  HTTPSPolicyCallbackData      httpsPolicy;
-                                       /* HTTPS Policy Struct */
-  CERT_CHAIN_POLICY_PARA       policyPara;
-                                       /* Cert chain policy parameters */
-  CERT_CHAIN_POLICY_STATUS     policyStatus;
-                                       /* Cert chain policy status */
-  CERT_CHAIN_PARA              chainPara;
-                                       /* Used for searching and matching criteria */
-  PCCERT_CHAIN_CONTEXT         chainContext = NULL;
+  HTTPSPolicyCallbackData httpsPolicy; /* HTTPS Policy Struct */
+  CERT_CHAIN_POLICY_PARA policyPara;   /* Cert chain policy parameters */
+  CERT_CHAIN_POLICY_STATUS policyStatus;/* Cert chain policy status */
+  CERT_CHAIN_PARA      chainPara;      /* Used for searching and matching criteria */
+  PCCERT_CHAIN_CONTEXT chainContext = NULL;
                                        /* Certificate chain */
-  PWSTR                                serverNameUnicode = NULL;
-                                       /* Unicode server name */
-  LPSTR                                rgszUsages[] = { szOID_PKIX_KP_SERVER_AUTH,
-                                                 szOID_SERVER_GATED_CRYPTO,
-                                                 szOID_SGC_NETSCAPE };
+  PWSTR                        commonNameUnicode = NULL;
+                                       /* Unicode common name */
+  LPSTR                        rgszUsages[] = { szOID_PKIX_KP_SERVER_AUTH,
+                                         szOID_SERVER_GATED_CRYPTO,
+                                         szOID_SGC_NETSCAPE };
                                        /* How are we using this certificate? */
-  DWORD                                cUsages = sizeof(rgszUsages) / sizeof(LPSTR);
+  DWORD                        cUsages = sizeof(rgszUsages) / sizeof(LPSTR);
                                        /* Number of ites in rgszUsages */
-  DWORD                                count;  /* 32 bit count variable */
-  DWORD                                status; /* Return value */
+  DWORD                        count;          /* 32 bit count variable */
+  DWORD                        status;         /* Return value */
 
-  if (!serverCert)
-  {
-    status = SEC_E_WRONG_PRINCIPAL;
-    goto cleanup;
-  }
+
+  if (!cert)
+    return (SEC_E_WRONG_PRINCIPAL);
 
  /*
-  *  Convert server name to unicode.
+  * Convert common name to Unicode.
   */
-  if (!serverName || (strlen(serverName) == 0))
-  {
-    status = SEC_E_WRONG_PRINCIPAL;
-    goto cleanup;
-  }
 
-  count = MultiByteToWideChar(CP_ACP, 0, serverName, -1, NULL, 0);
-  serverNameUnicode = LocalAlloc(LMEM_FIXED, count * sizeof(WCHAR));
-  if (!serverNameUnicode)
-  {
-    status = SEC_E_INSUFFICIENT_MEMORY;
-    goto cleanup;
-  }
-  count = MultiByteToWideChar(CP_ACP, 0, serverName, -1, serverNameUnicode, count);
-  if (count == 0)
+  if (!common_name || !*common_name)
+    return (SEC_E_WRONG_PRINCIPAL);
+
+  count             = MultiByteToWideChar(CP_ACP, 0, common_name, -1, NULL, 0);
+  commonNameUnicode = LocalAlloc(LMEM_FIXED, count * sizeof(WCHAR));
+  if (!commonNameUnicode)
+    return (SEC_E_INSUFFICIENT_MEMORY);
+
+  if (!MultiByteToWideChar(CP_ACP, 0, common_name, -1, commonNameUnicode, count))
   {
-    status = SEC_E_WRONG_PRINCIPAL;
-    goto cleanup;
+    LocalFree(commonNameUnicode);
+    return (SEC_E_WRONG_PRINCIPAL);
   }
 
  /*
   * Build certificate chain.
   */
+
   ZeroMemory(&chainPara, sizeof(chainPara));
+
   chainPara.cbSize                                     = sizeof(chainPara);
   chainPara.RequestedUsage.dwType                      = USAGE_MATCH_TYPE_OR;
   chainPara.RequestedUsage.Usage.cUsageIdentifier      = cUsages;
   chainPara.RequestedUsage.Usage.rgpszUsageIdentifier  = rgszUsages;
 
-  if (!CertGetCertificateChain(NULL, serverCert, NULL, serverCert->hCertStore,
-                               &chainPara, 0, NULL, &chainContext))
+  if (!CertGetCertificateChain(NULL, cert, NULL, cert->hCertStore, &chainPara, 0, NULL, &chainContext))
   {
     status = GetLastError();
     DEBUG_printf(("CertGetCertificateChain returned 0x%x\n", status));
-    goto cleanup;
+
+    LocalFree(commonNameUnicode);
+    return (status);
   }
 
  /*
   * Validate certificate chain.
   */
+
   ZeroMemory(&httpsPolicy, sizeof(HTTPSPolicyCallbackData));
   httpsPolicy.cbStruct         = sizeof(HTTPSPolicyCallbackData);
   httpsPolicy.dwAuthType       = AUTHTYPE_SERVER;
   httpsPolicy.fdwChecks                = dwCertFlags;
-  httpsPolicy.pwszServerName   = serverNameUnicode;
+  httpsPolicy.pwszServerName   = commonNameUnicode;
 
   memset(&policyPara, 0, sizeof(policyPara));
   policyPara.cbSize            = sizeof(policyPara);
@@ -2201,32 +1920,25 @@ sspi_verify_certificate(PCCERT_CONTEXT  serverCert,
   memset(&policyStatus, 0, sizeof(policyStatus));
   policyStatus.cbSize = sizeof(policyStatus);
 
-  if (!CertVerifyCertificateChainPolicy(CERT_CHAIN_POLICY_SSL, chainContext,
-                                        &policyPara, &policyStatus))
+  if (!CertVerifyCertificateChainPolicy(CERT_CHAIN_POLICY_SSL, chainContext, &policyPara, &policyStatus))
   {
     status = GetLastError();
     DEBUG_printf(("CertVerifyCertificateChainPolicy returned %d", status));
-    goto cleanup;
   }
-
-  if (policyStatus.dwError)
-  {
+  else if (policyStatus.dwError)
     status = policyStatus.dwError;
-    goto cleanup;
-  }
-
-  status = SEC_E_OK;
-
-cleanup:
+  else
+    status = SEC_E_OK;
 
   if (chainContext)
     CertFreeCertificateChain(chainContext);
 
-  if (serverNameUnicode)
-    LocalFree(serverNameUnicode);
+  if (commonNameUnicode)
+    LocalFree(commonNameUnicode);
 
   return (status);
 }
+#endif // 0
 
 
 /*
diff --git a/scheduler/tls-darwin.c b/scheduler/tls-darwin.c
deleted file mode 100644 (file)
index 9d62498..0000000
+++ /dev/null
@@ -1,570 +0,0 @@
-/*
- * "$Id$"
- *
- *   TLS support code for the CUPS scheduler on OS X.
- *
- *   Copyright 2007-2013 by Apple Inc.
- *   Copyright 1997-2007 by Easy Software Products, all rights reserved.
- *
- *   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/".
- *
- * Contents:
- *
- *   cupsdEndTLS()          - Shutdown a secure session with the client.
- *   cupsdStartTLS()        - Start a secure session with the client.
- *   copy_cdsa_certificate() - Copy a SSL/TLS certificate from the System
- *                            keychain.
- *   make_certificate()      - Make a self-signed SSL/TLS certificate.
- */
-
-
-/*
- * Local functions...
- */
-
-static CFArrayRef      copy_cdsa_certificate(cupsd_client_t *con);
-static int             make_certificate(cupsd_client_t *con);
-
-
-/*
- * 'cupsdEndTLS()' - Shutdown a secure session with the client.
- */
-
-int                                    /* O - 1 on success, 0 on error */
-cupsdEndTLS(cupsd_client_t *con)       /* I - Client connection */
-{
-  while (SSLClose(con->http->tls) == errSSLWouldBlock)
-    usleep(1000);
-
-  CFRelease(con->http->tls);
-  con->http->tls = NULL;
-
-  if (con->http->tls_credentials)
-    CFRelease(con->http->tls_credentials);
-
-  return (1);
-}
-
-
-/*
- * 'cupsdStartTLS()' - Start a secure session with the client.
- */
-
-int                                    /* O - 1 on success, 0 on error */
-cupsdStartTLS(cupsd_client_t *con)     /* I - Client connection */
-{
-  OSStatus     error = 0;              /* Error code */
-  SecTrustRef  peerTrust;              /* Peer certificates */
-
-
-  cupsdLogMessage(CUPSD_LOG_DEBUG, "[Client %d] Encrypting connection.",
-                  con->number);
-
-  con->http->encryption = HTTP_ENCRYPTION_ALWAYS;
-
-  con->http->tls_credentials = copy_cdsa_certificate(con);
-
-  if (!con->http->tls_credentials)
-  {
-   /*
-    * No keychain (yet), make a self-signed certificate...
-    */
-
-    if (make_certificate(con))
-      con->http->tls_credentials = copy_cdsa_certificate(con);
-  }
-
-  if (!con->http->tls_credentials)
-  {
-    cupsdLogMessage(CUPSD_LOG_ERROR,
-                   "Could not find signing key in keychain \"%s\"",
-                   ServerCertificate);
-    error = errSSLBadConfiguration;
-  }
-
-  if (!error)
-    con->http->tls = SSLCreateContext(kCFAllocatorDefault, kSSLServerSide,
-                                     kSSLStreamType);
-
-  if (!error)
-    error = SSLSetIOFuncs(con->http->tls, _httpReadCDSA, _httpWriteCDSA);
-
-  if (!error)
-    error = SSLSetConnection(con->http->tls, HTTP(con));
-
-  if (!error)
-    error = SSLSetCertificate(con->http->tls, con->http->tls_credentials);
-
-  if (!error)
-  {
-   /*
-    * Perform SSL/TLS handshake
-    */
-
-    while ((error = SSLHandshake(con->http->tls)) == errSSLWouldBlock)
-      usleep(1000);
-  }
-
-  if (error)
-  {
-    cupsdLogMessage(CUPSD_LOG_ERROR,
-                    "Unable to encrypt connection from %s - %s (%d)",
-                    con->http->hostname, cssmErrorString(error), (int)error);
-
-    con->http->error  = error;
-    con->http->status = HTTP_ERROR;
-
-    if (con->http->tls)
-    {
-      CFRelease(con->http->tls);
-      con->http->tls = NULL;
-    }
-
-    if (con->http->tls_credentials)
-    {
-      CFRelease(con->http->tls_credentials);
-      con->http->tls_credentials = NULL;
-    }
-
-    return (0);
-  }
-
-  cupsdLogMessage(CUPSD_LOG_DEBUG, "Connection from %s now encrypted.",
-                  con->http->hostname);
-
-  if (!SSLCopyPeerTrust(con->http->tls, &peerTrust) && peerTrust)
-  {
-    cupsdLogMessage(CUPSD_LOG_DEBUG, "Received %d peer certificates.",
-                   (int)SecTrustGetCertificateCount(peerTrust));
-    CFRelease(peerTrust);
-  }
-  else
-    cupsdLogMessage(CUPSD_LOG_DEBUG, "Received NO peer certificates.");
-
-  return (1);
-}
-
-
-/*
- * 'copy_cdsa_certificate()' - Copy a SSL/TLS certificate from the System
- *                             keychain.
- */
-
-static CFArrayRef                              /* O - Array of certificates */
-copy_cdsa_certificate(
-    cupsd_client_t *con)                       /* I - Client connection */
-{
-  OSStatus             err;            /* Error info */
-  SecKeychainRef       keychain = NULL;/* Keychain reference */
-  SecIdentitySearchRef search = NULL;  /* Search reference */
-  SecIdentityRef       identity = NULL;/* Identity */
-  CFArrayRef           certificates = NULL;
-                                       /* Certificate array */
-  SecPolicyRef         policy = NULL;  /* Policy ref */
-  CFStringRef          servername = NULL;
-                                       /* Server name */
-  CFMutableDictionaryRef query = NULL; /* Query qualifiers */
-  CFArrayRef           list = NULL;    /* Keychain list */
-#    if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
-  char                 localname[1024];/* Local hostname */
-#    endif /* HAVE_DNSSD || HAVE_AVAHI */
-
-
-  cupsdLogMessage(CUPSD_LOG_DEBUG,
-                  "copy_cdsa_certificate: Looking for certs for \"%s\".",
-                 con->servername);
-
-  if ((err = SecKeychainOpen(ServerCertificate, &keychain)))
-  {
-    cupsdLogMessage(CUPSD_LOG_ERROR, "Cannot open keychain \"%s\" - %s (%d)",
-                   ServerCertificate, cssmErrorString(err), (int)err);
-    goto cleanup;
-  }
-
-  servername = CFStringCreateWithCString(kCFAllocatorDefault, con->servername,
-                                        kCFStringEncodingUTF8);
-
-  policy = SecPolicyCreateSSL(1, servername);
-
-  if (servername)
-    CFRelease(servername);
-
-  if (!policy)
-  {
-    cupsdLogMessage(CUPSD_LOG_ERROR, "Cannot create ssl policy reference");
-    goto cleanup;
-  }
-
-  if (!(query = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
-                                         &kCFTypeDictionaryKeyCallBacks,
-                                         &kCFTypeDictionaryValueCallBacks)))
-  {
-    cupsdLogMessage(CUPSD_LOG_ERROR, "Cannot create query dictionary");
-    goto cleanup;
-  }
-
-  list = CFArrayCreate(kCFAllocatorDefault, (const void **)&keychain, 1,
-                       &kCFTypeArrayCallBacks);
-
-  CFDictionaryAddValue(query, kSecClass, kSecClassIdentity);
-  CFDictionaryAddValue(query, kSecMatchPolicy, policy);
-  CFDictionaryAddValue(query, kSecReturnRef, kCFBooleanTrue);
-  CFDictionaryAddValue(query, kSecMatchLimit, kSecMatchLimitOne);
-  CFDictionaryAddValue(query, kSecMatchSearchList, list);
-
-  CFRelease(list);
-
-  err = SecItemCopyMatching(query, (CFTypeRef *)&identity);
-
-#    if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
-  if (err && DNSSDHostName)
-  {
-   /*
-    * Search for the connection server name failed; try the DNS-SD .local
-    * hostname instead...
-    */
-
-    snprintf(localname, sizeof(localname), "%s.local", DNSSDHostName);
-
-    cupsdLogMessage(CUPSD_LOG_DEBUG,
-                   "copy_cdsa_certificate: Looking for certs for \"%s\".",
-                   localname);
-
-    servername = CFStringCreateWithCString(kCFAllocatorDefault, localname,
-                                          kCFStringEncodingUTF8);
-
-    CFRelease(policy);
-
-    policy = SecPolicyCreateSSL(1, servername);
-
-    if (servername)
-      CFRelease(servername);
-
-    if (!policy)
-    {
-      cupsdLogMessage(CUPSD_LOG_ERROR, "Cannot create ssl policy reference");
-      goto cleanup;
-    }
-
-    CFDictionarySetValue(query, kSecMatchPolicy, policy);
-
-    err = SecItemCopyMatching(query, (CFTypeRef *)&identity);
-  }
-#    endif /* HAVE_DNSSD || HAVE_AVAHI */
-
-  if (err)
-  {
-    cupsdLogMessage(CUPSD_LOG_DEBUG,
-                   "Cannot find signing key in keychain \"%s\": %s (%d)",
-                   ServerCertificate, cssmErrorString(err), (int)err);
-    goto cleanup;
-  }
-
-  if (CFGetTypeID(identity) != SecIdentityGetTypeID())
-  {
-    cupsdLogMessage(CUPSD_LOG_ERROR, "SecIdentity CFTypeID failure.");
-    goto cleanup;
-  }
-
-  if ((certificates = CFArrayCreate(NULL, (const void **)&identity,
-                                 1, &kCFTypeArrayCallBacks)) == NULL)
-  {
-    cupsdLogMessage(CUPSD_LOG_ERROR, "Cannot create certificate array");
-    goto cleanup;
-  }
-
-  cleanup :
-
-  if (keychain)
-    CFRelease(keychain);
-  if (search)
-    CFRelease(search);
-  if (identity)
-    CFRelease(identity);
-
-  if (policy)
-    CFRelease(policy);
-  if (query)
-    CFRelease(query);
-
-  return (certificates);
-}
-
-
-/*
- * 'make_certificate()' - Make a self-signed SSL/TLS certificate.
- */
-
-static int                             /* O - 1 on success, 0 on failure */
-make_certificate(cupsd_client_t *con)  /* I - Client connection */
-{
-#    ifdef HAVE_SECGENERATESELFSIGNEDCERTIFICATE
-  int                  status = 0;     /* Return status */
-  OSStatus             err;            /* Error code (if any) */
-#  if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
-  char                 localname[1024];/* Local hostname */
-#  endif /* HAVE_DNSSD || HAVE_AVAHI */
-  const char           *servername;    /* Name of server in cert */
-  CFStringRef          cfservername = NULL;
-                                       /* CF string for server name */
-  SecIdentityRef       ident = NULL;   /* Identity */
-  SecKeyRef            publicKey = NULL,
-                                       /* Public key */
-                       privateKey = NULL;
-                                       /* Private key */
-  CFMutableDictionaryRef keyParams = NULL;
-                                       /* Key generation parameters */
-
-
-  cupsdLogMessage(CUPSD_LOG_INFO,
-                  "Generating SSL server key and certificate.");
-
-#  if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
-  if (con->servername && isdigit(con->servername[0] & 255) && DNSSDHostName)
-  {
-    snprintf(localname, sizeof(localname), "%s.local", DNSSDHostName);
-    servername = localname;
-  }
-  else
-#  endif /* HAVE_DNSSD || HAVE_AVAHI */
-  servername = con->servername;
-
-  cfservername = CFStringCreateWithCString(kCFAllocatorDefault, servername,
-                                           kCFStringEncodingUTF8);
-  if (!cfservername)
-    goto cleanup;
-
- /*
-  * Create a public/private key pair...
-  */
-
-  keyParams = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
-                                       &kCFTypeDictionaryKeyCallBacks,
-                                       &kCFTypeDictionaryValueCallBacks);
-  if (!keyParams)
-    goto cleanup;
-
-  CFDictionaryAddValue(keyParams, kSecAttrKeyType, kSecAttrKeyTypeRSA);
-  CFDictionaryAddValue(keyParams, kSecAttrKeySizeInBits, CFSTR("2048"));
-  CFDictionaryAddValue(keyParams, kSecAttrLabel,
-                       CFSTR("CUPS Self-Signed Certificate"));
-
-  err = SecKeyGeneratePair(keyParams, &publicKey, &privateKey);
-  if (err != noErr)
-  {
-    cupsdLogMessage(CUPSD_LOG_DEBUG, "SecKeyGeneratePair returned %ld.",
-                    (long)err);
-    goto cleanup;
-  }
-
- /*
-  * Create a self-signed certificate using the public/private key pair...
-  */
-
-  CFIndex      usageInt = kSecKeyUsageAll;
-  CFNumberRef  usage = CFNumberCreate(alloc, kCFNumberCFIndexType, &usageInt);
-  CFDictionaryRef certParams = CFDictionaryCreateMutable(kCFAllocatorDefault,
-                                   kSecCSRBasicContraintsPathLen, CFINT(0),
-                                   kSecSubjectAltName, cfservername,
-                                   kSecCertificateKeyUsage, usage,
-                                   NULL, NULL);
-  CFRelease(usage);
-        
-  const void   *ca_o[] = { kSecOidOrganization, CFSTR("") };
-  const void   *ca_cn[] = { kSecOidCommonName, cfservername };
-  CFArrayRef   ca_o_dn = CFArrayCreate(kCFAllocatorDefault, ca_o, 2, NULL);
-  CFArrayRef   ca_cn_dn = CFArrayCreate(kCFAllocatorDefault, ca_cn, 2, NULL);
-  const void   *ca_dn_array[2];
-
-  ca_dn_array[0] = CFArrayCreate(kCFAllocatorDefault, (const void **)&ca_o_dn,
-                                 1, NULL);
-  ca_dn_array[1] = CFArrayCreate(kCFAllocatorDefault, (const void **)&ca_cn_dn,
-                                 1, NULL);
-
-  CFArrayRef   subject = CFArrayCreate(kCFAllocatorDefault, ca_dn_array, 2,
-                                        NULL);
-  SecCertificateRef cert = SecGenerateSelfSignedCertificate(subject, certParams,
-                                                            publicKey,
-                                                            privateKey);
-  CFRelease(subject);
-  CFRelease(certParams);
-
-  if (!cert)
-  {
-    cupsdLogMessage(CUPSD_LOG_DEBUG, "SecGenerateSelfSignedCertificate failed.");
-    goto cleanup;
-  }
-
-  ident = SecIdentityCreate(kCFAllocatorDefault, cert, privateKey);
-
-  if (ident)
-    cupsdLogMessage(CUPSD_LOG_INFO,
-                    "Created SSL server certificate file \"%s\".",
-                   ServerCertificate);
-
- /*
-  * Cleanup and return...
-  */
-
-cleanup:
-
-  if (cfservername)
-    CFRelease(cfservername);
-
-  if (keyParams)
-    CFRelease(keyParams);
-
-  if (ident)
-    CFRelease(ident);
-
-  if (cert)
-    CFRelease(cert);
-
-  if (publicKey)
-    CFRelease(publicKey);
-
-  if (privateKey)
-    CFRelease(publicKey);
-
-  if (!status)
-    cupsdLogMessage(CUPSD_LOG_ERROR,
-                    "Unable to create SSL server key and certificate.");
-
-  return (status);
-
-#    else /* !HAVE_SECGENERATESELFSIGNEDCERTIFICATE */
-  int          pid,                    /* Process ID of command */
-               status;                 /* Status of command */
-  char         command[1024],          /* Command */
-               *argv[4],               /* Command-line arguments */
-               *envp[MAX_ENV + 1],     /* Environment variables */
-               keychain[1024],         /* Keychain argument */
-               infofile[1024],         /* Type-in information for cert */
-#      if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
-               localname[1024],        /* Local hostname */
-#      endif /* HAVE_DNSSD || HAVE_AVAHI */
-               *servername;            /* Name of server in cert */
-  cups_file_t  *fp;                    /* Seed/info file */
-  int          infofd;                 /* Info file descriptor */
-
-
-#      if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
-  if (con->servername && isdigit(con->servername[0] & 255) && DNSSDHostName)
-  {
-    snprintf(localname, sizeof(localname), "%s.local", DNSSDHostName);
-    servername = localname;
-  }
-  else
-#      endif /* HAVE_DNSSD || HAVE_AVAHI */
-    servername = con->servername;
-
- /*
-  * Run the "certtool" command to generate a self-signed certificate...
-  */
-
-  if (!cupsFileFind("certtool", getenv("PATH"), 1, command, sizeof(command)))
-  {
-    cupsdLogMessage(CUPSD_LOG_ERROR,
-                    "No SSL certificate and certtool command not found.");
-    return (0);
-  }
-
- /*
-  * Create a file with the certificate information fields...
-  *
-  * Note: This assumes that the default questions are asked by the certtool
-  * command...
-  */
-
-  if ((fp = cupsTempFile2(infofile, sizeof(infofile))) == NULL)
-  {
-    cupsdLogMessage(CUPSD_LOG_ERROR,
-                    "Unable to create certificate information file %s - %s",
-                    infofile, strerror(errno));
-    return (0);
-  }
-
-  cupsFilePrintf(fp,
-                 "%s\n"                        /* Enter key and certificate label */
-                 "r\n"                 /* Generate RSA key pair */
-                 "2048\n"              /* Key size in bits */
-                 "y\n"                 /* OK (y = yes) */
-                 "b\n"                 /* Usage (b=signing/encryption) */
-                 "s\n"                 /* Sign with SHA1 */
-                 "y\n"                 /* OK (y = yes) */
-                 "%s\n"                        /* Common name */
-                 "\n"                  /* Country (default) */
-                 "\n"                  /* Organization (default) */
-                 "\n"                  /* Organizational unit (default) */
-                 "\n"                  /* State/Province (default) */
-                 "%s\n"                        /* Email address */
-                 "y\n",                        /* OK (y = yes) */
-                servername, servername, ServerAdmin);
-  cupsFileClose(fp);
-
-  cupsdLogMessage(CUPSD_LOG_INFO,
-                  "Generating SSL server key and certificate.");
-
-  snprintf(keychain, sizeof(keychain), "k=%s", ServerCertificate);
-
-  argv[0] = "certtool";
-  argv[1] = "c";
-  argv[2] = keychain;
-  argv[3] = NULL;
-
-  cupsdLoadEnv(envp, MAX_ENV);
-
-  infofd = open(infofile, O_RDONLY);
-
-  if (!cupsdStartProcess(command, argv, envp, infofd, -1, -1, -1, -1, 1, NULL,
-                         NULL, &pid))
-  {
-    close(infofd);
-    unlink(infofile);
-    return (0);
-  }
-
-  close(infofd);
-  unlink(infofile);
-
-  while (waitpid(pid, &status, 0) < 0)
-    if (errno != EINTR)
-    {
-      status = 1;
-      break;
-    }
-
-  cupsdFinishProcess(pid, command, sizeof(command), NULL);
-
-  if (status)
-  {
-    if (WIFEXITED(status))
-      cupsdLogMessage(CUPSD_LOG_ERROR,
-                      "Unable to create SSL server key and certificate - "
-                     "the certtool command stopped with status %d.",
-                     WEXITSTATUS(status));
-    else
-      cupsdLogMessage(CUPSD_LOG_ERROR,
-                      "Unable to create SSL server key and certificate - "
-                     "the certtool command crashed on signal %d.",
-                     WTERMSIG(status));
-  }
-  else
-  {
-    cupsdLogMessage(CUPSD_LOG_INFO,
-                    "Created SSL server certificate file \"%s\".",
-                   ServerCertificate);
-  }
-
-  return (!status);
-#    endif /* HAVE_SECGENERATESELFSIGNEDCERTIFICATE */
-}
-
-
-/*
- * End of "$Id$".
- */
diff --git a/scheduler/tls-gnutls.c b/scheduler/tls-gnutls.c
deleted file mode 100644 (file)
index 36518cd..0000000
+++ /dev/null
@@ -1,292 +0,0 @@
-/*
- * "$Id$"
- *
- *   TLS support code for the CUPS scheduler using GNU TLS.
- *
- *   Copyright 2007-2012 by Apple Inc.
- *   Copyright 1997-2007 by Easy Software Products, all rights reserved.
- *
- *   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/".
- *
- * Contents:
- *
- *   cupsdEndTLS()     - Shutdown a secure session with the client.
- *   cupsdStartTLS()   - Start a secure session with the client.
- *   make_certificate() - Make a self-signed SSL/TLS certificate.
- */
-
-
-/*
- * Local functions...
- */
-
-static int             make_certificate(cupsd_client_t *con);
-
-
-/*
- * 'cupsdEndTLS()' - Shutdown a secure session with the client.
- */
-
-int                                    /* O - 1 on success, 0 on error */
-cupsdEndTLS(cupsd_client_t *con)       /* I - Client connection */
-{
-  int          error;                  /* Error code */
-  gnutls_certificate_server_credentials *credentials;
-                                       /* TLS credentials */
-
-
-  credentials = (gnutls_certificate_server_credentials *)
-                    (con->http.tls_credentials);
-
-  error = gnutls_bye(con->http.tls, GNUTLS_SHUT_WR);
-  switch (error)
-  {
-    case GNUTLS_E_SUCCESS:
-      cupsdLogMessage(CUPSD_LOG_DEBUG,
-                     "SSL shutdown successful!");
-      break;
-    default:
-      cupsdLogMessage(CUPSD_LOG_ERROR,
-                     "SSL shutdown failed: %s", gnutls_strerror(error));
-      break;
-  }
-
-  gnutls_deinit(con->http.tls);
-  con->http.tls = NULL;
-
-  gnutls_certificate_free_credentials(*credentials);
-  free(credentials);
-
-  return (1);
-}
-
-
-/*
- * 'cupsdStartTLS()' - Start a secure session with the client.
- */
-
-int                                    /* O - 1 on success, 0 on error */
-cupsdStartTLS(cupsd_client_t *con)     /* I - Client connection */
-{
-  int          status;                 /* Error code */
-  gnutls_certificate_server_credentials *credentials;
-                                       /* TLS credentials */
-
-
-  cupsdLogMessage(CUPSD_LOG_DEBUG, "[Client %d] Encrypting connection.",
-                  con->http.fd);
-
- /*
-  * Verify that we have a certificate...
-  */
-
-  if (access(ServerKey, 0) || access(ServerCertificate, 0))
-  {
-   /*
-    * Nope, make a self-signed certificate...
-    */
-
-    if (!make_certificate(con))
-      return (0);
-  }
-
- /*
-  * Create the SSL object and perform the SSL handshake...
-  */
-
-  credentials = (gnutls_certificate_server_credentials *)
-                    malloc(sizeof(gnutls_certificate_server_credentials));
-  if (credentials == NULL)
-  {
-    cupsdLogMessage(CUPSD_LOG_ERROR,
-                    "Unable to encrypt connection from %s - %s",
-                    con->http.hostname, strerror(errno));
-
-    return (0);
-  }
-
-  gnutls_certificate_allocate_credentials(credentials);
-  gnutls_certificate_set_x509_key_file(*credentials, ServerCertificate,
-                                      ServerKey, GNUTLS_X509_FMT_PEM);
-
-  gnutls_init(&con->http.tls, GNUTLS_SERVER);
-  gnutls_set_default_priority(con->http.tls);
-
-  gnutls_credentials_set(con->http.tls, GNUTLS_CRD_CERTIFICATE, *credentials);
-  gnutls_transport_set_ptr(con->http.tls, (gnutls_transport_ptr)HTTP(con));
-  gnutls_transport_set_pull_function(con->http.tls, _httpReadGNUTLS);
-  gnutls_transport_set_push_function(con->http.tls, _httpWriteGNUTLS);
-
-  while ((status = gnutls_handshake(con->http.tls)) != GNUTLS_E_SUCCESS)
-  {
-    if (gnutls_error_is_fatal(status))
-    {
-      cupsdLogMessage(CUPSD_LOG_ERROR,
-                      "Unable to encrypt connection from %s - %s",
-                      con->http.hostname, gnutls_strerror(status));
-
-      gnutls_deinit(con->http.tls);
-      gnutls_certificate_free_credentials(*credentials);
-      con->http.tls = NULL;
-      free(credentials);
-      return (0);
-    }
-  }
-
-  cupsdLogMessage(CUPSD_LOG_DEBUG, "Connection from %s now encrypted.",
-                  con->http.hostname);
-
-  con->http.tls_credentials = credentials;
-  return (1);
-}
-
-
-/*
- * 'make_certificate()' - Make a self-signed SSL/TLS certificate.
- */
-
-static int                             /* O - 1 on success, 0 on failure */
-make_certificate(cupsd_client_t *con)  /* I - Client connection */
-{
-  gnutls_x509_crt      crt;            /* Self-signed certificate */
-  gnutls_x509_privkey  key;            /* Encryption key */
-  cups_lang_t          *language;      /* Default language info */
-  cups_file_t          *fp;            /* Key/cert file */
-  unsigned char                buffer[8192];   /* Buffer for x509 data */
-  size_t               bytes;          /* Number of bytes of data */
-  unsigned char                serial[4];      /* Serial number buffer */
-  time_t               curtime;        /* Current time */
-  int                  result;         /* Result of GNU TLS calls */
-
-
- /*
-  * Create the encryption key...
-  */
-
-  cupsdLogMessage(CUPSD_LOG_INFO, "Generating SSL server key...");
-
-  gnutls_x509_privkey_init(&key);
-  gnutls_x509_privkey_generate(key, GNUTLS_PK_RSA, 2048, 0);
-
- /*
-  * Save it...
-  */
-
-  bytes = sizeof(buffer);
-
-  if ((result = gnutls_x509_privkey_export(key, GNUTLS_X509_FMT_PEM,
-                                           buffer, &bytes)) < 0)
-  {
-    cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to export SSL server key - %s",
-                    gnutls_strerror(result));
-    gnutls_x509_privkey_deinit(key);
-    return (0);
-  }
-  else if ((fp = cupsFileOpen(ServerKey, "w")) != NULL)
-  {
-    cupsFileWrite(fp, (char *)buffer, bytes);
-    cupsFileClose(fp);
-
-    cupsdLogMessage(CUPSD_LOG_INFO, "Created SSL server key file \"%s\"...",
-                   ServerKey);
-  }
-  else
-  {
-    cupsdLogMessage(CUPSD_LOG_ERROR,
-                    "Unable to create SSL server key file \"%s\" - %s",
-                   ServerKey, strerror(errno));
-    gnutls_x509_privkey_deinit(key);
-    return (0);
-  }
-
- /*
-  * Create the self-signed certificate...
-  */
-
-  cupsdLogMessage(CUPSD_LOG_INFO, "Generating self-signed SSL certificate...");
-
-  language  = cupsLangDefault();
-  curtime   = time(NULL);
-  serial[0] = curtime >> 24;
-  serial[1] = curtime >> 16;
-  serial[2] = curtime >> 8;
-  serial[3] = curtime;
-
-  gnutls_x509_crt_init(&crt);
-  if (strlen(language->language) == 5)
-    gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_COUNTRY_NAME, 0,
-                                  language->language + 3, 2);
-  else
-    gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_COUNTRY_NAME, 0,
-                                  "US", 2);
-  gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_COMMON_NAME, 0,
-                                ServerName, strlen(ServerName));
-  gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_ORGANIZATION_NAME, 0,
-                                ServerName, strlen(ServerName));
-  gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_ORGANIZATIONAL_UNIT_NAME,
-                                0, "Unknown", 7);
-  gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_STATE_OR_PROVINCE_NAME, 0,
-                                "Unknown", 7);
-  gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_LOCALITY_NAME, 0,
-                                "Unknown", 7);
-  gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_PKCS9_EMAIL, 0,
-                                ServerAdmin, strlen(ServerAdmin));
-  gnutls_x509_crt_set_key(crt, key);
-  gnutls_x509_crt_set_serial(crt, serial, sizeof(serial));
-  gnutls_x509_crt_set_activation_time(crt, curtime);
-  gnutls_x509_crt_set_expiration_time(crt, curtime + 10 * 365 * 86400);
-  gnutls_x509_crt_set_ca_status(crt, 0);
-  gnutls_x509_crt_set_subject_alternative_name(crt, GNUTLS_SAN_DNSNAME,
-                                               ServerName);
-  gnutls_x509_crt_set_key_purpose_oid(crt, GNUTLS_KP_TLS_WWW_SERVER, 0);
-  gnutls_x509_crt_set_key_usage(crt, GNUTLS_KEY_KEY_ENCIPHERMENT);
-  gnutls_x509_crt_set_version(crt, 3);
-
-  bytes = sizeof(buffer);
-  if (gnutls_x509_crt_get_key_id(crt, 0, buffer, &bytes) >= 0)
-    gnutls_x509_crt_set_subject_key_id(crt, buffer, bytes);
-
-  gnutls_x509_crt_sign(crt, crt, key);
-
- /*
-  * Save it...
-  */
-
-  bytes = sizeof(buffer);
-  if ((result = gnutls_x509_crt_export(crt, GNUTLS_X509_FMT_PEM,
-                                       buffer, &bytes)) < 0)
-    cupsdLogMessage(CUPSD_LOG_ERROR,
-                    "Unable to export SSL server certificate - %s",
-                   gnutls_strerror(result));
-  else if ((fp = cupsFileOpen(ServerCertificate, "w")) != NULL)
-  {
-    cupsFileWrite(fp, (char *)buffer, bytes);
-    cupsFileClose(fp);
-
-    cupsdLogMessage(CUPSD_LOG_INFO,
-                    "Created SSL server certificate file \"%s\"...",
-                   ServerCertificate);
-  }
-  else
-    cupsdLogMessage(CUPSD_LOG_ERROR,
-                    "Unable to create SSL server certificate file \"%s\" - %s",
-                   ServerCertificate, strerror(errno));
-
- /*
-  * Cleanup...
-  */
-
-  gnutls_x509_crt_deinit(crt);
-  gnutls_x509_privkey_deinit(key);
-
-  return (1);
-}
-
-
-/*
- * End of "$Id$".
- */
index 9ad83ae1ef0af4b02d9fd88850958c25f99e1f5e..6914ff670731d7c3a9730d7f6907bf3309e104ab 100644 (file)
                727EF04D192E3602001EF690 /* testlpd.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = testlpd.c; path = ../scheduler/testlpd.c; sourceTree = "<group>"; };
                727EF04E192E3602001EF690 /* testspeed.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = testspeed.c; path = ../scheduler/testspeed.c; sourceTree = "<group>"; };
                727EF04F192E3602001EF690 /* testsub.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = testsub.c; path = ../scheduler/testsub.c; sourceTree = "<group>"; };
-               728FB7E0153600FA005426E1 /* tls-darwin.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = "tls-darwin.c"; path = "../scheduler/tls-darwin.c"; sourceTree = "<group>"; };
-               728FB7E1153600FA005426E1 /* tls-gnutls.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = "tls-gnutls.c"; path = "../scheduler/tls-gnutls.c"; sourceTree = "<group>"; };
                728FB7EC1536161C005426E1 /* libz.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.dylib; path = /usr/lib/libz.dylib; sourceTree = "<absolute>"; };
                728FB7EF1536167A005426E1 /* libiconv.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libiconv.dylib; path = /usr/lib/libiconv.dylib; sourceTree = "<absolute>"; };
                728FB7F01536167A005426E1 /* libresolv.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libresolv.dylib; path = /usr/lib/libresolv.dylib; sourceTree = "<absolute>"; };
                                72220F8D13330B0C00FCA411 /* subscriptions.h */,
                                72220F8E13330B0C00FCA411 /* sysman.c */,
                                72220F8F13330B0C00FCA411 /* sysman.h */,
-                               728FB7E0153600FA005426E1 /* tls-darwin.c */,
-                               728FB7E1153600FA005426E1 /* tls-gnutls.c */,
                        );
                        name = cupsd;
                        path = .;