]> git.ipfire.org Git - thirdparty/cups.git/blobdiff - cups/tls-darwin.c
Full sweep of all Clang warnings, plus some bug fixes for incorrect memcpy usage.
[thirdparty/cups.git] / cups / tls-darwin.c
index 66b6452873efe03b1d8b8b5046375aa76b394cc8..0344a8b14994ede19688bd03821537b012f24dfe 100644 (file)
@@ -3,7 +3,7 @@
  *
  * TLS support code for CUPS on OS X.
  *
- * Copyright 2007-2013 by Apple Inc.
+ * Copyright 2007-2014 by Apple Inc.
  * Copyright 1997-2007 by Easy Software Products, all rights reserved.
  *
  * These coded instructions, statements, and computer programs are the
  * This file is subject to the Apple OS-Developed Software exception.
  */
 
+/**** This file is included from http.c ****/
+
+/*
+ * Include necessary headers...
+ */
+
+#include <spawn.h>
+
+extern char **environ;
+
+
+/*
+ * Local globals...
+ */
+
+#ifdef HAVE_SECKEYCHAINOPEN
+static int             tls_auto_create = 0;
+                                       /* Auto-create self-signed certs? */
+static char            *tls_common_name = NULL;
+                                       /* Default common name */
+static SecKeychainRef  tls_keychain = NULL;
+                                       /* Server cert keychain */
+static char            *tls_keypath = NULL;
+                                       /* Server cert keychain path */
+static _cups_mutex_t   tls_mutex = _CUPS_MUTEX_INITIALIZER;
+                                       /* Mutex for keychain/certs */
+#endif /* HAVE_SECKEYCHAINOPEN */
+
 
 /*
  * Local functions...
  */
 
-//static CFArrayRef    copy_cdsa_certificate(cupsd_client_t *con);
-//static int           make_certificate(cupsd_client_t *con);
+#ifdef HAVE_SECKEYCHAINOPEN
+static CFArrayRef      http_cdsa_copy_server(const char *common_name);
+#endif /* HAVE_SECKEYCHAINOPEN */
+static OSStatus                http_cdsa_read(SSLConnectionRef connection, void *data, size_t *dataLength);
+static OSStatus                http_cdsa_write(SSLConnectionRef connection, const void *data, size_t *dataLength);
+
+
+/*
+ * 'cupsMakeServerCredentials()' - Make a self-signed certificate and private key pair.
+ *
+ * @since CUPS 2.0@
+ */
+
+int                                    /* O - 1 on success, 0 on failure */
+cupsMakeServerCredentials(
+    const char *path,                  /* I - Path to keychain/directory */
+    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 */
+{
+#if defined(HAVE_SECGENERATESELFSIGNEDCERTIFICATE) && defined(HAVE_SECKEYCHAINOPEN)
+  int                  status = 0;     /* Return status */
+  OSStatus             err;            /* Error code (if any) */
+  CFStringRef          cfcommon_name = 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 */
+
+
+  (void)num_alt_names;
+  (void)alt_names;
+  (void)expiration_date;
+
+  cfcommon_name = CFStringCreateWithCString(kCFAllocatorDefault, common_name,
+                                           kCFStringEncodingUTF8);
+  if (!cfcommon_name)
+    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)
+    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, cfcommon_name, kSecCertificateKeyUsage, usage, NULL, NULL);
+  CFRelease(usage);
+
+  const void   *ca_o[] = { kSecOidOrganization, CFSTR("") };
+  const void   *ca_cn[] = { kSecOidCommonName, cfcommon_name };
+  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)
+    goto cleanup;
+
+  ident = SecIdentityCreate(kCFAllocatorDefault, cert, privateKey);
+
+  if (ident)
+    status = 1;
+
+  /*
+   * Cleanup and return...
+   */
+
+cleanup:
+
+  if (cfcommon_name)
+    CFRelease(cfcommon_name);
+
+  if (keyParams)
+    CFRelease(keyParams);
+
+  if (ident)
+    CFRelease(ident);
+
+  if (cert)
+    CFRelease(cert);
+
+  if (publicKey)
+    CFRelease(publicKey);
+
+  if (privateKey)
+    CFRelease(publicKey);
+
+  return (status);
+
+#else /* !(HAVE_SECGENERATESELFSIGNEDCERTIFICATE && HAVE_SECKEYCHAINOPEN) */
+  int          pid,                    /* Process ID of command */
+               status;                 /* Status of command */
+  char         command[1024],          /* Command */
+               *argv[4],               /* Command-line arguments */
+               keychain[1024],         /* Keychain argument */
+               infofile[1024];         /* Type-in information for cert */
+  cups_file_t  *fp;                    /* Seed/info file */
+
+
+  (void)num_alt_names;
+  (void)alt_names;
+  (void)expiration_date;
+
+ /*
+  * Run the "certtool" command to generate a self-signed certificate...
+  */
+
+  if (!cupsFileFind("certtool", getenv("PATH"), 1, command, sizeof(command)))
+    return (-1);
+
+ /*
+  * 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)
+    return (-1);
+
+  cupsFilePrintf(fp,
+                 "CUPS Self-Signed Certificate\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) */
+                 "\n"                  /* Email address */
+                 "y\n",                        /* OK (y = yes) */
+                common_name);
+  cupsFileClose(fp);
+
+  snprintf(keychain, sizeof(keychain), "k=%s", path);
+
+  argv[0] = "certtool";
+  argv[1] = "c";
+  argv[2] = keychain;
+  argv[3] = NULL;
+
+  posix_spawn_file_actions_t actions;  /* File actions */
+
+  posix_spawn_file_actions_init(&actions);
+  posix_spawn_file_actions_addclose(&actions, 0);
+  posix_spawn_file_actions_addopen(&actions, 0, infofile, O_RDONLY, 0);
+
+  if (posix_spawn(&pid, command, &actions, NULL, argv, environ))
+  {
+    unlink(infofile);
+    return (-1);
+  }
+
+  posix_spawn_file_actions_destroy(&actions);
+
+  unlink(infofile);
+
+  while (waitpid(pid, &status, 0) < 0)
+    if (errno != EINTR)
+    {
+      status = -1;
+      break;
+    }
+
+  return (!status);
+#endif /* HAVE_SECGENERATESELFSIGNEDCERTIFICATE && HAVE_SECKEYCHAINOPEN */
+}
+
+
+/*
+ * '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@
+ */
+
+int                                    /* O - 1 on success, 0 on failure */
+cupsSetServerCredentials(
+    const char *path,                  /* I - Path to keychain/directory */
+    const char *common_name,           /* I - Default common name for server */
+    int        auto_create)            /* I - 1 = automatically create self-signed certificates */
+{
+#ifdef HAVE_SECKEYCHAINOPEN
+  SecKeychainRef       keychain = NULL;/* Temporary keychain */
+
+
+  if (SecKeychainOpen(path, &keychain) != noErr)
+  {
+    /* TODO: Set cups last error string */
+    return (0);
+  }
+
+  _cupsMutexLock(&tls_mutex);
+
+ /*
+  * Close any keychain that is currently open...
+  */
+
+  if (tls_keychain)
+    CFRelease(tls_keychain);
+
+  if (tls_keypath)
+    _cupsStrFree(tls_keypath);
+
+  if (tls_common_name)
+    _cupsStrFree(tls_common_name);
+
+ /*
+  * Save the new keychain...
+  */
+
+  tls_keychain    = keychain;
+  tls_keypath     = _cupsStrAlloc(path);
+  tls_auto_create = auto_create;
+  tls_common_name = _cupsStrAlloc(common_name);
+
+  _cupsMutexUnlock(&tls_mutex);
 
-static OSStatus        http_cdsa_read(SSLConnectionRef connection, void *data,
-                              size_t *dataLength);
-static OSStatus        http_cdsa_write(SSLConnectionRef connection, const void *data,
-                               size_t *dataLength);
+  return (1);
 
+#else
+  return (0);
+#endif /* HAVE_SECKEYCHAINOPEN */
+}
 
 
 /*
@@ -67,8 +350,7 @@ httpCopyCredentials(
        secCert = SecTrustGetCertificateAtIndex(peerTrust, i);
        if ((data = SecCertificateCopyData(secCert)))
        {
-         httpAddCredential(*credentials, CFDataGetBytePtr(data),
-                           CFDataGetLength(data));
+         httpAddCredential(*credentials, CFDataGetBytePtr(data), (size_t)CFDataGetLength(data));
          CFRelease(data);
        }
       }
@@ -107,8 +389,7 @@ _httpCreateCredentials(
        credential;
        credential = (http_credential_t *)cupsArrayNext(credentials))
   {
-    if ((data = CFDataCreate(kCFAllocatorDefault, credential->data,
-                            credential->datalen)))
+    if ((data = CFDataCreate(kCFAllocatorDefault, credential->data, (CFIndex)credential->datalen)))
     {
       if ((secCert = SecCertificateCreateWithData(kCFAllocatorDefault, data))
               != NULL)
@@ -125,6 +406,77 @@ _httpCreateCredentials(
 }
 
 
+/*
+ * 'httpCredentialsAreTrusted()' - Return whether the credentials are trusted.
+ *
+ * @since CUPS 2.0@
+ */
+
+int                                    /* O - 1 if trusted, 0 if not/unknown */
+httpCredentialsAreTrusted(
+    cups_array_t *credentials)         /* I - Credentials */
+{
+  (void)credentials;
+
+  return (0);
+}
+
+
+/*
+ * 'httpCredentialsGetExpiration()' - Return the expiration date of the credentials.
+ *
+ * @since CUPS 2.0@
+ */
+
+time_t                                 /* O - Expiration date of credentials */
+httpCredentialsGetExpiration(
+    cups_array_t *credentials)         /* I - Credentials */
+{
+  (void)credentials;
+
+  return (0);
+}
+
+
+/*
+ * 'httpCredentialsIsValidName()' - Return whether the credentials are valid for the given name.
+ *
+ * @since CUPS 2.0@
+ */
+
+int                                    /* O - 1 if valid, 0 otherwise */
+httpCredentialsIsValidName(
+    cups_array_t *credentials,         /* I - Credentials */
+    const char   *common_name)         /* I - Name to check */
+{
+  (void)credentials;
+  (void)common_name;
+
+  return (0);
+}
+
+
+/*
+ * 'httpCredentialsString()' - Return a string representing the credentials.
+ *
+ * @since CUPS 2.0@
+ */
+
+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 */
+{
+  (void)credentials;
+
+  if (buffer && bufsize > 0)
+    *buffer = '\0';
+
+  return (1);
+}
+
+
 /*
  * '_httpFreeCredentials()' - Free internal credentials.
  */
@@ -141,523 +493,541 @@ _httpFreeCredentials(
 
 
 /*
- * 'http_cdsa_read()' - Read function for the CDSA library.
+ * 'httpLoadCredentials()' - Load X.509 credentials from a keychain file.
+ *
+ * @since CUPS 2.0@
  */
 
-static OSStatus                                /* O  - -1 on error, 0 on success */
-http_cdsa_read(
-    SSLConnectionRef connection,       /* I  - SSL/TLS connection */
-    void             *data,            /* I  - Data buffer */
-    size_t           *dataLength)      /* IO - Number of bytes */
+int                                    /* O - 0 on success, -1 on error */
+httpLoadCredentials(
+    const char   *path,                        /* I  - Keychain/PKCS#12 path */
+    cups_array_t **credentials,                /* IO - Credentials */
+    const char   *common_name)         /* I  - Common name for credentials */
 {
-  OSStatus     result;                 /* Return value */
-  ssize_t      bytes;                  /* Number of bytes read */
-  http_t       *http;                  /* HTTP connection */
+  (void)path;
+  (void)credentials;
+  (void)common_name;
 
+  return (-1);
 
-  http = (http_t *)connection;
+#if 0
+  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          cfcommon_name = NULL;
+                                       /* Server name */
+  CFMutableDictionaryRef query = NULL; /* Query qualifiers */
+  CFArrayRef           list = NULL;    /* Keychain list */
 
-  if (!http->blocking)
-  {
-   /*
-    * Make sure we have data before we read...
-    */
 
-    while (!_httpWait(http, http->wait_value, 0))
-    {
-      if (http->timeout_cb && (*http->timeout_cb)(http, http->timeout_data))
-       continue;
+  if ((err = SecKeychainOpen(path, &keychain)))
+    goto cleanup;
 
-      http->error = ETIMEDOUT;
-      return (-1);
-    }
-  }
+  cfcommon_name = CFStringCreateWithCString(kCFAllocatorDefault, common_name, kCFStringEncodingUTF8);
 
-  do
-  {
-    bytes = recv(http->fd, data, *dataLength, 0);
-  }
-  while (bytes == -1 && (errno == EINTR || errno == EAGAIN));
+  policy = SecPolicyCreateSSL(1, cfcommon_name);
 
-  if (bytes == *dataLength)
-  {
-    result = 0;
-  }
-  else if (bytes > 0)
-  {
-    *dataLength = bytes;
-    result = errSSLWouldBlock;
-  }
-  else
-  {
-    *dataLength = 0;
+  if (cfcommon_name)
+    CFRelease(cfcommon_name);
 
-    if (bytes == 0)
-      result = errSSLClosedGraceful;
-    else if (errno == EAGAIN)
-      result = errSSLWouldBlock;
-    else
-      result = errSSLClosedAbort;
-  }
+  if (!policy)
+    goto cleanup;
 
-  return (result);
-}
+  if (!(query = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)))
+    goto cleanup;
 
+  list = CFArrayCreate(kCFAllocatorDefault, (const void **)&keychain, 1,
+                       &kCFTypeArrayCallBacks);
 
-/*
- * 'http_cdsa_write()' - Write function for the CDSA library.
- */
+  CFDictionaryAddValue(query, kSecClass, kSecClassIdentity);
+  CFDictionaryAddValue(query, kSecMatchPolicy, policy);
+  CFDictionaryAddValue(query, kSecReturnRef, kCFBooleanTrue);
+  CFDictionaryAddValue(query, kSecMatchLimit, kSecMatchLimitOne);
+  CFDictionaryAddValue(query, kSecMatchSearchList, list);
 
-static OSStatus                                /* O  - -1 on error, 0 on success */
-http_cdsa_write(
-    SSLConnectionRef connection,       /* I  - SSL/TLS connection */
-    const void       *data,            /* I  - Data buffer */
-    size_t           *dataLength)      /* IO - Number of bytes */
-{
-  OSStatus     result;                 /* Return value */
-  ssize_t      bytes;                  /* Number of bytes read */
-  http_t       *http;                  /* HTTP connection */
+  CFRelease(list);
 
+  err = SecItemCopyMatching(query, (CFTypeRef *)&identity);
 
-  http = (http_t *)connection;
+  if (err)
+    goto cleanup;
 
-  do
-  {
-    bytes = write(http->fd, data, *dataLength);
-  }
-  while (bytes == -1 && (errno == EINTR || errno == EAGAIN));
+  if (CFGetTypeID(identity) != SecIdentityGetTypeID())
+    goto cleanup;
 
-  if (bytes == *dataLength)
-  {
-    result = 0;
-  }
-  else if (bytes >= 0)
-  {
-    *dataLength = bytes;
-    result = errSSLWouldBlock;
-  }
-  else
-  {
-    *dataLength = 0;
+  if ((certificates = CFArrayCreate(NULL, (const void **)&identity, 1, &kCFTypeArrayCallBacks)) == NULL)
+    goto cleanup;
 
-    if (errno == EAGAIN)
-      result = errSSLWouldBlock;
-    else
-      result = errSSLClosedAbort;
-  }
+  cleanup :
 
-  return (result);
+  if (keychain)
+    CFRelease(keychain);
+  if (search)
+    CFRelease(search);
+  if (identity)
+    CFRelease(identity);
+
+  if (policy)
+    CFRelease(policy);
+  if (query)
+    CFRelease(query);
+
+  return (certificates);
+#endif /* 0 */
 }
 
 
+#if 0
 /*
- * 'http_tls_initialize()' - Initialize the TLS stack.
+ * 'cupsMakeCredentials()' - Create self-signed credentials for the given
+ *                           name.
+ *
+ * @since CUPS 2.0@
  */
 
-static void
-http_tls_initialize(void)
+int                                    /* O - 0 on success, -1 on error */
+cupsMakeCredentials(
+    const char   *path,                        /* I - Keychain/PKCS#12 path */
+    cups_array_t **credentials,                /* O - Credentials */
+    const char   *common_name)         /* I - Common name for X.509 cert */
 {
- /*
-  * Nothing to do...
-  */
-}
+#    ifdef HAVE_SECGENERATESELFSIGNEDCERTIFICATE
+  int                  status = -1;    /* Return status */
+  OSStatus             err;            /* Error code (if any) */
+  CFStringRef          cfcommon_name = 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 */
 
 
-/*
- * 'http_tls_pending()' - Return the number of pending TLS-encrypted bytes.
- */
+  if (credentials)
+    *credentials = NULL;
 
-static size_t
-http_tls_pending(http_t *http)         /* I - HTTP connection */
-{
-  size_t bytes;                                /* Bytes that are available */
+  cfcommon_name = CFStringCreateWithCString(kCFAllocatorDefault, servername,
+                                           kCFStringEncodingUTF8);
+  if (!cfcommon_name)
+    goto cleanup;
 
+ /*
+  * Create a public/private key pair...
+  */
 
-  if (!SSLGetBufferedReadSize(http->tls, &bytes))
-    return (bytes);
+  keyParams = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
+                                       &kCFTypeDictionaryKeyCallBacks,
+                                       &kCFTypeDictionaryValueCallBacks);
+  if (!keyParams)
+    goto cleanup;
 
-  return (0);
-}
+  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)
+    goto cleanup;
 
-/*
* 'http_tls_read()' - Read from a SSL/TLS connection.
- */
+ /*
 * Create a self-signed certificate using the public/private key pair...
 */
 
-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          result;                 /* Return value */
-  OSStatus     error;                  /* Error info */
-  size_t       processed;              /* Number of bytes processed */
+  CFIndex      usageInt = kSecKeyUsageAll;
+  CFNumberRef  usage = CFNumberCreate(alloc, kCFNumberCFIndexType, &usageInt);
+  CFDictionaryRef certParams = CFDictionaryCreateMutable(kCFAllocatorDefault,
+kSecCSRBasicContraintsPathLen, CFINT(0), kSecSubjectAltName, cfcommon_name, kSecCertificateKeyUsage, usage, NULL, NULL);
+  CFRelease(usage);
 
+  const void   *ca_o[] = { kSecOidOrganization, CFSTR("") };
+  const void   *ca_cn[] = { kSecOidCommonName, cfcommon_name };
+  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];
 
-  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;
+  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);
 
-    case errSSLWouldBlock :
-       if (processed)
-         result = (int)processed;
-       else
-       {
-         result = -1;
-         errno  = EINTR;
-       }
-       break;
+  CFArrayRef   subject = CFArrayCreate(kCFAllocatorDefault, ca_dn_array, 2, NULL);
+  SecCertificateRef cert = SecGenerateSelfSignedCertificate(subject, certParams, publicKey, privateKey);
+  CFRelease(subject);
+  CFRelease(certParams);
 
-    case errSSLClosedGraceful :
-    default :
-       if (processed)
-         result = (int)processed;
-       else
-       {
-         result = -1;
-         errno  = EPIPE;
-       }
-       break;
-  }
+  if (!cert)
+    goto cleanup;
 
-  return (result);
-}
+  ident = SecIdentityCreate(kCFAllocatorDefault, cert, privateKey);
 
+  if (ident)
+    status = 0;
 
-/*
* 'http_tls_set_credentials()' - Set the TLS credentials.
- */
+  /*
  * Cleanup and return...
  */
 
-static int                             /* O - Status of connection */
-http_tls_set_credentials(http_t *http) /* I - Connection to server */
-{
-  _cups_globals_t *cg = _cupsGlobals();        /* Pointer to library globals */
-  OSStatus             error = 0;      /* Error code */
-  http_tls_credentials_t credentials = NULL;
-                                       /* TLS credentials */
+cleanup:
 
+  if (cfcommon_name)
+    CFRelease(cfcommon_name);
 
-  DEBUG_printf(("7http_tls_set_credentials(%p)", http));
+  if (keyParams)
+    CFRelease(keyParams);
 
- /*
-  * Prefer connection specific credentials...
-  */
+  if (ident)
+    CFRelease(ident);
 
-  if ((credentials = http->tls_credentials) == NULL)
-    credentials = cg->tls_credentials;
+  if (cert)
+    CFRelease(cert);
 
-  if (credentials)
-  {
-    error = SSLSetCertificate(http->tls, credentials);
-    DEBUG_printf(("4http_tls_set_credentials: SSLSetCertificate, error=%d",
-                 (int)error));
-  }
-  else
-    DEBUG_puts("4http_tls_set_credentials: No credentials to set.");
+  if (publicKey)
+    CFRelease(publicKey);
 
-  return (error);
-}
+  if (privateKey)
+    CFRelease(publicKey);
 
+  return (status);
 
-/*
- * 'http_tls_start()' - Set up SSL/TLS support on a connection.
- */
+#    else /* !HAVE_SECGENERATESELFSIGNEDCERTIFICATE */
+  int          pid,                    /* Process ID of command */
+               status;                 /* Status of command */
+  char         command[1024],          /* Command */
+               *argv[4],               /* Command-line arguments */
+               keychain[1024],         /* Keychain argument */
+               infofile[1024];         /* Type-in information for cert */
+  cups_file_t  *fp;                    /* Seed/info file */
 
-static int                             /* O - 0 on success, -1 on failure */
-http_tls_start(http_t *http)           /* I - Connection to server */
-{
-  char                 hostname[256],  /* Hostname */
-                       *hostptr;       /* Pointer into hostname */
-  _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 */
 
+ /*
+  * Run the "certtool" command to generate a self-signed certificate...
+  */
 
-  DEBUG_printf(("7http_tls_start(http=%p)", http));
+  if (!cupsFileFind("certtool", getenv("PATH"), 1, command, sizeof(command)))
+    return (-1);
 
  /*
-  * Get the hostname to use for SSL...
+  * Create a file with the certificate information fields...
+  *
+  * Note: This assumes that the default questions are asked by the certtool
+  * command...
   */
 
-  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 ((fp = cupsTempFile2(infofile, sizeof(infofile))) == NULL)
+    return (-1);
 
-    strlcpy(hostname, http->hostname, sizeof(hostname));
-    if ((hostptr = hostname + strlen(hostname) - 1) >= hostname &&
-        *hostptr == '.')
-      *hostptr = '\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) */
+                common_name, common_name, "");
+  cupsFileClose(fp);
 
-  if ((http->tls = SSLCreateContext(kCFAllocatorDefault, kSSLClientSide,
-                                    kSSLStreamType)) == NULL)
-  {
-    DEBUG_puts("4http_tls_start: SSLCreateContext failed.");
-    http->error  = errno = ENOMEM;
-    http->status = HTTP_STATUS_ERROR;
-    _cupsSetHTTPError(HTTP_STATUS_ERROR);
+  snprintf(keychain, sizeof(keychain), "k=%s", path);
 
-    return (-1);
-  }
+  argv[0] = "certtool";
+  argv[1] = "c";
+  argv[2] = keychain;
+  argv[3] = NULL;
 
-  error = SSLSetConnection(http->tls, http);
-  DEBUG_printf(("4http_tls_start: SSLSetConnection, error=%d", (int)error));
+  posix_spawn_file_actions_t actions;  /* File actions */
 
-  if (!error)
-  {
-    error = SSLSetIOFuncs(http->tls, http_cdsa_read, http_cdsa_write);
-    DEBUG_printf(("4http_tls_start: SSLSetIOFuncs, error=%d", (int)error));
-  }
+  posix_spawn_file_actions_init(&actions);
+  posix_spawn_file_actions_addclose(&actions, 0);
+  posix_spawn_file_actions_addopen(&actions, 0, infofile, O_RDONLY, 0);
 
-  if (!error)
+  if (posix_spawn(&pid, command, &actions, NULL, argv, environ))
   {
-    error = SSLSetSessionOption(http->tls, kSSLSessionOptionBreakOnServerAuth,
-                                true);
-    DEBUG_printf(("4http_tls_start: SSLSetSessionOption, error=%d",
-                  (int)error));
+    unlink(infofile);
+    return (-1);
   }
 
-  if (!error)
-  {
-    if (cg->client_cert_cb)
-    {
-      error = SSLSetSessionOption(http->tls,
-                                 kSSLSessionOptionBreakOnCertRequested, true);
-      DEBUG_printf(("4http_tls_start: kSSLSessionOptionBreakOnCertRequested, "
-                    "error=%d", (int)error));
-    }
-    else
+  posix_spawn_file_actions_destroy(&actions);
+
+  unlink(infofile);
+
+  while (waitpid(pid, &status, 0) < 0)
+    if (errno != EINTR)
     {
-      error = http_tls_set_credentials(http);
-      DEBUG_printf(("4http_tls_start: http_tls_set_credentials, error=%d",
-                    (int)error));
+      status = -1;
+      break;
     }
-  }
 
- /*
-  * 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 (status)
+    return (-1);
 
-  if (!error)
-  {
-    error = SSLSetPeerDomainName(http->tls, hostname, strlen(hostname));
+#    endif /* HAVE_SECGENERATESELFSIGNEDCERTIFICATE */
 
-    DEBUG_printf(("4http_tls_start: SSLSetPeerDomainName, error=%d",
-                  (int)error));
-  }
+  return (httpLoadCredentials(path, credentials, common_name));
+}
+#endif /* 0 */
 
-  if (!error)
-  {
-    int done = 0;                      /* Are we done yet? */
 
-    while (!error && !done)
-    {
-      error = SSLHandshake(http->tls);
+/*
+ * 'httpSaveCredentials()' - Save X.509 credentials to a keychain file.
+ *
+ * @since CUPS 2.0@
+ */
 
-      DEBUG_printf(("4http_tls_start: SSLHandshake returned %d.", (int)error));
+int                                    /* O - -1 on error, 0 on success */
+httpSaveCredentials(
+    const char   *path,                        /* I - Keychain/PKCS#12 path */
+    cups_array_t *credentials,         /* I - Credentials */
+    const char   *common_name)         /* I - Common name for credentials */
+{
+  (void)path;
+  (void)credentials;
+  (void)common_name;
 
-      switch (error)
-      {
-       case noErr :
-           done = 1;
-           break;
+  return (-1);
+}
 
-       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);
-             }
+#ifdef HAVE_SECKEYCHAINOPEN
+/*
+ * 'http_cdsa_copy_server()' - Find and copy server credentials from the keychain.
+ */
 
-             DEBUG_printf(("4http_tls_start: Server certificate callback "
-                           "returned %d.", (int)error));
-           }
-           break;
+static CFArrayRef                      /* O - Array of certificates or NULL */
+http_cdsa_copy_server(
+    const char *common_name)           /* I - Server's hostname */
+{
+  OSStatus             err;            /* Error info */
+  SecIdentitySearchRef search = NULL;  /* Search reference */
+  SecIdentityRef       identity = NULL;/* Identity */
+  CFArrayRef           certificates = NULL;
+                                       /* Certificate array */
+  SecPolicyRef         policy = NULL;  /* Policy ref */
+  CFStringRef          cfcommon_name = NULL;
+                                       /* Server name */
+  CFMutableDictionaryRef query = NULL; /* Query qualifiers */
+  CFArrayRef           list = NULL;    /* Keychain list */
 
-       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);
+  cfcommon_name = CFStringCreateWithCString(kCFAllocatorDefault, common_name, kCFStringEncodingUTF8);
 
-                   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);
-                   }
-                 }
-               }
+  policy = SecPolicyCreateSSL(1, cfcommon_name);
 
-               CFRelease(dn_array);
-             }
+  if (cfcommon_name)
+    CFRelease(cfcommon_name);
 
-             if (!error)
-             {
-               error = (cg->client_cert_cb)(http, http->tls, names,
-                                            cg->client_cert_data);
+  if (!policy)
+    goto cleanup;
 
-               DEBUG_printf(("4http_tls_start: Client certificate callback "
-                             "returned %d.", (int)error));
-             }
+  if (!(query = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)))
+    goto cleanup;
 
-             httpFreeCredentials(names);
-           }
-           break;
+  list = CFArrayCreate(kCFAllocatorDefault, (const void **)&tls_keychain, 1, &kCFTypeArrayCallBacks);
 
-       case errSSLUnknownRootCert :
-           message = _("Unable to establish a secure connection to host "
-                       "(untrusted certificate).");
-           break;
+  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 (err)
+    goto cleanup;
+
+  if (CFGetTypeID(identity) != SecIdentityGetTypeID())
+    goto cleanup;
+
+  if ((certificates = CFArrayCreate(NULL, (const void **)&identity, 1, &kCFTypeArrayCallBacks)) == NULL)
+    goto cleanup;
+
+  cleanup :
+
+  if (search)
+    CFRelease(search);
+  if (identity)
+    CFRelease(identity);
+
+  if (policy)
+    CFRelease(policy);
+  if (query)
+    CFRelease(query);
+
+  return (certificates);
+}
+#endif /* HAVE_SECKEYCHAINOPEN */
+
+
+/*
+ * 'http_cdsa_read()' - Read function for the CDSA library.
+ */
+
+static OSStatus                                /* O  - -1 on error, 0 on success */
+http_cdsa_read(
+    SSLConnectionRef connection,       /* I  - SSL/TLS connection */
+    void             *data,            /* I  - Data buffer */
+    size_t           *dataLength)      /* IO - Number of bytes */
+{
+  OSStatus     result;                 /* Return value */
+  ssize_t      bytes;                  /* Number of bytes read */
+  http_t       *http;                  /* HTTP connection */
+
+
+  http = (http_t *)connection;
+
+  if (!http->blocking)
+  {
+   /*
+    * Make sure we have data before we read...
+    */
+
+    while (!_httpWait(http, http->wait_value, 0))
+    {
+      if (http->timeout_cb && (*http->timeout_cb)(http, http->timeout_data))
+       continue;
+
+      http->error = ETIMEDOUT;
+      return (-1);
+    }
+  }
+
+  do
+  {
+    bytes = recv(http->fd, data, *dataLength, 0);
+  }
+  while (bytes == -1 && (errno == EINTR || errno == EAGAIN));
+
+  if ((size_t)bytes == *dataLength)
+  {
+    result = 0;
+  }
+  else if (bytes > 0)
+  {
+    *dataLength = (size_t)bytes;
+    result = errSSLWouldBlock;
+  }
+  else
+  {
+    *dataLength = 0;
+
+    if (bytes == 0)
+      result = errSSLClosedGraceful;
+    else if (errno == EAGAIN)
+      result = errSSLWouldBlock;
+    else
+      result = errSSLClosedAbort;
+  }
 
-       case errSSLNoRootCert :
-           message = _("Unable to establish a secure connection to host "
-                       "(self-signed certificate).");
-           break;
+  return (result);
+}
 
-       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;
+/*
+ * 'http_cdsa_write()' - Write function for the CDSA library.
+ */
 
-       case errSSLHostNameMismatch :
-           message = _("Unable to establish a secure connection to host "
-                       "(host name mismatch).");
-           break;
+static OSStatus                                /* O  - -1 on error, 0 on success */
+http_cdsa_write(
+    SSLConnectionRef connection,       /* I  - SSL/TLS connection */
+    const void       *data,            /* I  - Data buffer */
+    size_t           *dataLength)      /* IO - Number of bytes */
+{
+  OSStatus     result;                 /* Return value */
+  ssize_t      bytes;                  /* Number of bytes read */
+  http_t       *http;                  /* HTTP connection */
 
-       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;
+  http = (http_t *)connection;
 
-       default :
-           break;
-      }
-    }
+  do
+  {
+    bytes = write(http->fd, data, *dataLength);
   }
+  while (bytes == -1 && (errno == EINTR || errno == EAGAIN));
 
-  if (error)
+  if ((size_t)bytes == *dataLength)
   {
-    http->error  = error;
-    http->status = HTTP_STATUS_ERROR;
-    errno        = ECONNREFUSED;
-
-    CFRelease(http->tls);
-    http->tls = NULL;
+    result = 0;
+  }
+  else if (bytes >= 0)
+  {
+    *dataLength = (size_t)bytes;
+    result = errSSLWouldBlock;
+  }
+  else
+  {
+    *dataLength = 0;
 
-   /*
-    * If an error string wasn't set by the callbacks use a generic one...
-    */
+    if (errno == EAGAIN)
+      result = errSSLWouldBlock;
+    else
+      result = errSSLClosedAbort;
+  }
 
-    if (!message)
-#ifdef HAVE_CSSMERRORSTRING
-      message = cssmErrorString(error);
-#else
-      message = _("Unable to establish a secure connection to host.");
-#endif /* HAVE_CSSMERRORSTRING */
+  return (result);
+}
 
-    _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI, message, 1);
 
-    return (-1);
-  }
+/*
+ * 'http_tls_initialize()' - Initialize the TLS stack.
+ */
 
-  return (0);
+static void
+http_tls_initialize(void)
+{
+ /*
+  * Nothing to do...
+  */
 }
 
 
 /*
- * 'http_tls_stop()' - Shut down SSL/TLS on a connection.
+ * 'http_tls_pending()' - Return the number of pending TLS-encrypted bytes.
  */
 
-static void
-http_tls_stop(http_t *http)            /* I - Connection to server */
+static size_t
+http_tls_pending(http_t *http)         /* I - HTTP connection */
 {
-  while (SSLClose(http->tls) == errSSLWouldBlock)
-    usleep(1000);
+  size_t bytes;                                /* Bytes that are available */
 
-  CFRelease(http->tls);
 
-  if (http->tls_credentials)
-    CFRelease(http->tls_credentials);
+  if (!SSLGetBufferedReadSize(http->tls, &bytes))
+    return (bytes);
 
-  http->tls             = NULL;
-  http->tls_credentials = NULL;
+  return (0);
 }
 
 
 /*
- * 'http_tls_write()' - Write to a SSL/TLS connection.
+ * 'http_tls_read()' - Read from a SSL/TLS connection.
  */
 
-static int                             /* O - Bytes written */
-http_tls_write(http_t     *http,       /* I - Connection to server */
-              const char *buf,         /* I - Buffer holding data */
-              int        len)          /* I - Length of buffer */
+static int                             /* O - Bytes read */
+http_tls_read(http_t *http,            /* I - HTTP connection */
+             char   *buf,              /* I - Buffer to store data */
+             int    len)               /* I - Length of buffer */
 {
-  ssize_t      result;                 /* Return value */
+  int          result;                 /* Return value */
   OSStatus     error;                  /* Error info */
   size_t       processed;              /* Number of bytes processed */
 
 
-  DEBUG_printf(("2http_tls_write(http=%p, buf=%p, len=%d)", http, buf, len));
-
-  error = SSLWrite(http->tls, buf, len, &processed);
-
+  error = SSLRead(http->tls, buf, (size_t)len, &processed);
+  DEBUG_printf(("6http_tls_read: error=%d, processed=%d", (int)error,
+                (int)processed));
   switch (error)
   {
     case 0 :
@@ -666,9 +1036,7 @@ http_tls_write(http_t     *http,   /* I - Connection to server */
 
     case errSSLWouldBlock :
        if (processed)
-       {
          result = (int)processed;
-       }
        else
        {
          result = -1;
@@ -679,9 +1047,7 @@ http_tls_write(http_t     *http,   /* I - Connection to server */
     case errSSLClosedGraceful :
     default :
        if (processed)
-       {
          result = (int)processed;
-       }
        else
        {
          result = -1;
@@ -690,543 +1056,583 @@ http_tls_write(http_t     *http,       /* I - Connection to server */
        break;
   }
 
-  DEBUG_printf(("3http_tls_write: Returning %d.", (int)result));
-
-  return ((int)result);
+  return (result);
 }
 
 
-#if 0
 /*
- * 'cupsdEndTLS()' - Shutdown a secure session with the client.
+ * 'http_tls_set_credentials()' - Set the TLS credentials.
  */
 
-int                                    /* O - 1 on success, 0 on error */
-cupsdEndTLS(cupsd_client_t *con)       /* I - Client connection */
+static int                             /* O - Status of connection */
+http_tls_set_credentials(http_t *http) /* I - HTTP connection */
 {
-  while (SSLClose(con->http.tls) == errSSLWouldBlock)
-    usleep(1000);
+  _cups_globals_t *cg = _cupsGlobals();        /* Pointer to library globals */
+  OSStatus             error = 0;      /* Error code */
+  http_tls_credentials_t credentials = NULL;
+                                       /* TLS credentials */
 
-  CFRelease(con->http.tls);
-  con->http.tls = NULL;
 
-  if (con->http.tls_credentials)
-    CFRelease(con->http.tls_credentials);
+  DEBUG_printf(("7http_tls_set_credentials(%p)", http));
 
-  return (1);
+ /*
+  * Prefer connection specific credentials...
+  */
+
+  if ((credentials = http->tls_credentials) == NULL)
+    credentials = cg->tls_credentials;
+
+  if (credentials)
+  {
+    error = SSLSetCertificate(http->tls, credentials);
+    DEBUG_printf(("4http_tls_set_credentials: SSLSetCertificate, error=%d",
+                 (int)error));
+  }
+  else
+    DEBUG_puts("4http_tls_set_credentials: No credentials to set.");
+
+  return (error);
 }
 
 
 /*
- * 'cupsdStartTLS()' - Start a secure session with the client.
+ * 'http_tls_start()' - Set up SSL/TLS support on a connection.
  */
 
-int                                    /* O - 1 on success, 0 on error */
-cupsdStartTLS(cupsd_client_t *con)     /* I - Client connection */
+static int                             /* O - 0 on success, -1 on failure */
+http_tls_start(http_t *http)           /* I - HTTP connection */
 {
-  OSStatus     error = 0;              /* Error code */
-  SecTrustRef  peerTrust;              /* Peer certificates */
+  char                 hostname[256],  /* Hostname */
+                       *hostptr;       /* Pointer into hostname */
+  _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 */
 
 
-  cupsdLogMessage(CUPSD_LOG_DEBUG, "[Client %d] Encrypting connection.",
-                  con->http.fd);
+  DEBUG_printf(("7http_tls_start(http=%p)", http));
 
-  con->http.tls_credentials = copy_cdsa_certificate(con);
+#ifdef HAVE_SECKEYCHAINOPEN
+  if (http->mode == _HTTP_MODE_SERVER && !tls_keychain)
+#else
+  if (http->mode == _HTTP_MODE_SERVER)
+#endif /* HAVE_SECKEYCHAINOPEN */
+  {
+    DEBUG_puts("4http_tls_start: cupsSetServerCredentials not called.");
+    http->error  = errno = EINVAL;
+    http->status = HTTP_STATUS_ERROR;
+    _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Server credentials not set."), 1);
 
-  if (!con->http.tls_credentials)
+    return (-1);
+  }
+
+  if ((http->tls = SSLCreateContext(kCFAllocatorDefault, http->mode == _HTTP_MODE_CLIENT ? kSSLClientSide : kSSLServerSide, kSSLStreamType)) == NULL)
+  {
+    DEBUG_puts("4http_tls_start: SSLCreateContext failed.");
+    http->error  = errno = ENOMEM;
+    http->status = HTTP_STATUS_ERROR;
+    _cupsSetHTTPError(HTTP_STATUS_ERROR);
+
+    return (-1);
+  }
+
+  error = SSLSetConnection(http->tls, http);
+  DEBUG_printf(("4http_tls_start: SSLSetConnection, error=%d", (int)error));
+
+  if (!error)
+  {
+    error = SSLSetIOFuncs(http->tls, http_cdsa_read, http_cdsa_write);
+    DEBUG_printf(("4http_tls_start: SSLSetIOFuncs, error=%d", (int)error));
+  }
+
+  if (!error)
+  {
+    error = SSLSetSessionOption(http->tls, kSSLSessionOptionBreakOnServerAuth,
+                                true);
+    DEBUG_printf(("4http_tls_start: SSLSetSessionOption, error=%d",
+                  (int)error));
+  }
+
+  if (!error && http->mode == _HTTP_MODE_CLIENT)
   {
    /*
-    * No keychain (yet), make a self-signed certificate...
+    * Client: set client-side credentials, if any...
     */
 
-    if (make_certificate(con))
-      con->http.tls_credentials = copy_cdsa_certificate(con);
+    if (cg->client_cert_cb)
+    {
+      error = SSLSetSessionOption(http->tls,
+                                 kSSLSessionOptionBreakOnCertRequested, true);
+      DEBUG_printf(("4http_tls_start: kSSLSessionOptionBreakOnCertRequested, "
+                    "error=%d", (int)error));
+    }
+    else
+    {
+      error = http_tls_set_credentials(http);
+      DEBUG_printf(("4http_tls_start: http_tls_set_credentials, error=%d",
+                    (int)error));
+    }
   }
+  else if (!error)
+  {
+   /*
+    * Server: find/create a certificate for TLS...
+    */
+
+    if (http->fields[HTTP_FIELD_HOST][0])
+    {
+     /*
+      * 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(("4http_tls_start: Unable to get socket address: %s", strerror(errno)));
+       hostname[0] = '\0';
+      }
+      else if (httpAddrLocalhost(&addr))
+       hostname[0] = '\0';
+      else
+       httpAddrString(&addr, hostname, sizeof(hostname));
+    }
+
+#ifdef HAVE_SECKEYCHAINOPEN
+    if (hostname[0])
+      http->tls_credentials = http_cdsa_copy_server(hostname);
+    else if (tls_common_name)
+      http->tls_credentials = http_cdsa_copy_server(tls_common_name);
+
+    if (!http->tls_credentials && tls_auto_create && (hostname[0] || tls_common_name))
+    {
+      DEBUG_printf(("4http_tls_start: Auto-create credentials for \"%s\".", hostname[0] ? hostname : tls_common_name));
 
-  if (!con->http.tls_credentials)
-  {
-    cupsdLogMessage(CUPSD_LOG_ERROR,
-                   "Could not find signing key in keychain \"%s\"",
-                   ServerCertificate);
-    error = errSSLBadConfiguration;
-  }
+      if (!cupsMakeServerCredentials(tls_keypath, hostname[0] ? hostname : tls_common_name, 0, NULL, time(NULL) + 365 * 86400))
+      {
+       DEBUG_puts("4http_tls_start: cupsMakeServerCredentials failed.");
+       http->error  = errno = EINVAL;
+       http->status = HTTP_STATUS_ERROR;
+       _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to create server credentials."), 1);
 
-  if (!error)
-    con->http.tls = SSLCreateContext(kCFAllocatorDefault, kSSLServerSide,
-                                     kSSLStreamType);
+       return (-1);
+      }
 
-  if (!error)
-    error = SSLSetIOFuncs(con->http.tls, http_cdsa_read, http_cdsa_write);
+      http->tls_credentials = http_cdsa_copy_server(hostname[0] ? hostname : tls_common_name);
+    }
+#endif /* HAVE_SECKEYCHAINOPEN */
 
-  if (!error)
-    error = SSLSetConnection(con->http.tls, HTTP(con));
+    if (!http->tls_credentials)
+    {
+      DEBUG_puts("4http_tls_start: Unable to find server credentials.");
+      http->error  = errno = EINVAL;
+      http->status = HTTP_STATUS_ERROR;
+      _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to find server credentials."), 1);
 
-  if (!error)
-    error = SSLSetCertificate(con->http.tls, con->http.tls_credentials);
+      return (-1);
+    }
 
-  if (!error)
-  {
-   /*
-    * Perform SSL/TLS handshake
-    */
+    error = SSLSetCertificate(http->tls, http->tls_credentials);
 
-    while ((error = SSLHandshake(con->http.tls)) == errSSLWouldBlock)
-      usleep(1000);
+    DEBUG_printf(("4http_tls_start: SSLSetCertificate, error=%d", (int)error));
   }
 
-  if (error)
-  {
-    cupsdLogMessage(CUPSD_LOG_ERROR,
-                    "Unable to encrypt connection from %s - %s (%d)",
-                    con->http.hostname, cssmErrorString(error), (int)error);
+  DEBUG_printf(("4http_tls_start: tls_credentials=%p", http->tls_credentials));
 
-    con->http.error  = error;
-    con->http.status = HTTP_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 (con->http.tls)
+  if (!error && http->mode == _HTTP_MODE_CLIENT)
+  {
+   /*
+    * Client: get the hostname to use for TLS...
+    */
+
+    if (httpAddrLocalhost(http->hostaddr))
     {
-      CFRelease(con->http.tls);
-      con->http.tls = NULL;
+      strlcpy(hostname, "localhost", sizeof(hostname));
     }
-
-    if (con->http.tls_credentials)
+    else
     {
-      CFRelease(con->http.tls_credentials);
-      con->http.tls_credentials = NULL;
+     /*
+      * 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';
     }
 
-    return (0);
-  }
+    error = SSLSetPeerDomainName(http->tls, hostname, strlen(hostname));
 
-  cupsdLogMessage(CUPSD_LOG_DEBUG, "Connection from %s now encrypted.",
-                  con->http.hostname);
+    DEBUG_printf(("4http_tls_start: SSLSetPeerDomainName, error=%d", (int)error));
+  }
 
-  if (!SSLCopyPeerTrust(con->http.tls, &peerTrust) && peerTrust)
+  if (!error)
   {
-    cupsdLogMessage(CUPSD_LOG_DEBUG, "Received %d peer certificates.",
-                   (int)SecTrustGetCertificateCount(peerTrust));
-    CFRelease(peerTrust);
-  }
-  else
-    cupsdLogMessage(CUPSD_LOG_DEBUG, "Received NO peer certificates.");
+    int done = 0;                      /* Are we done yet? */
 
-  return (1);
-}
+    while (!error && !done)
+    {
+      error = SSLHandshake(http->tls);
 
+      DEBUG_printf(("4http_tls_start: SSLHandshake returned %d.", (int)error));
 
-/*
- * 'copy_cdsa_certificate()' - Copy a SSL/TLS certificate from the System
- *                             keychain.
- */
+      switch (error)
+      {
+       case noErr :
+           done = 1;
+           break;
 
-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 */
+       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);
+             }
 
-  cupsdLogMessage(CUPSD_LOG_DEBUG,
-                  "copy_cdsa_certificate: Looking for certs for \"%s\".",
-                 con->servername);
+             DEBUG_printf(("4http_tls_start: Server certificate callback "
+                           "returned %d.", (int)error));
+           }
+           break;
 
-  if ((err = SecKeychainOpen(ServerCertificate, &keychain)))
-  {
-    cupsdLogMessage(CUPSD_LOG_ERROR, "Cannot open keychain \"%s\" - %s (%d)",
-                   ServerCertificate, cssmErrorString(err), (int)err);
-    goto cleanup;
-  }
+       case errSSLClientCertRequested :
+           error = 0;
 
-  servername = CFStringCreateWithCString(kCFAllocatorDefault, con->servername,
-                                        kCFStringEncodingUTF8);
+           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);
 
-  policy = SecPolicyCreateSSL(1, servername);
+                   if ((credential = malloc(sizeof(*credential))) != NULL)
+                   {
+                     credential->datalen = (size_t)CFDataGetLength(data);
+                     if ((credential->data = malloc(credential->datalen)))
+                     {
+                       memcpy((void *)credential->data, CFDataGetBytePtr(data),
+                              credential->datalen);
+                       cupsArrayAdd(names, credential);
+                     }
+                     else
+                       free(credential);
+                   }
+                 }
+               }
 
-  if (servername)
-    CFRelease(servername);
+               CFRelease(dn_array);
+             }
 
-  if (!policy)
-  {
-    cupsdLogMessage(CUPSD_LOG_ERROR, "Cannot create ssl policy reference");
-    goto cleanup;
-  }
+             if (!error)
+             {
+               error = (cg->client_cert_cb)(http, http->tls, names,
+                                            cg->client_cert_data);
 
-  if (!(query = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
-                                         &kCFTypeDictionaryKeyCallBacks,
-                                         &kCFTypeDictionaryValueCallBacks)))
-  {
-    cupsdLogMessage(CUPSD_LOG_ERROR, "Cannot create query dictionary");
-    goto cleanup;
-  }
+               DEBUG_printf(("4http_tls_start: Client certificate callback "
+                             "returned %d.", (int)error));
+             }
 
-  list = CFArrayCreate(kCFAllocatorDefault, (const void **)&keychain, 1,
-                       &kCFTypeArrayCallBacks);
+             httpFreeCredentials(names);
+           }
+           break;
 
-  CFDictionaryAddValue(query, kSecClass, kSecClassIdentity);
-  CFDictionaryAddValue(query, kSecMatchPolicy, policy);
-  CFDictionaryAddValue(query, kSecReturnRef, kCFBooleanTrue);
-  CFDictionaryAddValue(query, kSecMatchLimit, kSecMatchLimitOne);
-  CFDictionaryAddValue(query, kSecMatchSearchList, list);
+       case errSSLUnknownRootCert :
+           message = _("Unable to establish a secure connection to host "
+                       "(untrusted certificate).");
+           break;
 
-  CFRelease(list);
+       case errSSLNoRootCert :
+           message = _("Unable to establish a secure connection to host "
+                       "(self-signed certificate).");
+           break;
 
-  err = SecItemCopyMatching(query, (CFTypeRef *)&identity);
+       case errSSLCertExpired :
+           message = _("Unable to establish a secure connection to host "
+                       "(expired certificate).");
+           break;
 
-#    if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
-  if (err && DNSSDHostName)
-  {
-   /*
-    * Search for the connection server name failed; try the DNS-SD .local
-    * hostname instead...
-    */
+       case errSSLCertNotYetValid :
+           message = _("Unable to establish a secure connection to host "
+                       "(certificate not yet valid).");
+           break;
 
-    snprintf(localname, sizeof(localname), "%s.local", DNSSDHostName);
+       case errSSLHostNameMismatch :
+           message = _("Unable to establish a secure connection to host "
+                       "(host name mismatch).");
+           break;
 
-    cupsdLogMessage(CUPSD_LOG_DEBUG,
-                   "copy_cdsa_certificate: Looking for certs for \"%s\".",
-                   localname);
+       case errSSLXCertChainInvalid :
+           message = _("Unable to establish a secure connection to host "
+                       "(certificate chain invalid).");
+           break;
 
-    servername = CFStringCreateWithCString(kCFAllocatorDefault, localname,
-                                          kCFStringEncodingUTF8);
+       case errSSLConnectionRefused :
+           message = _("Unable to establish a secure connection to host "
+                       "(peer dropped connection before responding).");
+           break;
 
-    CFRelease(policy);
+       default :
+           break;
+      }
+    }
+  }
 
-    policy = SecPolicyCreateSSL(1, servername);
+  if (error)
+  {
+    http->error  = error;
+    http->status = HTTP_STATUS_ERROR;
+    errno        = ECONNREFUSED;
 
-    if (servername)
-      CFRelease(servername);
+    CFRelease(http->tls);
+    http->tls = NULL;
 
-    if (!policy)
-    {
-      cupsdLogMessage(CUPSD_LOG_ERROR, "Cannot create ssl policy reference");
-      goto cleanup;
-    }
+   /*
+    * If an error string wasn't set by the callbacks use a generic one...
+    */
 
-    CFDictionarySetValue(query, kSecMatchPolicy, policy);
+    if (!message)
+#ifdef HAVE_CSSMERRORSTRING
+      message = cssmErrorString(error);
+#else
+      message = _("Unable to establish a secure connection to host.");
+#endif /* HAVE_CSSMERRORSTRING */
 
-    err = SecItemCopyMatching(query, (CFTypeRef *)&identity);
-  }
-#    endif /* HAVE_DNSSD || HAVE_AVAHI */
+    _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI, message, 1);
 
-  if (err)
-  {
-    cupsdLogMessage(CUPSD_LOG_DEBUG,
-                   "Cannot find signing key in keychain \"%s\": %s (%d)",
-                   ServerCertificate, cssmErrorString(err), (int)err);
-    goto cleanup;
+    return (-1);
   }
 
-  if (CFGetTypeID(identity) != SecIdentityGetTypeID())
-  {
-    cupsdLogMessage(CUPSD_LOG_ERROR, "SecIdentity CFTypeID failure.");
-    goto cleanup;
-  }
+  return (0);
+}
 
-  if ((certificates = CFArrayCreate(NULL, (const void **)&identity,
-                                 1, &kCFTypeArrayCallBacks)) == NULL)
-  {
-    cupsdLogMessage(CUPSD_LOG_ERROR, "Cannot create certificate array");
-    goto cleanup;
-  }
 
-  cleanup :
+/*
+ * 'http_tls_stop()' - Shut down SSL/TLS on a connection.
+ */
 
-  if (keychain)
-    CFRelease(keychain);
-  if (search)
-    CFRelease(search);
-  if (identity)
-    CFRelease(identity);
+static void
+http_tls_stop(http_t *http)            /* I - HTTP connection */
+{
+  while (SSLClose(http->tls) == errSSLWouldBlock)
+    usleep(1000);
 
-  if (policy)
-    CFRelease(policy);
-  if (query)
-    CFRelease(query);
+  CFRelease(http->tls);
 
-  return (certificates);
+  if (http->tls_credentials)
+    CFRelease(http->tls_credentials);
+
+  http->tls             = NULL;
+  http->tls_credentials = NULL;
 }
 
 
 /*
- * 'make_certificate()' - Make a self-signed SSL/TLS certificate.
+ * 'http_tls_write()' - Write to a SSL/TLS connection.
  */
 
-static int                             /* O - 1 on success, 0 on failure */
-make_certificate(cupsd_client_t *con)  /* I - Client connection */
+static int                             /* O - Bytes written */
+http_tls_write(http_t     *http,       /* I - HTTP connection */
+              const char *buf,         /* I - Buffer holding data */
+              int        len)          /* I - Length of buffer */
 {
-#    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;
+  ssize_t      result;                 /* Return value */
+  OSStatus     error;                  /* Error info */
+  size_t       processed;              /* Number of bytes processed */
 
- /*
-  * Create a public/private key pair...
-  */
 
-  keyParams = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
-                                       &kCFTypeDictionaryKeyCallBacks,
-                                       &kCFTypeDictionaryValueCallBacks);
-  if (!keyParams)
-    goto cleanup;
+  DEBUG_printf(("2http_tls_write(http=%p, buf=%p, len=%d)", http, buf, len));
 
-  CFDictionaryAddValue(keyParams, kSecAttrKeyType, kSecAttrKeyTypeRSA);
-  CFDictionaryAddValue(keyParams, kSecAttrKeySizeInBits, CFSTR("2048"));
-  CFDictionaryAddValue(keyParams, kSecAttrLabel,
-                       CFSTR("CUPS Self-Signed Certificate"));
+  error = SSLWrite(http->tls, buf, (size_t)len, &processed);
 
-  err = SecKeyGeneratePair(keyParams, &publicKey, &privateKey);
-  if (err != noErr)
+  switch (error)
   {
-    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);
+    case 0 :
+       result = (int)processed;
+       break;
 
-  CFArrayRef   subject = CFArrayCreate(kCFAllocatorDefault, ca_dn_array, 2,
-                                        NULL);
-  SecCertificateRef cert = SecGenerateSelfSignedCertificate(subject, certParams,
-                                                            publicKey,
-                                                            privateKey);
-  CFRelease(subject);
-  CFRelease(certParams);
+    case errSSLWouldBlock :
+       if (processed)
+       {
+         result = (int)processed;
+       }
+       else
+       {
+         result = -1;
+         errno  = EINTR;
+       }
+       break;
 
-  if (!cert)
-  {
-    cupsdLogMessage(CUPSD_LOG_DEBUG, "SecGenerateSelfSignedCertificate failed.");
-    goto cleanup;
+    case errSSLClosedGraceful :
+    default :
+       if (processed)
+       {
+         result = (int)processed;
+       }
+       else
+       {
+         result = -1;
+         errno  = EPIPE;
+       }
+       break;
   }
 
-  ident = SecIdentityCreate(kCFAllocatorDefault, cert, privateKey);
+  DEBUG_printf(("3http_tls_write: Returning %d.", (int)result));
 
-  if (ident)
-    cupsdLogMessage(CUPSD_LOG_INFO,
-                    "Created SSL server certificate file \"%s\".",
-                   ServerCertificate);
+  return ((int)result);
+}
 
- /*
-  * Cleanup and return...
-  */
 
-cleanup:
+#if 0
+/*
+ * 'cupsdEndTLS()' - Shutdown a secure session with the client.
+ */
 
-  if (cfservername)
-    CFRelease(cfservername);
+int                                    /* O - 1 on success, 0 on error */
+cupsdEndTLS(cupsd_client_t *con)       /* I - Client connection */
+{
+  while (SSLClose(con->http.tls) == errSSLWouldBlock)
+    usleep(1000);
 
-  if (keyParams)
-    CFRelease(keyParams);
+  CFRelease(con->http.tls);
+  con->http.tls = NULL;
 
-  if (ident)
-    CFRelease(ident);
+  if (con->http.tls_credentials)
+    CFRelease(con->http.tls_credentials);
 
-  if (cert)
-    CFRelease(cert);
+  return (1);
+}
 
-  if (publicKey)
-    CFRelease(publicKey);
 
-  if (privateKey)
-    CFRelease(publicKey);
+/*
+ * 'cupsdStartTLS()' - Start a secure session with the client.
+ */
 
-  if (!status)
-    cupsdLogMessage(CUPSD_LOG_ERROR,
-                    "Unable to create SSL server key and certificate.");
+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 */
 
-  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 */
+  cupsdLogMessage(CUPSD_LOG_DEBUG, "[Client %d] Encrypting connection.",
+                  con->http.fd);
 
+  con->http.tls_credentials = copy_cdsa_certificate(con);
 
-#      if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
-  if (con->servername && isdigit(con->servername[0] & 255) && DNSSDHostName)
+  if (!con->http.tls_credentials)
   {
-    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...
-  */
+   /*
+    * No keychain (yet), make 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);
+    if (make_certificate(con))
+      con->http.tls_credentials = copy_cdsa_certificate(con);
   }
 
- /*
-  * 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)
+  if (!con->http.tls_credentials)
   {
     cupsdLogMessage(CUPSD_LOG_ERROR,
-                    "Unable to create certificate information file %s - %s",
-                    infofile, strerror(errno));
-    return (0);
+                   "Could not find signing key in keychain \"%s\"",
+                   ServerCertificate);
+    error = errSSLBadConfiguration;
   }
 
-  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);
+  if (!error)
+    con->http.tls = SSLCreateContext(kCFAllocatorDefault, kSSLServerSide,
+                                     kSSLStreamType);
 
-  cupsdLogMessage(CUPSD_LOG_INFO,
-                  "Generating SSL server key and certificate.");
+  if (!error)
+    error = SSLSetIOFuncs(con->http.tls, http_cdsa_read, http_cdsa_write);
 
-  snprintf(keychain, sizeof(keychain), "k=%s", ServerCertificate);
+  if (!error)
+    error = SSLSetConnection(con->http.tls, HTTP(con));
 
-  argv[0] = "certtool";
-  argv[1] = "c";
-  argv[2] = keychain;
-  argv[3] = NULL;
+  if (!error)
+    error = SSLSetCertificate(con->http.tls, con->http.tls_credentials);
 
-  cupsdLoadEnv(envp, MAX_ENV);
+  if (!error)
+  {
+   /*
+    * Perform SSL/TLS handshake
+    */
 
-  infofd = open(infofile, O_RDONLY);
+    while ((error = SSLHandshake(con->http.tls)) == errSSLWouldBlock)
+      usleep(1000);
+  }
 
-  if (!cupsdStartProcess(command, argv, envp, infofd, -1, -1, -1, -1, 1, NULL,
-                         NULL, &pid))
+  if (error)
   {
-    close(infofd);
-    unlink(infofile);
-    return (0);
-  }
+    cupsdLogMessage(CUPSD_LOG_ERROR,
+                    "Unable to encrypt connection from %s - %s (%d)",
+                    con->http.hostname, cssmErrorString(error), (int)error);
 
-  close(infofd);
-  unlink(infofile);
+    con->http.error  = error;
+    con->http.status = HTTP_ERROR;
 
-  while (waitpid(pid, &status, 0) < 0)
-    if (errno != EINTR)
+    if (con->http.tls)
     {
-      status = 1;
-      break;
+      CFRelease(con->http.tls);
+      con->http.tls = NULL;
     }
 
-  cupsdFinishProcess(pid, command, sizeof(command), NULL);
+    if (con->http.tls_credentials)
+    {
+      CFRelease(con->http.tls_credentials);
+      con->http.tls_credentials = 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));
+    return (0);
   }
-  else
+
+  cupsdLogMessage(CUPSD_LOG_DEBUG, "Connection from %s now encrypted.",
+                  con->http.hostname);
+
+  if (!SSLCopyPeerTrust(con->http.tls, &peerTrust) && peerTrust)
   {
-    cupsdLogMessage(CUPSD_LOG_INFO,
-                    "Created SSL server certificate file \"%s\".",
-                   ServerCertificate);
+    cupsdLogMessage(CUPSD_LOG_DEBUG, "Received %d peer certificates.",
+                   (int)SecTrustGetCertificateCount(peerTrust));
+    CFRelease(peerTrust);
   }
+  else
+    cupsdLogMessage(CUPSD_LOG_DEBUG, "Received NO peer certificates.");
 
-  return (!status);
-#    endif /* HAVE_SECGENERATESELFSIGNEDCERTIFICATE */
+  return (1);
 }
 #endif /* 0 */