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