]> git.ipfire.org Git - thirdparty/cups.git/commitdiff
Implement http_create_credential, now working for HTTPS/IPPS clients.
authorMichael R Sweet <michael.r.sweet@gmail.com>
Tue, 29 Mar 2022 20:34:32 +0000 (16:34 -0400)
committerMichael R Sweet <michael.r.sweet@gmail.com>
Tue, 29 Mar 2022 20:34:32 +0000 (16:34 -0400)
cups/tls-openssl.c

index 7ec22103674c2c16ce5dc6bf871873d5558d8c34..4a6d9e96fa1fbb543a75498fe10e5a1dfe86e9df 100644 (file)
@@ -31,7 +31,7 @@ static int            http_bio_write(BIO *h, const char *buf, int num);
 
 static X509            *http_create_credential(http_credential_t *credential);
 static const char      *http_default_path(char *buffer, size_t bufsize);
-static void            http_load_crl(void);
+//static void          http_load_crl(void);
 static const char      *http_make_path(char *buffer, size_t bufsize, const char *dirname, const char *filename, const char *ext);
 
 
@@ -56,7 +56,7 @@ static int            tls_auto_create = 0;
                                        /* Auto-create self-signed certs? */
 static char            *tls_common_name = NULL;
                                        /* Default common name */
-static X509_CRL                *tls_crl = NULL;/* Certificate revocation list */
+//static X509_CRL              *tls_crl = NULL;/* Certificate revocation list */
 static char            *tls_keypath = NULL;
                                        /* Server cert keychain path */
 static _cups_mutex_t   tls_mutex = _CUPS_MUTEX_INITIALIZER;
@@ -414,14 +414,15 @@ httpCredentialsAreValidForName(
     cups_array_t *credentials,         // I - Credentials
     const char   *common_name)         // I - Name to check
 {
-#if 0
-  openssl_x509_crt_t   cert;           // Certificate
-  int                  result = 0;     // Result
+  X509 *cert;                          // Certificate
+  int  result = 0;                     // Result
 
 
   cert = http_create_credential((http_credential_t *)cupsArrayFirst(credentials));
   if (cert)
   {
+    result = 1;
+#if 0
     result = openssl_x509_crt_check_hostname(cert, common_name) != 0;
 
     if (result)
@@ -457,14 +458,12 @@ httpCredentialsAreValidForName(
 
       _cupsMutexUnlock(&tls_mutex);
     }
+#endif // 0
 
-    openssl_x509_crt_deinit(cert);
+    X509_free(cert);
   }
 
   return (result);
-#else
-  return (1);
-#endif // 0
 }
 
 
@@ -928,676 +927,657 @@ httpSaveCredentials(
 
 
 /*
- * 'http_bio_ctrl()' - Control the HTTP connection.
- */
-
-static long                            // O - Result/data
-http_bio_ctrl(BIO  *h,                 // I - BIO data
-              int  cmd,                        // I - Control command
-             long arg1,                // I - First argument
-             void *arg2)               // I - Second argument
-{
-  switch (cmd)
-  {
-    default :
-        return (0);
-
-    case BIO_CTRL_RESET :
-        h->ptr = NULL;
-       return (0);
-
-    case BIO_C_SET_FILE_PTR :
-        h->ptr  = arg2;
-       h->init = 1;
-       return (1);
-
-    case BIO_C_GET_FILE_PTR :
-        if (arg2)
-       {
-         *((void **)arg2) = h->ptr;
-         return (1);
-       }
-       else
-         return (0);
-
-    case BIO_CTRL_DUP :
-    case BIO_CTRL_FLUSH :
-        return (1);
-  }
-}
-
-
-/*
- * 'http_bio_free()' - Free OpenSSL data.
+ * '_httpTLSInitialize()' - Initialize the TLS stack.
  */
 
-static int                             // O - 1 on success, 0 on failure
-http_bio_free(BIO *h)                  // I - BIO data
+void
+_httpTLSInitialize(void)
 {
-  if (!h)
-    return (0);
-
-  if (h->shutdown)
-  {
-    h->init  = 0;
-    h->flags = 0;
-  }
-
-  return (1);
+  // OpenSSL no longer requires explicit initialization...
 }
 
 
 /*
- * 'http_bio_new()' - Initialize an OpenSSL BIO structure.
+ * '_httpTLSPending()' - Return the number of pending TLS-encrypted bytes.
  */
 
-static int                             // O - 1 on success, 0 on failure
-http_bio_new(BIO *h)                   // I - BIO data
+size_t                                 // O - Bytes available
+_httpTLSPending(http_t *http)          // I - HTTP connection
 {
-  if (!h)
-    return (0);
-
-  h->init  = 0;
-  h->num   = 0;
-  h->ptr   = NULL;
-  h->flags = 0;
-
-  return (1);
+  return ((size_t)SSL_pending(http->tls));
 }
 
 
 /*
- * 'http_bio_puts()' - Send a string for OpenSSL.
+ * '_httpTLSRead()' - Read from a SSL/TLS connection.
  */
 
-static int                             // O - Bytes written
-http_bio_puts(BIO        *h,           // I - BIO data
-              const char *str)         // I - String to write
+int                                    // O - Bytes read
+_httpTLSRead(http_t *http,             // I - Connection to server
+            char   *buf,               // I - Buffer to store data
+            int    len)                // I - Length of buffer
 {
-#ifdef WIN32
-  return (send(((http_t *)h->ptr)->fd, str, (int)strlen(str), 0));
-#else
-  return ((int)send(((http_t *)h->ptr)->fd, str, strlen(str), 0));
-#endif // WIN32
+  return (SSL_read((SSL *)(http->tls), buf, len));
 }
 
 
 /*
- * 'http_bio_read()' - Read data for OpenSSL.
+ * '_httpTLSSetOptions()' - Set TLS protocol and cipher suite options.
  */
 
-static int                             // O - Bytes read
-http_bio_read(BIO  *h,                 // I - BIO data
-              char *buf,               // I - Buffer
-             int  size)                // I - Number of bytes to read
+void
+_httpTLSSetOptions(int options,                // I - Options
+                   int min_version,    // I - Minimum TLS version
+                   int max_version)    // I - Maximum TLS version
 {
-  http_t       *http;                  // HTTP connection
-
-
-  http = (http_t *)h->ptr;
-
-  if (!http->blocking)
+  if (!(options & _HTTP_TLS_SET_DEFAULT) || tls_options < 0)
   {
-   /*
-    * Make sure we have data before we read...
-    */
-
-    if (!_httpWait(http, 10000, 0))
-    {
-#ifdef WIN32
-      http->error = WSAETIMEDOUT;
-#else
-      http->error = ETIMEDOUT;
-#endif // WIN32
-
-      return (-1);
-    }
+    tls_options     = options;
+    tls_min_version = min_version;
+    tls_max_version = max_version;
   }
-
-  return ((int)recv(http->fd, buf, (size_t)size, 0));
-}
-
-
-/*
- * 'http_bio_write()' - Write data for OpenSSL.
- */
-
-static int                             // O - Bytes written
-http_bio_write(BIO        *h,          // I - BIO data
-               const char *buf,                // I - Buffer to write
-              int        num)          // I - Number of bytes to write
-{
-  return (send(((http_t *)h->ptr)->fd, buf, num, 0));
 }
 
 
 /*
- * 'http_create_credential()' - Create a single credential in the internal format.
+ * '_httpTLSStart()' - Set up SSL/TLS support on a connection.
  */
 
-static X509 *                          // O - Certificate
-http_create_credential(
-    http_credential_t *credential)     // I - Credential
+int                                    // O - 0 on success, -1 on failure
+_httpTLSStart(http_t *http)            // I - Connection to server
 {
-#if 0
-  int                  result;                 // Result from GNU TLS
-  openssl_x509_crt_t   cert;                   // Certificate
-  openssl_datum_t      datum;                  // Data record
-
+  BIO          *bio;                   // Basic input/output context
+  SSL_CTX      *context;               // Encryption context
+  char         hostname[256],          // Hostname
+               cipherlist[256];        // List of cipher suites
+  unsigned long        error;                  // Error code, if any
+  static const int versions[] =                // SSL/TLS versions
+  {
+    TLS1_VERSION,                      // No more SSL support in OpenSSL
+    TLS1_VERSION,                      // TLS/1.0
+    TLS1_1_VERSION,                    // TLS/1.1
+    TLS1_2_VERSION,                    // TLS/1.2
+#ifdef TLS1_3_VERSION
+    TLS1_3_VERSION,                    // TLS/1.3
+    TLS1_3_VERSION                     // TLS/1.3 (max)
+#else
+    TLS1_2_VERSION,                    // TLS/1.2
+    TLS1_2_VERSION                     // TLS/1.2 (max)
+#endif // TLS1_3_VERSION
+  };
 
-  DEBUG_printf(("3http_create_credential(credential=%p)", credential));
 
-  if (!credential)
-    return (NULL);
+  DEBUG_printf(("3_httpTLSStart(http=%p)", http));
 
-  if ((result = openssl_x509_crt_init(&cert)) < 0)
+  if (tls_options < 0)
   {
-    DEBUG_printf(("4http_create_credential: init error: %s", openssl_strerror(result)));
-    return (NULL);
+    DEBUG_puts("4_httpTLSStart: Setting defaults.");
+    _cupsSetDefaults();
+    DEBUG_printf(("4_httpTLSStart: tls_options=%x", tls_options));
   }
 
-  datum.data = credential->data;
-  datum.size = credential->datalen;
-
-  if ((result = openssl_x509_crt_import(cert, &datum, GNUTLS_X509_FMT_DER)) < 0)
+  if (http->mode == _HTTP_MODE_SERVER && !tls_keypath)
   {
-    DEBUG_printf(("4http_create_credential: import error: %s", openssl_strerror(result)));
+    DEBUG_puts("4_httpTLSStart: cupsSetServerCredentials not called.");
+    http->error  = errno = EINVAL;
+    http->status = HTTP_STATUS_ERROR;
+    _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Server credentials not set."), 1);
 
-    openssl_x509_crt_deinit(cert);
-    return (NULL);
+    return (-1);
   }
 
-  return (cert);
-#else
-  return (NULL);
-#endif // 0
-}
-
-
-/*
- * 'http_default_path()' - Get the default credential store path.
- */
-
-static const char *                    // O - Path or NULL on error
-http_default_path(
-    char   *buffer,                    // I - Path buffer
-    size_t bufsize)                    // I - Size of path buffer
-{
-  _cups_globals_t      *cg = _cupsGlobals();
-                                       // Pointer to library globals
+  if (http->mode == _HTTP_MODE_CLIENT)
+  {
+    // Negotiate a TLS connection as a client...
+    context = SSL_CTX_new(TLS_client_method());
+  }
+  else
+  {
+    // Negotiate a TLS connection as a server
+    char       crtfile[1024],          // Certificate file
+               keyfile[1024];          // Private key file
+    const char *cn,                    // Common name to lookup
+               *cnptr;                 // Pointer into common name
+    int                have_creds = 0;         // Have credentials?
 
+    context = SSL_CTX_new(TLS_server_method());
 
-  if (cg->home && getuid())
-  {
-    snprintf(buffer, bufsize, "%s/.cups", cg->home);
-    if (access(buffer, 0))
+    // Find the TLS certificate...
+    if (http->fields[HTTP_FIELD_HOST])
     {
-      DEBUG_printf(("1http_default_path: Making directory \"%s\".", buffer));
-      if (mkdir(buffer, 0700))
-      {
-        DEBUG_printf(("1http_default_path: Failed to make directory: %s", strerror(errno)));
-        return (NULL);
-      }
+      // Use hostname for TLS upgrade...
+      strlcpy(hostname, http->fields[HTTP_FIELD_HOST], sizeof(hostname));
     }
-
-    snprintf(buffer, bufsize, "%s/.cups/ssl", cg->home);
-    if (access(buffer, 0))
+    else
     {
-      DEBUG_printf(("1http_default_path: Making directory \"%s\".", buffer));
-      if (mkdir(buffer, 0700))
+      // 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(("1http_default_path: Failed to make directory: %s", strerror(errno)));
-        return (NULL);
+        // Unable to get local socket address so use default...
+       DEBUG_printf(("4_httpTLSStart: Unable to get socket address: %s", strerror(errno)));
+       hostname[0] = '\0';
       }
-    }
-  }
-  else
-    strlcpy(buffer, CUPS_SERVERROOT "/ssl", bufsize);
+      else if (httpAddrLocalhost(&addr))
+      {
+        // Local access top use default...
+       hostname[0] = '\0';
+      }
+      else
+      {
+        // Lookup the socket address...
+       httpAddrLookup(&addr, hostname, sizeof(hostname));
+        DEBUG_printf(("4_httpTLSStart: Resolved socket address to \"%s\".", hostname));
+      }
+    }
 
-  DEBUG_printf(("1http_default_path: Using default path \"%s\".", buffer));
+    if (isdigit(hostname[0] & 255) || hostname[0] == '[')
+      hostname[0] = '\0';              // Don't allow numeric addresses
 
-  return (buffer);
-}
+    if (hostname[0])
+      cn = hostname;
+    else
+      cn = tls_common_name;
 
+    if (cn)
+    {
+      // First look in the CUPS keystore...
+      http_make_path(crtfile, sizeof(crtfile), tls_keypath, cn, "crt");
+      http_make_path(keyfile, sizeof(keyfile), tls_keypath, cn, "key");
 
-/*
- * 'http_load_crl()' - Load the certificate revocation list, if any.
- */
+      if (access(crtfile, R_OK) || access(keyfile, R_OK))
+      {
+        // No CUPS-managed certs, look for CA certs...
+        char cacrtfile[1024], cakeyfile[1024]; // CA cert files
 
-static void
-http_load_crl(void)
-{
-#if 0
-  _cupsMutexLock(&tls_mutex);
+        snprintf(cacrtfile, sizeof(cacrtfile), "/etc/letsencrypt/live/%s/fullchain.pem", cn);
+        snprintf(cakeyfile, sizeof(cakeyfile), "/etc/letsencrypt/live/%s/privkey.pem", cn);
 
-  if (!openssl_x509_crl_init(&tls_crl))
-  {
-    cups_file_t                *fp;            // CRL file
-    char               filename[1024], // site.crl
-                       line[256];      // Base64-encoded line
-    unsigned char      *data = NULL;   // Buffer for cert data
-    size_t             alloc_data = 0, // Bytes allocated
-                       num_data = 0;   // Bytes used
-    int                        decoded;        // Bytes decoded
-    openssl_datum_t    datum;          // Data record
+        if ((access(cacrtfile, R_OK) || access(cakeyfile, R_OK)) && (cnptr = strchr(cn, '.')) != NULL)
+        {
+          // Try just domain name...
+          cnptr ++;
+          if (strchr(cnptr, '.'))
+          {
+            snprintf(cacrtfile, sizeof(cacrtfile), "/etc/letsencrypt/live/%s/fullchain.pem", cnptr);
+            snprintf(cakeyfile, sizeof(cakeyfile), "/etc/letsencrypt/live/%s/privkey.pem", cnptr);
+          }
+        }
 
+        if (!access(cacrtfile, R_OK) && !access(cakeyfile, R_OK))
+        {
+          // Use the CA certs...
+          strlcpy(crtfile, cacrtfile, sizeof(crtfile));
+          strlcpy(keyfile, cakeyfile, sizeof(keyfile));
+        }
+      }
 
-    http_make_path(filename, sizeof(filename), CUPS_SERVERROOT, "site", "crl");
+      have_creds = !access(crtfile, R_OK) && !access(keyfile, R_OK);
+    }
 
-    if ((fp = cupsFileOpen(filename, "r")) != NULL)
+    if (!have_creds && tls_auto_create && cn)
     {
-      while (cupsFileGets(fp, line, sizeof(line)))
+      DEBUG_printf(("4_httpTLSStart: Auto-create credentials for \"%s\".", cn));
+
+      if (!cupsMakeServerCredentials(tls_keypath, cn, 0, NULL, time(NULL) + 365 * 86400))
       {
-       if (!strcmp(line, "-----BEGIN X509 CRL-----"))
-       {
-         if (num_data)
-         {
-          /*
-           * Missing END X509 CRL...
-           */
+       DEBUG_puts("4_httpTLSStart: cupsMakeServerCredentials failed.");
+       http->error  = errno = EINVAL;
+       http->status = HTTP_STATUS_ERROR;
+       _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to create server credentials."), 1);
 
-           break;
-         }
-       }
-       else if (!strcmp(line, "-----END X509 CRL-----"))
-       {
-         if (!num_data)
-         {
-          /*
-           * Missing data...
-           */
+       return (-1);
+      }
+    }
 
-           break;
-         }
+    SSL_CTX_use_PrivateKey_file(context, keyfile, SSL_FILETYPE_PEM);
+    SSL_CTX_use_certificate_file(context, crtfile, SSL_FILETYPE_PEM);
+  }
 
-          datum.data = data;
-         datum.size = num_data;
+  // Set TLS options...
+  strlcpy(cipherlist, "HIGH:!DH:+DHE", sizeof(cipherlist));
+  if ((tls_options & _HTTP_TLS_ALLOW_RC4) && http->mode == _HTTP_MODE_CLIENT)
+    strlcat(cipherlist, ":+RC4", sizeof(cipherlist));
+  else
+    strlcat(cipherlist, ":!RC4", sizeof(cipherlist));
+  if (tls_options & _HTTP_TLS_DENY_CBC)
+    strlcat(cipherlist, ":!SHA1:!SHA256:!SHA384", sizeof(cipherlist));
+  strlcat(cipherlist, ":@STRENGTH", sizeof(cipherlist));
 
-         openssl_x509_crl_import(tls_crl, &datum, GNUTLS_X509_FMT_PEM);
+  SSL_CTX_set_min_proto_version(context, versions[tls_min_version]);
+  SSL_CTX_set_max_proto_version(context, versions[tls_max_version]);
+  SSL_CTX_set_cipher_list(context, cipherlist);
 
-         num_data = 0;
-       }
-       else
-       {
-         if (alloc_data == 0)
-         {
-           data       = malloc(2048);
-           alloc_data = 2048;
+  // Setup a TLS session
+  bio = BIO_new(&http_bio_methods);
+  BIO_ctrl(bio, BIO_C_SET_FILE_PTR, 0, (char *)http);
 
-           if (!data)
-             break;
-         }
-         else if ((num_data + strlen(line)) >= alloc_data)
-         {
-           unsigned char *tdata = realloc(data, alloc_data + 1024);
-                                           // Expanded buffer
+  http->tls = SSL_new(context);
+  SSL_set_bio(http->tls, bio, bio);
 
-           if (!tdata)
-             break;
+  if (http->mode == _HTTP_MODE_CLIENT)
+  {
+    // Negotiate as a server...
+    if (SSL_connect(http->tls) < 1)
+    {
+      // Failed
+      if ((error = ERR_get_error()) != 0)
+        _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI, ERR_error_string(error, NULL), 0);
 
-           data       = tdata;
-           alloc_data += 1024;
-         }
+      http->status = HTTP_STATUS_ERROR;
+      http->error  = EPIPE;
 
-         decoded = alloc_data - num_data;
-         httpDecode64_2((char *)data + num_data, &decoded, line);
-         num_data += (size_t)decoded;
-       }
-      }
+      SSL_CTX_free(context);
 
-      cupsFileClose(fp);
+      SSL_free(http->tls);
+      http->tls = NULL;
 
-      if (data)
-       free(data);
+      return (-1);
     }
   }
+  else
+  {
+    // Negotiate as a server...
+    if (SSL_accept(http->tls) < 1)
+    {
+      // Failed
+      if ((error = ERR_get_error()) != 0)
+        _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI, ERR_error_string(error, NULL), 0);
 
-  _cupsMutexUnlock(&tls_mutex);
-#endif // 0
-}
-
-
-/*
- * 'http_make_path()' - Format a filename for a certificate or key file.
- */
-
-static const char *                    // O - Filename
-http_make_path(
-    char       *buffer,                        // I - Filename buffer
-    size_t     bufsize,                        // I - Size of buffer
-    const char *dirname,               // I - Directory
-    const char *filename,              // I - Filename (usually hostname)
-    const char *ext)                   // I - Extension
-{
-  char *bufptr,                        // Pointer into buffer
-       *bufend = buffer + bufsize - 1; // End of buffer
-
+      http->status = HTTP_STATUS_ERROR;
+      http->error  = EPIPE;
 
-  snprintf(buffer, bufsize, "%s/", dirname);
-  bufptr = buffer + strlen(buffer);
+      SSL_CTX_free(context);
 
-  while (*filename && bufptr < bufend)
-  {
-    if (_cups_isalnum(*filename) || *filename == '-' || *filename == '.')
-      *bufptr++ = *filename;
-    else
-      *bufptr++ = '_';
+      SSL_free(http->tls);
+      http->tls = NULL;
 
-    filename ++;
+      return (-1);
+    }
   }
 
-  if (bufptr < bufend)
-    *bufptr++ = '.';
-
-  strlcpy(bufptr, ext, (size_t)(bufend - bufptr + 1));
-
-  return (buffer);
+  return (0);
 }
 
 
 /*
- * '_httpTLSInitialize()' - Initialize the TLS stack.
+ * '_httpTLSStop()' - Shut down SSL/TLS on a connection.
  */
 
 void
-_httpTLSInitialize(void)
+_httpTLSStop(http_t *http)             // I - Connection to server
 {
-  // OpenSSL no longer requires explicit initialization...
-}
+  SSL_CTX      *context;               // Context for encryption
 
 
-/*
- * '_httpTLSPending()' - Return the number of pending TLS-encrypted bytes.
- */
+  context = SSL_get_SSL_CTX(http->tls);
 
-size_t                                 // O - Bytes available
-_httpTLSPending(http_t *http)          // I - HTTP connection
-{
-  return ((size_t)SSL_pending(http->tls));
+  SSL_shutdown(http->tls);
+  SSL_CTX_free(context);
+  SSL_free(http->tls);
 }
 
 
 /*
- * '_httpTLSRead()' - Read from a SSL/TLS connection.
+ * '_httpTLSWrite()' - Write to a SSL/TLS connection.
  */
 
-int                                    // O - Bytes read
-_httpTLSRead(http_t *http,             // I - Connection to server
-            char   *buf,               // I - Buffer to store data
-            int    len)                // I - Length of buffer
+int                                    // O - Bytes written
+_httpTLSWrite(http_t     *http,                // I - Connection to server
+             const char *buf,          // I - Buffer holding data
+             int        len)           // I - Length of buffer
 {
-  return (SSL_read((SSL *)(http->tls), buf, len));
+  return (SSL_write(http->tls, buf, len));
 }
 
 
 /*
- * '_httpTLSSetOptions()' - Set TLS protocol and cipher suite options.
+ * 'http_bio_ctrl()' - Control the HTTP connection.
  */
 
-void
-_httpTLSSetOptions(int options,                // I - Options
-                   int min_version,    // I - Minimum TLS version
-                   int max_version)    // I - Maximum TLS version
-{
-  if (!(options & _HTTP_TLS_SET_DEFAULT) || tls_options < 0)
+static long                            // O - Result/data
+http_bio_ctrl(BIO  *h,                 // I - BIO data
+              int  cmd,                        // I - Control command
+             long arg1,                // I - First argument
+             void *arg2)               // I - Second argument
+{
+  switch (cmd)
   {
-    tls_options     = options;
-    tls_min_version = min_version;
-    tls_max_version = max_version;
+    default :
+        return (0);
+
+    case BIO_CTRL_RESET :
+        h->ptr = NULL;
+       return (0);
+
+    case BIO_C_SET_FILE_PTR :
+        h->ptr  = arg2;
+       h->init = 1;
+       return (1);
+
+    case BIO_C_GET_FILE_PTR :
+        if (arg2)
+       {
+         *((void **)arg2) = h->ptr;
+         return (1);
+       }
+       else
+         return (0);
+
+    case BIO_CTRL_DUP :
+    case BIO_CTRL_FLUSH :
+        return (1);
   }
 }
 
 
 /*
- * '_httpTLSStart()' - Set up SSL/TLS support on a connection.
+ * 'http_bio_free()' - Free OpenSSL data.
  */
 
-int                                    // O - 0 on success, -1 on failure
-_httpTLSStart(http_t *http)            // I - Connection to server
+static int                             // O - 1 on success, 0 on failure
+http_bio_free(BIO *h)                  // I - BIO data
 {
-  BIO          *bio;                   // Basic input/output context
-  SSL_CTX      *context;               // Encryption context
-  char         hostname[256],          // Hostname
-               cipherlist[256];        // List of cipher suites
-  unsigned long        error;                  // Error code, if any
-  static const int versions[] =                // SSL/TLS versions
+  if (!h)
+    return (0);
+
+  if (h->shutdown)
   {
-    TLS1_VERSION,                      // No more SSL support in OpenSSL
-    TLS1_VERSION,                      // TLS/1.0
-    TLS1_1_VERSION,                    // TLS/1.1
-    TLS1_2_VERSION,                    // TLS/1.2
-#ifdef TLS1_3_VERSION
-    TLS1_3_VERSION,                    // TLS/1.3
-    TLS1_3_VERSION                     // TLS/1.3 (max)
+    h->init  = 0;
+    h->flags = 0;
+  }
+
+  return (1);
+}
+
+
+/*
+ * 'http_bio_new()' - Initialize an OpenSSL BIO structure.
+ */
+
+static int                             // O - 1 on success, 0 on failure
+http_bio_new(BIO *h)                   // I - BIO data
+{
+  if (!h)
+    return (0);
+
+  h->init  = 0;
+  h->num   = 0;
+  h->ptr   = NULL;
+  h->flags = 0;
+
+  return (1);
+}
+
+
+/*
+ * 'http_bio_puts()' - Send a string for OpenSSL.
+ */
+
+static int                             // O - Bytes written
+http_bio_puts(BIO        *h,           // I - BIO data
+              const char *str)         // I - String to write
+{
+#ifdef WIN32
+  return (send(((http_t *)h->ptr)->fd, str, (int)strlen(str), 0));
 #else
-    TLS1_2_VERSION,                    // TLS/1.2
-    TLS1_2_VERSION                     // TLS/1.2 (max)
-#endif // TLS1_3_VERSION
-  };
+  return ((int)send(((http_t *)h->ptr)->fd, str, strlen(str), 0));
+#endif // WIN32
+}
 
 
-  DEBUG_printf(("3_httpTLSStart(http=%p)", http));
+/*
+ * 'http_bio_read()' - Read data for OpenSSL.
+ */
 
-  if (tls_options < 0)
-  {
-    DEBUG_puts("4_httpTLSStart: Setting defaults.");
-    _cupsSetDefaults();
-    DEBUG_printf(("4_httpTLSStart: tls_options=%x", tls_options));
-  }
+static int                             // O - Bytes read
+http_bio_read(BIO  *h,                 // I - BIO data
+              char *buf,               // I - Buffer
+             int  size)                // I - Number of bytes to read
+{
+  http_t       *http;                  // HTTP connection
 
-  if (http->mode == _HTTP_MODE_SERVER && !tls_keypath)
-  {
-    DEBUG_puts("4_httpTLSStart: cupsSetServerCredentials not called.");
-    http->error  = errno = EINVAL;
-    http->status = HTTP_STATUS_ERROR;
-    _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Server credentials not set."), 1);
 
-    return (-1);
-  }
+  http = (http_t *)h->ptr;
 
-  if (http->mode == _HTTP_MODE_CLIENT)
-  {
-    // Negotiate a TLS connection as a client...
-    context = SSL_CTX_new(TLS_client_method());
-  }
-  else
+  if (!http->blocking)
   {
-    // Negotiate a TLS connection as a server
-    char       crtfile[1024],          // Certificate file
-               keyfile[1024];          // Private key file
-    const char *cn,                    // Common name to lookup
-               *cnptr;                 // Pointer into common name
-    int                have_creds = 0;         // Have credentials?
-
-    context = SSL_CTX_new(TLS_server_method());
+   /*
+    * Make sure we have data before we read...
+    */
 
-    // Find the TLS certificate...
-    if (http->fields[HTTP_FIELD_HOST])
-    {
-      // Use hostname for TLS upgrade...
-      strlcpy(hostname, http->fields[HTTP_FIELD_HOST], sizeof(hostname));
-    }
-    else
+    if (!_httpWait(http, 10000, 0))
     {
-      // Resolve hostname from connection address...
-      http_addr_t      addr;           // Connection address
-      socklen_t                addrlen;        // Length of address
+#ifdef WIN32
+      http->error = WSAETIMEDOUT;
+#else
+      http->error = ETIMEDOUT;
+#endif // WIN32
 
-      addrlen = sizeof(addr);
-      if (getsockname(http->fd, (struct sockaddr *)&addr, &addrlen))
-      {
-        // Unable to get local socket address so use default...
-       DEBUG_printf(("4_httpTLSStart: Unable to get socket address: %s", strerror(errno)));
-       hostname[0] = '\0';
-      }
-      else if (httpAddrLocalhost(&addr))
-      {
-        // Local access top use default...
-       hostname[0] = '\0';
-      }
-      else
-      {
-        // Lookup the socket address...
-       httpAddrLookup(&addr, hostname, sizeof(hostname));
-        DEBUG_printf(("4_httpTLSStart: Resolved socket address to \"%s\".", hostname));
-      }
+      return (-1);
     }
+  }
 
-    if (isdigit(hostname[0] & 255) || hostname[0] == '[')
-      hostname[0] = '\0';              // Don't allow numeric addresses
+  return ((int)recv(http->fd, buf, (size_t)size, 0));
+}
 
-    if (hostname[0])
-      cn = hostname;
-    else
-      cn = tls_common_name;
 
-    if (cn)
-    {
-      // First look in the CUPS keystore...
-      http_make_path(crtfile, sizeof(crtfile), tls_keypath, cn, "crt");
-      http_make_path(keyfile, sizeof(keyfile), tls_keypath, cn, "key");
+/*
+ * 'http_bio_write()' - Write data for OpenSSL.
+ */
 
-      if (access(crtfile, R_OK) || access(keyfile, R_OK))
-      {
-        // No CUPS-managed certs, look for CA certs...
-        char cacrtfile[1024], cakeyfile[1024]; // CA cert files
+static int                             // O - Bytes written
+http_bio_write(BIO        *h,          // I - BIO data
+               const char *buf,                // I - Buffer to write
+              int        num)          // I - Number of bytes to write
+{
+  return (send(((http_t *)h->ptr)->fd, buf, num, 0));
+}
 
-        snprintf(cacrtfile, sizeof(cacrtfile), "/etc/letsencrypt/live/%s/fullchain.pem", cn);
-        snprintf(cakeyfile, sizeof(cakeyfile), "/etc/letsencrypt/live/%s/privkey.pem", cn);
 
-        if ((access(cacrtfile, R_OK) || access(cakeyfile, R_OK)) && (cnptr = strchr(cn, '.')) != NULL)
-        {
-          // Try just domain name...
-          cnptr ++;
-          if (strchr(cnptr, '.'))
-          {
-            snprintf(cacrtfile, sizeof(cacrtfile), "/etc/letsencrypt/live/%s/fullchain.pem", cnptr);
-            snprintf(cakeyfile, sizeof(cakeyfile), "/etc/letsencrypt/live/%s/privkey.pem", cnptr);
-          }
-        }
+/*
+ * 'http_create_credential()' - Create a single credential in the internal format.
+ */
 
-        if (!access(cacrtfile, R_OK) && !access(cakeyfile, R_OK))
-        {
-          // Use the CA certs...
-          strlcpy(crtfile, cacrtfile, sizeof(crtfile));
-          strlcpy(keyfile, cakeyfile, sizeof(keyfile));
-        }
-      }
+static X509 *                          // O - Certificate
+http_create_credential(
+    http_credential_t *credential)     // I - Credential
+{
+  X509 *cert = NULL;                   // Certificate
+  BIO  *bio;                           // Basic I/O for string
 
-      have_creds = !access(crtfile, R_OK) && !access(keyfile, R_OK);
-    }
 
-    if (!have_creds && tls_auto_create && cn)
-    {
-      DEBUG_printf(("4_httpTLSStart: Auto-create credentials for \"%s\".", cn));
 
-      if (!cupsMakeServerCredentials(tls_keypath, cn, 0, NULL, time(NULL) + 365 * 86400))
-      {
-       DEBUG_puts("4_httpTLSStart: cupsMakeServerCredentials failed.");
-       http->error  = errno = EINVAL;
-       http->status = HTTP_STATUS_ERROR;
-       _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to create server credentials."), 1);
+  if ((bio = BIO_new_mem_buf(credential->data, credential->datalen)) == NULL)
+    return (NULL);
 
-       return (-1);
+  PEM_read_bio_X509(bio, &cert, NULL, (void *)"");
+
+  BIO_free(bio);
+
+  return (cert);
+}
+
+
+/*
+ * 'http_default_path()' - Get the default credential store path.
+ */
+
+static const char *                    // O - Path or NULL on error
+http_default_path(
+    char   *buffer,                    // I - Path buffer
+    size_t bufsize)                    // I - Size of path buffer
+{
+  _cups_globals_t      *cg = _cupsGlobals();
+                                       // Pointer to library globals
+
+
+  if (cg->home && getuid())
+  {
+    snprintf(buffer, bufsize, "%s/.cups", cg->home);
+    if (access(buffer, 0))
+    {
+      DEBUG_printf(("1http_default_path: Making directory \"%s\".", buffer));
+      if (mkdir(buffer, 0700))
+      {
+        DEBUG_printf(("1http_default_path: Failed to make directory: %s", strerror(errno)));
+        return (NULL);
       }
     }
 
-    SSL_CTX_use_PrivateKey_file(context, keyfile, SSL_FILETYPE_PEM);
-    SSL_CTX_use_certificate_file(context, crtfile, SSL_FILETYPE_PEM);
+    snprintf(buffer, bufsize, "%s/.cups/ssl", cg->home);
+    if (access(buffer, 0))
+    {
+      DEBUG_printf(("1http_default_path: Making directory \"%s\".", buffer));
+      if (mkdir(buffer, 0700))
+      {
+        DEBUG_printf(("1http_default_path: Failed to make directory: %s", strerror(errno)));
+        return (NULL);
+      }
+    }
   }
-
-  // Set TLS options...
-  strlcpy(cipherlist, "HIGH:!DH:+DHE", sizeof(cipherlist));
-  if ((tls_options & _HTTP_TLS_ALLOW_RC4) && http->mode == _HTTP_MODE_CLIENT)
-    strlcat(cipherlist, ":+RC4", sizeof(cipherlist));
   else
-    strlcat(cipherlist, ":!RC4", sizeof(cipherlist));
-  if (tls_options & _HTTP_TLS_DENY_CBC)
-    strlcat(cipherlist, ":!SHA1:!SHA256:!SHA384", sizeof(cipherlist));
-  strlcat(cipherlist, ":@STRENGTH", sizeof(cipherlist));
+    strlcpy(buffer, CUPS_SERVERROOT "/ssl", bufsize);
 
-  SSL_CTX_set_min_proto_version(context, versions[tls_min_version]);
-  SSL_CTX_set_max_proto_version(context, versions[tls_max_version]);
-  SSL_CTX_set_cipher_list(context, cipherlist);
+  DEBUG_printf(("1http_default_path: Using default path \"%s\".", buffer));
 
-  // Setup a TLS session
-  bio = BIO_new(&http_bio_methods);
-  BIO_ctrl(bio, BIO_C_SET_FILE_PTR, 0, (char *)http);
+  return (buffer);
+}
 
-  http->tls = SSL_new(context);
-  SSL_set_bio(http->tls, bio, bio);
 
-  if (http->mode == _HTTP_MODE_CLIENT)
+#if 0
+/*
+ * 'http_load_crl()' - Load the certificate revocation list, if any.
+ */
+
+static void
+http_load_crl(void)
+{
+  _cupsMutexLock(&tls_mutex);
+
+  if (!openssl_x509_crl_init(&tls_crl))
   {
-    // Negotiate as a server...
-    if (SSL_connect(http->tls) < 1)
+    cups_file_t                *fp;            // CRL file
+    char               filename[1024], // site.crl
+                       line[256];      // Base64-encoded line
+    unsigned char      *data = NULL;   // Buffer for cert data
+    size_t             alloc_data = 0, // Bytes allocated
+                       num_data = 0;   // Bytes used
+    int                        decoded;        // Bytes decoded
+    openssl_datum_t    datum;          // Data record
+
+
+    http_make_path(filename, sizeof(filename), CUPS_SERVERROOT, "site", "crl");
+
+    if ((fp = cupsFileOpen(filename, "r")) != NULL)
     {
-      // Failed
-      if ((error = ERR_get_error()) != 0)
-        _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI, ERR_error_string(error, NULL), 0);
+      while (cupsFileGets(fp, line, sizeof(line)))
+      {
+       if (!strcmp(line, "-----BEGIN X509 CRL-----"))
+       {
+         if (num_data)
+         {
+          /*
+           * Missing END X509 CRL...
+           */
 
-      http->status = HTTP_STATUS_ERROR;
-      http->error  = EPIPE;
+           break;
+         }
+       }
+       else if (!strcmp(line, "-----END X509 CRL-----"))
+       {
+         if (!num_data)
+         {
+          /*
+           * Missing data...
+           */
 
-      SSL_CTX_free(context);
+           break;
+         }
 
-      SSL_free(http->tls);
-      http->tls = NULL;
+          datum.data = data;
+         datum.size = num_data;
 
-      return (-1);
-    }
-  }
-  else
-  {
-    // Negotiate as a server...
-    if (SSL_accept(http->tls) < 1)
-    {
-      // Failed
-      if ((error = ERR_get_error()) != 0)
-        _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI, ERR_error_string(error, NULL), 0);
+         openssl_x509_crl_import(tls_crl, &datum, GNUTLS_X509_FMT_PEM);
 
-      http->status = HTTP_STATUS_ERROR;
-      http->error  = EPIPE;
+         num_data = 0;
+       }
+       else
+       {
+         if (alloc_data == 0)
+         {
+           data       = malloc(2048);
+           alloc_data = 2048;
 
-      SSL_CTX_free(context);
+           if (!data)
+             break;
+         }
+         else if ((num_data + strlen(line)) >= alloc_data)
+         {
+           unsigned char *tdata = realloc(data, alloc_data + 1024);
+                                           // Expanded buffer
 
-      SSL_free(http->tls);
-      http->tls = NULL;
+           if (!tdata)
+             break;
 
-      return (-1);
+           data       = tdata;
+           alloc_data += 1024;
+         }
+
+         decoded = alloc_data - num_data;
+         httpDecode64_2((char *)data + num_data, &decoded, line);
+         num_data += (size_t)decoded;
+       }
+      }
+
+      cupsFileClose(fp);
+
+      if (data)
+       free(data);
     }
   }
 
-  return (0);
+  _cupsMutexUnlock(&tls_mutex);
 }
+#endif // 0
 
 
 /*
- * '_httpTLSStop()' - Shut down SSL/TLS on a connection.
+ * 'http_make_path()' - Format a filename for a certificate or key file.
  */
 
-void
-_httpTLSStop(http_t *http)             // I - Connection to server
+static const char *                    // O - Filename
+http_make_path(
+    char       *buffer,                        // I - Filename buffer
+    size_t     bufsize,                        // I - Size of buffer
+    const char *dirname,               // I - Directory
+    const char *filename,              // I - Filename (usually hostname)
+    const char *ext)                   // I - Extension
 {
-  SSL_CTX      *context;               // Context for encryption
+  char *bufptr,                        // Pointer into buffer
+       *bufend = buffer + bufsize - 1; // End of buffer
 
 
-  context = SSL_get_SSL_CTX(http->tls);
+  snprintf(buffer, bufsize, "%s/", dirname);
+  bufptr = buffer + strlen(buffer);
 
-  SSL_shutdown(http->tls);
-  SSL_CTX_free(context);
-  SSL_free(http->tls);
-}
+  while (*filename && bufptr < bufend)
+  {
+    if (_cups_isalnum(*filename) || *filename == '-' || *filename == '.')
+      *bufptr++ = *filename;
+    else
+      *bufptr++ = '_';
+
+    filename ++;
+  }
 
+  if (bufptr < bufend)
+    *bufptr++ = '.';
 
-/*
- * '_httpTLSWrite()' - Write to a SSL/TLS connection.
- */
+  strlcpy(bufptr, ext, (size_t)(bufend - bufptr + 1));
 
-int                                    // O - Bytes written
-_httpTLSWrite(http_t     *http,                // I - Connection to server
-             const char *buf,          // I - Buffer holding data
-             int        len)           // I - Length of buffer
-{
-  return (SSL_write(http->tls, buf, len));
+  return (buffer);
 }