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