]> git.ipfire.org Git - thirdparty/cups.git/commitdiff
Save work to move all of the TLS support code into libcups (this does not compile)
authormsweet <msweet@a1ca3aef-8c08-0410-bb20-df032aa958be>
Thu, 17 Oct 2013 15:53:07 +0000 (15:53 +0000)
committermsweet <msweet@a1ca3aef-8c08-0410-bb20-df032aa958be>
Thu, 17 Oct 2013 15:53:07 +0000 (15:53 +0000)
git-svn-id: svn+ssh://src.apple.com/svn/cups/cups.org/trunk@11338 a1ca3aef-8c08-0410-bb20-df032aa958be

cups/http-private.h
cups/http.c
cups/http.h
cups/tls-darwin.c [new file with mode: 0644]
cups/tls-gnutls.c [new file with mode: 0644]
cups/tls-openssl.c [new file with mode: 0644]
cups/tls-sspi.c [moved from cups/sspi.c with 69% similarity]
xcode/CUPS.xcodeproj/project.pbxproj

index d768c84affa27bf69835bfdd8bbb8246e5b34c05..1eaf09dddf3140bc4e49d8f5ae4854993000790e 100644 (file)
@@ -1,18 +1,18 @@
 /*
  * "$Id$"
  *
- *   Private HTTP definitions for CUPS.
+ * Private HTTP definitions for CUPS.
  *
- *   Copyright 2007-2013 by Apple Inc.
- *   Copyright 1997-2007 by Easy Software Products, all rights reserved.
+ * Copyright 2007-2013 by Apple Inc.
+ * Copyright 1997-2007 by Easy Software Products, all rights reserved.
  *
- *   These coded instructions, statements, and computer programs are the
- *   property of Apple Inc. and are protected by Federal copyright
- *   law.  Distribution and use rights are outlined in the file "LICENSE.txt"
- *   which should have been included with this file.  If this file is
- *   file is missing or damaged, see the license at "http://www.cups.org/".
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law.  Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file.  If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
  *
- *   This file is subject to the Apple OS-Developed Software exception.
+ * This file is subject to the Apple OS-Developed Software exception.
  */
 
 #ifndef _CUPS_HTTP_PRIVATE_H_
@@ -205,11 +205,6 @@ extern OSStatus SecPolicySetValue(SecPolicyRef policyRef,
 typedef SSLContextRef  http_tls_t;
 typedef CFArrayRef     http_tls_credentials_t;
 
-extern OSStatus        _httpReadCDSA(SSLConnectionRef connection, void *data,
-                             size_t *dataLength);
-extern OSStatus        _httpWriteCDSA(SSLConnectionRef connection, const void *data,
-                              size_t *dataLength);
-
 #  elif defined(HAVE_SSPISSL)
 /*
  * Windows' SSPI library gets a CUPS wrapper...
index 3db98fca2199eedf0beec728d55ea0223db9a8b5..752706978f6ce3da2f2f5a2bc26efdf70761cda9 100644 (file)
@@ -63,18 +63,27 @@ static ssize_t              http_write(http_t *http, const char *buffer,
                                   size_t length);
 static ssize_t         http_write_chunk(http_t *http, const char *buffer,
                                         size_t length);
-#ifdef HAVE_SSL
-static int             http_read_ssl(http_t *http, char *buf, int len);
-static int             http_set_credentials(http_t *http);
-#endif /* HAVE_SSL */
 static off_t           http_set_length(http_t *http);
 static void            http_set_timeout(int fd, double timeout);
 static void            http_set_wait(http_t *http);
+
 #ifdef HAVE_SSL
-static int             http_setup_ssl(http_t *http);
-static void            http_shutdown_ssl(http_t *http);
-static int             http_upgrade(http_t *http);
-static int             http_write_ssl(http_t *http, const char *buf, int len);
+static size_t          http_tls_pending(http_t *http);
+static int             http_tls_read(http_t *http, char *buf, int len);
+static int             http_tls_set_credentials(http_t *http);
+static int             http_tls_start(http_t *http);
+static void            http_tls_stop(http_t *http);
+static int             http_tls_upgrade(http_t *http);
+static int             http_tls_write(http_t *http, const char *buf, int len);
+#  ifdef HAVE_LIBSSL
+#    include "tls-openssl.c"
+#  elif defined(HAVE_GNUTLS)
+#    include "tls-gnutls.c"
+#  elif defined(HAVE_CDSASSL)
+#    include "tls-darwin.c"
+#  else
+#    include "tls-sspi.c"
+#  endif /* HAVE_LIBSSL */
 #endif /* HAVE_SSL */
 
 
@@ -117,34 +126,6 @@ static const char * const http_fields[] =
                        };
 
 
-#if defined(HAVE_SSL) && defined(HAVE_LIBSSL)
-/*
- * BIO methods for OpenSSL...
- */
-
-static int             http_bio_write(BIO *h, const char *buf, int num);
-static int             http_bio_read(BIO *h, char *buf, int size);
-static int             http_bio_puts(BIO *h, const char *str);
-static long            http_bio_ctrl(BIO *h, int cmd, long arg1, void *arg2);
-static int             http_bio_new(BIO *h);
-static int             http_bio_free(BIO *data);
-
-static BIO_METHOD      http_bio_methods =
-                       {
-                         BIO_TYPE_SOCKET,
-                         "http",
-                         http_bio_write,
-                         http_bio_read,
-                         http_bio_puts,
-                         NULL, /* http_bio_gets, */
-                         http_bio_ctrl,
-                         http_bio_new,
-                         http_bio_free,
-                         NULL,
-                       };
-#endif /* HAVE_SSL && HAVE_LIBSSL */
-
-
 /*
  * 'httpAcceptConnection()' - Accept a new HTTP client connection from the
  *                            specified listening socket.
@@ -269,19 +250,6 @@ httpAddCredential(
 }
 
 
-#if defined(HAVE_SSL) && defined(HAVE_LIBSSL)
-/*
- * '_httpBIOMethods()' - Get the OpenSSL BIO methods for HTTP connections.
- */
-
-BIO_METHOD *                           /* O - BIO methods for OpenSSL */
-_httpBIOMethods(void)
-{
-  return (&http_bio_methods);
-}
-#endif /* HAVE_SSL && HAVE_LIBSSL */
-
-
 /*
  * 'httpBlocking()' - Set blocking/non-blocking behavior on a connection.
  */
@@ -449,8 +417,8 @@ http_t *                            /* O - New HTTP connection */
 httpConnect(const char *host,          /* I - Host to connect to */
             int        port)           /* I - Port number */
 {
-  return (httpConnect2(host, port, NULL, AF_UNSPEC, HTTP_ENCRYPTION_IF_REQUESTED,
-                       1, 30000, NULL));
+  return (httpConnect2(host, port, NULL, AF_UNSPEC,
+                       HTTP_ENCRYPTION_IF_REQUESTED, 1, 30000, NULL));
 }
 
 
@@ -526,134 +494,6 @@ httpConnectEncrypt(
 }
 
 
-/*
- * 'httpCopyCredentials()' - Copy the credentials associated with an encrypted
- *                          connection.
- *
- * @since CUPS 1.5/OS X 10.7@
- */
-
-int                                    /* O - Status of call (0 = success) */
-httpCopyCredentials(
-    http_t      *http,                 /* I - HTTP connection */
-    cups_array_t **credentials)                /* O - Array of credentials */
-{
-#  ifdef HAVE_LIBSSL
-#  elif defined(HAVE_GNUTLS)
-#  elif defined(HAVE_CDSASSL)
-  OSStatus             error;          /* Error code */
-  SecTrustRef          peerTrust;      /* Peer trust reference */
-  CFIndex              count;          /* Number of credentials */
-  SecCertificateRef    secCert;        /* Certificate reference */
-  CFDataRef            data;           /* Certificate data */
-  int                  i;              /* Looping var */
-#  elif defined(HAVE_SSPISSL)
-#  endif /* HAVE_LIBSSL */
-
-
-  if (credentials)
-    *credentials = NULL;
-
-  if (!http || !http->tls || !credentials)
-    return (-1);
-
-#  ifdef HAVE_LIBSSL
-  return (-1);
-
-#  elif defined(HAVE_GNUTLS)
-  return (-1);
-
-#  elif defined(HAVE_CDSASSL)
-  if (!(error = SSLCopyPeerTrust(http->tls, &peerTrust)) && peerTrust)
-  {
-    if ((*credentials = cupsArrayNew(NULL, NULL)) != NULL)
-    {
-      count = SecTrustGetCertificateCount(peerTrust);
-
-      for (i = 0; i < count; i ++)
-      {
-       secCert = SecTrustGetCertificateAtIndex(peerTrust, i);
-       if ((data = SecCertificateCopyData(secCert)))
-       {
-         httpAddCredential(*credentials, CFDataGetBytePtr(data),
-                           CFDataGetLength(data));
-         CFRelease(data);
-       }
-      }
-    }
-
-    CFRelease(peerTrust);
-  }
-
-  return (error);
-
-#  elif defined(HAVE_SSPISSL)
-  return (-1);
-
-#  else
-  return (-1);
-#  endif /* HAVE_LIBSSL */
-}
-
-
-/*
- * '_httpCreateCredentials()' - Create credentials in the internal format.
- */
-
-http_tls_credentials_t                 /* O - Internal credentials */
-_httpCreateCredentials(
-    cups_array_t *credentials)         /* I - Array of credentials */
-{
-  if (!credentials)
-    return (NULL);
-
-#  ifdef HAVE_LIBSSL
-  return (NULL);
-
-#  elif defined(HAVE_GNUTLS)
-  return (NULL);
-
-#  elif defined(HAVE_CDSASSL)
-  CFMutableArrayRef    peerCerts;      /* Peer credentials reference */
-  SecCertificateRef    secCert;        /* Certificate reference */
-  CFDataRef            data;           /* Credential data reference */
-  http_credential_t    *credential;    /* Credential data */
-
-
-  if ((peerCerts = CFArrayCreateMutable(kCFAllocatorDefault,
-                                       cupsArrayCount(credentials),
-                                       &kCFTypeArrayCallBacks)) == NULL)
-    return (NULL);
-
-  for (credential = (http_credential_t *)cupsArrayFirst(credentials);
-       credential;
-       credential = (http_credential_t *)cupsArrayNext(credentials))
-  {
-    if ((data = CFDataCreate(kCFAllocatorDefault, credential->data,
-                            credential->datalen)))
-    {
-      if ((secCert = SecCertificateCreateWithData(kCFAllocatorDefault, data))
-              != NULL)
-      {
-       CFArrayAppendValue(peerCerts, secCert);
-       CFRelease(secCert);
-      }
-
-      CFRelease(data);
-    }
-  }
-
-  return (peerCerts);
-
-#  elif defined(HAVE_SSPISSL)
-  return (NULL);
-
-#  else
-  return (NULL);
-#  endif /* HAVE_LIBSSL */
-}
-
-
 /*
  * 'httpDelete()' - Send a DELETE request to the server.
  */
@@ -675,7 +515,7 @@ _httpDisconnect(http_t *http)               /* I - HTTP connection */
 {
 #ifdef HAVE_SSL
   if (http->tls)
-    http_shutdown_ssl(http);
+    http_tls_stop(http);
 #endif /* HAVE_SSL */
 
   httpAddrClose(NULL, http->fd);
@@ -704,7 +544,7 @@ httpEncryption(http_t            *http,     /* I - HTTP connection */
       (http->encryption == HTTP_ENCRYPTION_NEVER && http->tls))
     return (httpReconnect2(http, 30000, NULL));
   else if (http->encryption == HTTP_ENCRYPTION_REQUIRED && !http->tls)
-    return (http_upgrade(http));
+    return (http_tls_upgrade(http));
   else
     return (0);
 #else
@@ -810,7 +650,7 @@ httpFlush(http_t *http)                     /* I - HTTP connection */
 
 #ifdef HAVE_SSL
     if (http->tls)
-      http_shutdown_ssl(http);
+      http_tls_stop(http);
 #endif /* HAVE_SSL */
 
     httpAddrClose(NULL, http->fd);
@@ -855,33 +695,6 @@ httpFlushWrite(http_t *http)               /* I - HTTP connection */
 }
 
 
-/*
- * '_httpFreeCredentials()' - Free internal credentials.
- */
-
-void
-_httpFreeCredentials(
-    http_tls_credentials_t credentials)        /* I - Internal credentials */
-{
-  if (!credentials)
-    return;
-
-#ifdef HAVE_LIBSSL
-  (void)credentials;
-
-#elif defined(HAVE_GNUTLS)
-  (void)credentials;
-
-#elif defined(HAVE_CDSASSL)
-  CFRelease(credentials);
-
-#elif defined(HAVE_SSPISSL)
-  (void)credentials;
-
-#endif /* HAVE_LIBSSL */
-}
-
-
 /*
  * 'httpFreeCredentials()' - Free an array of credentials.
  */
@@ -982,7 +795,7 @@ httpGetBlocking(http_t *http)               /* I - HTTP connection */
 const char *                           /* O - Content-Coding value or
                                               @code NULL@ for the identity
                                               coding. */
-httpGetContentEncoding(http_t *http)   /* I - Connection to client/server */
+httpGetContentEncoding(http_t *http)   /* I - HTTP connection */
 {
 #ifdef HAVE_LIBZ
   if (http && http->accept_encoding)
@@ -1095,7 +908,7 @@ httpGetEncryption(http_t *http)            /* I - HTTP connection */
  */
 
 http_status_t                          /* O - Expect: status, if any */
-httpGetExpect(http_t *http)            /* I - Connection to client */
+httpGetExpect(http_t *http)            /* I - HTTP connection */
 {
   if (!http)
     return (HTTP_STATUS_ERROR);
@@ -1749,33 +1562,9 @@ httpInitialize(void)
 #  endif /* !SO_NOSIGPIPE */
 #endif /* WIN32 */
 
-#ifdef HAVE_GNUTLS
- /*
-  * Initialize GNU TLS...
-  */
-
-  gnutls_global_init();
-
-#elif defined(HAVE_LIBSSL)
- /*
-  * Initialize OpenSSL...
-  */
-
-  SSL_load_error_strings();
-  SSL_library_init();
-
- /*
-  * Using the current time is a dubious random seed, but on some systems
-  * it is the best we can do (on others, this seed isn't even used...)
-  */
-
-  CUPS_SRAND(time(NULL));
-
-  for (i = 0; i < sizeof(data); i ++)
-    data[i] = CUPS_RAND();
-
-  RAND_seed(data, sizeof(data));
-#endif /* HAVE_GNUTLS */
+#  ifdef HAVE_SSL
+  http_tls_initialize();
+#  endif /* HAVE_SSL */
 
   initialized = 1;
   _cupsGlobalUnlock();
@@ -2388,114 +2177,6 @@ httpRead2(http_t *http,                 /* I - HTTP connection */
 }
 
 
-#if defined(HAVE_SSL) && defined(HAVE_CDSASSL)
-/*
- * '_httpReadCDSA()' - Read function for the CDSA library.
- */
-
-OSStatus                               /* O  - -1 on error, 0 on success */
-_httpReadCDSA(
-    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 (bytes == *dataLength)
-  {
-    result = 0;
-  }
-  else if (bytes > 0)
-  {
-    *dataLength = bytes;
-    result = errSSLWouldBlock;
-  }
-  else
-  {
-    *dataLength = 0;
-
-    if (bytes == 0)
-      result = errSSLClosedGraceful;
-    else if (errno == EAGAIN)
-      result = errSSLWouldBlock;
-    else
-      result = errSSLClosedAbort;
-  }
-
-  return (result);
-}
-#endif /* HAVE_SSL && HAVE_CDSASSL */
-
-
-#if defined(HAVE_SSL) && defined(HAVE_GNUTLS)
-/*
- * '_httpReadGNUTLS()' - Read function for the GNU TLS library.
- */
-
-ssize_t                                        /* O - Number of bytes read or -1 on error */
-_httpReadGNUTLS(
-    gnutls_transport_ptr ptr,          /* I - HTTP connection */
-    void                 *data,                /* I - Buffer */
-    size_t               length)       /* I - Number of bytes to read */
-{
-  http_t       *http;                  /* HTTP connection */
-  ssize_t      bytes;                  /* Bytes read */
-
-
-  DEBUG_printf(("6_httpReadGNUTLS(ptr=%p, data=%p, length=%d)", ptr, data, (int)length));
-
-  http = (http_t *)ptr;
-
-  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);
-    }
-  }
-
-  bytes = recv(http->fd, data, length, 0);
-  DEBUG_printf(("6_httpReadGNUTLS: bytes=%d", (int)bytes));
-  return (bytes);
-}
-#endif /* HAVE_SSL && HAVE_GNUTLS */
-
-
 /*
  * 'httpReadRequest()' - Read a HTTP request from a connection.
  *
@@ -2707,7 +2388,7 @@ httpReconnect2(http_t *http,              /* I - HTTP connection */
   if (http->tls)
   {
     DEBUG_puts("2httpReconnect2: Shutting down SSL/TLS...");
-    http_shutdown_ssl(http);
+    http_tls_stop(http);
   }
 #endif /* HAVE_SSL */
 
@@ -2785,7 +2466,7 @@ httpReconnect2(http_t *http,              /* I - HTTP connection */
     * Always do encryption via SSL.
     */
 
-    if (http_setup_ssl(http) != 0)
+    if (http_tls_start(http) != 0)
     {
       httpAddrClose(NULL, http->fd);
 
@@ -2793,7 +2474,7 @@ httpReconnect2(http_t *http,              /* I - HTTP connection */
     }
   }
   else if (http->encryption == HTTP_ENCRYPTION_REQUIRED && !http->tls_upgrade)
-    return (http_upgrade(http));
+    return (http_tls_upgrade(http));
 #endif /* HAVE_SSL */
 
   DEBUG_printf(("1httpReconnect2: Connected to %s:%d...",
@@ -3252,7 +2933,7 @@ _httpUpdate(http_t        *http,  /* I - HTTP connection */
 #ifdef HAVE_SSL
     if (http->status == HTTP_STATUS_SWITCHING_PROTOCOLS && !http->tls)
     {
-      if (http_setup_ssl(http) != 0)
+      if (http_tls_start(http) != 0)
       {
         httpAddrClose(NULL, http->fd);
 
@@ -3469,32 +3150,10 @@ _httpWait(http_t *http,                 /* I - HTTP connection */
   */
 
 #ifdef HAVE_SSL
-  if (http->tls && usessl)
+  if (http_tls_pending(http))
   {
-#  ifdef HAVE_LIBSSL
-    if (SSL_pending(http->tls))
-    {
-      DEBUG_puts("5_httpWait: Return 1 since there is pending SSL data.");
-      return (1);
-    }
-
-#  elif defined(HAVE_GNUTLS)
-    if (gnutls_record_check_pending(http->tls))
-    {
-      DEBUG_puts("5_httpWait: Return 1 since there is pending SSL data.");
-      return (1);
-    }
-
-#  elif defined(HAVE_CDSASSL)
-    size_t bytes;                      /* Bytes that are available */
-
-    if (!SSLGetBufferedReadSize(http->tls, &bytes) &&
-        bytes > 0)
-    {
-      DEBUG_puts("5_httpWait: Return 1 since there is pending SSL data.");
-      return (1);
-    }
-#  endif /* HAVE_LIBSSL */
+    DEBUG_puts("5_httpWait: Return 1 since there is pending TLS data.");
+    return (1);
   }
 #endif /* HAVE_SSL */
 
@@ -3799,82 +3458,6 @@ httpWrite2(http_t     *http,             /* I - HTTP connection */
 }
 
 
-#if defined(HAVE_SSL) && defined(HAVE_CDSASSL)
-/*
- * '_httpWriteCDSA()' - Write function for the CDSA library.
- */
-
-OSStatus                               /* O  - -1 on error, 0 on success */
-_httpWriteCDSA(
-    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 */
-
-
-  http = (http_t *)connection;
-
-  do
-  {
-    bytes = write(http->fd, data, *dataLength);
-  }
-  while (bytes == -1 && (errno == EINTR || errno == EAGAIN));
-
-  if (bytes == *dataLength)
-  {
-    result = 0;
-  }
-  else if (bytes >= 0)
-  {
-    *dataLength = bytes;
-    result = errSSLWouldBlock;
-  }
-  else
-  {
-    *dataLength = 0;
-
-    if (errno == EAGAIN)
-      result = errSSLWouldBlock;
-    else
-      result = errSSLClosedAbort;
-  }
-
-  return (result);
-}
-#endif /* HAVE_SSL && HAVE_CDSASSL */
-
-
-#if defined(HAVE_SSL) && defined(HAVE_GNUTLS)
-/*
- * '_httpWriteGNUTLS()' - Write function for the GNU TLS library.
- */
-
-ssize_t                                        /* O - Number of bytes written or -1 on error */
-_httpWriteGNUTLS(
-    gnutls_transport_ptr ptr,          /* I - HTTP connection */
-    const void           *data,                /* I - Data buffer */
-    size_t               length)       /* I - Number of bytes to write */
-{
-  ssize_t bytes;                       /* Bytes written */
-
-
-  DEBUG_printf(("6_httpWriteGNUTLS(ptr=%p, data=%p, length=%d)", ptr, data,
-                (int)length));
-#ifdef DEBUG
-  http_debug_hex("_httpWriteGNUTLS", data, (int)length);
-#endif /* DEBUG */
-
-  bytes = send(((http_t *)ptr)->fd, data, length, 0);
-  DEBUG_printf(("_httpWriteGNUTLS: bytes=%d", (int)bytes));
-
-  return (bytes);
-}
-#endif /* HAVE_SSL && HAVE_GNUTLS */
-
-
 /*
  * 'httpWriteResponse()' - Write a HTTP response to a client connection.
  *
@@ -4069,163 +3652,14 @@ httpWriteResponse(http_t        *http, /* I - HTTP connection */
 }
 
 
-#if defined(HAVE_SSL) && defined(HAVE_LIBSSL)
+#ifdef HAVE_LIBZ
 /*
- * 'http_bio_ctrl()' - Control the HTTP connection.
+ * 'http_content_coding_finish()' - Finish doing any content encoding.
  */
 
-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.
- */
-
-static int                             /* O - 1 on success, 0 on failure */
-http_bio_free(BIO *h)                  /* I - BIO data */
-{
-  if (!h)
-    return (0);
-
-  if (h->shutdown)
-  {
-    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
-  return (send(((http_t *)h->ptr)->fd, str, strlen(str), 0));
-#endif /* WIN32 */
-}
-
-
-/*
- * 'http_bio_read()' - Read data for OpenSSL.
- */
-
-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 */
-
-
-  http = (http_t *)h->ptr;
-
-  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;
-
-#ifdef WIN32
-      http->error = WSAETIMEDOUT;
-#else
-      http->error = ETIMEDOUT;
-#endif /* WIN32 */
-
-      return (-1);
-    }
-  }
-
-  return (recv(http->fd, buf, 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));
-}
-#endif /* HAVE_SSL && HAVE_LIBSSL */
-
-
-#ifdef HAVE_LIBZ
-/*
- * 'http_content_coding_finish()' - Finish doing any content encoding.
- */
-
-static void
-http_content_coding_finish(
-    http_t *http)                      /* I - HTTP connection */
+static void
+http_content_coding_finish(
+    http_t *http)                      /* I - HTTP connection */
 {
   int  zerr;                           /* Compression status */
 
@@ -4585,7 +4019,7 @@ http_read(http_t *http,                   /* I - HTTP connection */
   {
 #ifdef HAVE_SSL
     if (http->tls)
-      bytes = http_read_ssl(http, buffer, length);
+      bytes = http_tls_read(http, buffer, length);
     else
 #endif /* HAVE_SSL */
     bytes = recv(http->fd, buffer, length, 0);
@@ -4774,97 +4208,6 @@ http_read_chunk(http_t *http,            /* I - HTTP connection */
 }
 
 
-#ifdef HAVE_SSL
-/*
- * 'http_read_ssl()' - Read from a SSL/TLS connection.
- */
-
-static int                             /* O - Bytes read */
-http_read_ssl(http_t *http,            /* I - HTTP connection */
-             char   *buf,              /* I - Buffer to store data */
-             int    len)               /* I - Length of buffer */
-{
-#  if defined(HAVE_LIBSSL)
-  return (SSL_read((SSL *)(http->tls), buf, len));
-
-#  elif defined(HAVE_GNUTLS)
-  ssize_t      result;                 /* Return value */
-
-
-  result = gnutls_record_recv(http->tls, buf, len);
-
-  if (result < 0 && !errno)
-  {
-   /*
-    * Convert GNU TLS error to errno value...
-    */
-
-    switch (result)
-    {
-      case GNUTLS_E_INTERRUPTED :
-         errno = EINTR;
-         break;
-
-      case GNUTLS_E_AGAIN :
-          errno = EAGAIN;
-          break;
-
-      default :
-          errno = EPIPE;
-          break;
-    }
-
-    result = -1;
-  }
-
-  return ((int)result);
-
-#  elif defined(HAVE_CDSASSL)
-  int          result;                 /* Return value */
-  OSStatus     error;                  /* Error info */
-  size_t       processed;              /* Number of bytes processed */
-
-
-  error = SSLRead(http->tls, buf, len, &processed);
-  DEBUG_printf(("6http_read_ssl: error=%d, processed=%d", (int)error,
-                (int)processed));
-  switch (error)
-  {
-    case 0 :
-       result = (int)processed;
-       break;
-
-    case errSSLWouldBlock :
-       if (processed)
-         result = (int)processed;
-       else
-       {
-         result = -1;
-         errno  = EINTR;
-       }
-       break;
-
-    case errSSLClosedGraceful :
-    default :
-       if (processed)
-         result = (int)processed;
-       else
-       {
-         result = -1;
-         errno  = EPIPE;
-       }
-       break;
-  }
-
-  return (result);
-
-#  elif defined(HAVE_SSPISSL)
-  return _sspiRead((_sspi_struct_t*) http->tls, buf, len);
-#  endif /* HAVE_LIBSSL */
-}
-#endif /* HAVE_SSL */
-
-
 /*
  * 'http_send()' - Send a request with all fields and the trailing blank line.
  */
@@ -5052,45 +4395,6 @@ http_send(http_t       *http,            /* I - HTTP connection */
 }
 
 
-#ifdef HAVE_SSL
-#  if defined(HAVE_CDSASSL)
-/*
- * 'http_set_credentials()' - Set the SSL/TLS credentials.
- */
-
-static int                             /* O - Status of connection */
-http_set_credentials(http_t *http)     /* I - HTTP connection */
-{
-  _cups_globals_t *cg = _cupsGlobals();        /* Pointer to library globals */
-  OSStatus             error = 0;      /* Error code */
-  http_tls_credentials_t credentials = NULL;
-                                       /* TLS credentials */
-
-
-  DEBUG_printf(("7http_set_credentials(%p)", http));
-
- /*
-  * Prefer connection specific credentials...
-  */
-
-  if ((credentials = http->tls_credentials) == NULL)
-    credentials = cg->tls_credentials;
-
-  if (credentials)
-  {
-    error = SSLSetCertificate(http->tls, credentials);
-    DEBUG_printf(("4http_set_credentials: SSLSetCertificate, error=%d",
-                 (int)error));
-  }
-  else
-    DEBUG_puts("4http_set_credentials: No credentials to set.");
-
-  return (error);
-}
-#  endif /* HAVE_CDSASSL */
-#endif /* HAVE_SSL */
-
-
 /*
  * 'http_set_length()' - Set the data_encoding and data_remaining values.
  */
@@ -5191,476 +4495,17 @@ http_set_wait(http_t *http)            /* I - HTTP connection */
 
 #ifdef HAVE_SSL
 /*
- * 'http_setup_ssl()' - Set up SSL/TLS support on a connection.
- */
-
-static int                             /* O - 0 on success, -1 on failure */
-http_setup_ssl(http_t *http)           /* I - HTTP connection */
-{
-  char                 hostname[256],  /* Hostname */
-                       *hostptr;       /* Pointer into hostname */
-
-#  ifdef HAVE_LIBSSL
-  SSL_CTX              *context;       /* Context for encryption */
-  BIO                  *bio;           /* BIO data */
-  const char           *message = NULL;/* Error message */
-#  elif defined(HAVE_GNUTLS)
-  int                  status;         /* Status of handshake */
-  gnutls_certificate_client_credentials *credentials;
-                                       /* TLS credentials */
-#  elif defined(HAVE_CDSASSL)
-  _cups_globals_t      *cg = _cupsGlobals();
-                                       /* Pointer to library globals */
-  OSStatus             error;          /* Error code */
-  const char           *message = NULL;/* Error message */
-  cups_array_t         *credentials;   /* Credentials array */
-  cups_array_t         *names;         /* CUPS distinguished names */
-  CFArrayRef           dn_array;       /* CF distinguished names array */
-  CFIndex              count;          /* Number of credentials */
-  CFDataRef            data;           /* Certificate data */
-  int                  i;              /* Looping var */
-  http_credential_t    *credential;    /* Credential data */
-#  elif defined(HAVE_SSPISSL)
-  TCHAR                        username[256];  /* Username returned from GetUserName() */
-  TCHAR                        commonName[256];/* Common name for certificate */
-  DWORD                        dwSize;         /* 32 bit size */
-#  endif /* HAVE_LIBSSL */
-
-
-  DEBUG_printf(("7http_setup_ssl(http=%p)", http));
-
- /*
-  * Get the hostname to use for SSL...
-  */
-
-  if (httpAddrLocalhost(http->hostaddr))
-  {
-    strlcpy(hostname, "localhost", sizeof(hostname));
-  }
-  else
-  {
-   /*
-    * Otherwise make sure the hostname we have does not end in a trailing dot.
-    */
-
-    strlcpy(hostname, http->hostname, sizeof(hostname));
-    if ((hostptr = hostname + strlen(hostname) - 1) >= hostname &&
-        *hostptr == '.')
-      *hostptr = '\0';
-  }
-
-#  ifdef HAVE_LIBSSL
-  context = SSL_CTX_new(SSLv23_client_method());
-
-  SSL_CTX_set_options(context, SSL_OP_NO_SSLv2); /* Only use SSLv3 or TLS */
-
-  bio = BIO_new(_httpBIOMethods());
-  BIO_ctrl(bio, BIO_C_SET_FILE_PTR, 0, (char *)http);
-
-  http->tls = SSL_new(context);
-  SSL_set_bio(http->tls, bio, bio);
-
-#   ifdef HAVE_SSL_SET_TLSEXT_HOST_NAME
-  SSL_set_tlsext_host_name(http->tls, hostname);
-#   endif /* HAVE_SSL_SET_TLSEXT_HOST_NAME */
-
-  if (SSL_connect(http->tls) != 1)
-  {
-    unsigned long      error;  /* Error code */
-
-    while ((error = ERR_get_error()) != 0)
-    {
-      message = ERR_error_string(error, NULL);
-      DEBUG_printf(("8http_setup_ssl: %s", message));
-    }
-
-    SSL_CTX_free(context);
-    SSL_free(http->tls);
-    http->tls = NULL;
-
-#    ifdef WIN32
-    http->error  = WSAGetLastError();
-#    else
-    http->error  = errno;
-#    endif /* WIN32 */
-    http->status = HTTP_STATUS_ERROR;
-
-    if (!message)
-      message = _("Unable to establish a secure connection to host.");
-
-    _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI, message, 1);
-
-    return (-1);
-  }
-
-#  elif defined(HAVE_GNUTLS)
-  credentials = (gnutls_certificate_client_credentials *)
-                    malloc(sizeof(gnutls_certificate_client_credentials));
-  if (credentials == NULL)
-  {
-    DEBUG_printf(("8http_setup_ssl: Unable to allocate credentials: %s",
-                  strerror(errno)));
-    http->error  = errno;
-    http->status = HTTP_STATUS_ERROR;
-    _cupsSetHTTPError(HTTP_STATUS_ERROR);
-
-    return (-1);
-  }
-
-  gnutls_certificate_allocate_credentials(credentials);
-
-  gnutls_init(&http->tls, GNUTLS_CLIENT);
-  gnutls_set_default_priority(http->tls);
-  gnutls_server_name_set(http->tls, GNUTLS_NAME_DNS, hostname,
-                         strlen(hostname));
-  gnutls_credentials_set(http->tls, GNUTLS_CRD_CERTIFICATE, *credentials);
-  gnutls_transport_set_ptr(http->tls, (gnutls_transport_ptr)http);
-  gnutls_transport_set_pull_function(http->tls, _httpReadGNUTLS);
-  gnutls_transport_set_push_function(http->tls, _httpWriteGNUTLS);
-
-  while ((status = gnutls_handshake(http->tls)) != GNUTLS_E_SUCCESS)
-  {
-    DEBUG_printf(("8http_setup_ssl: gnutls_handshake returned %d (%s)",
-                  status, gnutls_strerror(status)));
-
-    if (gnutls_error_is_fatal(status))
-    {
-      http->error  = EIO;
-      http->status = HTTP_STATUS_ERROR;
-
-      _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI, gnutls_strerror(status), 0);
-
-      gnutls_deinit(http->tls);
-      gnutls_certificate_free_credentials(*credentials);
-      free(credentials);
-      http->tls = NULL;
-
-      return (-1);
-    }
-  }
-
-  http->tls_credentials = credentials;
-
-#  elif defined(HAVE_CDSASSL)
-  if ((http->tls = SSLCreateContext(kCFAllocatorDefault, kSSLClientSide,
-                                    kSSLStreamType)) == NULL)
-  {
-    DEBUG_puts("4http_setup_ssl: SSLCreateContext failed.");
-    http->error  = errno = ENOMEM;
-    http->status = HTTP_STATUS_ERROR;
-    _cupsSetHTTPError(HTTP_STATUS_ERROR);
-
-    return (-1);
-  }
-
-  error = SSLSetConnection(http->tls, http);
-  DEBUG_printf(("4http_setup_ssl: SSLSetConnection, error=%d", (int)error));
-
-  if (!error)
-  {
-    error = SSLSetIOFuncs(http->tls, _httpReadCDSA, _httpWriteCDSA);
-    DEBUG_printf(("4http_setup_ssl: SSLSetIOFuncs, error=%d", (int)error));
-  }
-
-  if (!error)
-  {
-    error = SSLSetSessionOption(http->tls, kSSLSessionOptionBreakOnServerAuth,
-                                true);
-    DEBUG_printf(("4http_setup_ssl: SSLSetSessionOption, error=%d",
-                  (int)error));
-  }
-
-  if (!error)
-  {
-    if (cg->client_cert_cb)
-    {
-      error = SSLSetSessionOption(http->tls,
-                                 kSSLSessionOptionBreakOnCertRequested, true);
-      DEBUG_printf(("4http_setup_ssl: kSSLSessionOptionBreakOnCertRequested, "
-                    "error=%d", (int)error));
-    }
-    else
-    {
-      error = http_set_credentials(http);
-      DEBUG_printf(("4http_setup_ssl: http_set_credentials, error=%d",
-                    (int)error));
-    }
-  }
-
- /*
-  * Let the server know which hostname/domain we are trying to connect to
-  * in case it wants to serve up a certificate with a matching common name.
-  */
-
-  if (!error)
-  {
-    error = SSLSetPeerDomainName(http->tls, hostname, strlen(hostname));
-
-    DEBUG_printf(("4http_setup_ssl: SSLSetPeerDomainName, error=%d",
-                  (int)error));
-  }
-
-  if (!error)
-  {
-    int done = 0;                      /* Are we done yet? */
-
-    while (!error && !done)
-    {
-      error = SSLHandshake(http->tls);
-
-      DEBUG_printf(("4http_setup_ssl: SSLHandshake returned %d.", (int)error));
-
-      switch (error)
-      {
-       case noErr :
-           done = 1;
-           break;
-
-       case errSSLWouldBlock :
-           error = noErr;              /* Force a retry */
-           usleep(1000);               /* in 1 millisecond */
-           break;
-
-       case errSSLServerAuthCompleted :
-           error = 0;
-           if (cg->server_cert_cb)
-           {
-             error = httpCopyCredentials(http, &credentials);
-             if (!error)
-             {
-               error = (cg->server_cert_cb)(http, http->tls, credentials,
-                                            cg->server_cert_data);
-               httpFreeCredentials(credentials);
-             }
-
-             DEBUG_printf(("4http_setup_ssl: Server certificate callback "
-                           "returned %d.", (int)error));
-           }
-           break;
-
-       case errSSLClientCertRequested :
-           error = 0;
-
-           if (cg->client_cert_cb)
-           {
-             names = NULL;
-             if (!(error = SSLCopyDistinguishedNames(http->tls, &dn_array)) &&
-                 dn_array)
-             {
-               if ((names = cupsArrayNew(NULL, NULL)) != NULL)
-               {
-                 for (i = 0, count = CFArrayGetCount(dn_array); i < count; i++)
-                 {
-                   data = (CFDataRef)CFArrayGetValueAtIndex(dn_array, i);
-
-                   if ((credential = malloc(sizeof(*credential))) != NULL)
-                   {
-                     credential->datalen = CFDataGetLength(data);
-                     if ((credential->data = malloc(credential->datalen)))
-                     {
-                       memcpy((void *)credential->data, CFDataGetBytePtr(data),
-                              credential->datalen);
-                       cupsArrayAdd(names, credential);
-                     }
-                     else
-                       free(credential);
-                   }
-                 }
-               }
-
-               CFRelease(dn_array);
-             }
-
-             if (!error)
-             {
-               error = (cg->client_cert_cb)(http, http->tls, names,
-                                            cg->client_cert_data);
-
-               DEBUG_printf(("4http_setup_ssl: Client certificate callback "
-                             "returned %d.", (int)error));
-             }
-
-             httpFreeCredentials(names);
-           }
-           break;
-
-       case errSSLUnknownRootCert :
-           message = _("Unable to establish a secure connection to host "
-                       "(untrusted certificate).");
-           break;
-
-       case errSSLNoRootCert :
-           message = _("Unable to establish a secure connection to host "
-                       "(self-signed certificate).");
-           break;
-
-       case errSSLCertExpired :
-           message = _("Unable to establish a secure connection to host "
-                       "(expired certificate).");
-           break;
-
-       case errSSLCertNotYetValid :
-           message = _("Unable to establish a secure connection to host "
-                       "(certificate not yet valid).");
-           break;
-
-       case errSSLHostNameMismatch :
-           message = _("Unable to establish a secure connection to host "
-                       "(host name mismatch).");
-           break;
-
-       case errSSLXCertChainInvalid :
-           message = _("Unable to establish a secure connection to host "
-                       "(certificate chain invalid).");
-           break;
-
-       case errSSLConnectionRefused :
-           message = _("Unable to establish a secure connection to host "
-                       "(peer dropped connection before responding).");
-           break;
-
-       default :
-           break;
-      }
-    }
-  }
-
-  if (error)
-  {
-    http->error  = error;
-    http->status = HTTP_STATUS_ERROR;
-    errno        = ECONNREFUSED;
-
-    CFRelease(http->tls);
-    http->tls = NULL;
-
-   /*
-    * If an error string wasn't set by the callbacks use a generic one...
-    */
-
-    if (!message)
-#ifdef HAVE_CSSMERRORSTRING
-      message = cssmErrorString(error);
-#else
-      message = _("Unable to establish a secure connection to host.");
-#endif /* HAVE_CSSMERRORSTRING */
-
-    _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI, message, 1);
-
-    return (-1);
-  }
-
-#  elif defined(HAVE_SSPISSL)
-  http->tls = _sspiAlloc();
-
-  if (!http->tls)
-  {
-    _cupsSetHTTPError(HTTP_STATUS_ERROR);
-    return (-1);
-  }
-
-  http->tls->sock = http->fd;
-  dwSize          = sizeof(username) / sizeof(TCHAR);
-  GetUserName(username, &dwSize);
-  _sntprintf_s(commonName, sizeof(commonName) / sizeof(TCHAR),
-               sizeof(commonName) / sizeof(TCHAR), TEXT("CN=%s"), username);
-
-  if (!_sspiGetCredentials(http->tls_credentials, L"ClientContainer",
-                           commonName, FALSE))
-  {
-    _sspiFree(http->tls_credentials);
-    http->tls_credentials = NULL;
-
-    http->error  = EIO;
-    http->status = HTTP_STATUS_ERROR;
-
-    _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI,
-                  _("Unable to establish a secure connection to host."), 1);
-
-    return (-1);
-  }
-
-  _sspiSetAllowsAnyRoot(http->tls_credentials, TRUE);
-  _sspiSetAllowsExpiredCerts(http->tls_credentials, TRUE);
-
-  if (!_sspiConnect(http->tls_credentials, hostname))
-  {
-    _sspiFree(http->tls_credentials);
-    http->tls_credentials = NULL;
-
-    http->error  = EIO;
-    http->status = HTTP_STATUS_ERROR;
-
-    _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI,
-                  _("Unable to establish a secure connection to host."), 1);
-
-    return (-1);
-  }
-#  endif /* HAVE_CDSASSL */
-
-  return (0);
-}
-
-
-/*
- * 'http_shutdown_ssl()' - Shut down SSL/TLS on a connection.
- */
-
-static void
-http_shutdown_ssl(http_t *http)                /* I - HTTP connection */
-{
-#  ifdef HAVE_LIBSSL
-  SSL_CTX      *context;               /* Context for encryption */
-
-  context = SSL_get_SSL_CTX(http->tls);
-
-  SSL_shutdown(http->tls);
-  SSL_CTX_free(context);
-  SSL_free(http->tls);
-
-#  elif defined(HAVE_GNUTLS)
-  gnutls_certificate_client_credentials *credentials;
-                                       /* TLS credentials */
-
-  credentials = (gnutls_certificate_client_credentials *)(http->tls_credentials);
-
-  gnutls_bye(http->tls, GNUTLS_SHUT_RDWR);
-  gnutls_deinit(http->tls);
-  gnutls_certificate_free_credentials(*credentials);
-  free(credentials);
-
-#  elif defined(HAVE_CDSASSL)
-  while (SSLClose(http->tls) == errSSLWouldBlock)
-    usleep(1000);
-
-  CFRelease(http->tls);
-
-  if (http->tls_credentials)
-    CFRelease(http->tls_credentials);
-
-#  elif defined(HAVE_SSPISSL)
-  _sspiFree(http->tls_credentials);
-#  endif /* HAVE_LIBSSL */
-
-  http->tls             = NULL;
-  http->tls_credentials = NULL;
-}
-#endif /* HAVE_SSL */
-
-
-#ifdef HAVE_SSL
-/*
- * 'http_upgrade()' - Force upgrade to TLS encryption.
+ * 'http_tls_upgrade()' - Force upgrade to TLS encryption.
  */
 
 static int                             /* O - Status of connection */
-http_upgrade(http_t *http)             /* I - HTTP connection */
+http_tls_upgrade(http_t *http)         /* I - HTTP connection */
 {
   int          ret;                    /* Return value */
   http_t       myhttp;                 /* Local copy of HTTP data */
 
 
-  DEBUG_printf(("7http_upgrade(%p)", http));
+  DEBUG_printf(("7http_tls_upgrade(%p)", http));
 
  /*
   * Flush the connection to make sure any previous "Upgrade" message
@@ -5720,7 +4565,7 @@ http_upgrade(http_t *http)                /* I - HTTP connection */
     * Server does not support HTTP upgrade...
     */
 
-    DEBUG_puts("8http_upgrade: Server does not support HTTP upgrade!");
+    DEBUG_puts("8http_tls_upgrade: Server does not support HTTP upgrade!");
 
     httpAddrClose(NULL, http->fd);
 
@@ -5815,7 +4660,7 @@ http_write(http_t     *http,              /* I - HTTP connection */
 
 #ifdef HAVE_SSL
     if (http->tls)
-      bytes = http_write_ssl(http, buffer, length);
+      bytes = http_tls_write(http, buffer, length);
     else
 #endif /* HAVE_SSL */
     bytes = send(http->fd, buffer, length, 0);
@@ -5925,96 +4770,6 @@ http_write_chunk(http_t     *http,       /* I - HTTP connection */
 }
 
 
-#ifdef HAVE_SSL
-/*
- * 'http_write_ssl()' - Write to a SSL/TLS connection.
- */
-
-static int                             /* O - Bytes written */
-http_write_ssl(http_t     *http,       /* I - HTTP connection */
-              const char *buf,         /* I - Buffer holding data */
-              int        len)          /* I - Length of buffer */
-{
-  ssize_t      result;                 /* Return value */
-
-
-  DEBUG_printf(("2http_write_ssl(http=%p, buf=%p, len=%d)", http, buf, len));
-
-#  if defined(HAVE_LIBSSL)
-  result = SSL_write((SSL *)(http->tls), buf, len);
-
-#  elif defined(HAVE_GNUTLS)
-  result = gnutls_record_send(http->tls, buf, len);
-
-  if (result < 0 && !errno)
-  {
-   /*
-    * Convert GNU TLS error to errno value...
-    */
-
-    switch (result)
-    {
-      case GNUTLS_E_INTERRUPTED :
-         errno = EINTR;
-         break;
-
-      case GNUTLS_E_AGAIN :
-          errno = EAGAIN;
-          break;
-
-      default :
-          errno = EPIPE;
-          break;
-    }
-
-    result = -1;
-  }
-
-#  elif defined(HAVE_CDSASSL)
-  OSStatus     error;                  /* Error info */
-  size_t       processed;              /* Number of bytes processed */
-
-
-  error = SSLWrite(http->tls, buf, len, &processed);
-
-  switch (error)
-  {
-    case 0 :
-       result = (int)processed;
-       break;
-
-    case errSSLWouldBlock :
-       if (processed)
-         result = (int)processed;
-       else
-       {
-         result = -1;
-         errno  = EINTR;
-       }
-       break;
-
-    case errSSLClosedGraceful :
-    default :
-       if (processed)
-         result = (int)processed;
-       else
-       {
-         result = -1;
-         errno  = EPIPE;
-       }
-       break;
-  }
-#  elif defined(HAVE_SSPISSL)
-  return _sspiWrite((_sspi_struct_t *)http->tls, (void *)buf, len);
-#  endif /* HAVE_LIBSSL */
-
-  DEBUG_printf(("3http_write_ssl: Returning %d.", (int)result));
-
-  return ((int)result);
-}
-#endif /* HAVE_SSL */
-
-
 /*
  * End of "$Id$".
  */
index b1fbb37b0f2a6238ecb00a71f3ceb240533f3b71..9848514e9d7a8f49cb3f3222b7e7082a77b4af14 100644 (file)
@@ -612,8 +612,17 @@ extern void                httpSetDefaultField(http_t *http, http_field_t field,
 extern http_state_t    httpWriteResponse(http_t *http,
                                          http_status_t status) _CUPS_API_1_7;
 
-
-/**** New in CUPS 2.0 ****/
+/* New in CUPS 2.0 */
+extern int             httpCompareCredentials(cups_array_t *cred1,
+                                              cups_array_t *cred2)
+                                              _CUPS_API_2_0;
+extern int             httpCreateCredentials(const char *path,
+                                             cups_array_t **credentials,
+                                             const char *common_name)
+                                             _CUPS_API_2_0;
+extern size_t          httpCredentialsString(cups_array_t *credentials,
+                                             char *buffer, size_t bufsize)
+                                             _CUPS_API_2_0;
 extern int             httpAddrClose(http_addr_t *addr, int fd) _CUPS_API_2_0;
 extern int             httpAddrFamily(http_addr_t *addr) _CUPS_API_2_0;
 extern http_field_t    httpFieldValue(const char *name) _CUPS_API_2_0;
@@ -626,7 +635,15 @@ extern size_t              httpGetReady(http_t *http) _CUPS_API_2_0;
 extern size_t          httpGetRemaining(http_t *http) _CUPS_API_2_0;
 extern int             httpIsChunked(http_t *http) _CUPS_API_2_0;
 extern int             httpIsEncrypted(http_t *http) _CUPS_API_2_0;
+extern int             httpLoadCredentials(const char *path,
+                                           cups_array_t **credentials,
+                                           const char *common_name)
+                                           _CUPS_API_2_0;
 extern const char      *httpResolveHostname(http_t *http, char *buffer, size_t bufsize) _CUPS_API_2_0;
+extern int             httpSaveCredentials(const char *path,
+                                           cups_array_t *credentials,
+                                           const char *common_name)
+                                           _CUPS_API_2_0;
 extern void            httpSetKeepAlive(http_t *http, http_keepalive_t keep_alive) _CUPS_API_2_0;
 extern void            httpShutdown(http_t *http) _CUPS_API_2_0;
 extern const char      *httpStateString(http_state_t state);
diff --git a/cups/tls-darwin.c b/cups/tls-darwin.c
new file mode 100644 (file)
index 0000000..db4aa5d
--- /dev/null
@@ -0,0 +1,1234 @@
+/*
+ * "$Id$"
+ *
+ * TLS support code for CUPS on OS X.
+ *
+ * Copyright 2007-2013 by Apple Inc.
+ * Copyright 1997-2007 by Easy Software Products, all rights reserved.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law.  Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file.  If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ */
+
+
+/*
+ * Local functions...
+ */
+
+//static CFArrayRef    copy_cdsa_certificate(cupsd_client_t *con);
+//static int           make_certificate(cupsd_client_t *con);
+
+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);
+
+
+
+/*
+ * 'httpCopyCredentials()' - Copy the credentials associated with the peer in
+ *                           an encrypted connection.
+ *
+ * @since CUPS 1.5/OS X 10.7@
+ */
+
+int                                    /* O - Status of call (0 = success) */
+httpCopyCredentials(
+    http_t      *http,                 /* I - Connection to server */
+    cups_array_t **credentials)                /* O - Array of credentials */
+{
+  OSStatus             error;          /* Error code */
+  SecTrustRef          peerTrust;      /* Peer trust reference */
+  CFIndex              count;          /* Number of credentials */
+  SecCertificateRef    secCert;        /* Certificate reference */
+  CFDataRef            data;           /* Certificate data */
+  int                  i;              /* Looping var */
+
+
+  if (credentials)
+    *credentials = NULL;
+
+  if (!http || !http->tls || !credentials)
+    return (-1);
+
+  if (!(error = SSLCopyPeerTrust(http->tls, &peerTrust)) && peerTrust)
+  {
+    if ((*credentials = cupsArrayNew(NULL, NULL)) != NULL)
+    {
+      count = SecTrustGetCertificateCount(peerTrust);
+
+      for (i = 0; i < count; i ++)
+      {
+       secCert = SecTrustGetCertificateAtIndex(peerTrust, i);
+       if ((data = SecCertificateCopyData(secCert)))
+       {
+         httpAddCredential(*credentials, CFDataGetBytePtr(data),
+                           CFDataGetLength(data));
+         CFRelease(data);
+       }
+      }
+    }
+
+    CFRelease(peerTrust);
+  }
+
+  return (error);
+}
+
+
+/*
+ * '_httpCreateCredentials()' - Create credentials in the internal format.
+ */
+
+http_tls_credentials_t                 /* O - Internal credentials */
+_httpCreateCredentials(
+    cups_array_t *credentials)         /* I - Array of credentials */
+{
+  CFMutableArrayRef    peerCerts;      /* Peer credentials reference */
+  SecCertificateRef    secCert;        /* Certificate reference */
+  CFDataRef            data;           /* Credential data reference */
+  http_credential_t    *credential;    /* Credential data */
+
+
+  if (!credentials)
+    return (NULL);
+
+  if ((peerCerts = CFArrayCreateMutable(kCFAllocatorDefault,
+                                       cupsArrayCount(credentials),
+                                       &kCFTypeArrayCallBacks)) == NULL)
+    return (NULL);
+
+  for (credential = (http_credential_t *)cupsArrayFirst(credentials);
+       credential;
+       credential = (http_credential_t *)cupsArrayNext(credentials))
+  {
+    if ((data = CFDataCreate(kCFAllocatorDefault, credential->data,
+                            credential->datalen)))
+    {
+      if ((secCert = SecCertificateCreateWithData(kCFAllocatorDefault, data))
+              != NULL)
+      {
+       CFArrayAppendValue(peerCerts, secCert);
+       CFRelease(secCert);
+      }
+
+      CFRelease(data);
+    }
+  }
+
+  return (peerCerts);
+}
+
+
+/*
+ * '_httpFreeCredentials()' - Free internal credentials.
+ */
+
+void
+_httpFreeCredentials(
+    http_tls_credentials_t credentials)        /* I - Internal credentials */
+{
+  if (!credentials)
+    return;
+
+  CFRelease(credentials);
+}
+
+
+/*
+ * '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 (bytes == *dataLength)
+  {
+    result = 0;
+  }
+  else if (bytes > 0)
+  {
+    *dataLength = bytes;
+    result = errSSLWouldBlock;
+  }
+  else
+  {
+    *dataLength = 0;
+
+    if (bytes == 0)
+      result = errSSLClosedGraceful;
+    else if (errno == EAGAIN)
+      result = errSSLWouldBlock;
+    else
+      result = errSSLClosedAbort;
+  }
+
+  return (result);
+}
+
+
+/*
+ * 'http_cdsa_write()' - Write function for the CDSA library.
+ */
+
+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 */
+
+
+  http = (http_t *)connection;
+
+  do
+  {
+    bytes = write(http->fd, data, *dataLength);
+  }
+  while (bytes == -1 && (errno == EINTR || errno == EAGAIN));
+
+  if (bytes == *dataLength)
+  {
+    result = 0;
+  }
+  else if (bytes >= 0)
+  {
+    *dataLength = bytes;
+    result = errSSLWouldBlock;
+  }
+  else
+  {
+    *dataLength = 0;
+
+    if (errno == EAGAIN)
+      result = errSSLWouldBlock;
+    else
+      result = errSSLClosedAbort;
+  }
+
+  return (result);
+}
+
+
+/*
+ * 'http_tls_initialize()' - Initialize the TLS stack.
+ */
+
+static void
+http_tls_initialize(void)
+{
+ /*
+  * Nothing to do...
+  */
+}
+
+
+/*
+ * 'http_tls_pending()' - Return the number of pending TLS-encrypted bytes.
+ */
+
+static size_t
+http_tls_pending(http_t *http)         /* I - HTTP connection */
+{
+  size_t bytes;                                /* Bytes that are available */
+
+
+  if (!SSLGetBufferedReadSize(http->tls, &bytes))
+    return (bytes);
+
+  return (0);
+}
+
+
+/*
+ * 'http_tls_read()' - Read from a SSL/TLS connection.
+ */
+
+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 */
+
+
+  error = SSLRead(http->tls, buf, len, &processed);
+  DEBUG_printf(("6http_tls_read: error=%d, processed=%d", (int)error,
+                (int)processed));
+  switch (error)
+  {
+    case 0 :
+       result = (int)processed;
+       break;
+
+    case errSSLWouldBlock :
+       if (processed)
+         result = (int)processed;
+       else
+       {
+         result = -1;
+         errno  = EINTR;
+       }
+       break;
+
+    case errSSLClosedGraceful :
+    default :
+       if (processed)
+         result = (int)processed;
+       else
+       {
+         result = -1;
+         errno  = EPIPE;
+       }
+       break;
+  }
+
+  return (result);
+}
+
+
+/*
+ * 'http_tls_set_credentials()' - Set the TLS credentials.
+ */
+
+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 */
+
+
+  DEBUG_printf(("7http_tls_set_credentials(%p)", http));
+
+ /*
+  * 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);
+}
+
+
+/*
+ * 'http_tls_start()' - Set up SSL/TLS support on a connection.
+ */
+
+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 */
+
+
+  DEBUG_printf(("7http_tls_start(http=%p)", http));
+
+ /*
+  * Get the hostname to use for SSL...
+  */
+
+  if (httpAddrLocalhost(http->hostaddr))
+  {
+    strlcpy(hostname, "localhost", sizeof(hostname));
+  }
+  else
+  {
+   /*
+    * Otherwise make sure the hostname we have does not end in a trailing dot.
+    */
+
+    strlcpy(hostname, http->hostname, sizeof(hostname));
+    if ((hostptr = hostname + strlen(hostname) - 1) >= hostname &&
+        *hostptr == '.')
+      *hostptr = '\0';
+  }
+
+  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);
+
+    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)
+  {
+    if (cg->client_cert_cb)
+    {
+      error = SSLSetSessionOption(http->tls,
+                                 kSSLSessionOptionBreakOnCertRequested, true);
+      DEBUG_printf(("4http_tls_start: kSSLSessionOptionBreakOnCertRequested, "
+                    "error=%d", (int)error));
+    }
+    else
+    {
+      error = http_set_credentials(http);
+      DEBUG_printf(("4http_tls_start: http_set_credentials, error=%d",
+                    (int)error));
+    }
+  }
+
+ /*
+  * Let the server know which hostname/domain we are trying to connect to
+  * in case it wants to serve up a certificate with a matching common name.
+  */
+
+  if (!error)
+  {
+    error = SSLSetPeerDomainName(http->tls, hostname, strlen(hostname));
+
+    DEBUG_printf(("4http_tls_start: SSLSetPeerDomainName, error=%d",
+                  (int)error));
+  }
+
+  if (!error)
+  {
+    int done = 0;                      /* Are we done yet? */
+
+    while (!error && !done)
+    {
+      error = SSLHandshake(http->tls);
+
+      DEBUG_printf(("4http_tls_start: SSLHandshake returned %d.", (int)error));
+
+      switch (error)
+      {
+       case noErr :
+           done = 1;
+           break;
+
+       case errSSLWouldBlock :
+           error = noErr;              /* Force a retry */
+           usleep(1000);               /* in 1 millisecond */
+           break;
+
+       case errSSLServerAuthCompleted :
+           error = 0;
+           if (cg->server_cert_cb)
+           {
+             error = httpCopyCredentials(http, &credentials);
+             if (!error)
+             {
+               error = (cg->server_cert_cb)(http, http->tls, credentials,
+                                            cg->server_cert_data);
+               httpFreeCredentials(credentials);
+             }
+
+             DEBUG_printf(("4http_tls_start: Server certificate callback "
+                           "returned %d.", (int)error));
+           }
+           break;
+
+       case errSSLClientCertRequested :
+           error = 0;
+
+           if (cg->client_cert_cb)
+           {
+             names = NULL;
+             if (!(error = SSLCopyDistinguishedNames(http->tls, &dn_array)) &&
+                 dn_array)
+             {
+               if ((names = cupsArrayNew(NULL, NULL)) != NULL)
+               {
+                 for (i = 0, count = CFArrayGetCount(dn_array); i < count; i++)
+                 {
+                   data = (CFDataRef)CFArrayGetValueAtIndex(dn_array, i);
+
+                   if ((credential = malloc(sizeof(*credential))) != NULL)
+                   {
+                     credential->datalen = CFDataGetLength(data);
+                     if ((credential->data = malloc(credential->datalen)))
+                     {
+                       memcpy((void *)credential->data, CFDataGetBytePtr(data),
+                              credential->datalen);
+                       cupsArrayAdd(names, credential);
+                     }
+                     else
+                       free(credential);
+                   }
+                 }
+               }
+
+               CFRelease(dn_array);
+             }
+
+             if (!error)
+             {
+               error = (cg->client_cert_cb)(http, http->tls, names,
+                                            cg->client_cert_data);
+
+               DEBUG_printf(("4http_tls_start: Client certificate callback "
+                             "returned %d.", (int)error));
+             }
+
+             httpFreeCredentials(names);
+           }
+           break;
+
+       case errSSLUnknownRootCert :
+           message = _("Unable to establish a secure connection to host "
+                       "(untrusted certificate).");
+           break;
+
+       case errSSLNoRootCert :
+           message = _("Unable to establish a secure connection to host "
+                       "(self-signed certificate).");
+           break;
+
+       case errSSLCertExpired :
+           message = _("Unable to establish a secure connection to host "
+                       "(expired certificate).");
+           break;
+
+       case errSSLCertNotYetValid :
+           message = _("Unable to establish a secure connection to host "
+                       "(certificate not yet valid).");
+           break;
+
+       case errSSLHostNameMismatch :
+           message = _("Unable to establish a secure connection to host "
+                       "(host name mismatch).");
+           break;
+
+       case errSSLXCertChainInvalid :
+           message = _("Unable to establish a secure connection to host "
+                       "(certificate chain invalid).");
+           break;
+
+       case errSSLConnectionRefused :
+           message = _("Unable to establish a secure connection to host "
+                       "(peer dropped connection before responding).");
+           break;
+
+       default :
+           break;
+      }
+    }
+  }
+
+  if (error)
+  {
+    http->error  = error;
+    http->status = HTTP_STATUS_ERROR;
+    errno        = ECONNREFUSED;
+
+    CFRelease(http->tls);
+    http->tls = NULL;
+
+   /*
+    * If an error string wasn't set by the callbacks use a generic one...
+    */
+
+    if (!message)
+#ifdef HAVE_CSSMERRORSTRING
+      message = cssmErrorString(error);
+#else
+      message = _("Unable to establish a secure connection to host.");
+#endif /* HAVE_CSSMERRORSTRING */
+
+    _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI, message, 1);
+
+    return (-1);
+  }
+
+  return (0);
+}
+
+
+/*
+ * 'http_tls_stop()' - Shut down SSL/TLS on a connection.
+ */
+
+static void
+http_tls_stop(http_t *http)            /* I - Connection to server */
+{
+  while (SSLClose(http->tls) == errSSLWouldBlock)
+    usleep(1000);
+
+  CFRelease(http->tls);
+
+  if (http->tls_credentials)
+    CFRelease(http->tls_credentials);
+
+  http->tls             = NULL;
+  http->tls_credentials = NULL;
+}
+
+
+/*
+ * 'http_tls_write()' - Write to 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 */
+{
+  ssize_t      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);
+
+  switch (error)
+  {
+    case 0 :
+       result = (int)processed;
+       break;
+
+    case errSSLWouldBlock :
+       if (processed)
+       {
+         result = (int)processed;
+       }
+       else
+       {
+         result = -1;
+         errno  = EINTR;
+       }
+       break;
+
+    case errSSLClosedGraceful :
+    default :
+       if (processed)
+       {
+         result = (int)processed;
+       }
+       else
+       {
+         result = -1;
+         errno  = EPIPE;
+       }
+       break;
+  }
+
+  DEBUG_printf(("3http_tls_write: Returning %d.", (int)result));
+
+  return ((int)result);
+}
+
+
+/*
+ * 'cupsdEndTLS()' - Shutdown a secure session with the client.
+ */
+
+int                                    /* O - 1 on success, 0 on error */
+cupsdEndTLS(cupsd_client_t *con)       /* I - Client connection */
+{
+  while (SSLClose(con->http.tls) == errSSLWouldBlock)
+    usleep(1000);
+
+  CFRelease(con->http.tls);
+  con->http.tls = NULL;
+
+  if (con->http.tls_credentials)
+    CFRelease(con->http.tls_credentials);
+
+  return (1);
+}
+
+
+/*
+ * 'cupsdStartTLS()' - Start a secure session with the client.
+ */
+
+int                                    /* O - 1 on success, 0 on error */
+cupsdStartTLS(cupsd_client_t *con)     /* I - Client connection */
+{
+  OSStatus     error = 0;              /* Error code */
+  SecTrustRef  peerTrust;              /* Peer certificates */
+
+
+  cupsdLogMessage(CUPSD_LOG_DEBUG, "[Client %d] Encrypting connection.",
+                  con->http.fd);
+
+  con->http.tls_credentials = copy_cdsa_certificate(con);
+
+  if (!con->http.tls_credentials)
+  {
+   /*
+    * No keychain (yet), make a self-signed certificate...
+    */
+
+    if (make_certificate(con))
+      con->http.tls_credentials = copy_cdsa_certificate(con);
+  }
+
+  if (!con->http.tls_credentials)
+  {
+    cupsdLogMessage(CUPSD_LOG_ERROR,
+                   "Could not find signing key in keychain \"%s\"",
+                   ServerCertificate);
+    error = errSSLBadConfiguration;
+  }
+
+  if (!error)
+    con->http.tls = SSLCreateContext(kCFAllocatorDefault, kSSLServerSide,
+                                     kSSLStreamType);
+
+  if (!error)
+    error = SSLSetIOFuncs(con->http.tls, http_cdsa_read, http_cdsa_write);
+
+  if (!error)
+    error = SSLSetConnection(con->http.tls, HTTP(con));
+
+  if (!error)
+    error = SSLSetCertificate(con->http.tls, con->http.tls_credentials);
+
+  if (!error)
+  {
+   /*
+    * Perform SSL/TLS handshake
+    */
+
+    while ((error = SSLHandshake(con->http.tls)) == errSSLWouldBlock)
+      usleep(1000);
+  }
+
+  if (error)
+  {
+    cupsdLogMessage(CUPSD_LOG_ERROR,
+                    "Unable to encrypt connection from %s - %s (%d)",
+                    con->http.hostname, cssmErrorString(error), (int)error);
+
+    con->http.error  = error;
+    con->http.status = HTTP_ERROR;
+
+    if (con->http.tls)
+    {
+      CFRelease(con->http.tls);
+      con->http.tls = NULL;
+    }
+
+    if (con->http.tls_credentials)
+    {
+      CFRelease(con->http.tls_credentials);
+      con->http.tls_credentials = NULL;
+    }
+
+    return (0);
+  }
+
+  cupsdLogMessage(CUPSD_LOG_DEBUG, "Connection from %s now encrypted.",
+                  con->http.hostname);
+
+  if (!SSLCopyPeerTrust(con->http.tls, &peerTrust) && peerTrust)
+  {
+    cupsdLogMessage(CUPSD_LOG_DEBUG, "Received %d peer certificates.",
+                   (int)SecTrustGetCertificateCount(peerTrust));
+    CFRelease(peerTrust);
+  }
+  else
+    cupsdLogMessage(CUPSD_LOG_DEBUG, "Received NO peer certificates.");
+
+  return (1);
+}
+
+
+/*
+ * 'copy_cdsa_certificate()' - Copy a SSL/TLS certificate from the System
+ *                             keychain.
+ */
+
+static CFArrayRef                              /* O - Array of certificates */
+copy_cdsa_certificate(
+    cupsd_client_t *con)                       /* I - Client connection */
+{
+  OSStatus             err;            /* Error info */
+  SecKeychainRef       keychain = NULL;/* Keychain reference */
+  SecIdentitySearchRef search = NULL;  /* Search reference */
+  SecIdentityRef       identity = NULL;/* Identity */
+  CFArrayRef           certificates = NULL;
+                                       /* Certificate array */
+  SecPolicyRef         policy = NULL;  /* Policy ref */
+  CFStringRef          servername = NULL;
+                                       /* Server name */
+  CFMutableDictionaryRef query = NULL; /* Query qualifiers */
+  CFArrayRef           list = NULL;    /* Keychain list */
+#    if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
+  char                 localname[1024];/* Local hostname */
+#    endif /* HAVE_DNSSD || HAVE_AVAHI */
+
+
+  cupsdLogMessage(CUPSD_LOG_DEBUG,
+                  "copy_cdsa_certificate: Looking for certs for \"%s\".",
+                 con->servername);
+
+  if ((err = SecKeychainOpen(ServerCertificate, &keychain)))
+  {
+    cupsdLogMessage(CUPSD_LOG_ERROR, "Cannot open keychain \"%s\" - %s (%d)",
+                   ServerCertificate, cssmErrorString(err), (int)err);
+    goto cleanup;
+  }
+
+  servername = CFStringCreateWithCString(kCFAllocatorDefault, con->servername,
+                                        kCFStringEncodingUTF8);
+
+  policy = SecPolicyCreateSSL(1, servername);
+
+  if (servername)
+    CFRelease(servername);
+
+  if (!policy)
+  {
+    cupsdLogMessage(CUPSD_LOG_ERROR, "Cannot create ssl policy reference");
+    goto cleanup;
+  }
+
+  if (!(query = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
+                                         &kCFTypeDictionaryKeyCallBacks,
+                                         &kCFTypeDictionaryValueCallBacks)))
+  {
+    cupsdLogMessage(CUPSD_LOG_ERROR, "Cannot create query dictionary");
+    goto cleanup;
+  }
+
+  list = CFArrayCreate(kCFAllocatorDefault, (const void **)&keychain, 1,
+                       &kCFTypeArrayCallBacks);
+
+  CFDictionaryAddValue(query, kSecClass, kSecClassIdentity);
+  CFDictionaryAddValue(query, kSecMatchPolicy, policy);
+  CFDictionaryAddValue(query, kSecReturnRef, kCFBooleanTrue);
+  CFDictionaryAddValue(query, kSecMatchLimit, kSecMatchLimitOne);
+  CFDictionaryAddValue(query, kSecMatchSearchList, list);
+
+  CFRelease(list);
+
+  err = SecItemCopyMatching(query, (CFTypeRef *)&identity);
+
+#    if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
+  if (err && DNSSDHostName)
+  {
+   /*
+    * Search for the connection server name failed; try the DNS-SD .local
+    * hostname instead...
+    */
+
+    snprintf(localname, sizeof(localname), "%s.local", DNSSDHostName);
+
+    cupsdLogMessage(CUPSD_LOG_DEBUG,
+                   "copy_cdsa_certificate: Looking for certs for \"%s\".",
+                   localname);
+
+    servername = CFStringCreateWithCString(kCFAllocatorDefault, localname,
+                                          kCFStringEncodingUTF8);
+
+    CFRelease(policy);
+
+    policy = SecPolicyCreateSSL(1, servername);
+
+    if (servername)
+      CFRelease(servername);
+
+    if (!policy)
+    {
+      cupsdLogMessage(CUPSD_LOG_ERROR, "Cannot create ssl policy reference");
+      goto cleanup;
+    }
+
+    CFDictionarySetValue(query, kSecMatchPolicy, policy);
+
+    err = SecItemCopyMatching(query, (CFTypeRef *)&identity);
+  }
+#    endif /* HAVE_DNSSD || HAVE_AVAHI */
+
+  if (err)
+  {
+    cupsdLogMessage(CUPSD_LOG_DEBUG,
+                   "Cannot find signing key in keychain \"%s\": %s (%d)",
+                   ServerCertificate, cssmErrorString(err), (int)err);
+    goto cleanup;
+  }
+
+  if (CFGetTypeID(identity) != SecIdentityGetTypeID())
+  {
+    cupsdLogMessage(CUPSD_LOG_ERROR, "SecIdentity CFTypeID failure.");
+    goto cleanup;
+  }
+
+  if ((certificates = CFArrayCreate(NULL, (const void **)&identity,
+                                 1, &kCFTypeArrayCallBacks)) == NULL)
+  {
+    cupsdLogMessage(CUPSD_LOG_ERROR, "Cannot create certificate array");
+    goto cleanup;
+  }
+
+  cleanup :
+
+  if (keychain)
+    CFRelease(keychain);
+  if (search)
+    CFRelease(search);
+  if (identity)
+    CFRelease(identity);
+
+  if (policy)
+    CFRelease(policy);
+  if (query)
+    CFRelease(query);
+
+  return (certificates);
+}
+
+
+/*
+ * 'make_certificate()' - Make a self-signed SSL/TLS certificate.
+ */
+
+static int                             /* O - 1 on success, 0 on failure */
+make_certificate(cupsd_client_t *con)  /* I - Client connection */
+{
+#    ifdef HAVE_SECGENERATESELFSIGNEDCERTIFICATE
+  int                  status = 0;     /* Return status */
+  OSStatus             err;            /* Error code (if any) */
+#  if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
+  char                 localname[1024];/* Local hostname */
+#  endif /* HAVE_DNSSD || HAVE_AVAHI */
+  const char           *servername;    /* Name of server in cert */
+  CFStringRef          cfservername = NULL;
+                                       /* CF string for server name */
+  SecIdentityRef       ident = NULL;   /* Identity */
+  SecKeyRef            publicKey = NULL,
+                                       /* Public key */
+                       privateKey = NULL;
+                                       /* Private key */
+  CFMutableDictionaryRef keyParams = NULL;
+                                       /* Key generation parameters */
+
+
+  cupsdLogMessage(CUPSD_LOG_INFO,
+                  "Generating SSL server key and certificate.");
+
+#  if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
+  if (con->servername && isdigit(con->servername[0] & 255) && DNSSDHostName)
+  {
+    snprintf(localname, sizeof(localname), "%s.local", DNSSDHostName);
+    servername = localname;
+  }
+  else
+#  endif /* HAVE_DNSSD || HAVE_AVAHI */
+  servername = con->servername;
+
+  cfservername = CFStringCreateWithCString(kCFAllocatorDefault, servername,
+                                           kCFStringEncodingUTF8);
+  if (!cfservername)
+    goto cleanup;
+
+ /*
+  * Create a public/private key pair...
+  */
+
+  keyParams = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
+                                       &kCFTypeDictionaryKeyCallBacks,
+                                       &kCFTypeDictionaryValueCallBacks);
+  if (!keyParams)
+    goto cleanup;
+
+  CFDictionaryAddValue(keyParams, kSecAttrKeyType, kSecAttrKeyTypeRSA);
+  CFDictionaryAddValue(keyParams, kSecAttrKeySizeInBits, CFSTR("2048"));
+  CFDictionaryAddValue(keyParams, kSecAttrLabel,
+                       CFSTR("CUPS Self-Signed Certificate"));
+
+  err = SecKeyGeneratePair(keyParams, &publicKey, &privateKey);
+  if (err != noErr)
+  {
+    cupsdLogMessage(CUPSD_LOG_DEBUG, "SecKeyGeneratePair returned %ld.",
+                    (long)err);
+    goto cleanup;
+  }
+
+ /*
+  * Create a self-signed certificate using the public/private key pair...
+  */
+
+  CFIndex      usageInt = kSecKeyUsageAll;
+  CFNumberRef  usage = CFNumberCreate(alloc, kCFNumberCFIndexType, &usageInt);
+  CFDictionaryRef certParams = CFDictionaryCreateMutable(kCFAllocatorDefault,
+                                   kSecCSRBasicContraintsPathLen, CFINT(0),
+                                   kSecSubjectAltName, cfservername,
+                                   kSecCertificateKeyUsage, usage,
+                                   NULL, NULL);
+  CFRelease(usage);
+        
+  const void   *ca_o[] = { kSecOidOrganization, CFSTR("") };
+  const void   *ca_cn[] = { kSecOidCommonName, cfservername };
+  CFArrayRef   ca_o_dn = CFArrayCreate(kCFAllocatorDefault, ca_o, 2, NULL);
+  CFArrayRef   ca_cn_dn = CFArrayCreate(kCFAllocatorDefault, ca_cn, 2, NULL);
+  const void   *ca_dn_array[2];
+
+  ca_dn_array[0] = CFArrayCreate(kCFAllocatorDefault, (const void **)&ca_o_dn,
+                                 1, NULL);
+  ca_dn_array[1] = CFArrayCreate(kCFAllocatorDefault, (const void **)&ca_cn_dn,
+                                 1, NULL);
+
+  CFArrayRef   subject = CFArrayCreate(kCFAllocatorDefault, ca_dn_array, 2,
+                                        NULL);
+  SecCertificateRef cert = SecGenerateSelfSignedCertificate(subject, certParams,
+                                                            publicKey,
+                                                            privateKey);
+  CFRelease(subject);
+  CFRelease(certParams);
+
+  if (!cert)
+  {
+    cupsdLogMessage(CUPSD_LOG_DEBUG, "SecGenerateSelfSignedCertificate failed.");
+    goto cleanup;
+  }
+
+  ident = SecIdentityCreate(kCFAllocatorDefault, cert, privateKey);
+
+  if (ident)
+    cupsdLogMessage(CUPSD_LOG_INFO,
+                    "Created SSL server certificate file \"%s\".",
+                   ServerCertificate);
+
+ /*
+  * Cleanup and return...
+  */
+
+cleanup:
+
+  if (cfservername)
+    CFRelease(cfservername);
+
+  if (keyParams)
+    CFRelease(keyParams);
+
+  if (ident)
+    CFRelease(ident);
+
+  if (cert)
+    CFRelease(cert);
+
+  if (publicKey)
+    CFRelease(publicKey);
+
+  if (privateKey)
+    CFRelease(publicKey);
+
+  if (!status)
+    cupsdLogMessage(CUPSD_LOG_ERROR,
+                    "Unable to create SSL server key and certificate.");
+
+  return (status);
+
+#    else /* !HAVE_SECGENERATESELFSIGNEDCERTIFICATE */
+  int          pid,                    /* Process ID of command */
+               status;                 /* Status of command */
+  char         command[1024],          /* Command */
+               *argv[4],               /* Command-line arguments */
+               *envp[MAX_ENV + 1],     /* Environment variables */
+               keychain[1024],         /* Keychain argument */
+               infofile[1024],         /* Type-in information for cert */
+#      if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
+               localname[1024],        /* Local hostname */
+#      endif /* HAVE_DNSSD || HAVE_AVAHI */
+               *servername;            /* Name of server in cert */
+  cups_file_t  *fp;                    /* Seed/info file */
+  int          infofd;                 /* Info file descriptor */
+
+
+#      if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
+  if (con->servername && isdigit(con->servername[0] & 255) && DNSSDHostName)
+  {
+    snprintf(localname, sizeof(localname), "%s.local", DNSSDHostName);
+    servername = localname;
+  }
+  else
+#      endif /* HAVE_DNSSD || HAVE_AVAHI */
+    servername = con->servername;
+
+ /*
+  * Run the "certtool" command to generate a self-signed certificate...
+  */
+
+  if (!cupsFileFind("certtool", getenv("PATH"), 1, command, sizeof(command)))
+  {
+    cupsdLogMessage(CUPSD_LOG_ERROR,
+                    "No SSL certificate and certtool command not found.");
+    return (0);
+  }
+
+ /*
+  * Create a file with the certificate information fields...
+  *
+  * Note: This assumes that the default questions are asked by the certtool
+  * command...
+  */
+
+  if ((fp = cupsTempFile2(infofile, sizeof(infofile))) == NULL)
+  {
+    cupsdLogMessage(CUPSD_LOG_ERROR,
+                    "Unable to create certificate information file %s - %s",
+                    infofile, strerror(errno));
+    return (0);
+  }
+
+  cupsFilePrintf(fp,
+                 "%s\n"                        /* Enter key and certificate label */
+                 "r\n"                 /* Generate RSA key pair */
+                 "2048\n"              /* Key size in bits */
+                 "y\n"                 /* OK (y = yes) */
+                 "b\n"                 /* Usage (b=signing/encryption) */
+                 "s\n"                 /* Sign with SHA1 */
+                 "y\n"                 /* OK (y = yes) */
+                 "%s\n"                        /* Common name */
+                 "\n"                  /* Country (default) */
+                 "\n"                  /* Organization (default) */
+                 "\n"                  /* Organizational unit (default) */
+                 "\n"                  /* State/Province (default) */
+                 "%s\n"                        /* Email address */
+                 "y\n",                        /* OK (y = yes) */
+                servername, servername, ServerAdmin);
+  cupsFileClose(fp);
+
+  cupsdLogMessage(CUPSD_LOG_INFO,
+                  "Generating SSL server key and certificate.");
+
+  snprintf(keychain, sizeof(keychain), "k=%s", ServerCertificate);
+
+  argv[0] = "certtool";
+  argv[1] = "c";
+  argv[2] = keychain;
+  argv[3] = NULL;
+
+  cupsdLoadEnv(envp, MAX_ENV);
+
+  infofd = open(infofile, O_RDONLY);
+
+  if (!cupsdStartProcess(command, argv, envp, infofd, -1, -1, -1, -1, 1, NULL,
+                         NULL, &pid))
+  {
+    close(infofd);
+    unlink(infofile);
+    return (0);
+  }
+
+  close(infofd);
+  unlink(infofile);
+
+  while (waitpid(pid, &status, 0) < 0)
+    if (errno != EINTR)
+    {
+      status = 1;
+      break;
+    }
+
+  cupsdFinishProcess(pid, command, sizeof(command), NULL);
+
+  if (status)
+  {
+    if (WIFEXITED(status))
+      cupsdLogMessage(CUPSD_LOG_ERROR,
+                      "Unable to create SSL server key and certificate - "
+                     "the certtool command stopped with status %d.",
+                     WEXITSTATUS(status));
+    else
+      cupsdLogMessage(CUPSD_LOG_ERROR,
+                      "Unable to create SSL server key and certificate - "
+                     "the certtool command crashed on signal %d.",
+                     WTERMSIG(status));
+  }
+  else
+  {
+    cupsdLogMessage(CUPSD_LOG_INFO,
+                    "Created SSL server certificate file \"%s\".",
+                   ServerCertificate);
+  }
+
+  return (!status);
+#    endif /* HAVE_SECGENERATESELFSIGNEDCERTIFICATE */
+}
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/cups/tls-gnutls.c b/cups/tls-gnutls.c
new file mode 100644 (file)
index 0000000..6ef10c6
--- /dev/null
@@ -0,0 +1,1071 @@
+/*
+ * "$Id$"
+ *
+ * TLS support code for CUPS using GNU TLS.
+ *
+ * Copyright 2007-2013 by Apple Inc.
+ * Copyright 1997-2007 by Easy Software Products, all rights reserved.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law.  Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file.  If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ */
+
+
+/*
+ * Local functions...
+ */
+
+static int             make_certificate(cupsd_client_t *con);
+
+
+/*
+ * 'http_tls_initialize()' - Initialize the TLS stack.
+ */
+
+static void
+http_tls_initialize(void)
+{
+#ifdef HAVE_GNUTLS
+ /*
+  * Initialize GNU TLS...
+  */
+
+  gnutls_global_init();
+
+#elif defined(HAVE_LIBSSL)
+ /*
+  * Initialize OpenSSL...
+  */
+
+  SSL_load_error_strings();
+  SSL_library_init();
+
+ /*
+  * Using the current time is a dubious random seed, but on some systems
+  * it is the best we can do (on others, this seed isn't even used...)
+  */
+
+  CUPS_SRAND(time(NULL));
+
+  for (i = 0; i < sizeof(data); i ++)
+    data[i] = CUPS_RAND();
+
+  RAND_seed(data, sizeof(data));
+#endif /* HAVE_GNUTLS */
+}
+
+
+#ifdef HAVE_SSL
+/*
+ * 'http_tls_read()' - Read from a SSL/TLS connection.
+ */
+
+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 */
+{
+#  if defined(HAVE_LIBSSL)
+  return (SSL_read((SSL *)(http->tls), buf, len));
+
+#  elif defined(HAVE_GNUTLS)
+  ssize_t      result;                 /* Return value */
+
+
+  result = gnutls_record_recv(http->tls, buf, len);
+
+  if (result < 0 && !errno)
+  {
+   /*
+    * Convert GNU TLS error to errno value...
+    */
+
+    switch (result)
+    {
+      case GNUTLS_E_INTERRUPTED :
+         errno = EINTR;
+         break;
+
+      case GNUTLS_E_AGAIN :
+          errno = EAGAIN;
+          break;
+
+      default :
+          errno = EPIPE;
+          break;
+    }
+
+    result = -1;
+  }
+
+  return ((int)result);
+
+#  elif defined(HAVE_CDSASSL)
+  int          result;                 /* Return value */
+  OSStatus     error;                  /* Error info */
+  size_t       processed;              /* Number of bytes processed */
+
+
+  error = SSLRead(http->tls, buf, len, &processed);
+  DEBUG_printf(("6http_tls_read: error=%d, processed=%d", (int)error,
+                (int)processed));
+  switch (error)
+  {
+    case 0 :
+       result = (int)processed;
+       break;
+
+    case errSSLWouldBlock :
+       if (processed)
+         result = (int)processed;
+       else
+       {
+         result = -1;
+         errno  = EINTR;
+       }
+       break;
+
+    case errSSLClosedGraceful :
+    default :
+       if (processed)
+         result = (int)processed;
+       else
+       {
+         result = -1;
+         errno  = EPIPE;
+       }
+       break;
+  }
+
+  return (result);
+
+#  elif defined(HAVE_SSPISSL)
+  return _sspiRead((_sspi_struct_t*) http->tls, buf, len);
+#  endif /* HAVE_LIBSSL */
+}
+#endif /* HAVE_SSL */
+
+
+#ifdef HAVE_SSL
+/*
+ * 'http_setup_ssl()' - Set up SSL/TLS support on a connection.
+ */
+
+static int                             /* O - 0 on success, -1 on failure */
+http_setup_ssl(http_t *http)           /* I - Connection to server */
+{
+  char                 hostname[256],  /* Hostname */
+                       *hostptr;       /* Pointer into hostname */
+
+#  ifdef HAVE_LIBSSL
+  SSL_CTX              *context;       /* Context for encryption */
+  BIO                  *bio;           /* BIO data */
+  const char           *message = NULL;/* Error message */
+#  elif defined(HAVE_GNUTLS)
+  int                  status;         /* Status of handshake */
+  gnutls_certificate_client_credentials *credentials;
+                                       /* TLS credentials */
+#  elif defined(HAVE_CDSASSL)
+  _cups_globals_t      *cg = _cupsGlobals();
+                                       /* Pointer to library globals */
+  OSStatus             error;          /* Error code */
+  const char           *message = NULL;/* Error message */
+  cups_array_t         *credentials;   /* Credentials array */
+  cups_array_t         *names;         /* CUPS distinguished names */
+  CFArrayRef           dn_array;       /* CF distinguished names array */
+  CFIndex              count;          /* Number of credentials */
+  CFDataRef            data;           /* Certificate data */
+  int                  i;              /* Looping var */
+  http_credential_t    *credential;    /* Credential data */
+#  elif defined(HAVE_SSPISSL)
+  TCHAR                        username[256];  /* Username returned from GetUserName() */
+  TCHAR                        commonName[256];/* Common name for certificate */
+  DWORD                        dwSize;         /* 32 bit size */
+#  endif /* HAVE_LIBSSL */
+
+
+  DEBUG_printf(("7http_setup_ssl(http=%p)", http));
+
+ /*
+  * Get the hostname to use for SSL...
+  */
+
+  if (httpAddrLocalhost(http->hostaddr))
+  {
+    strlcpy(hostname, "localhost", sizeof(hostname));
+  }
+  else
+  {
+   /*
+    * Otherwise make sure the hostname we have does not end in a trailing dot.
+    */
+
+    strlcpy(hostname, http->hostname, sizeof(hostname));
+    if ((hostptr = hostname + strlen(hostname) - 1) >= hostname &&
+        *hostptr == '.')
+      *hostptr = '\0';
+  }
+
+#  ifdef HAVE_LIBSSL
+  context = SSL_CTX_new(SSLv23_client_method());
+
+  SSL_CTX_set_options(context, SSL_OP_NO_SSLv2); /* Only use SSLv3 or TLS */
+
+  bio = BIO_new(_httpBIOMethods());
+  BIO_ctrl(bio, BIO_C_SET_FILE_PTR, 0, (char *)http);
+
+  http->tls = SSL_new(context);
+  SSL_set_bio(http->tls, bio, bio);
+
+#   ifdef HAVE_SSL_SET_TLSEXT_HOST_NAME
+  SSL_set_tlsext_host_name(http->tls, hostname);
+#   endif /* HAVE_SSL_SET_TLSEXT_HOST_NAME */
+
+  if (SSL_connect(http->tls) != 1)
+  {
+    unsigned long      error;  /* Error code */
+
+    while ((error = ERR_get_error()) != 0)
+    {
+      message = ERR_error_string(error, NULL);
+      DEBUG_printf(("8http_setup_ssl: %s", message));
+    }
+
+    SSL_CTX_free(context);
+    SSL_free(http->tls);
+    http->tls = NULL;
+
+#    ifdef WIN32
+    http->error  = WSAGetLastError();
+#    else
+    http->error  = errno;
+#    endif /* WIN32 */
+    http->status = HTTP_STATUS_ERROR;
+
+    if (!message)
+      message = _("Unable to establish a secure connection to host.");
+
+    _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI, message, 1);
+
+    return (-1);
+  }
+
+#  elif defined(HAVE_GNUTLS)
+  credentials = (gnutls_certificate_client_credentials *)
+                    malloc(sizeof(gnutls_certificate_client_credentials));
+  if (credentials == NULL)
+  {
+    DEBUG_printf(("8http_setup_ssl: Unable to allocate credentials: %s",
+                  strerror(errno)));
+    http->error  = errno;
+    http->status = HTTP_STATUS_ERROR;
+    _cupsSetHTTPError(HTTP_STATUS_ERROR);
+
+    return (-1);
+  }
+
+  gnutls_certificate_allocate_credentials(credentials);
+
+  gnutls_init(&http->tls, GNUTLS_CLIENT);
+  gnutls_set_default_priority(http->tls);
+  gnutls_server_name_set(http->tls, GNUTLS_NAME_DNS, hostname,
+                         strlen(hostname));
+  gnutls_credentials_set(http->tls, GNUTLS_CRD_CERTIFICATE, *credentials);
+  gnutls_transport_set_ptr(http->tls, (gnutls_transport_ptr)http);
+  gnutls_transport_set_pull_function(http->tls, _httpReadGNUTLS);
+  gnutls_transport_set_push_function(http->tls, _httpWriteGNUTLS);
+
+  while ((status = gnutls_handshake(http->tls)) != GNUTLS_E_SUCCESS)
+  {
+    DEBUG_printf(("8http_setup_ssl: gnutls_handshake returned %d (%s)",
+                  status, gnutls_strerror(status)));
+
+    if (gnutls_error_is_fatal(status))
+    {
+      http->error  = EIO;
+      http->status = HTTP_STATUS_ERROR;
+
+      _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI, gnutls_strerror(status), 0);
+
+      gnutls_deinit(http->tls);
+      gnutls_certificate_free_credentials(*credentials);
+      free(credentials);
+      http->tls = NULL;
+
+      return (-1);
+    }
+  }
+
+  http->tls_credentials = credentials;
+
+#  elif defined(HAVE_CDSASSL)
+  if ((http->tls = SSLCreateContext(kCFAllocatorDefault, kSSLClientSide,
+                                    kSSLStreamType)) == NULL)
+  {
+    DEBUG_puts("4http_setup_ssl: SSLCreateContext failed.");
+    http->error  = errno = ENOMEM;
+    http->status = HTTP_STATUS_ERROR;
+    _cupsSetHTTPError(HTTP_STATUS_ERROR);
+
+    return (-1);
+  }
+
+  error = SSLSetConnection(http->tls, http);
+  DEBUG_printf(("4http_setup_ssl: SSLSetConnection, error=%d", (int)error));
+
+  if (!error)
+  {
+    error = SSLSetIOFuncs(http->tls, _httpReadCDSA, _httpWriteCDSA);
+    DEBUG_printf(("4http_setup_ssl: SSLSetIOFuncs, error=%d", (int)error));
+  }
+
+  if (!error)
+  {
+    error = SSLSetSessionOption(http->tls, kSSLSessionOptionBreakOnServerAuth,
+                                true);
+    DEBUG_printf(("4http_setup_ssl: SSLSetSessionOption, error=%d",
+                  (int)error));
+  }
+
+  if (!error)
+  {
+    if (cg->client_cert_cb)
+    {
+      error = SSLSetSessionOption(http->tls,
+                                 kSSLSessionOptionBreakOnCertRequested, true);
+      DEBUG_printf(("4http_setup_ssl: kSSLSessionOptionBreakOnCertRequested, "
+                    "error=%d", (int)error));
+    }
+    else
+    {
+      error = http_set_credentials(http);
+      DEBUG_printf(("4http_setup_ssl: http_set_credentials, error=%d",
+                    (int)error));
+    }
+  }
+
+ /*
+  * Let the server know which hostname/domain we are trying to connect to
+  * in case it wants to serve up a certificate with a matching common name.
+  */
+
+  if (!error)
+  {
+    error = SSLSetPeerDomainName(http->tls, hostname, strlen(hostname));
+
+    DEBUG_printf(("4http_setup_ssl: SSLSetPeerDomainName, error=%d",
+                  (int)error));
+  }
+
+  if (!error)
+  {
+    int done = 0;                      /* Are we done yet? */
+
+    while (!error && !done)
+    {
+      error = SSLHandshake(http->tls);
+
+      DEBUG_printf(("4http_setup_ssl: SSLHandshake returned %d.", (int)error));
+
+      switch (error)
+      {
+       case noErr :
+           done = 1;
+           break;
+
+       case errSSLWouldBlock :
+           error = noErr;              /* Force a retry */
+           usleep(1000);               /* in 1 millisecond */
+           break;
+
+       case errSSLServerAuthCompleted :
+           error = 0;
+           if (cg->server_cert_cb)
+           {
+             error = httpCopyCredentials(http, &credentials);
+             if (!error)
+             {
+               error = (cg->server_cert_cb)(http, http->tls, credentials,
+                                            cg->server_cert_data);
+               httpFreeCredentials(credentials);
+             }
+
+             DEBUG_printf(("4http_setup_ssl: Server certificate callback "
+                           "returned %d.", (int)error));
+           }
+           break;
+
+       case errSSLClientCertRequested :
+           error = 0;
+
+           if (cg->client_cert_cb)
+           {
+             names = NULL;
+             if (!(error = SSLCopyDistinguishedNames(http->tls, &dn_array)) &&
+                 dn_array)
+             {
+               if ((names = cupsArrayNew(NULL, NULL)) != NULL)
+               {
+                 for (i = 0, count = CFArrayGetCount(dn_array); i < count; i++)
+                 {
+                   data = (CFDataRef)CFArrayGetValueAtIndex(dn_array, i);
+
+                   if ((credential = malloc(sizeof(*credential))) != NULL)
+                   {
+                     credential->datalen = CFDataGetLength(data);
+                     if ((credential->data = malloc(credential->datalen)))
+                     {
+                       memcpy((void *)credential->data, CFDataGetBytePtr(data),
+                              credential->datalen);
+                       cupsArrayAdd(names, credential);
+                     }
+                     else
+                       free(credential);
+                   }
+                 }
+               }
+
+               CFRelease(dn_array);
+             }
+
+             if (!error)
+             {
+               error = (cg->client_cert_cb)(http, http->tls, names,
+                                            cg->client_cert_data);
+
+               DEBUG_printf(("4http_setup_ssl: Client certificate callback "
+                             "returned %d.", (int)error));
+             }
+
+             httpFreeCredentials(names);
+           }
+           break;
+
+       case errSSLUnknownRootCert :
+           message = _("Unable to establish a secure connection to host "
+                       "(untrusted certificate).");
+           break;
+
+       case errSSLNoRootCert :
+           message = _("Unable to establish a secure connection to host "
+                       "(self-signed certificate).");
+           break;
+
+       case errSSLCertExpired :
+           message = _("Unable to establish a secure connection to host "
+                       "(expired certificate).");
+           break;
+
+       case errSSLCertNotYetValid :
+           message = _("Unable to establish a secure connection to host "
+                       "(certificate not yet valid).");
+           break;
+
+       case errSSLHostNameMismatch :
+           message = _("Unable to establish a secure connection to host "
+                       "(host name mismatch).");
+           break;
+
+       case errSSLXCertChainInvalid :
+           message = _("Unable to establish a secure connection to host "
+                       "(certificate chain invalid).");
+           break;
+
+       case errSSLConnectionRefused :
+           message = _("Unable to establish a secure connection to host "
+                       "(peer dropped connection before responding).");
+           break;
+
+       default :
+           break;
+      }
+    }
+  }
+
+  if (error)
+  {
+    http->error  = error;
+    http->status = HTTP_STATUS_ERROR;
+    errno        = ECONNREFUSED;
+
+    CFRelease(http->tls);
+    http->tls = NULL;
+
+   /*
+    * If an error string wasn't set by the callbacks use a generic one...
+    */
+
+    if (!message)
+#ifdef HAVE_CSSMERRORSTRING
+      message = cssmErrorString(error);
+#else
+      message = _("Unable to establish a secure connection to host.");
+#endif /* HAVE_CSSMERRORSTRING */
+
+    _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI, message, 1);
+
+    return (-1);
+  }
+
+#  elif defined(HAVE_SSPISSL)
+  http->tls = _sspiAlloc();
+
+  if (!http->tls)
+  {
+    _cupsSetHTTPError(HTTP_STATUS_ERROR);
+    return (-1);
+  }
+
+  http->tls->sock = http->fd;
+  dwSize          = sizeof(username) / sizeof(TCHAR);
+  GetUserName(username, &dwSize);
+  _sntprintf_s(commonName, sizeof(commonName) / sizeof(TCHAR),
+               sizeof(commonName) / sizeof(TCHAR), TEXT("CN=%s"), username);
+
+  if (!_sspiGetCredentials(http->tls_credentials, L"ClientContainer",
+                           commonName, FALSE))
+  {
+    _sspiFree(http->tls_credentials);
+    http->tls_credentials = NULL;
+
+    http->error  = EIO;
+    http->status = HTTP_STATUS_ERROR;
+
+    _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI,
+                  _("Unable to establish a secure connection to host."), 1);
+
+    return (-1);
+  }
+
+  _sspiSetAllowsAnyRoot(http->tls_credentials, TRUE);
+  _sspiSetAllowsExpiredCerts(http->tls_credentials, TRUE);
+
+  if (!_sspiConnect(http->tls_credentials, hostname))
+  {
+    _sspiFree(http->tls_credentials);
+    http->tls_credentials = NULL;
+
+    http->error  = EIO;
+    http->status = HTTP_STATUS_ERROR;
+
+    _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI,
+                  _("Unable to establish a secure connection to host."), 1);
+
+    return (-1);
+  }
+#  endif /* HAVE_CDSASSL */
+
+  return (0);
+}
+
+
+/*
+ * 'http_shutdown_ssl()' - Shut down SSL/TLS on a connection.
+ */
+
+static void
+http_shutdown_ssl(http_t *http)                /* I - Connection to server */
+{
+#  ifdef HAVE_LIBSSL
+  SSL_CTX      *context;               /* Context for encryption */
+
+  context = SSL_get_SSL_CTX(http->tls);
+
+  SSL_shutdown(http->tls);
+  SSL_CTX_free(context);
+  SSL_free(http->tls);
+
+#  elif defined(HAVE_GNUTLS)
+  gnutls_certificate_client_credentials *credentials;
+                                       /* TLS credentials */
+
+  credentials = (gnutls_certificate_client_credentials *)(http->tls_credentials);
+
+  gnutls_bye(http->tls, GNUTLS_SHUT_RDWR);
+  gnutls_deinit(http->tls);
+  gnutls_certificate_free_credentials(*credentials);
+  free(credentials);
+
+#  elif defined(HAVE_CDSASSL)
+  while (SSLClose(http->tls) == errSSLWouldBlock)
+    usleep(1000);
+
+  CFRelease(http->tls);
+
+  if (http->tls_credentials)
+    CFRelease(http->tls_credentials);
+
+#  elif defined(HAVE_SSPISSL)
+  _sspiFree(http->tls_credentials);
+#  endif /* HAVE_LIBSSL */
+
+  http->tls             = NULL;
+  http->tls_credentials = NULL;
+}
+#endif /* HAVE_SSL */
+
+
+#ifdef HAVE_SSL
+/*
+ * 'http_write_ssl()' - Write to a SSL/TLS connection.
+ */
+
+static int                             /* O - Bytes written */
+http_write_ssl(http_t     *http,       /* I - Connection to server */
+              const char *buf,         /* I - Buffer holding data */
+              int        len)          /* I - Length of buffer */
+{
+  ssize_t      result;                 /* Return value */
+
+
+  DEBUG_printf(("2http_write_ssl(http=%p, buf=%p, len=%d)", http, buf, len));
+
+#  if defined(HAVE_LIBSSL)
+  result = SSL_write((SSL *)(http->tls), buf, len);
+
+#  elif defined(HAVE_GNUTLS)
+  result = gnutls_record_send(http->tls, buf, len);
+
+  if (result < 0 && !errno)
+  {
+   /*
+    * Convert GNU TLS error to errno value...
+    */
+
+    switch (result)
+    {
+      case GNUTLS_E_INTERRUPTED :
+         errno = EINTR;
+         break;
+
+      case GNUTLS_E_AGAIN :
+          errno = EAGAIN;
+          break;
+
+      default :
+          errno = EPIPE;
+          break;
+    }
+
+    result = -1;
+  }
+
+#  elif defined(HAVE_CDSASSL)
+  OSStatus     error;                  /* Error info */
+  size_t       processed;              /* Number of bytes processed */
+
+
+  error = SSLWrite(http->tls, buf, len, &processed);
+
+  switch (error)
+  {
+    case 0 :
+       result = (int)processed;
+       break;
+
+    case errSSLWouldBlock :
+       if (processed)
+         result = (int)processed;
+       else
+       {
+         result = -1;
+         errno  = EINTR;
+       }
+       break;
+
+    case errSSLClosedGraceful :
+    default :
+       if (processed)
+         result = (int)processed;
+       else
+       {
+         result = -1;
+         errno  = EPIPE;
+       }
+       break;
+  }
+#  elif defined(HAVE_SSPISSL)
+  return _sspiWrite((_sspi_struct_t *)http->tls, (void *)buf, len);
+#  endif /* HAVE_LIBSSL */
+
+  DEBUG_printf(("3http_write_ssl: Returning %d.", (int)result));
+
+  return ((int)result);
+}
+#endif /* HAVE_SSL */
+
+
+/*
+ * 'http_tls_pending()' - Return the number of pending TLS-encrypted bytes.
+ */
+
+static size_t
+http_tls_pending(http_t *http)         /* I - HTTP connection */
+{
+  if (http->tls && usessl)
+  {
+#  ifdef HAVE_LIBSSL
+    if (SSL_pending(http->tls))
+    {
+      DEBUG_puts("5_httpWait: Return 1 since there is pending SSL data.");
+      return (1);
+    }
+
+#  elif defined(HAVE_GNUTLS)
+    if (gnutls_record_check_pending(http->tls))
+    {
+      DEBUG_puts("5_httpWait: Return 1 since there is pending SSL data.");
+      return (1);
+    }
+
+#  elif defined(HAVE_CDSASSL)
+    size_t bytes;                      /* Bytes that are available */
+
+    if (!SSLGetBufferedReadSize(http->tls, &bytes) &&
+        bytes > 0)
+    {
+      DEBUG_puts("5_httpWait: Return 1 since there is pending SSL data.");
+      return (1);
+    }
+#  endif /* HAVE_LIBSSL */
+}
+
+
+#if defined(HAVE_SSL) && defined(HAVE_GNUTLS)
+/*
+ * '_httpReadGNUTLS()' - Read function for the GNU TLS library.
+ */
+
+ssize_t                                        /* O - Number of bytes read or -1 on error */
+_httpReadGNUTLS(
+    gnutls_transport_ptr ptr,          /* I - Connection to server */
+    void                 *data,                /* I - Buffer */
+    size_t               length)       /* I - Number of bytes to read */
+{
+  http_t       *http;                  /* HTTP connection */
+  ssize_t      bytes;                  /* Bytes read */
+
+
+  DEBUG_printf(("6_httpReadGNUTLS(ptr=%p, data=%p, length=%d)", ptr, data, (int)length));
+
+  http = (http_t *)ptr;
+
+  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);
+    }
+  }
+
+  bytes = recv(http->fd, data, length, 0);
+  DEBUG_printf(("6_httpReadGNUTLS: bytes=%d", (int)bytes));
+  return (bytes);
+}
+#endif /* HAVE_SSL && HAVE_GNUTLS */
+
+
+#if defined(HAVE_SSL) && defined(HAVE_GNUTLS)
+/*
+ * '_httpWriteGNUTLS()' - Write function for the GNU TLS library.
+ */
+
+ssize_t                                        /* O - Number of bytes written or -1 on error */
+_httpWriteGNUTLS(
+    gnutls_transport_ptr ptr,          /* I - Connection to server */
+    const void           *data,                /* I - Data buffer */
+    size_t               length)       /* I - Number of bytes to write */
+{
+  ssize_t bytes;                       /* Bytes written */
+
+
+  DEBUG_printf(("6_httpWriteGNUTLS(ptr=%p, data=%p, length=%d)", ptr, data,
+                (int)length));
+#ifdef DEBUG
+  http_debug_hex("_httpWriteGNUTLS", data, (int)length);
+#endif /* DEBUG */
+
+  bytes = send(((http_t *)ptr)->fd, data, length, 0);
+  DEBUG_printf(("_httpWriteGNUTLS: bytes=%d", (int)bytes));
+
+  return (bytes);
+}
+#endif /* HAVE_SSL && HAVE_GNUTLS */
+
+
+/*
+ * 'cupsdEndTLS()' - Shutdown a secure session with the client.
+ */
+
+int                                    /* O - 1 on success, 0 on error */
+cupsdEndTLS(cupsd_client_t *con)       /* I - Client connection */
+{
+  int          error;                  /* Error code */
+  gnutls_certificate_server_credentials *credentials;
+                                       /* TLS credentials */
+
+
+  credentials = (gnutls_certificate_server_credentials *)
+                    (con->http.tls_credentials);
+
+  error = gnutls_bye(con->http.tls, GNUTLS_SHUT_WR);
+  switch (error)
+  {
+    case GNUTLS_E_SUCCESS:
+      cupsdLogMessage(CUPSD_LOG_DEBUG,
+                     "SSL shutdown successful!");
+      break;
+    default:
+      cupsdLogMessage(CUPSD_LOG_ERROR,
+                     "SSL shutdown failed: %s", gnutls_strerror(error));
+      break;
+  }
+
+  gnutls_deinit(con->http.tls);
+  con->http.tls = NULL;
+
+  gnutls_certificate_free_credentials(*credentials);
+  free(credentials);
+
+  return (1);
+}
+
+
+/*
+ * 'cupsdStartTLS()' - Start a secure session with the client.
+ */
+
+int                                    /* O - 1 on success, 0 on error */
+cupsdStartTLS(cupsd_client_t *con)     /* I - Client connection */
+{
+  int          status;                 /* Error code */
+  gnutls_certificate_server_credentials *credentials;
+                                       /* TLS credentials */
+
+
+  cupsdLogMessage(CUPSD_LOG_DEBUG, "[Client %d] Encrypting connection.",
+                  con->http.fd);
+
+ /*
+  * Verify that we have a certificate...
+  */
+
+  if (access(ServerKey, 0) || access(ServerCertificate, 0))
+  {
+   /*
+    * Nope, make a self-signed certificate...
+    */
+
+    if (!make_certificate(con))
+      return (0);
+  }
+
+ /*
+  * Create the SSL object and perform the SSL handshake...
+  */
+
+  credentials = (gnutls_certificate_server_credentials *)
+                    malloc(sizeof(gnutls_certificate_server_credentials));
+  if (credentials == NULL)
+  {
+    cupsdLogMessage(CUPSD_LOG_ERROR,
+                    "Unable to encrypt connection from %s - %s",
+                    con->http.hostname, strerror(errno));
+
+    return (0);
+  }
+
+  gnutls_certificate_allocate_credentials(credentials);
+  gnutls_certificate_set_x509_key_file(*credentials, ServerCertificate,
+                                      ServerKey, GNUTLS_X509_FMT_PEM);
+
+  gnutls_init(&con->http.tls, GNUTLS_SERVER);
+  gnutls_set_default_priority(con->http.tls);
+
+  gnutls_credentials_set(con->http.tls, GNUTLS_CRD_CERTIFICATE, *credentials);
+  gnutls_transport_set_ptr(con->http.tls, (gnutls_transport_ptr)HTTP(con));
+  gnutls_transport_set_pull_function(con->http.tls, _httpReadGNUTLS);
+  gnutls_transport_set_push_function(con->http.tls, _httpWriteGNUTLS);
+
+  while ((status = gnutls_handshake(con->http.tls)) != GNUTLS_E_SUCCESS)
+  {
+    if (gnutls_error_is_fatal(status))
+    {
+      cupsdLogMessage(CUPSD_LOG_ERROR,
+                      "Unable to encrypt connection from %s - %s",
+                      con->http.hostname, gnutls_strerror(status));
+
+      gnutls_deinit(con->http.tls);
+      gnutls_certificate_free_credentials(*credentials);
+      con->http.tls = NULL;
+      free(credentials);
+      return (0);
+    }
+  }
+
+  cupsdLogMessage(CUPSD_LOG_DEBUG, "Connection from %s now encrypted.",
+                  con->http.hostname);
+
+  con->http.tls_credentials = credentials;
+  return (1);
+}
+
+
+/*
+ * 'make_certificate()' - Make a self-signed SSL/TLS certificate.
+ */
+
+static int                             /* O - 1 on success, 0 on failure */
+make_certificate(cupsd_client_t *con)  /* I - Client connection */
+{
+  gnutls_x509_crt      crt;            /* Self-signed certificate */
+  gnutls_x509_privkey  key;            /* Encryption key */
+  cups_lang_t          *language;      /* Default language info */
+  cups_file_t          *fp;            /* Key/cert file */
+  unsigned char                buffer[8192];   /* Buffer for x509 data */
+  size_t               bytes;          /* Number of bytes of data */
+  unsigned char                serial[4];      /* Serial number buffer */
+  time_t               curtime;        /* Current time */
+  int                  result;         /* Result of GNU TLS calls */
+
+
+ /*
+  * Create the encryption key...
+  */
+
+  cupsdLogMessage(CUPSD_LOG_INFO, "Generating SSL server key...");
+
+  gnutls_x509_privkey_init(&key);
+  gnutls_x509_privkey_generate(key, GNUTLS_PK_RSA, 2048, 0);
+
+ /*
+  * Save it...
+  */
+
+  bytes = sizeof(buffer);
+
+  if ((result = gnutls_x509_privkey_export(key, GNUTLS_X509_FMT_PEM,
+                                           buffer, &bytes)) < 0)
+  {
+    cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to export SSL server key - %s",
+                    gnutls_strerror(result));
+    gnutls_x509_privkey_deinit(key);
+    return (0);
+  }
+  else if ((fp = cupsFileOpen(ServerKey, "w")) != NULL)
+  {
+    cupsFileWrite(fp, (char *)buffer, bytes);
+    cupsFileClose(fp);
+
+    cupsdLogMessage(CUPSD_LOG_INFO, "Created SSL server key file \"%s\"...",
+                   ServerKey);
+  }
+  else
+  {
+    cupsdLogMessage(CUPSD_LOG_ERROR,
+                    "Unable to create SSL server key file \"%s\" - %s",
+                   ServerKey, strerror(errno));
+    gnutls_x509_privkey_deinit(key);
+    return (0);
+  }
+
+ /*
+  * Create the self-signed certificate...
+  */
+
+  cupsdLogMessage(CUPSD_LOG_INFO, "Generating self-signed SSL certificate...");
+
+  language  = cupsLangDefault();
+  curtime   = time(NULL);
+  serial[0] = curtime >> 24;
+  serial[1] = curtime >> 16;
+  serial[2] = curtime >> 8;
+  serial[3] = curtime;
+
+  gnutls_x509_crt_init(&crt);
+  if (strlen(language->language) == 5)
+    gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_COUNTRY_NAME, 0,
+                                  language->language + 3, 2);
+  else
+    gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_COUNTRY_NAME, 0,
+                                  "US", 2);
+  gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_COMMON_NAME, 0,
+                                ServerName, strlen(ServerName));
+  gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_ORGANIZATION_NAME, 0,
+                                ServerName, strlen(ServerName));
+  gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_ORGANIZATIONAL_UNIT_NAME,
+                                0, "Unknown", 7);
+  gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_STATE_OR_PROVINCE_NAME, 0,
+                                "Unknown", 7);
+  gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_LOCALITY_NAME, 0,
+                                "Unknown", 7);
+  gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_PKCS9_EMAIL, 0,
+                                ServerAdmin, strlen(ServerAdmin));
+  gnutls_x509_crt_set_key(crt, key);
+  gnutls_x509_crt_set_serial(crt, serial, sizeof(serial));
+  gnutls_x509_crt_set_activation_time(crt, curtime);
+  gnutls_x509_crt_set_expiration_time(crt, curtime + 10 * 365 * 86400);
+  gnutls_x509_crt_set_ca_status(crt, 0);
+  gnutls_x509_crt_set_subject_alternative_name(crt, GNUTLS_SAN_DNSNAME,
+                                               ServerName);
+  gnutls_x509_crt_set_key_purpose_oid(crt, GNUTLS_KP_TLS_WWW_SERVER, 0);
+  gnutls_x509_crt_set_key_usage(crt, GNUTLS_KEY_KEY_ENCIPHERMENT);
+  gnutls_x509_crt_set_version(crt, 3);
+
+  bytes = sizeof(buffer);
+  if (gnutls_x509_crt_get_key_id(crt, 0, buffer, &bytes) >= 0)
+    gnutls_x509_crt_set_subject_key_id(crt, buffer, bytes);
+
+  gnutls_x509_crt_sign(crt, crt, key);
+
+ /*
+  * Save it...
+  */
+
+  bytes = sizeof(buffer);
+  if ((result = gnutls_x509_crt_export(crt, GNUTLS_X509_FMT_PEM,
+                                       buffer, &bytes)) < 0)
+    cupsdLogMessage(CUPSD_LOG_ERROR,
+                    "Unable to export SSL server certificate - %s",
+                   gnutls_strerror(result));
+  else if ((fp = cupsFileOpen(ServerCertificate, "w")) != NULL)
+  {
+    cupsFileWrite(fp, (char *)buffer, bytes);
+    cupsFileClose(fp);
+
+    cupsdLogMessage(CUPSD_LOG_INFO,
+                    "Created SSL server certificate file \"%s\"...",
+                   ServerCertificate);
+  }
+  else
+    cupsdLogMessage(CUPSD_LOG_ERROR,
+                    "Unable to create SSL server certificate file \"%s\" - %s",
+                   ServerCertificate, strerror(errno));
+
+ /*
+  * Cleanup...
+  */
+
+  gnutls_x509_crt_deinit(crt);
+  gnutls_x509_privkey_deinit(key);
+
+  return (1);
+}
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/cups/tls-openssl.c b/cups/tls-openssl.c
new file mode 100644 (file)
index 0000000..78ec40f
--- /dev/null
@@ -0,0 +1,1239 @@
+/*
+ * "$Id$"
+ *
+ * TLS support code for CUPS using OpenSSL.
+ *
+ * Copyright 2007-2012 by Apple Inc.
+ * Copyright 1997-2007 by Easy Software Products, all rights reserved.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law.  Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file.  If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ */
+
+
+/*
+ * Local functions...
+ */
+
+static int             make_certificate(cupsd_client_t *con);
+#if defined(HAVE_SSL) && defined(HAVE_LIBSSL)
+/*
+ * BIO methods for OpenSSL...
+ */
+
+static int             http_bio_write(BIO *h, const char *buf, int num);
+static int             http_bio_read(BIO *h, char *buf, int size);
+static int             http_bio_puts(BIO *h, const char *str);
+static long            http_bio_ctrl(BIO *h, int cmd, long arg1, void *arg2);
+static int             http_bio_new(BIO *h);
+static int             http_bio_free(BIO *data);
+
+static BIO_METHOD      http_bio_methods =
+                       {
+                         BIO_TYPE_SOCKET,
+                         "http",
+                         http_bio_write,
+                         http_bio_read,
+                         http_bio_puts,
+                         NULL, /* http_bio_gets, */
+                         http_bio_ctrl,
+                         http_bio_new,
+                         http_bio_free,
+                         NULL,
+                       };
+#endif /* HAVE_SSL && HAVE_LIBSSL */
+
+
+
+
+/*
+ * 'http_tls_initialize()' - Initialize the TLS stack.
+ */
+
+static void
+http_tls_initialize(void)
+{
+#ifdef HAVE_GNUTLS
+ /*
+  * Initialize GNU TLS...
+  */
+
+  gnutls_global_init();
+
+#elif defined(HAVE_LIBSSL)
+ /*
+  * Initialize OpenSSL...
+  */
+
+  SSL_load_error_strings();
+  SSL_library_init();
+
+ /*
+  * Using the current time is a dubious random seed, but on some systems
+  * it is the best we can do (on others, this seed isn't even used...)
+  */
+
+  CUPS_SRAND(time(NULL));
+
+  for (i = 0; i < sizeof(data); i ++)
+    data[i] = CUPS_RAND();
+
+  RAND_seed(data, sizeof(data));
+#endif /* HAVE_GNUTLS */
+}
+
+
+#ifdef HAVE_SSL
+/*
+ * 'http_tls_read()' - Read from a SSL/TLS connection.
+ */
+
+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 */
+{
+#  if defined(HAVE_LIBSSL)
+  return (SSL_read((SSL *)(http->tls), buf, len));
+
+#  elif defined(HAVE_GNUTLS)
+  ssize_t      result;                 /* Return value */
+
+
+  result = gnutls_record_recv(http->tls, buf, len);
+
+  if (result < 0 && !errno)
+  {
+   /*
+    * Convert GNU TLS error to errno value...
+    */
+
+    switch (result)
+    {
+      case GNUTLS_E_INTERRUPTED :
+         errno = EINTR;
+         break;
+
+      case GNUTLS_E_AGAIN :
+          errno = EAGAIN;
+          break;
+
+      default :
+          errno = EPIPE;
+          break;
+    }
+
+    result = -1;
+  }
+
+  return ((int)result);
+
+#  elif defined(HAVE_CDSASSL)
+  int          result;                 /* Return value */
+  OSStatus     error;                  /* Error info */
+  size_t       processed;              /* Number of bytes processed */
+
+
+  error = SSLRead(http->tls, buf, len, &processed);
+  DEBUG_printf(("6http_tls_read: error=%d, processed=%d", (int)error,
+                (int)processed));
+  switch (error)
+  {
+    case 0 :
+       result = (int)processed;
+       break;
+
+    case errSSLWouldBlock :
+       if (processed)
+         result = (int)processed;
+       else
+       {
+         result = -1;
+         errno  = EINTR;
+       }
+       break;
+
+    case errSSLClosedGraceful :
+    default :
+       if (processed)
+         result = (int)processed;
+       else
+       {
+         result = -1;
+         errno  = EPIPE;
+       }
+       break;
+  }
+
+  return (result);
+
+#  elif defined(HAVE_SSPISSL)
+  return _sspiRead((_sspi_struct_t*) http->tls, buf, len);
+#  endif /* HAVE_LIBSSL */
+}
+#endif /* HAVE_SSL */
+
+
+#ifdef HAVE_SSL
+/*
+ * 'http_setup_ssl()' - Set up SSL/TLS support on a connection.
+ */
+
+static int                             /* O - 0 on success, -1 on failure */
+http_setup_ssl(http_t *http)           /* I - Connection to server */
+{
+  char                 hostname[256],  /* Hostname */
+                       *hostptr;       /* Pointer into hostname */
+
+#  ifdef HAVE_LIBSSL
+  SSL_CTX              *context;       /* Context for encryption */
+  BIO                  *bio;           /* BIO data */
+  const char           *message = NULL;/* Error message */
+#  elif defined(HAVE_GNUTLS)
+  int                  status;         /* Status of handshake */
+  gnutls_certificate_client_credentials *credentials;
+                                       /* TLS credentials */
+#  elif defined(HAVE_CDSASSL)
+  _cups_globals_t      *cg = _cupsGlobals();
+                                       /* Pointer to library globals */
+  OSStatus             error;          /* Error code */
+  const char           *message = NULL;/* Error message */
+  cups_array_t         *credentials;   /* Credentials array */
+  cups_array_t         *names;         /* CUPS distinguished names */
+  CFArrayRef           dn_array;       /* CF distinguished names array */
+  CFIndex              count;          /* Number of credentials */
+  CFDataRef            data;           /* Certificate data */
+  int                  i;              /* Looping var */
+  http_credential_t    *credential;    /* Credential data */
+#  elif defined(HAVE_SSPISSL)
+  TCHAR                        username[256];  /* Username returned from GetUserName() */
+  TCHAR                        commonName[256];/* Common name for certificate */
+  DWORD                        dwSize;         /* 32 bit size */
+#  endif /* HAVE_LIBSSL */
+
+
+  DEBUG_printf(("7http_setup_ssl(http=%p)", http));
+
+ /*
+  * Get the hostname to use for SSL...
+  */
+
+  if (httpAddrLocalhost(http->hostaddr))
+  {
+    strlcpy(hostname, "localhost", sizeof(hostname));
+  }
+  else
+  {
+   /*
+    * Otherwise make sure the hostname we have does not end in a trailing dot.
+    */
+
+    strlcpy(hostname, http->hostname, sizeof(hostname));
+    if ((hostptr = hostname + strlen(hostname) - 1) >= hostname &&
+        *hostptr == '.')
+      *hostptr = '\0';
+  }
+
+#  ifdef HAVE_LIBSSL
+  context = SSL_CTX_new(SSLv23_client_method());
+
+  SSL_CTX_set_options(context, SSL_OP_NO_SSLv2); /* Only use SSLv3 or TLS */
+
+  bio = BIO_new(_httpBIOMethods());
+  BIO_ctrl(bio, BIO_C_SET_FILE_PTR, 0, (char *)http);
+
+  http->tls = SSL_new(context);
+  SSL_set_bio(http->tls, bio, bio);
+
+#   ifdef HAVE_SSL_SET_TLSEXT_HOST_NAME
+  SSL_set_tlsext_host_name(http->tls, hostname);
+#   endif /* HAVE_SSL_SET_TLSEXT_HOST_NAME */
+
+  if (SSL_connect(http->tls) != 1)
+  {
+    unsigned long      error;  /* Error code */
+
+    while ((error = ERR_get_error()) != 0)
+    {
+      message = ERR_error_string(error, NULL);
+      DEBUG_printf(("8http_setup_ssl: %s", message));
+    }
+
+    SSL_CTX_free(context);
+    SSL_free(http->tls);
+    http->tls = NULL;
+
+#    ifdef WIN32
+    http->error  = WSAGetLastError();
+#    else
+    http->error  = errno;
+#    endif /* WIN32 */
+    http->status = HTTP_STATUS_ERROR;
+
+    if (!message)
+      message = _("Unable to establish a secure connection to host.");
+
+    _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI, message, 1);
+
+    return (-1);
+  }
+
+#  elif defined(HAVE_GNUTLS)
+  credentials = (gnutls_certificate_client_credentials *)
+                    malloc(sizeof(gnutls_certificate_client_credentials));
+  if (credentials == NULL)
+  {
+    DEBUG_printf(("8http_setup_ssl: Unable to allocate credentials: %s",
+                  strerror(errno)));
+    http->error  = errno;
+    http->status = HTTP_STATUS_ERROR;
+    _cupsSetHTTPError(HTTP_STATUS_ERROR);
+
+    return (-1);
+  }
+
+  gnutls_certificate_allocate_credentials(credentials);
+
+  gnutls_init(&http->tls, GNUTLS_CLIENT);
+  gnutls_set_default_priority(http->tls);
+  gnutls_server_name_set(http->tls, GNUTLS_NAME_DNS, hostname,
+                         strlen(hostname));
+  gnutls_credentials_set(http->tls, GNUTLS_CRD_CERTIFICATE, *credentials);
+  gnutls_transport_set_ptr(http->tls, (gnutls_transport_ptr)http);
+  gnutls_transport_set_pull_function(http->tls, _httpReadGNUTLS);
+  gnutls_transport_set_push_function(http->tls, _httpWriteGNUTLS);
+
+  while ((status = gnutls_handshake(http->tls)) != GNUTLS_E_SUCCESS)
+  {
+    DEBUG_printf(("8http_setup_ssl: gnutls_handshake returned %d (%s)",
+                  status, gnutls_strerror(status)));
+
+    if (gnutls_error_is_fatal(status))
+    {
+      http->error  = EIO;
+      http->status = HTTP_STATUS_ERROR;
+
+      _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI, gnutls_strerror(status), 0);
+
+      gnutls_deinit(http->tls);
+      gnutls_certificate_free_credentials(*credentials);
+      free(credentials);
+      http->tls = NULL;
+
+      return (-1);
+    }
+  }
+
+  http->tls_credentials = credentials;
+
+#  elif defined(HAVE_CDSASSL)
+  if ((http->tls = SSLCreateContext(kCFAllocatorDefault, kSSLClientSide,
+                                    kSSLStreamType)) == NULL)
+  {
+    DEBUG_puts("4http_setup_ssl: SSLCreateContext failed.");
+    http->error  = errno = ENOMEM;
+    http->status = HTTP_STATUS_ERROR;
+    _cupsSetHTTPError(HTTP_STATUS_ERROR);
+
+    return (-1);
+  }
+
+  error = SSLSetConnection(http->tls, http);
+  DEBUG_printf(("4http_setup_ssl: SSLSetConnection, error=%d", (int)error));
+
+  if (!error)
+  {
+    error = SSLSetIOFuncs(http->tls, _httpReadCDSA, _httpWriteCDSA);
+    DEBUG_printf(("4http_setup_ssl: SSLSetIOFuncs, error=%d", (int)error));
+  }
+
+  if (!error)
+  {
+    error = SSLSetSessionOption(http->tls, kSSLSessionOptionBreakOnServerAuth,
+                                true);
+    DEBUG_printf(("4http_setup_ssl: SSLSetSessionOption, error=%d",
+                  (int)error));
+  }
+
+  if (!error)
+  {
+    if (cg->client_cert_cb)
+    {
+      error = SSLSetSessionOption(http->tls,
+                                 kSSLSessionOptionBreakOnCertRequested, true);
+      DEBUG_printf(("4http_setup_ssl: kSSLSessionOptionBreakOnCertRequested, "
+                    "error=%d", (int)error));
+    }
+    else
+    {
+      error = http_set_credentials(http);
+      DEBUG_printf(("4http_setup_ssl: http_set_credentials, error=%d",
+                    (int)error));
+    }
+  }
+
+ /*
+  * Let the server know which hostname/domain we are trying to connect to
+  * in case it wants to serve up a certificate with a matching common name.
+  */
+
+  if (!error)
+  {
+    error = SSLSetPeerDomainName(http->tls, hostname, strlen(hostname));
+
+    DEBUG_printf(("4http_setup_ssl: SSLSetPeerDomainName, error=%d",
+                  (int)error));
+  }
+
+  if (!error)
+  {
+    int done = 0;                      /* Are we done yet? */
+
+    while (!error && !done)
+    {
+      error = SSLHandshake(http->tls);
+
+      DEBUG_printf(("4http_setup_ssl: SSLHandshake returned %d.", (int)error));
+
+      switch (error)
+      {
+       case noErr :
+           done = 1;
+           break;
+
+       case errSSLWouldBlock :
+           error = noErr;              /* Force a retry */
+           usleep(1000);               /* in 1 millisecond */
+           break;
+
+       case errSSLServerAuthCompleted :
+           error = 0;
+           if (cg->server_cert_cb)
+           {
+             error = httpCopyCredentials(http, &credentials);
+             if (!error)
+             {
+               error = (cg->server_cert_cb)(http, http->tls, credentials,
+                                            cg->server_cert_data);
+               httpFreeCredentials(credentials);
+             }
+
+             DEBUG_printf(("4http_setup_ssl: Server certificate callback "
+                           "returned %d.", (int)error));
+           }
+           break;
+
+       case errSSLClientCertRequested :
+           error = 0;
+
+           if (cg->client_cert_cb)
+           {
+             names = NULL;
+             if (!(error = SSLCopyDistinguishedNames(http->tls, &dn_array)) &&
+                 dn_array)
+             {
+               if ((names = cupsArrayNew(NULL, NULL)) != NULL)
+               {
+                 for (i = 0, count = CFArrayGetCount(dn_array); i < count; i++)
+                 {
+                   data = (CFDataRef)CFArrayGetValueAtIndex(dn_array, i);
+
+                   if ((credential = malloc(sizeof(*credential))) != NULL)
+                   {
+                     credential->datalen = CFDataGetLength(data);
+                     if ((credential->data = malloc(credential->datalen)))
+                     {
+                       memcpy((void *)credential->data, CFDataGetBytePtr(data),
+                              credential->datalen);
+                       cupsArrayAdd(names, credential);
+                     }
+                     else
+                       free(credential);
+                   }
+                 }
+               }
+
+               CFRelease(dn_array);
+             }
+
+             if (!error)
+             {
+               error = (cg->client_cert_cb)(http, http->tls, names,
+                                            cg->client_cert_data);
+
+               DEBUG_printf(("4http_setup_ssl: Client certificate callback "
+                             "returned %d.", (int)error));
+             }
+
+             httpFreeCredentials(names);
+           }
+           break;
+
+       case errSSLUnknownRootCert :
+           message = _("Unable to establish a secure connection to host "
+                       "(untrusted certificate).");
+           break;
+
+       case errSSLNoRootCert :
+           message = _("Unable to establish a secure connection to host "
+                       "(self-signed certificate).");
+           break;
+
+       case errSSLCertExpired :
+           message = _("Unable to establish a secure connection to host "
+                       "(expired certificate).");
+           break;
+
+       case errSSLCertNotYetValid :
+           message = _("Unable to establish a secure connection to host "
+                       "(certificate not yet valid).");
+           break;
+
+       case errSSLHostNameMismatch :
+           message = _("Unable to establish a secure connection to host "
+                       "(host name mismatch).");
+           break;
+
+       case errSSLXCertChainInvalid :
+           message = _("Unable to establish a secure connection to host "
+                       "(certificate chain invalid).");
+           break;
+
+       case errSSLConnectionRefused :
+           message = _("Unable to establish a secure connection to host "
+                       "(peer dropped connection before responding).");
+           break;
+
+       default :
+           break;
+      }
+    }
+  }
+
+  if (error)
+  {
+    http->error  = error;
+    http->status = HTTP_STATUS_ERROR;
+    errno        = ECONNREFUSED;
+
+    CFRelease(http->tls);
+    http->tls = NULL;
+
+   /*
+    * If an error string wasn't set by the callbacks use a generic one...
+    */
+
+    if (!message)
+#ifdef HAVE_CSSMERRORSTRING
+      message = cssmErrorString(error);
+#else
+      message = _("Unable to establish a secure connection to host.");
+#endif /* HAVE_CSSMERRORSTRING */
+
+    _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI, message, 1);
+
+    return (-1);
+  }
+
+#  elif defined(HAVE_SSPISSL)
+  http->tls = _sspiAlloc();
+
+  if (!http->tls)
+  {
+    _cupsSetHTTPError(HTTP_STATUS_ERROR);
+    return (-1);
+  }
+
+  http->tls->sock = http->fd;
+  dwSize          = sizeof(username) / sizeof(TCHAR);
+  GetUserName(username, &dwSize);
+  _sntprintf_s(commonName, sizeof(commonName) / sizeof(TCHAR),
+               sizeof(commonName) / sizeof(TCHAR), TEXT("CN=%s"), username);
+
+  if (!_sspiGetCredentials(http->tls_credentials, L"ClientContainer",
+                           commonName, FALSE))
+  {
+    _sspiFree(http->tls_credentials);
+    http->tls_credentials = NULL;
+
+    http->error  = EIO;
+    http->status = HTTP_STATUS_ERROR;
+
+    _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI,
+                  _("Unable to establish a secure connection to host."), 1);
+
+    return (-1);
+  }
+
+  _sspiSetAllowsAnyRoot(http->tls_credentials, TRUE);
+  _sspiSetAllowsExpiredCerts(http->tls_credentials, TRUE);
+
+  if (!_sspiConnect(http->tls_credentials, hostname))
+  {
+    _sspiFree(http->tls_credentials);
+    http->tls_credentials = NULL;
+
+    http->error  = EIO;
+    http->status = HTTP_STATUS_ERROR;
+
+    _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI,
+                  _("Unable to establish a secure connection to host."), 1);
+
+    return (-1);
+  }
+#  endif /* HAVE_CDSASSL */
+
+  return (0);
+}
+
+
+/*
+ * 'http_shutdown_ssl()' - Shut down SSL/TLS on a connection.
+ */
+
+static void
+http_shutdown_ssl(http_t *http)                /* I - Connection to server */
+{
+#  ifdef HAVE_LIBSSL
+  SSL_CTX      *context;               /* Context for encryption */
+
+  context = SSL_get_SSL_CTX(http->tls);
+
+  SSL_shutdown(http->tls);
+  SSL_CTX_free(context);
+  SSL_free(http->tls);
+
+#  elif defined(HAVE_GNUTLS)
+  gnutls_certificate_client_credentials *credentials;
+                                       /* TLS credentials */
+
+  credentials = (gnutls_certificate_client_credentials *)(http->tls_credentials);
+
+  gnutls_bye(http->tls, GNUTLS_SHUT_RDWR);
+  gnutls_deinit(http->tls);
+  gnutls_certificate_free_credentials(*credentials);
+  free(credentials);
+
+#  elif defined(HAVE_CDSASSL)
+  while (SSLClose(http->tls) == errSSLWouldBlock)
+    usleep(1000);
+
+  CFRelease(http->tls);
+
+  if (http->tls_credentials)
+    CFRelease(http->tls_credentials);
+
+#  elif defined(HAVE_SSPISSL)
+  _sspiFree(http->tls_credentials);
+#  endif /* HAVE_LIBSSL */
+
+  http->tls             = NULL;
+  http->tls_credentials = NULL;
+}
+#endif /* HAVE_SSL */
+
+
+#ifdef HAVE_SSL
+/*
+ * 'http_write_ssl()' - Write to a SSL/TLS connection.
+ */
+
+static int                             /* O - Bytes written */
+http_write_ssl(http_t     *http,       /* I - Connection to server */
+              const char *buf,         /* I - Buffer holding data */
+              int        len)          /* I - Length of buffer */
+{
+  ssize_t      result;                 /* Return value */
+
+
+  DEBUG_printf(("2http_write_ssl(http=%p, buf=%p, len=%d)", http, buf, len));
+
+#  if defined(HAVE_LIBSSL)
+  result = SSL_write((SSL *)(http->tls), buf, len);
+
+#  elif defined(HAVE_GNUTLS)
+  result = gnutls_record_send(http->tls, buf, len);
+
+  if (result < 0 && !errno)
+  {
+   /*
+    * Convert GNU TLS error to errno value...
+    */
+
+    switch (result)
+    {
+      case GNUTLS_E_INTERRUPTED :
+         errno = EINTR;
+         break;
+
+      case GNUTLS_E_AGAIN :
+          errno = EAGAIN;
+          break;
+
+      default :
+          errno = EPIPE;
+          break;
+    }
+
+    result = -1;
+  }
+
+#  elif defined(HAVE_CDSASSL)
+  OSStatus     error;                  /* Error info */
+  size_t       processed;              /* Number of bytes processed */
+
+
+  error = SSLWrite(http->tls, buf, len, &processed);
+
+  switch (error)
+  {
+    case 0 :
+       result = (int)processed;
+       break;
+
+    case errSSLWouldBlock :
+       if (processed)
+         result = (int)processed;
+       else
+       {
+         result = -1;
+         errno  = EINTR;
+       }
+       break;
+
+    case errSSLClosedGraceful :
+    default :
+       if (processed)
+         result = (int)processed;
+       else
+       {
+         result = -1;
+         errno  = EPIPE;
+       }
+       break;
+  }
+#  elif defined(HAVE_SSPISSL)
+  return _sspiWrite((_sspi_struct_t *)http->tls, (void *)buf, len);
+#  endif /* HAVE_LIBSSL */
+
+  DEBUG_printf(("3http_write_ssl: Returning %d.", (int)result));
+
+  return ((int)result);
+}
+#endif /* HAVE_SSL */
+
+
+/*
+ * 'http_tls_pending()' - Return the number of pending TLS-encrypted bytes.
+ */
+
+static size_t
+http_tls_pending(http_t *http)         /* I - HTTP connection */
+{
+  if (http->tls && usessl)
+  {
+#  ifdef HAVE_LIBSSL
+    if (SSL_pending(http->tls))
+    {
+      DEBUG_puts("5_httpWait: Return 1 since there is pending SSL data.");
+      return (1);
+    }
+
+#  elif defined(HAVE_GNUTLS)
+    if (gnutls_record_check_pending(http->tls))
+    {
+      DEBUG_puts("5_httpWait: Return 1 since there is pending SSL data.");
+      return (1);
+    }
+
+#  elif defined(HAVE_CDSASSL)
+    size_t bytes;                      /* Bytes that are available */
+
+    if (!SSLGetBufferedReadSize(http->tls, &bytes) &&
+        bytes > 0)
+    {
+      DEBUG_puts("5_httpWait: Return 1 since there is pending SSL data.");
+      return (1);
+    }
+#  endif /* HAVE_LIBSSL */
+}
+
+
+#if defined(HAVE_SSL) && defined(HAVE_LIBSSL)
+/*
+ * '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.
+ */
+
+static int                             /* O - 1 on success, 0 on failure */
+http_bio_free(BIO *h)                  /* I - BIO data */
+{
+  if (!h)
+    return (0);
+
+  if (h->shutdown)
+  {
+    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
+  return (send(((http_t *)h->ptr)->fd, str, strlen(str), 0));
+#endif /* WIN32 */
+}
+
+
+/*
+ * 'http_bio_read()' - Read data for OpenSSL.
+ */
+
+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 */
+
+
+  http = (http_t *)h->ptr;
+
+  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;
+
+#ifdef WIN32
+      http->error = WSAETIMEDOUT;
+#else
+      http->error = ETIMEDOUT;
+#endif /* WIN32 */
+
+      return (-1);
+    }
+  }
+
+  return (recv(http->fd, buf, 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));
+}
+#endif /* HAVE_SSL && HAVE_LIBSSL */
+
+
+/*
+ * 'cupsdEndTLS()' - Shutdown a secure session with the client.
+ */
+
+int                                    /* O - 1 on success, 0 on error */
+cupsdEndTLS(cupsd_client_t *con)       /* I - Client connection */
+{
+  SSL_CTX      *context;               /* Context for encryption */
+  unsigned long        error;                  /* Error code */
+  int          status;                 /* Return status */
+
+
+  context = SSL_get_SSL_CTX(con->http.tls);
+
+  switch (SSL_shutdown(con->http.tls))
+  {
+    case 1 :
+       cupsdLogMessage(CUPSD_LOG_DEBUG,
+                       "SSL shutdown successful!");
+       status = 1;
+       break;
+
+    case -1 :
+       cupsdLogMessage(CUPSD_LOG_ERROR,
+                       "Fatal error during SSL shutdown!");
+
+    default :
+       while ((error = ERR_get_error()) != 0)
+         cupsdLogMessage(CUPSD_LOG_ERROR, "SSL shutdown failed: %s",
+                         ERR_error_string(error, NULL));
+       status = 0;
+       break;
+  }
+
+  SSL_CTX_free(context);
+  SSL_free(con->http.tls);
+  con->http.tls = NULL;
+
+  return (status);
+}
+
+
+/*
+ * 'cupsdStartTLS()' - Start a secure session with the client.
+ */
+
+int                                    /* O - 1 on success, 0 on error */
+cupsdStartTLS(cupsd_client_t *con)     /* I - Client connection */
+{
+  SSL_CTX      *context;               /* Context for encryption */
+  BIO          *bio;                   /* BIO data */
+  unsigned long        error;                  /* Error code */
+
+
+  cupsdLogMessage(CUPSD_LOG_DEBUG, "[Client %d] Encrypting connection.",
+                  con->http.fd);
+
+ /*
+  * Verify that we have a certificate...
+  */
+
+  if (access(ServerKey, 0) || access(ServerCertificate, 0))
+  {
+   /*
+    * Nope, make a self-signed certificate...
+    */
+
+    if (!make_certificate(con))
+      return (0);
+  }
+
+ /*
+  * Create the SSL context and accept the connection...
+  */
+
+  context = SSL_CTX_new(SSLv23_server_method());
+
+  SSL_CTX_set_options(context, SSL_OP_NO_SSLv2); /* Only use SSLv3 or TLS */
+  if (SSLOptions & CUPSD_SSL_NOEMPTY)
+    SSL_CTX_set_options(context, SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS);
+  SSL_CTX_use_PrivateKey_file(context, ServerKey, SSL_FILETYPE_PEM);
+  SSL_CTX_use_certificate_chain_file(context, ServerCertificate);
+
+  bio = BIO_new(_httpBIOMethods());
+  BIO_ctrl(bio, BIO_C_SET_FILE_PTR, 0, (char *)HTTP(con));
+
+  con->http.tls = SSL_new(context);
+  SSL_set_bio(con->http.tls, bio, bio);
+
+  if (SSL_accept(con->http.tls) != 1)
+  {
+    cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to encrypt connection from %s.",
+                    con->http.hostname);
+
+    while ((error = ERR_get_error()) != 0)
+      cupsdLogMessage(CUPSD_LOG_ERROR, "%s", ERR_error_string(error, NULL));
+
+    SSL_CTX_free(context);
+    SSL_free(con->http.tls);
+    con->http.tls = NULL;
+    return (0);
+  }
+
+  cupsdLogMessage(CUPSD_LOG_DEBUG, "Connection from %s now encrypted.",
+                  con->http.hostname);
+
+  return (1);
+}
+
+
+/*
+ * 'make_certificate()' - Make a self-signed SSL/TLS certificate.
+ */
+
+static int                             /* O - 1 on success, 0 on failure */
+make_certificate(cupsd_client_t *con)  /* I - Client connection */
+{
+#ifdef HAVE_WAITPID
+  int          pid,                    /* Process ID of command */
+               status;                 /* Status of command */
+  char         command[1024],          /* Command */
+               *argv[12],              /* Command-line arguments */
+               *envp[MAX_ENV + 1],     /* Environment variables */
+               infofile[1024],         /* Type-in information for cert */
+               seedfile[1024];         /* Random number seed file */
+  int          envc,                   /* Number of environment variables */
+               bytes;                  /* Bytes written */
+  cups_file_t  *fp;                    /* Seed/info file */
+  int          infofd;                 /* Info file descriptor */
+
+
+ /*
+  * Run the "openssl" command to seed the random number generator and
+  * generate a self-signed certificate that is good for 10 years:
+  *
+  *     openssl rand -rand seedfile 1
+  *
+  *     openssl req -new -x509 -keyout ServerKey \
+  *             -out ServerCertificate -days 3650 -nodes
+  *
+  * The seeding step is crucial in ensuring that the openssl command
+  * does not block on systems without sufficient entropy...
+  */
+
+  if (!cupsFileFind("openssl", getenv("PATH"), 1, command, sizeof(command)))
+  {
+    cupsdLogMessage(CUPSD_LOG_ERROR,
+                    "No SSL certificate and openssl command not found!");
+    return (0);
+  }
+
+  if (access("/dev/urandom", 0))
+  {
+   /*
+    * If the system doesn't provide /dev/urandom, then any random source
+    * will probably be blocking-style, so generate some random data to
+    * use as a seed for the certificate.  Note that we have already
+    * seeded the random number generator in cupsdInitCerts()...
+    */
+
+    cupsdLogMessage(CUPSD_LOG_INFO,
+                    "Seeding the random number generator...");
+
+   /*
+    * Write the seed file...
+    */
+
+    if ((fp = cupsTempFile2(seedfile, sizeof(seedfile))) == NULL)
+    {
+      cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to create seed file %s - %s",
+                      seedfile, strerror(errno));
+      return (0);
+    }
+
+    for (bytes = 0; bytes < 262144; bytes ++)
+      cupsFilePutChar(fp, CUPS_RAND());
+
+    cupsFileClose(fp);
+
+   /*
+    * Run the openssl command to seed its random number generator...
+    */
+
+    argv[0] = "openssl";
+    argv[1] = "rand";
+    argv[2] = "-rand";
+    argv[3] = seedfile;
+    argv[4] = "1";
+    argv[5] = NULL;
+
+    envc = cupsdLoadEnv(envp, MAX_ENV);
+    envp[envc] = NULL;
+
+    if (!cupsdStartProcess(command, argv, envp, -1, -1, -1, -1, -1, 1, NULL,
+                           NULL, &pid))
+    {
+      unlink(seedfile);
+      return (0);
+    }
+
+    while (waitpid(pid, &status, 0) < 0)
+      if (errno != EINTR)
+      {
+       status = 1;
+       break;
+      }
+
+    cupsdFinishProcess(pid, command, sizeof(command), NULL);
+
+   /*
+    * Remove the seed file, as it is no longer needed...
+    */
+
+    unlink(seedfile);
+
+    if (status)
+    {
+      if (WIFEXITED(status))
+       cupsdLogMessage(CUPSD_LOG_ERROR,
+                       "Unable to seed random number generator - "
+                       "the openssl command stopped with status %d!",
+                       WEXITSTATUS(status));
+      else
+       cupsdLogMessage(CUPSD_LOG_ERROR,
+                       "Unable to seed random number generator - "
+                       "the openssl command crashed on signal %d!",
+                       WTERMSIG(status));
+
+      return (0);
+    }
+  }
+
+ /*
+  * Create a file with the certificate information fields...
+  *
+  * Note: This assumes that the default questions are asked by the openssl
+  * command...
+  */
+
+  if ((fp = cupsTempFile2(infofile, sizeof(infofile))) == NULL)
+  {
+    cupsdLogMessage(CUPSD_LOG_ERROR,
+                    "Unable to create certificate information file %s - %s",
+                    infofile, strerror(errno));
+    return (0);
+  }
+
+  cupsFilePrintf(fp, ".\n.\n.\n%s\n.\n%s\n%s\n",
+                 ServerName, ServerName, ServerAdmin);
+  cupsFileClose(fp);
+
+  cupsdLogMessage(CUPSD_LOG_INFO,
+                  "Generating SSL server key and certificate...");
+
+  argv[0]  = "openssl";
+  argv[1]  = "req";
+  argv[2]  = "-new";
+  argv[3]  = "-x509";
+  argv[4]  = "-keyout";
+  argv[5]  = ServerKey;
+  argv[6]  = "-out";
+  argv[7]  = ServerCertificate;
+  argv[8]  = "-days";
+  argv[9]  = "3650";
+  argv[10] = "-nodes";
+  argv[11] = NULL;
+
+  cupsdLoadEnv(envp, MAX_ENV);
+
+  infofd = open(infofile, O_RDONLY);
+
+  if (!cupsdStartProcess(command, argv, envp, infofd, -1, -1, -1, -1, 1, NULL,
+                         NULL, &pid))
+  {
+    close(infofd);
+    unlink(infofile);
+    return (0);
+  }
+
+  close(infofd);
+  unlink(infofile);
+
+  while (waitpid(pid, &status, 0) < 0)
+    if (errno != EINTR)
+    {
+      status = 1;
+      break;
+    }
+
+  cupsdFinishProcess(pid, command, sizeof(command), NULL);
+
+  if (status)
+  {
+    if (WIFEXITED(status))
+      cupsdLogMessage(CUPSD_LOG_ERROR,
+                      "Unable to create SSL server key and certificate - "
+                     "the openssl command stopped with status %d!",
+                     WEXITSTATUS(status));
+    else
+      cupsdLogMessage(CUPSD_LOG_ERROR,
+                      "Unable to create SSL server key and certificate - "
+                     "the openssl command crashed on signal %d!",
+                     WTERMSIG(status));
+  }
+  else
+  {
+    cupsdLogMessage(CUPSD_LOG_INFO, "Created SSL server key file \"%s\"...",
+                   ServerKey);
+    cupsdLogMessage(CUPSD_LOG_INFO,
+                    "Created SSL server certificate file \"%s\"...",
+                   ServerCertificate);
+  }
+
+  return (!status);
+
+#else
+  return (0);
+#endif /* HAVE_WAITPID */
+}
+
+
+/*
+ * End of "$Id$".
+ */
similarity index 69%
rename from cups/sspi.c
rename to cups/tls-sspi.c
index 0efcb56793cfbd3b5748ff04a0dc291439c852ee..0e5431f55c182b98d993aa3fa9f4211080a6fbd7 100644 (file)
@@ -1,35 +1,17 @@
 /*
  * "$Id$"
  *
- *   Windows SSPI SSL implementation for CUPS.
+ * TLS support for CUPS on Windows using SSPI.
  *
- *   Copyright 2010-2011 by Apple Inc.
+ * Copyright 2010-2013 by Apple Inc.
  *
- *   These coded instructions, statements, and computer programs are the
- *   property of Apple Inc. and are protected by Federal copyright
- *   law.  Distribution and use rights are outlined in the file "LICENSE.txt"
- *   which should have been included with this file.  If this file is
- *   file is missing or damaged, see the license at "http://www.cups.org/".
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law.  Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file.  If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
  *
- * Contents:
- *
- *   sspi_alloc()                 - Allocate SSPI ssl object
- *   _sspiGetCredentials()        - Retrieve an SSL/TLS certificate from the
- *                                  system store If one cannot be found, one is
- *                                  created.
- *   _sspiConnect()               - Make an SSL connection. This function
- *                                  assumes a TCP/IP connection has already been
- *                                  successfully made
- *   _sspiAccept()                - Accept an SSL/TLS connection
- *   _sspiSetAllowsAnyRoot()      - Set the client cert policy for untrusted
- *                                  root certs
- *   _sspiSetAllowsExpiredCerts() - Set the client cert policy for expired root
- *                                  certs
- *   _sspiWrite()                 - Write a buffer to an ssl socket
- *   _sspiRead()                  - Read a buffer from an ssl socket
- *   _sspiPending()               - Returns the number of available bytes
- *   _sspiFree()                  - Close a connection and free resources
- *   sspi_verify_certificate()    - Verify a server certificate
+ * This file is subject to the Apple OS-Developed Software exception.
  */
 
 /*
@@ -59,6 +41,773 @@ static DWORD sspi_verify_certificate(PCCERT_CONTEXT  serverCert,
                                      DWORD           dwCertFlags);
 
 
+/*
+ * 'http_tls_initialize()' - Initialize the TLS stack.
+ */
+
+static void
+http_tls_initialize(void)
+{
+#ifdef HAVE_GNUTLS
+ /*
+  * Initialize GNU TLS...
+  */
+
+  gnutls_global_init();
+
+#elif defined(HAVE_LIBSSL)
+ /*
+  * Initialize OpenSSL...
+  */
+
+  SSL_load_error_strings();
+  SSL_library_init();
+
+ /*
+  * Using the current time is a dubious random seed, but on some systems
+  * it is the best we can do (on others, this seed isn't even used...)
+  */
+
+  CUPS_SRAND(time(NULL));
+
+  for (i = 0; i < sizeof(data); i ++)
+    data[i] = CUPS_RAND();
+
+  RAND_seed(data, sizeof(data));
+#endif /* HAVE_GNUTLS */
+}
+
+
+#ifdef HAVE_SSL
+/*
+ * 'http_tls_read()' - Read from a SSL/TLS connection.
+ */
+
+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 */
+{
+#  if defined(HAVE_LIBSSL)
+  return (SSL_read((SSL *)(http->tls), buf, len));
+
+#  elif defined(HAVE_GNUTLS)
+  ssize_t      result;                 /* Return value */
+
+
+  result = gnutls_record_recv(http->tls, buf, len);
+
+  if (result < 0 && !errno)
+  {
+   /*
+    * Convert GNU TLS error to errno value...
+    */
+
+    switch (result)
+    {
+      case GNUTLS_E_INTERRUPTED :
+         errno = EINTR;
+         break;
+
+      case GNUTLS_E_AGAIN :
+          errno = EAGAIN;
+          break;
+
+      default :
+          errno = EPIPE;
+          break;
+    }
+
+    result = -1;
+  }
+
+  return ((int)result);
+
+#  elif defined(HAVE_CDSASSL)
+  int          result;                 /* Return value */
+  OSStatus     error;                  /* Error info */
+  size_t       processed;              /* Number of bytes processed */
+
+
+  error = SSLRead(http->tls, buf, len, &processed);
+  DEBUG_printf(("6http_tls_read: error=%d, processed=%d", (int)error,
+                (int)processed));
+  switch (error)
+  {
+    case 0 :
+       result = (int)processed;
+       break;
+
+    case errSSLWouldBlock :
+       if (processed)
+         result = (int)processed;
+       else
+       {
+         result = -1;
+         errno  = EINTR;
+       }
+       break;
+
+    case errSSLClosedGraceful :
+    default :
+       if (processed)
+         result = (int)processed;
+       else
+       {
+         result = -1;
+         errno  = EPIPE;
+       }
+       break;
+  }
+
+  return (result);
+
+#  elif defined(HAVE_SSPISSL)
+  return _sspiRead((_sspi_struct_t*) http->tls, buf, len);
+#  endif /* HAVE_LIBSSL */
+}
+#endif /* HAVE_SSL */
+
+
+#ifdef HAVE_SSL
+/*
+ * 'http_setup_ssl()' - Set up SSL/TLS support on a connection.
+ */
+
+static int                             /* O - 0 on success, -1 on failure */
+http_setup_ssl(http_t *http)           /* I - Connection to server */
+{
+  char                 hostname[256],  /* Hostname */
+                       *hostptr;       /* Pointer into hostname */
+
+#  ifdef HAVE_LIBSSL
+  SSL_CTX              *context;       /* Context for encryption */
+  BIO                  *bio;           /* BIO data */
+  const char           *message = NULL;/* Error message */
+#  elif defined(HAVE_GNUTLS)
+  int                  status;         /* Status of handshake */
+  gnutls_certificate_client_credentials *credentials;
+                                       /* TLS credentials */
+#  elif defined(HAVE_CDSASSL)
+  _cups_globals_t      *cg = _cupsGlobals();
+                                       /* Pointer to library globals */
+  OSStatus             error;          /* Error code */
+  const char           *message = NULL;/* Error message */
+  cups_array_t         *credentials;   /* Credentials array */
+  cups_array_t         *names;         /* CUPS distinguished names */
+  CFArrayRef           dn_array;       /* CF distinguished names array */
+  CFIndex              count;          /* Number of credentials */
+  CFDataRef            data;           /* Certificate data */
+  int                  i;              /* Looping var */
+  http_credential_t    *credential;    /* Credential data */
+#  elif defined(HAVE_SSPISSL)
+  TCHAR                        username[256];  /* Username returned from GetUserName() */
+  TCHAR                        commonName[256];/* Common name for certificate */
+  DWORD                        dwSize;         /* 32 bit size */
+#  endif /* HAVE_LIBSSL */
+
+
+  DEBUG_printf(("7http_setup_ssl(http=%p)", http));
+
+ /*
+  * Get the hostname to use for SSL...
+  */
+
+  if (httpAddrLocalhost(http->hostaddr))
+  {
+    strlcpy(hostname, "localhost", sizeof(hostname));
+  }
+  else
+  {
+   /*
+    * Otherwise make sure the hostname we have does not end in a trailing dot.
+    */
+
+    strlcpy(hostname, http->hostname, sizeof(hostname));
+    if ((hostptr = hostname + strlen(hostname) - 1) >= hostname &&
+        *hostptr == '.')
+      *hostptr = '\0';
+  }
+
+#  ifdef HAVE_LIBSSL
+  context = SSL_CTX_new(SSLv23_client_method());
+
+  SSL_CTX_set_options(context, SSL_OP_NO_SSLv2); /* Only use SSLv3 or TLS */
+
+  bio = BIO_new(_httpBIOMethods());
+  BIO_ctrl(bio, BIO_C_SET_FILE_PTR, 0, (char *)http);
+
+  http->tls = SSL_new(context);
+  SSL_set_bio(http->tls, bio, bio);
+
+#   ifdef HAVE_SSL_SET_TLSEXT_HOST_NAME
+  SSL_set_tlsext_host_name(http->tls, hostname);
+#   endif /* HAVE_SSL_SET_TLSEXT_HOST_NAME */
+
+  if (SSL_connect(http->tls) != 1)
+  {
+    unsigned long      error;  /* Error code */
+
+    while ((error = ERR_get_error()) != 0)
+    {
+      message = ERR_error_string(error, NULL);
+      DEBUG_printf(("8http_setup_ssl: %s", message));
+    }
+
+    SSL_CTX_free(context);
+    SSL_free(http->tls);
+    http->tls = NULL;
+
+#    ifdef WIN32
+    http->error  = WSAGetLastError();
+#    else
+    http->error  = errno;
+#    endif /* WIN32 */
+    http->status = HTTP_STATUS_ERROR;
+
+    if (!message)
+      message = _("Unable to establish a secure connection to host.");
+
+    _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI, message, 1);
+
+    return (-1);
+  }
+
+#  elif defined(HAVE_GNUTLS)
+  credentials = (gnutls_certificate_client_credentials *)
+                    malloc(sizeof(gnutls_certificate_client_credentials));
+  if (credentials == NULL)
+  {
+    DEBUG_printf(("8http_setup_ssl: Unable to allocate credentials: %s",
+                  strerror(errno)));
+    http->error  = errno;
+    http->status = HTTP_STATUS_ERROR;
+    _cupsSetHTTPError(HTTP_STATUS_ERROR);
+
+    return (-1);
+  }
+
+  gnutls_certificate_allocate_credentials(credentials);
+
+  gnutls_init(&http->tls, GNUTLS_CLIENT);
+  gnutls_set_default_priority(http->tls);
+  gnutls_server_name_set(http->tls, GNUTLS_NAME_DNS, hostname,
+                         strlen(hostname));
+  gnutls_credentials_set(http->tls, GNUTLS_CRD_CERTIFICATE, *credentials);
+  gnutls_transport_set_ptr(http->tls, (gnutls_transport_ptr)http);
+  gnutls_transport_set_pull_function(http->tls, _httpReadGNUTLS);
+  gnutls_transport_set_push_function(http->tls, _httpWriteGNUTLS);
+
+  while ((status = gnutls_handshake(http->tls)) != GNUTLS_E_SUCCESS)
+  {
+    DEBUG_printf(("8http_setup_ssl: gnutls_handshake returned %d (%s)",
+                  status, gnutls_strerror(status)));
+
+    if (gnutls_error_is_fatal(status))
+    {
+      http->error  = EIO;
+      http->status = HTTP_STATUS_ERROR;
+
+      _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI, gnutls_strerror(status), 0);
+
+      gnutls_deinit(http->tls);
+      gnutls_certificate_free_credentials(*credentials);
+      free(credentials);
+      http->tls = NULL;
+
+      return (-1);
+    }
+  }
+
+  http->tls_credentials = credentials;
+
+#  elif defined(HAVE_CDSASSL)
+  if ((http->tls = SSLCreateContext(kCFAllocatorDefault, kSSLClientSide,
+                                    kSSLStreamType)) == NULL)
+  {
+    DEBUG_puts("4http_setup_ssl: SSLCreateContext failed.");
+    http->error  = errno = ENOMEM;
+    http->status = HTTP_STATUS_ERROR;
+    _cupsSetHTTPError(HTTP_STATUS_ERROR);
+
+    return (-1);
+  }
+
+  error = SSLSetConnection(http->tls, http);
+  DEBUG_printf(("4http_setup_ssl: SSLSetConnection, error=%d", (int)error));
+
+  if (!error)
+  {
+    error = SSLSetIOFuncs(http->tls, _httpReadCDSA, _httpWriteCDSA);
+    DEBUG_printf(("4http_setup_ssl: SSLSetIOFuncs, error=%d", (int)error));
+  }
+
+  if (!error)
+  {
+    error = SSLSetSessionOption(http->tls, kSSLSessionOptionBreakOnServerAuth,
+                                true);
+    DEBUG_printf(("4http_setup_ssl: SSLSetSessionOption, error=%d",
+                  (int)error));
+  }
+
+  if (!error)
+  {
+    if (cg->client_cert_cb)
+    {
+      error = SSLSetSessionOption(http->tls,
+                                 kSSLSessionOptionBreakOnCertRequested, true);
+      DEBUG_printf(("4http_setup_ssl: kSSLSessionOptionBreakOnCertRequested, "
+                    "error=%d", (int)error));
+    }
+    else
+    {
+      error = http_set_credentials(http);
+      DEBUG_printf(("4http_setup_ssl: http_set_credentials, error=%d",
+                    (int)error));
+    }
+  }
+
+ /*
+  * Let the server know which hostname/domain we are trying to connect to
+  * in case it wants to serve up a certificate with a matching common name.
+  */
+
+  if (!error)
+  {
+    error = SSLSetPeerDomainName(http->tls, hostname, strlen(hostname));
+
+    DEBUG_printf(("4http_setup_ssl: SSLSetPeerDomainName, error=%d",
+                  (int)error));
+  }
+
+  if (!error)
+  {
+    int done = 0;                      /* Are we done yet? */
+
+    while (!error && !done)
+    {
+      error = SSLHandshake(http->tls);
+
+      DEBUG_printf(("4http_setup_ssl: SSLHandshake returned %d.", (int)error));
+
+      switch (error)
+      {
+       case noErr :
+           done = 1;
+           break;
+
+       case errSSLWouldBlock :
+           error = noErr;              /* Force a retry */
+           usleep(1000);               /* in 1 millisecond */
+           break;
+
+       case errSSLServerAuthCompleted :
+           error = 0;
+           if (cg->server_cert_cb)
+           {
+             error = httpCopyCredentials(http, &credentials);
+             if (!error)
+             {
+               error = (cg->server_cert_cb)(http, http->tls, credentials,
+                                            cg->server_cert_data);
+               httpFreeCredentials(credentials);
+             }
+
+             DEBUG_printf(("4http_setup_ssl: Server certificate callback "
+                           "returned %d.", (int)error));
+           }
+           break;
+
+       case errSSLClientCertRequested :
+           error = 0;
+
+           if (cg->client_cert_cb)
+           {
+             names = NULL;
+             if (!(error = SSLCopyDistinguishedNames(http->tls, &dn_array)) &&
+                 dn_array)
+             {
+               if ((names = cupsArrayNew(NULL, NULL)) != NULL)
+               {
+                 for (i = 0, count = CFArrayGetCount(dn_array); i < count; i++)
+                 {
+                   data = (CFDataRef)CFArrayGetValueAtIndex(dn_array, i);
+
+                   if ((credential = malloc(sizeof(*credential))) != NULL)
+                   {
+                     credential->datalen = CFDataGetLength(data);
+                     if ((credential->data = malloc(credential->datalen)))
+                     {
+                       memcpy((void *)credential->data, CFDataGetBytePtr(data),
+                              credential->datalen);
+                       cupsArrayAdd(names, credential);
+                     }
+                     else
+                       free(credential);
+                   }
+                 }
+               }
+
+               CFRelease(dn_array);
+             }
+
+             if (!error)
+             {
+               error = (cg->client_cert_cb)(http, http->tls, names,
+                                            cg->client_cert_data);
+
+               DEBUG_printf(("4http_setup_ssl: Client certificate callback "
+                             "returned %d.", (int)error));
+             }
+
+             httpFreeCredentials(names);
+           }
+           break;
+
+       case errSSLUnknownRootCert :
+           message = _("Unable to establish a secure connection to host "
+                       "(untrusted certificate).");
+           break;
+
+       case errSSLNoRootCert :
+           message = _("Unable to establish a secure connection to host "
+                       "(self-signed certificate).");
+           break;
+
+       case errSSLCertExpired :
+           message = _("Unable to establish a secure connection to host "
+                       "(expired certificate).");
+           break;
+
+       case errSSLCertNotYetValid :
+           message = _("Unable to establish a secure connection to host "
+                       "(certificate not yet valid).");
+           break;
+
+       case errSSLHostNameMismatch :
+           message = _("Unable to establish a secure connection to host "
+                       "(host name mismatch).");
+           break;
+
+       case errSSLXCertChainInvalid :
+           message = _("Unable to establish a secure connection to host "
+                       "(certificate chain invalid).");
+           break;
+
+       case errSSLConnectionRefused :
+           message = _("Unable to establish a secure connection to host "
+                       "(peer dropped connection before responding).");
+           break;
+
+       default :
+           break;
+      }
+    }
+  }
+
+  if (error)
+  {
+    http->error  = error;
+    http->status = HTTP_STATUS_ERROR;
+    errno        = ECONNREFUSED;
+
+    CFRelease(http->tls);
+    http->tls = NULL;
+
+   /*
+    * If an error string wasn't set by the callbacks use a generic one...
+    */
+
+    if (!message)
+#ifdef HAVE_CSSMERRORSTRING
+      message = cssmErrorString(error);
+#else
+      message = _("Unable to establish a secure connection to host.");
+#endif /* HAVE_CSSMERRORSTRING */
+
+    _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI, message, 1);
+
+    return (-1);
+  }
+
+#  elif defined(HAVE_SSPISSL)
+  http->tls = _sspiAlloc();
+
+  if (!http->tls)
+  {
+    _cupsSetHTTPError(HTTP_STATUS_ERROR);
+    return (-1);
+  }
+
+  http->tls->sock = http->fd;
+  dwSize          = sizeof(username) / sizeof(TCHAR);
+  GetUserName(username, &dwSize);
+  _sntprintf_s(commonName, sizeof(commonName) / sizeof(TCHAR),
+               sizeof(commonName) / sizeof(TCHAR), TEXT("CN=%s"), username);
+
+  if (!_sspiGetCredentials(http->tls_credentials, L"ClientContainer",
+                           commonName, FALSE))
+  {
+    _sspiFree(http->tls_credentials);
+    http->tls_credentials = NULL;
+
+    http->error  = EIO;
+    http->status = HTTP_STATUS_ERROR;
+
+    _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI,
+                  _("Unable to establish a secure connection to host."), 1);
+
+    return (-1);
+  }
+
+  _sspiSetAllowsAnyRoot(http->tls_credentials, TRUE);
+  _sspiSetAllowsExpiredCerts(http->tls_credentials, TRUE);
+
+  if (!_sspiConnect(http->tls_credentials, hostname))
+  {
+    _sspiFree(http->tls_credentials);
+    http->tls_credentials = NULL;
+
+    http->error  = EIO;
+    http->status = HTTP_STATUS_ERROR;
+
+    _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI,
+                  _("Unable to establish a secure connection to host."), 1);
+
+    return (-1);
+  }
+#  endif /* HAVE_CDSASSL */
+
+  return (0);
+}
+
+
+/*
+ * 'http_shutdown_ssl()' - Shut down SSL/TLS on a connection.
+ */
+
+static void
+http_shutdown_ssl(http_t *http)                /* I - Connection to server */
+{
+#  ifdef HAVE_LIBSSL
+  SSL_CTX      *context;               /* Context for encryption */
+
+  context = SSL_get_SSL_CTX(http->tls);
+
+  SSL_shutdown(http->tls);
+  SSL_CTX_free(context);
+  SSL_free(http->tls);
+
+#  elif defined(HAVE_GNUTLS)
+  gnutls_certificate_client_credentials *credentials;
+                                       /* TLS credentials */
+
+  credentials = (gnutls_certificate_client_credentials *)(http->tls_credentials);
+
+  gnutls_bye(http->tls, GNUTLS_SHUT_RDWR);
+  gnutls_deinit(http->tls);
+  gnutls_certificate_free_credentials(*credentials);
+  free(credentials);
+
+#  elif defined(HAVE_CDSASSL)
+  while (SSLClose(http->tls) == errSSLWouldBlock)
+    usleep(1000);
+
+  CFRelease(http->tls);
+
+  if (http->tls_credentials)
+    CFRelease(http->tls_credentials);
+
+#  elif defined(HAVE_SSPISSL)
+  _sspiFree(http->tls_credentials);
+#  endif /* HAVE_LIBSSL */
+
+  http->tls             = NULL;
+  http->tls_credentials = NULL;
+}
+#endif /* HAVE_SSL */
+
+
+#ifdef HAVE_SSL
+/*
+ * 'http_write_ssl()' - Write to a SSL/TLS connection.
+ */
+
+static int                             /* O - Bytes written */
+http_write_ssl(http_t     *http,       /* I - Connection to server */
+              const char *buf,         /* I - Buffer holding data */
+              int        len)          /* I - Length of buffer */
+{
+  ssize_t      result;                 /* Return value */
+
+
+  DEBUG_printf(("2http_write_ssl(http=%p, buf=%p, len=%d)", http, buf, len));
+
+#  if defined(HAVE_LIBSSL)
+  result = SSL_write((SSL *)(http->tls), buf, len);
+
+#  elif defined(HAVE_GNUTLS)
+  result = gnutls_record_send(http->tls, buf, len);
+
+  if (result < 0 && !errno)
+  {
+   /*
+    * Convert GNU TLS error to errno value...
+    */
+
+    switch (result)
+    {
+      case GNUTLS_E_INTERRUPTED :
+         errno = EINTR;
+         break;
+
+      case GNUTLS_E_AGAIN :
+          errno = EAGAIN;
+          break;
+
+      default :
+          errno = EPIPE;
+          break;
+    }
+
+    result = -1;
+  }
+
+#  elif defined(HAVE_CDSASSL)
+  OSStatus     error;                  /* Error info */
+  size_t       processed;              /* Number of bytes processed */
+
+
+  error = SSLWrite(http->tls, buf, len, &processed);
+
+  switch (error)
+  {
+    case 0 :
+       result = (int)processed;
+       break;
+
+    case errSSLWouldBlock :
+       if (processed)
+         result = (int)processed;
+       else
+       {
+         result = -1;
+         errno  = EINTR;
+       }
+       break;
+
+    case errSSLClosedGraceful :
+    default :
+       if (processed)
+         result = (int)processed;
+       else
+       {
+         result = -1;
+         errno  = EPIPE;
+       }
+       break;
+  }
+#  elif defined(HAVE_SSPISSL)
+  return _sspiWrite((_sspi_struct_t *)http->tls, (void *)buf, len);
+#  endif /* HAVE_LIBSSL */
+
+  DEBUG_printf(("3http_write_ssl: Returning %d.", (int)result));
+
+  return ((int)result);
+}
+#endif /* HAVE_SSL */
+
+
+#ifdef HAVE_SSL
+/*
+ * 'http_write_ssl()' - Write to a SSL/TLS connection.
+ */
+
+static int                             /* O - Bytes written */
+http_write_ssl(http_t     *http,       /* I - Connection to server */
+              const char *buf,         /* I - Buffer holding data */
+              int        len)          /* I - Length of buffer */
+{
+  ssize_t      result;                 /* Return value */
+
+
+  DEBUG_printf(("2http_write_ssl(http=%p, buf=%p, len=%d)", http, buf, len));
+
+#  if defined(HAVE_LIBSSL)
+  result = SSL_write((SSL *)(http->tls), buf, len);
+
+#  elif defined(HAVE_GNUTLS)
+  result = gnutls_record_send(http->tls, buf, len);
+
+  if (result < 0 && !errno)
+  {
+   /*
+    * Convert GNU TLS error to errno value...
+    */
+
+    switch (result)
+    {
+      case GNUTLS_E_INTERRUPTED :
+         errno = EINTR;
+         break;
+
+      case GNUTLS_E_AGAIN :
+          errno = EAGAIN;
+          break;
+
+      default :
+          errno = EPIPE;
+          break;
+    }
+
+    result = -1;
+  }
+
+#  elif defined(HAVE_CDSASSL)
+  OSStatus     error;                  /* Error info */
+  size_t       processed;              /* Number of bytes processed */
+
+
+  error = SSLWrite(http->tls, buf, len, &processed);
+
+  switch (error)
+  {
+    case 0 :
+       result = (int)processed;
+       break;
+
+    case errSSLWouldBlock :
+       if (processed)
+         result = (int)processed;
+       else
+       {
+         result = -1;
+         errno  = EINTR;
+       }
+       break;
+
+    case errSSLClosedGraceful :
+    default :
+       if (processed)
+         result = (int)processed;
+       else
+       {
+         result = -1;
+         errno  = EPIPE;
+       }
+       break;
+  }
+#  elif defined(HAVE_SSPISSL)
+  return _sspiWrite((_sspi_struct_t *)http->tls, (void *)buf, len);
+#  endif /* HAVE_LIBSSL */
+
+  DEBUG_printf(("3http_write_ssl: Returning %d.", (int)result));
+
+  return ((int)result);
+}
+#endif /* HAVE_SSL */
+
+
 /*
  * 'sspi_alloc()' - Allocate SSPI ssl object
  */
index b5ce76d9ddd03b2e9135480389c37a0e8e6e2e89..ebce922a6d8fe2f1083762d799d1c1d83b7f4b41 100644 (file)
 /* End PBXCopyFilesBuildPhase section */
 
 /* Begin PBXFileReference section */
+               270B267D17F5C06700C8A3A9 /* tls-darwin.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = "tls-darwin.c"; path = "../cups/tls-darwin.c"; sourceTree = "<group>"; };
+               270B267E17F5C06700C8A3A9 /* tls-gnutls.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = "tls-gnutls.c"; path = "../cups/tls-gnutls.c"; sourceTree = "<group>"; };
+               270B267F17F5C06700C8A3A9 /* tls-openssl.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = "tls-openssl.c"; path = "../cups/tls-openssl.c"; sourceTree = "<group>"; };
+               270B268017F5C5D600C8A3A9 /* sspi-private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "sspi-private.h"; path = "../cups/sspi-private.h"; sourceTree = "<group>"; };
+               270B268117F5C5D600C8A3A9 /* tls-sspi.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = "tls-sspi.c"; path = "../cups/tls-sspi.c"; sourceTree = "<group>"; };
                270CCDA7135E3C9E00007BE2 /* testmime */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = testmime; sourceTree = BUILT_PRODUCTS_DIR; };
                270CCDBB135E3D3E00007BE2 /* testmime.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = testmime.c; path = ../scheduler/testmime.c; sourceTree = "<group>"; };
                2732E089137A3F5200FAFEF6 /* cancel.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = cancel.c; path = ../systemv/cancel.c; sourceTree = "<group>"; };
                728FB7E1153600FA005426E1 /* tls-gnutls.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = "tls-gnutls.c"; path = "../scheduler/tls-gnutls.c"; sourceTree = "<group>"; };
                728FB7E2153600FA005426E1 /* tls-openssl.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = "tls-openssl.c"; path = "../scheduler/tls-openssl.c"; sourceTree = "<group>"; };
                728FB7E3153600FA005426E1 /* tls.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = tls.c; path = ../scheduler/tls.c; sourceTree = "<group>"; };
-               728FB7EC1536161C005426E1 /* libz.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.dylib; path = ../../../../../usr/lib/libz.dylib; sourceTree = "<group>"; };
-               728FB7EF1536167A005426E1 /* libiconv.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libiconv.dylib; path = ../../../../../usr/lib/libiconv.dylib; sourceTree = "<group>"; };
-               728FB7F01536167A005426E1 /* libresolv.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libresolv.dylib; path = ../../../../../usr/lib/libresolv.dylib; sourceTree = "<group>"; };
+               728FB7EC1536161C005426E1 /* libz.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.dylib; path = /usr/lib/libz.dylib; sourceTree = "<absolute>"; };
+               728FB7EF1536167A005426E1 /* libiconv.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libiconv.dylib; path = /usr/lib/libiconv.dylib; sourceTree = "<absolute>"; };
+               728FB7F01536167A005426E1 /* libresolv.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libresolv.dylib; path = /usr/lib/libresolv.dylib; sourceTree = "<absolute>"; };
                72A4332F155844CF002E172D /* libcups_static.a */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libcups_static.a; sourceTree = BUILT_PRODUCTS_DIR; };
                72C16CB8137B195D007E4BF4 /* file.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = file.c; path = ../scheduler/file.c; sourceTree = SOURCE_ROOT; };
-               72D53A2915B49110003F877F /* GSS.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = GSS.framework; path = ../../../../../System/Library/Frameworks/GSS.framework; sourceTree = "<group>"; };
-               72D53A2C15B4913D003F877F /* IOKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOKit.framework; path = ../../../../../System/Library/Frameworks/IOKit.framework; sourceTree = "<group>"; };
-               72D53A3315B4925B003F877F /* ApplicationServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ApplicationServices.framework; path = ../../../../../System/Library/Frameworks/ApplicationServices.framework; sourceTree = "<group>"; };
+               72D53A2915B49110003F877F /* GSS.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = GSS.framework; path = /System/Library/Frameworks/GSS.framework; sourceTree = "<absolute>"; };
+               72D53A2C15B4913D003F877F /* IOKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOKit.framework; path = /System/Library/Frameworks/IOKit.framework; sourceTree = "<absolute>"; };
+               72D53A3315B4925B003F877F /* ApplicationServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ApplicationServices.framework; path = /System/Library/Frameworks/ApplicationServices.framework; sourceTree = "<absolute>"; };
                72D53A3615B4929D003F877F /* colorman.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = colorman.c; path = ../scheduler/colorman.c; sourceTree = "<group>"; };
                72D53A3715B4929D003F877F /* colorman.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = colorman.h; path = ../scheduler/colorman.h; sourceTree = "<group>"; };
-               72D53A3915B492FA003F877F /* libpam.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libpam.dylib; path = ../../../../../usr/lib/libpam.dylib; sourceTree = "<group>"; };
+               72D53A3915B492FA003F877F /* libpam.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libpam.dylib; path = /usr/lib/libpam.dylib; sourceTree = "<absolute>"; };
                72F75A521336F950004BB496 /* cupstestppd */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = cupstestppd; sourceTree = BUILT_PRODUCTS_DIR; };
                72F75A5B1336F988004BB496 /* cupstestppd.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = cupstestppd.c; path = ../systemv/cupstestppd.c; sourceTree = "<group>"; };
                72F75A611336F9A3004BB496 /* libcupsimage.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libcupsimage.dylib; sourceTree = BUILT_PRODUCTS_DIR; };
                                72220F02133305BB00FCA411 /* string.c */,
                                72220F03133305BB00FCA411 /* tempfile.c */,
                                72220F05133305BB00FCA411 /* thread.c */,
+                               270B267D17F5C06700C8A3A9 /* tls-darwin.c */,
+                               270B267E17F5C06700C8A3A9 /* tls-gnutls.c */,
+                               270B267F17F5C06700C8A3A9 /* tls-openssl.c */,
+                               270B268117F5C5D600C8A3A9 /* tls-sspi.c */,
+                               270B268017F5C5D600C8A3A9 /* sspi-private.h */,
                                72220F06133305BB00FCA411 /* transcode.c */,
                                72220F08133305BB00FCA411 /* usersys.c */,
                                72220F09133305BB00FCA411 /* util.c */,
                72220FB113330B4A00FCA411 /* Frameworks */ = {
                        isa = PBXGroup;
                        children = (
+                               72D53A3915B492FA003F877F /* libpam.dylib */,
+                               72D53A3315B4925B003F877F /* ApplicationServices.framework */,
+                               72D53A2C15B4913D003F877F /* IOKit.framework */,
+                               72D53A2915B49110003F877F /* GSS.framework */,
+                               728FB7EF1536167A005426E1 /* libiconv.dylib */,
+                               728FB7F01536167A005426E1 /* libresolv.dylib */,
+                               728FB7EC1536161C005426E1 /* libz.dylib */,
                                278C58E5136B64AF00836530 /* CoreFoundation.framework */,
                                278C58E6136B64B000836530 /* Kerberos.framework */,
                                278C58E7136B64B000836530 /* Security.framework */,
                72BF96351333042100B1EAD7 = {
                        isa = PBXGroup;
                        children = (
-                               72D53A3915B492FA003F877F /* libpam.dylib */,
-                               72D53A3315B4925B003F877F /* ApplicationServices.framework */,
-                               72D53A2C15B4913D003F877F /* IOKit.framework */,
-                               72D53A2915B49110003F877F /* GSS.framework */,
-                               728FB7EF1536167A005426E1 /* libiconv.dylib */,
-                               728FB7F01536167A005426E1 /* libresolv.dylib */,
-                               728FB7EC1536161C005426E1 /* libz.dylib */,
                                72220FB113330B4A00FCA411 /* Frameworks */,
                                72220F45133305D000FCA411 /* Public Headers */,
                                72220F461333060C00FCA411 /* Private Headers */,
                        isa = XCBuildConfiguration;
                        buildSettings = {
                                PRODUCT_NAME = "$(TARGET_NAME)";
+                               SDKROOT = macosx;
                        };
                        name = Debug;
                };
                        isa = XCBuildConfiguration;
                        buildSettings = {
                                PRODUCT_NAME = "$(TARGET_NAME)";
+                               SDKROOT = macosx;
                        };
                        name = Release;
                };