third_party_cups/ohos-add-openssl.patch
dongzhengkuan f8f5358b2e add tls protocol Signed-off-by:dongzhengkuan@huawei.com
Signed-off-by: dongzhengkuan <dongzhengkuan@huawei.com>
2023-08-14 15:57:28 +08:00

1678 lines
45 KiB
Diff
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

diff --git a/cups/tls-openssl.c b/cups/tls-openssl.c
new file mode 100644
index 00000000..804e45fd
--- /dev/null
+++ b/cups-2.4.0/cups/tls-openssl.c
@@ -0,0 +1,1671 @@
+/*
+ * TLS support code for CUPS using OpenSSL/LibreSSL.
+ *
+ * Copyright © 2020-2023 by OpenPrinting
+ * Copyright © 2007-2019 by Apple Inc.
+ * Copyright © 1997-2007 by Easy Software Products, all rights reserved.
+ *
+ * Licensed under Apache License v2.0. See the file "LICENSE" for more
+ * information.
+ */
+
+/**** This file is included from tls.c ****/
+
+/*
+ * Include necessary headers...
+ */
+
+#include <sys/stat.h>
+#include <openssl/x509v3.h>
+#include <openssl/ssl.h>
+#include "cups-private.h"
+#include "debug-internal.h"
+/*
+ * Local functions...
+ */
+static long http_bio_ctrl(BIO *h, int cmd, long arg1, void *arg2);
+static int http_bio_free(BIO *data);
+static int http_bio_new(BIO *h);
+static int http_bio_puts(BIO *h, const char *str);
+static int http_bio_read(BIO *h, char *buf, int size);
+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 time_t http_get_date(X509 *cert, int which);
+//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);
+static int http_x509_add_ext(X509 *cert, int nid, const char *value);
+static void http_x509_add_san(GENERAL_NAMES *gens, const char *name);
+
+
+/*
+ * Local globals...
+ */
+
+static int tls_auto_create = 0;
+ /* Auto-create self-signed certs? */
+static BIO_METHOD *tls_bio_method = NULL;
+ /* OpenSSL BIO method */
+static char *tls_common_name = NULL;
+ /* Default common name */
+//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;
+ /* Mutex for keychain/certs */
+static int tls_options = -1,/* Options for TLS connections */
+ tls_min_version = _HTTP_TLS_1_0,
+ tls_max_version = _HTTP_TLS_MAX;
+
+
+/*
+ * 'cupsMakeServerCredentials()' - Make a self-signed certificate and private key pair.
+ *
+ * @since CUPS 2.0/OS 10.10@
+ */
+
+int // O - 1 on success, 0 on failure
+cupsMakeServerCredentials(
+ const char *path, // I - Path to keychain/directory
+ const char *common_name, // I - Common name
+ int num_alt_names, // I - Number of subject alternate names
+ const char **alt_names, // I - Subject Alternate Names
+ time_t expiration_date) // I - Expiration date
+{
+ int result = 0; // Return value
+ EVP_PKEY *pkey; // Private key
+ RSA *rsa; // RSA key pair
+ X509 *cert; // Certificate
+ cups_lang_t *language; // Default language info
+ time_t curtime; // Current time
+ X509_NAME *name; // Subject/issuer name
+ ASN1_INTEGER *serial; // Serial number
+ ASN1_TIME *notBefore, // Initial date
+ *notAfter; // Expiration date
+ BIO *bio; // Output file
+ char temp[1024], // Temporary directory name
+ crtfile[1024], // Certificate filename
+ keyfile[1024]; // Private key filename
+ const char *common_ptr; // Pointer into common name
+ GENERAL_NAMES *gens; // Names for SubjectAltName certificate extension
+
+
+ DEBUG_printf(("cupsMakeServerCredentials(path=\"%s\", common_name=\"%s\", num_alt_names=%d, alt_names=%p, expiration_date=%d)", path, common_name, num_alt_names, alt_names, (int)expiration_date));
+
+ // Filenames...
+ if (!path)
+ path = http_default_path(temp, sizeof(temp));
+
+ if (!path || !common_name)
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
+ return (0);
+ }
+
+ http_make_path(crtfile, sizeof(crtfile), path, common_name, "crt");
+ http_make_path(keyfile, sizeof(keyfile), path, common_name, "key");
+
+ // Create the encryption key...
+ DEBUG_puts("1cupsMakeServerCredentials: Creating key pair.");
+
+ if ((rsa = RSA_generate_key(3072, RSA_F4, NULL, NULL)) == NULL)
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to create key pair."), 1);
+ return (0);
+ }
+
+ if ((pkey = EVP_PKEY_new()) == NULL)
+ {
+ RSA_free(rsa);
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to create private key."), 1);
+ return (0);
+ }
+
+ EVP_PKEY_assign_RSA(pkey, rsa);
+
+ DEBUG_puts("1cupsMakeServerCredentials: Key pair created.");
+
+ // Create the X.509 certificate...
+ DEBUG_puts("1cupsMakeServerCredentials: Generating self-signed X.509 certificate.");
+
+ if ((cert = X509_new()) == NULL)
+ {
+ EVP_PKEY_free(pkey);
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to create X.509 certificate."), 1);
+ return (0);
+ }
+
+ curtime = time(NULL);
+
+ notBefore = ASN1_TIME_new();
+ ASN1_TIME_set(notBefore, curtime);
+ X509_set_notBefore(cert, notBefore);
+ ASN1_TIME_free(notBefore);
+
+ notAfter = ASN1_TIME_new();
+ ASN1_TIME_set(notAfter, expiration_date);
+ X509_set_notAfter(cert, notAfter);
+ ASN1_TIME_free(notAfter);
+
+ serial = ASN1_INTEGER_new();
+ ASN1_INTEGER_set(serial, (int)curtime);
+ X509_set_serialNumber(cert, serial);
+ ASN1_INTEGER_free(serial);
+
+ X509_set_pubkey(cert, pkey);
+
+ language = cupsLangDefault();
+ name = X509_NAME_new();
+ if (strlen(language->language) == 5)
+ X509_NAME_add_entry_by_txt(name, SN_countryName, MBSTRING_ASC, (unsigned char *)language->language + 3, -1, -1, 0);
+ else
+ X509_NAME_add_entry_by_txt(name, SN_countryName, MBSTRING_ASC, (unsigned char *)"US", -1, -1, 0);
+ X509_NAME_add_entry_by_txt(name, SN_commonName, MBSTRING_ASC, (unsigned char *)common_name, -1, -1, 0);
+ X509_NAME_add_entry_by_txt(name, SN_organizationName, MBSTRING_ASC, (unsigned char *)common_name, -1, -1, 0);
+ X509_NAME_add_entry_by_txt(name, SN_organizationalUnitName, MBSTRING_ASC, (unsigned char *)"Unknown", -1, -1, 0);
+ X509_NAME_add_entry_by_txt(name, SN_stateOrProvinceName, MBSTRING_ASC, (unsigned char *)"Unknown", -1, -1, 0);
+ X509_NAME_add_entry_by_txt(name, SN_localityName, MBSTRING_ASC, (unsigned char *)"Unknown", -1, -1, 0);
+
+ X509_set_issuer_name(cert, name);
+ X509_set_subject_name(cert, name);
+ X509_NAME_free(name);
+
+ gens = sk_GENERAL_NAME_new_null();
+ http_x509_add_san(gens, common_name);
+ if ((common_ptr = strstr(common_name, ".local")) == NULL)
+ {
+ // Add common_name.local to the list, too...
+ char localname[256], // hostname.local
+ *localptr; // Pointer into localname
+
+ strlcpy(localname, common_name, sizeof(localname));
+ if ((localptr = strchr(localname, '.')) != NULL)
+ *localptr = '\0';
+ strlcat(localname, ".local", sizeof(localname));
+
+ http_x509_add_san(gens, localname);
+ }
+
+ if (num_alt_names > 0)
+ {
+ int i; // Looping var...
+
+ for (i = 0; i < num_alt_names; i ++)
+ {
+ if (strcmp(alt_names[i], "localhost"))
+ http_x509_add_san(gens, alt_names[i]);
+ }
+ }
+
+ // Add extension with DNS names and free buffer for GENERAL_NAME
+ X509_add1_ext_i2d(cert, NID_subject_alt_name, gens, 0, X509V3_ADD_DEFAULT);
+ sk_GENERAL_NAME_pop_free(gens, GENERAL_NAME_free);
+
+ // Add extensions that are required to make Chrome happy...
+ http_x509_add_ext(cert, NID_basic_constraints, "critical,CA:FALSE,pathlen:0");
+ http_x509_add_ext(cert, NID_key_usage, "critical,digitalSignature,keyEncipherment");
+ http_x509_add_ext(cert, NID_ext_key_usage, "1.3.6.1.5.5.7.3.1");
+ http_x509_add_ext(cert, NID_subject_key_identifier, "hash");
+ http_x509_add_ext(cert, NID_authority_key_identifier, "keyid,issuer");
+ X509_set_version(cert, 2); // v3
+
+ X509_sign(cert, pkey, EVP_sha256());
+
+ // Save them...
+ if ((bio = BIO_new_file(keyfile, "wb")) == NULL)
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
+ goto done;
+ }
+
+ if (!PEM_write_bio_PrivateKey(bio, pkey, NULL, NULL, 0, NULL, NULL))
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to write private key."), 1);
+ BIO_free(bio);
+ goto done;
+ }
+
+ BIO_free(bio);
+
+ if ((bio = BIO_new_file(crtfile, "wb")) == NULL)
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
+ goto done;
+ }
+
+ if (!PEM_write_bio_X509(bio, cert))
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to write X.509 certificate."), 1);
+ BIO_free(bio);
+ goto done;
+ }
+
+ BIO_free(bio);
+
+ result = 1;
+ DEBUG_puts("1cupsMakeServerCredentials: Successfully created credentials.");
+
+ // Cleanup...
+ done:
+
+ X509_free(cert);
+ EVP_PKEY_free(pkey);
+
+ return (result);
+}
+
+
+/*
+ * 'cupsSetServerCredentials()' - Set the default server credentials.
+ *
+ * Note: The server credentials are used by all threads in the running process.
+ * This function is threadsafe.
+ *
+ * @since CUPS 2.0/OS 10.10@
+ */
+
+int // O - 1 on success, 0 on failure
+cupsSetServerCredentials(
+ const char *path, // I - Path to keychain/directory
+ const char *common_name, // I - Default common name for server
+ int auto_create) // I - 1 = automatically create self-signed certificates
+{
+ char temp[1024]; // Default path buffer
+
+
+ DEBUG_printf(("cupsSetServerCredentials(path=\"%s\", common_name=\"%s\", auto_create=%d)", path, common_name, auto_create));
+
+ /*
+ * Use defaults as needed...
+ */
+
+ if (!path)
+ path = http_default_path(temp, sizeof(temp));
+
+ /*
+ * Range check input...
+ */
+
+ if (!path || !common_name)
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
+ return (0);
+ }
+
+ _cupsMutexLock(&tls_mutex);
+
+ /*
+ * Free old values...
+ */
+
+ if (tls_keypath)
+ _cupsStrFree(tls_keypath);
+
+ if (tls_common_name)
+ _cupsStrFree(tls_common_name);
+
+ /*
+ * Save the new values...
+ */
+
+ tls_keypath = _cupsStrAlloc(path);
+ tls_auto_create = auto_create;
+ tls_common_name = _cupsStrAlloc(common_name);
+
+ _cupsMutexUnlock(&tls_mutex);
+
+ return (1);
+}
+
+
+/*
+ * 'httpCopyCredentials()' - Copy the credentials associated with the peer in
+ * an encrypted connection.
+ *
+ * @since CUPS 1.5/macOS 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
+{
+ STACK_OF(X509) *chain; // Certificate chain
+
+
+ DEBUG_printf(("httpCopyCredentials(http=%p, credentials=%p)", http, credentials));
+
+ if (credentials)
+ *credentials = NULL;
+
+ if (!http || !http->tls || !credentials)
+ return (-1);
+
+ *credentials = cupsArrayNew(NULL, NULL);
+ chain = SSL_get_peer_cert_chain(http->tls);
+
+ DEBUG_printf(("1httpCopyCredentials: chain=%p", chain));
+
+ if (chain)
+ {
+ int i, // Looping var
+ count; // Number of certs
+
+ for (i = 0, count = sk_X509_num(chain); i < count; i ++)
+ {
+ X509 *cert = sk_X509_value(chain, i);
+ // Current certificate
+ BIO *bio = BIO_new(BIO_s_mem());
+ // Memory buffer for cert
+
+ if (bio)
+ {
+ long bytes; // Number of bytes
+ char *buffer; // Pointer to bytes
+
+ if (PEM_write_bio_X509(bio, cert))
+ {
+ bytes = BIO_get_mem_data(bio, &buffer);
+ httpAddCredential(*credentials, buffer, (int)bytes);
+ }
+
+ BIO_free(bio);
+ }
+ }
+ }
+
+ return (0);
+}
+
+
+/*
+ * '_httpCreateCredentials()' - Create credentials in the internal format.
+ */
+
+http_tls_credentials_t // O - Internal credentials
+_httpCreateCredentials(
+ cups_array_t *credentials) // I - Array of credentials
+{
+ (void)credentials;
+
+ return (NULL);
+}
+
+
+/*
+ * '_httpFreeCredentials()' - Free internal credentials.
+ */
+
+void
+_httpFreeCredentials(
+ http_tls_credentials_t credentials) // I - Internal credentials
+{
+ X509_free(credentials);
+}
+
+
+/*
+ * 'httpCredentialsAreValidForName()' - Return whether the credentials are valid for the given name.
+ *
+ * @since CUPS 2.0/OS 10.10@
+ */
+
+int // O - 1 if valid, 0 otherwise
+httpCredentialsAreValidForName(
+ cups_array_t *credentials, // I - Credentials
+ const char *common_name) // I - Name to check
+{
+ X509 *cert; // Certificate
+ int result = 0; // Result
+
+
+ cert = http_create_credential((http_credential_t *)cupsArrayFirst(credentials));
+ if (cert)
+ {
+ result = X509_check_host(cert, common_name, strlen(common_name), 0, NULL);
+
+ X509_free(cert);
+ }
+
+ return (result);
+}
+
+
+/*
+ * 'httpCredentialsGetTrust()' - Return the trust of credentials.
+ *
+ * @since CUPS 2.0/OS 10.10@
+ */
+
+http_trust_t // O - Level of trust
+httpCredentialsGetTrust(
+ cups_array_t *credentials, // I - Credentials
+ const char *common_name) // I - Common name for trust lookup
+{
+ http_trust_t trust = HTTP_TRUST_OK; // Trusted?
+ X509 *cert; // Certificate
+ cups_array_t *tcreds = NULL; // Trusted credentials
+ _cups_globals_t *cg = _cupsGlobals(); // Per-thread globals
+
+
+ if (!common_name)
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("No common name specified."), 1);
+ return (HTTP_TRUST_UNKNOWN);
+ }
+
+ if ((cert = http_create_credential((http_credential_t *)cupsArrayFirst(credentials))) == NULL)
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to create credentials from array."), 1);
+ return (HTTP_TRUST_UNKNOWN);
+ }
+
+ if (cg->any_root < 0)
+ {
+ _cupsSetDefaults();
+// http_load_crl();
+ }
+
+ // Look this common name up in the default keychains...
+ httpLoadCredentials(NULL, &tcreds, common_name);
+
+ if (tcreds)
+ {
+ char credentials_str[1024], /* String for incoming credentials */
+ tcreds_str[1024]; /* String for saved credentials */
+
+ httpCredentialsString(credentials, credentials_str, sizeof(credentials_str));
+ httpCredentialsString(tcreds, tcreds_str, sizeof(tcreds_str));
+
+ if (strcmp(credentials_str, tcreds_str))
+ {
+ // Credentials don't match, let's look at the expiration date of the new
+ // credentials and allow if the new ones have a later expiration...
+ if (!cg->trust_first)
+ {
+ // Do not trust certificates on first use...
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Trust on first use is disabled."), 1);
+
+ trust = HTTP_TRUST_INVALID;
+ }
+ else if (httpCredentialsGetExpiration(credentials) <= httpCredentialsGetExpiration(tcreds))
+ {
+ // The new credentials are not newly issued...
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("New credentials are older than stored credentials."), 1);
+
+ trust = HTTP_TRUST_INVALID;
+ }
+ else if (!httpCredentialsAreValidForName(credentials, common_name))
+ {
+ // The common name does not match the issued certificate...
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("New credentials are not valid for name."), 1);
+
+ trust = HTTP_TRUST_INVALID;
+ }
+ else if (httpCredentialsGetExpiration(tcreds) < time(NULL))
+ {
+ // Save the renewed credentials...
+ trust = HTTP_TRUST_RENEWED;
+
+ httpSaveCredentials(NULL, credentials, common_name);
+ }
+ }
+
+ httpFreeCredentials(tcreds);
+ }
+ else if (cg->validate_certs && !httpCredentialsAreValidForName(credentials, common_name))
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("No stored credentials, not valid for name."), 1);
+ trust = HTTP_TRUST_INVALID;
+ }
+ else if (!cg->trust_first)
+ {
+ // See if we have a site CA certificate we can compare...
+ if (!httpLoadCredentials(NULL, &tcreds, "site"))
+ {
+ if (cupsArrayCount(credentials) != (cupsArrayCount(tcreds) + 1))
+ {
+ // Certificate isn't directly generated from the CA cert...
+ trust = HTTP_TRUST_INVALID;
+ }
+ else
+ {
+ // Do a tail comparison of the two certificates...
+ http_credential_t *a, *b; // Certificates
+
+ for (a = (http_credential_t *)cupsArrayFirst(tcreds), b = (http_credential_t *)cupsArrayIndex(credentials, 1); a && b; a = (http_credential_t *)cupsArrayNext(tcreds), b = (http_credential_t *)cupsArrayNext(credentials))
+ {
+ if (a->datalen != b->datalen || memcmp(a->data, b->data, a->datalen))
+ break;
+ }
+
+ if (a || b)
+ trust = HTTP_TRUST_INVALID;
+ }
+
+ if (trust != HTTP_TRUST_OK)
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Credentials do not validate against site CA certificate."), 1);
+ }
+ else
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Trust on first use is disabled."), 1);
+ trust = HTTP_TRUST_INVALID;
+ }
+ }
+
+ if (trust == HTTP_TRUST_OK && !cg->expired_certs)
+ {
+ time_t curtime; // Current date/time
+
+ time(&curtime);
+ if (curtime < http_get_date(cert, 0) || curtime > http_get_date(cert, 1))
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Credentials have expired."), 1);
+ trust = HTTP_TRUST_EXPIRED;
+ }
+ }
+
+ if (trust == HTTP_TRUST_OK && !cg->any_root && cupsArrayCount(credentials) == 1)
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Self-signed credentials are blocked."), 1);
+ trust = HTTP_TRUST_INVALID;
+ }
+
+ X509_free(cert);
+
+ return (trust);
+}
+
+
+/*
+ * 'httpCredentialsGetExpiration()' - Return the expiration date of the credentials.
+ *
+ * @since CUPS 2.0/OS 10.10@
+ */
+
+time_t // O - Expiration date of credentials
+httpCredentialsGetExpiration(
+ cups_array_t *credentials) // I - Credentials
+{
+ time_t result = 0; // Result
+ X509 *cert; // Certificate
+
+
+ if ((cert = http_create_credential((http_credential_t *)cupsArrayFirst(credentials))) != NULL)
+ {
+ result = http_get_date(cert, 1);
+ X509_free(cert);
+ }
+
+ return (result);
+}
+
+
+/*
+ * 'httpCredentialsString()' - Return a string representing the credentials.
+ *
+ * @since CUPS 2.0/OS 10.10@
+ */
+
+size_t // O - Total size of credentials string
+httpCredentialsString(
+ cups_array_t *credentials, // I - Credentials
+ char *buffer, // I - Buffer
+ size_t bufsize) // I - Size of buffer
+{
+ http_credential_t *first; // First certificate
+ X509 *cert; // Certificate
+
+
+ DEBUG_printf(("httpCredentialsString(credentials=%p, buffer=%p, bufsize=" CUPS_LLFMT ")", credentials, buffer, CUPS_LLCAST bufsize));
+
+ if (!buffer)
+ return (0);
+
+ if (bufsize > 0)
+ *buffer = '\0';
+
+ first = (http_credential_t *)cupsArrayFirst(credentials);
+ cert = http_create_credential(first);
+
+ if (cert)
+ {
+ char name[256], // Common name associated with cert
+ issuer[256]; // Issuer associated with cert
+ time_t expiration; // Expiration date of cert
+ const char *sigalg; // Signature algorithm
+ unsigned char md5_digest[16]; // MD5 result
+
+
+ X509_NAME_get_text_by_NID(X509_get_subject_name(cert), NID_commonName, name, sizeof(name));
+ X509_NAME_get_text_by_NID(X509_get_issuer_name(cert), NID_commonName, issuer, sizeof(issuer));
+ expiration = http_get_date(cert, 1);
+
+ switch (X509_get_signature_nid(cert))
+ {
+ case NID_ecdsa_with_SHA1 :
+ sigalg = "SHA1WithECDSAEncryption";
+ break;
+ case NID_ecdsa_with_SHA224 :
+ sigalg = "SHA224WithECDSAEncryption";
+ break;
+ case NID_ecdsa_with_SHA256 :
+ sigalg = "SHA256WithECDSAEncryption";
+ break;
+ case NID_ecdsa_with_SHA384 :
+ sigalg = "SHA384WithECDSAEncryption";
+ break;
+ case NID_ecdsa_with_SHA512 :
+ sigalg = "SHA512WithECDSAEncryption";
+ break;
+ case NID_sha1WithRSAEncryption :
+ sigalg = "SHA1WithRSAEncryption";
+ break;
+ case NID_sha224WithRSAEncryption :
+ sigalg = "SHA224WithRSAEncryption";
+ break;
+ case NID_sha256WithRSAEncryption :
+ sigalg = "SHA256WithRSAEncryption";
+ break;
+ case NID_sha384WithRSAEncryption :
+ sigalg = "SHA384WithRSAEncryption";
+ break;
+ case NID_sha512WithRSAEncryption :
+ sigalg = "SHA512WithRSAEncryption";
+ break;
+ default :
+ sigalg = "Unknown";
+ break;
+ }
+
+ cupsHashData("md5", first->data, first->datalen, md5_digest, sizeof(md5_digest));
+
+ snprintf(buffer, bufsize, "%s (issued by %s) / %s / %s / %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X", name, issuer, httpGetDateString(expiration), sigalg, md5_digest[0], md5_digest[1], md5_digest[2], md5_digest[3], md5_digest[4], md5_digest[5], md5_digest[6], md5_digest[7], md5_digest[8], md5_digest[9], md5_digest[10], md5_digest[11], md5_digest[12], md5_digest[13], md5_digest[14], md5_digest[15]);
+ X509_free(cert);
+ }
+
+ DEBUG_printf(("1httpCredentialsString: Returning \"%s\".", buffer));
+
+ return (strlen(buffer));
+}
+
+
+/*
+ * 'httpLoadCredentials()' - Load X.509 credentials from a keychain file.
+ *
+ * @since CUPS 2.0/OS 10.10@
+ */
+
+int // O - 0 on success, -1 on error
+httpLoadCredentials(
+ const char *path, // I - Keychain/PKCS#12 path
+ cups_array_t **credentials, // IO - Credentials
+ const char *common_name) // I - Common name for credentials
+{
+ cups_file_t *fp; // Certificate file
+ char filename[1024], // filename.crt
+ temp[1024], // Temporary string
+ 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
+ int in_certificate = 0;
+ // In a certificate?
+
+
+ if (!credentials || !common_name)
+ return (-1);
+
+ if (!path)
+ path = http_default_path(temp, sizeof(temp));
+ if (!path)
+ return (-1);
+
+ http_make_path(filename, sizeof(filename), path, common_name, "crt");
+
+ if ((fp = cupsFileOpen(filename, "r")) == NULL)
+ return (-1);
+
+ while (cupsFileGets(fp, line, sizeof(line)))
+ {
+ if (!strcmp(line, "-----BEGIN CERTIFICATE-----"))
+ {
+ if (in_certificate)
+ {
+ /*
+ * Missing END CERTIFICATE...
+ */
+
+ httpFreeCredentials(*credentials);
+ *credentials = NULL;
+ break;
+ }
+
+ in_certificate = 1;
+ }
+ else if (!strcmp(line, "-----END CERTIFICATE-----"))
+ {
+ if (!in_certificate || !num_data)
+ {
+ /*
+ * Missing data...
+ */
+
+ httpFreeCredentials(*credentials);
+ *credentials = NULL;
+ break;
+ }
+
+ if (!*credentials)
+ *credentials = cupsArrayNew(NULL, NULL);
+
+ if (httpAddCredential(*credentials, data, num_data))
+ {
+ httpFreeCredentials(*credentials);
+ *credentials = NULL;
+ break;
+ }
+
+ num_data = 0;
+ in_certificate = 0;
+ }
+ else if (in_certificate)
+ {
+ if (alloc_data == 0)
+ {
+ data = malloc(2048);
+ alloc_data = 2048;
+
+ if (!data)
+ break;
+ }
+ else if ((num_data + strlen(line)) >= alloc_data)
+ {
+ unsigned char *tdata = realloc(data, alloc_data + 1024);
+ /* Expanded buffer */
+
+ if (!tdata)
+ {
+ httpFreeCredentials(*credentials);
+ *credentials = NULL;
+ break;
+ }
+
+ 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 (in_certificate)
+ {
+ /*
+ * Missing END CERTIFICATE...
+ */
+
+ httpFreeCredentials(*credentials);
+ *credentials = NULL;
+ }
+
+ if (data)
+ free(data);
+
+ return (*credentials ? 0 : -1);
+}
+
+
+/*
+ * 'httpSaveCredentials()' - Save X.509 credentials to a keychain file.
+ *
+ * @since CUPS 2.0/OS 10.10@
+ */
+
+int // O - -1 on error, 0 on success
+httpSaveCredentials(
+ const char *path, // I - Keychain/PKCS#12 path
+ cups_array_t *credentials, // I - Credentials
+ const char *common_name) // I - Common name for credentials
+{
+ cups_file_t *fp; // Certificate file
+ char filename[1024], // filename.crt
+ nfilename[1024],// filename.crt.N
+ temp[1024], // Temporary string
+ line[256]; // Base64-encoded line
+ const unsigned char *ptr; // Pointer into certificate
+ ssize_t remaining; // Bytes left
+ http_credential_t *cred; // Current credential
+
+
+ if (!credentials || !common_name)
+ return (-1);
+
+ if (!path)
+ path = http_default_path(temp, sizeof(temp));
+ if (!path)
+ return (-1);
+
+ http_make_path(filename, sizeof(filename), path, common_name, "crt");
+ snprintf(nfilename, sizeof(nfilename), "%s.N", filename);
+
+ if ((fp = cupsFileOpen(nfilename, "w")) == NULL)
+ return (-1);
+
+#ifndef _WIN32
+ fchmod(cupsFileNumber(fp), 0600);
+#endif // !_WIN32
+
+ for (cred = (http_credential_t *)cupsArrayFirst(credentials);
+ cred;
+ cred = (http_credential_t *)cupsArrayNext(credentials))
+ {
+ cupsFilePuts(fp, "-----BEGIN CERTIFICATE-----\n");
+ for (ptr = cred->data, remaining = (ssize_t)cred->datalen; remaining > 0; remaining -= 45, ptr += 45)
+ {
+ httpEncode64_2(line, sizeof(line), (char *)ptr, remaining > 45 ? 45 : remaining);
+ cupsFilePrintf(fp, "%s\n", line);
+ }
+ cupsFilePuts(fp, "-----END CERTIFICATE-----\n");
+ }
+
+ cupsFileClose(fp);
+
+ return (rename(nfilename, filename));
+}
+
+
+/*
+ * '_httpTLSInitialize()' - Initialize the TLS stack.
+ */
+
+void
+_httpTLSInitialize(void)
+{
+ // OpenSSL no longer requires explicit initialization...
+}
+
+
+/*
+ * '_httpTLSPending()' - Return the number of pending TLS-encrypted bytes.
+ */
+
+size_t // O - Bytes available
+_httpTLSPending(http_t *http) // I - HTTP connection
+{
+ return ((size_t)SSL_pending(http->tls));
+}
+
+
+/*
+ * '_httpTLSRead()' - Read from 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
+{
+ return (SSL_read((SSL *)(http->tls), buf, len));
+}
+
+
+/*
+ * '_httpTLSSetOptions()' - Set TLS protocol and cipher suite options.
+ */
+
+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)
+ {
+ tls_options = options;
+ tls_min_version = min_version;
+ tls_max_version = max_version;
+ }
+}
+
+
+/*
+ * '_httpTLSStart()' - Set up SSL/TLS support on a connection.
+ */
+
+int // O - 0 on success, -1 on failure
+_httpTLSStart(http_t *http) // I - Connection to server
+{
+ 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(("3_httpTLSStart(http=%p)", http));
+
+ if (tls_options < 0)
+ {
+ DEBUG_puts("4_httpTLSStart: Setting defaults.");
+ _cupsSetDefaults();
+ DEBUG_printf(("4_httpTLSStart: tls_options=%x", tls_options));
+ }
+
+ 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);
+ }
+
+ 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?
+ int key_status, crt_status; // Key and certificate load status
+
+ context = SSL_CTX_new(TLS_server_method());
+
+ // 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
+ {
+ // 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))
+ {
+ // 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));
+ }
+ }
+
+ if (isdigit(hostname[0] & 255) || hostname[0] == '[')
+ hostname[0] = '\0'; // Don't allow numeric addresses
+
+ if (hostname[0])
+ cn = hostname;
+ else
+ cn = tls_common_name;
+
+ _cupsMutexLock(&tls_mutex);
+
+ 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");
+
+ 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
+
+ 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);
+ }
+ }
+
+ if (!access(cacrtfile, R_OK) && !access(cakeyfile, R_OK))
+ {
+ // Use the CA certs...
+ strlcpy(crtfile, cacrtfile, sizeof(crtfile));
+ strlcpy(keyfile, cakeyfile, sizeof(keyfile));
+ }
+ }
+
+ 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) + 3650 * 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);
+ SSL_CTX_free(context);
+ _cupsMutexUnlock(&tls_mutex);
+
+ return (-1);
+ }
+ }
+
+ _cupsMutexUnlock(&tls_mutex);
+
+ DEBUG_printf(("4_httpTLSStart: Using private key file '%s'.", keyfile));
+ DEBUG_printf(("4_httpTLSStart: Using certificate file '%s'.", crtfile));
+
+ crt_status = SSL_CTX_use_certificate_chain_file(context, crtfile);
+ key_status = SSL_CTX_use_PrivateKey_file(context, keyfile, SSL_FILETYPE_PEM);
+
+ if (!key_status || !crt_status)
+ {
+ // Unable to load private key or certificate...
+ DEBUG_puts("4_httpTLSStart: Unable to use private key or certificate chain file.");
+ if ((error = ERR_get_error()) != 0)
+ _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI, ERR_error_string(error, NULL), 0);
+
+ http->status = HTTP_STATUS_ERROR;
+ http->error = EIO;
+
+ SSL_CTX_free(context);
+
+ return (-1);
+ }
+ }
+
+ // 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));
+
+ DEBUG_printf(("4_httpTLSStart: cipherlist='%s', tls_min_version=%d, tls_max_version=%d", cipherlist, tls_min_version, tls_max_version));
+
+ 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);
+
+ // Setup a TLS session
+ _cupsMutexLock(&tls_mutex);
+ if (!tls_bio_method)
+ {
+ tls_bio_method = BIO_meth_new(BIO_get_new_index(), "http");
+ BIO_meth_set_ctrl(tls_bio_method, http_bio_ctrl);
+ BIO_meth_set_create(tls_bio_method, http_bio_new);
+ BIO_meth_set_destroy(tls_bio_method, http_bio_free);
+ BIO_meth_set_read(tls_bio_method, http_bio_read);
+ BIO_meth_set_puts(tls_bio_method, http_bio_puts);
+ BIO_meth_set_write(tls_bio_method, http_bio_write);
+ }
+ _cupsMutexUnlock(&tls_mutex);
+
+ bio = BIO_new(tls_bio_method);
+ BIO_ctrl(bio, BIO_C_SET_FILE_PTR, 0, (char *)http);
+
+ http->tls = SSL_new(context);
+ SSL_set_bio(http->tls, bio, bio);
+
+ if (http->mode == _HTTP_MODE_CLIENT)
+ {
+ // Negotiate as a client...
+ DEBUG_puts("4_httpTLSStart: Calling SSL_connect...");
+ 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);
+
+ http->status = HTTP_STATUS_ERROR;
+ http->error = EPIPE;
+
+ SSL_CTX_free(context);
+
+ SSL_free(http->tls);
+ http->tls = NULL;
+
+ DEBUG_printf(("4_httpTLSStart: Returning -1 (%s)", ERR_error_string(error, NULL)));
+
+ return (-1);
+ }
+ }
+ else
+ {
+ // Negotiate as a server...
+ DEBUG_puts("4_httpTLSStart: Calling SSL_accept...");
+ 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);
+
+ http->status = HTTP_STATUS_ERROR;
+ http->error = EPIPE;
+
+ SSL_CTX_free(context);
+
+ SSL_free(http->tls);
+ http->tls = NULL;
+
+ DEBUG_printf(("4_httpTLSStart: Returning -1 (%s)", ERR_error_string(error, NULL)));
+
+ return (-1);
+ }
+ }
+
+ DEBUG_puts("4_httpTLSStart: Returning 0.");
+
+ return (0);
+}
+
+
+/*
+ * '_httpTLSStop()' - Shut down SSL/TLS on a connection.
+ */
+
+void
+_httpTLSStop(http_t *http) // I - Connection to server
+{
+ 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);
+
+ http->tls = NULL;
+}
+
+
+/*
+ * '_httpTLSWrite()' - Write to a SSL/TLS connection.
+ */
+
+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));
+}
+
+
+/*
+ * '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 :
+ BIO_set_data(h, NULL);
+ return (0);
+
+ case BIO_C_SET_FILE_PTR :
+ BIO_set_data(h, arg2);
+ BIO_set_init(h, 1);
+ return (1);
+
+ case BIO_C_GET_FILE_PTR :
+ if (arg2)
+ {
+ *((void **)arg2) = BIO_get_data(h);
+ 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 (BIO_get_shutdown(h))
+ BIO_set_init(h, 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);
+
+ BIO_set_init(h, 0);
+ BIO_set_data(h, NULL);
+
+ 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 *)BIO_get_data(h))->fd, str, (int)strlen(str), 0));
+#else
+ return ((int)send(((http_t *)BIO_get_data(h))->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 *)BIO_get_data(h);
+
+ if (!http->blocking)
+ {
+ /*
+ * 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);
+ }
+ }
+
+ 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 *)BIO_get_data(h))->fd, buf, num, 0));
+}
+
+
+/*
+ * 'http_create_credential()' - Create a single credential in the internal format.
+ */
+
+static X509 * // O - Certificate
+http_create_credential(
+ http_credential_t *credential) // I - Credential
+{
+ X509 *cert = NULL; // Certificate
+ BIO *bio; // Basic I/O for string
+
+
+ if (!credential)
+ return (NULL);
+
+ if ((bio = BIO_new_mem_buf(credential->data, credential->datalen)) == NULL)
+ return (NULL);
+
+ 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
+
+
+#ifdef _WIN32
+ if (cg->home)
+#else
+ if (cg->home && getuid())
+#endif // _WIN32
+ {
+ 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);
+ }
+ }
+
+ 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);
+ }
+ }
+ }
+ else
+ strlcpy(buffer, CUPS_SERVERROOT "/ssl", bufsize);
+
+ DEBUG_printf(("1http_default_path: Using default path \"%s\".", buffer));
+
+ return (buffer);
+}
+
+
+//
+// 'http_get_date()' - Get the notBefore or notAfter date of a certificate.
+//
+
+static time_t // O - UNIX time in seconds
+http_get_date(X509 *cert, // I - Certificate
+ int which) // I - 0 for notBefore, 1 for notAfter
+{
+ struct tm exptm; // Expiration date components
+
+
+ if (which)
+ ASN1_TIME_to_tm(X509_get0_notAfter(cert), &exptm);
+ else
+ ASN1_TIME_to_tm(X509_get0_notBefore(cert), &exptm);
+
+ return (mktime(&exptm));
+}
+
+
+#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))
+ {
+ 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)
+ {
+ while (cupsFileGets(fp, line, sizeof(line)))
+ {
+ if (!strcmp(line, "-----BEGIN X509 CRL-----"))
+ {
+ if (num_data)
+ {
+ /*
+ * Missing END X509 CRL...
+ */
+
+ break;
+ }
+ }
+ else if (!strcmp(line, "-----END X509 CRL-----"))
+ {
+ if (!num_data)
+ {
+ /*
+ * Missing data...
+ */
+
+ break;
+ }
+
+ datum.data = data;
+ datum.size = num_data;
+
+ openssl_x509_crl_import(tls_crl, &datum, GNUTLS_X509_FMT_PEM);
+
+ num_data = 0;
+ }
+ else
+ {
+ if (alloc_data == 0)
+ {
+ data = malloc(2048);
+ alloc_data = 2048;
+
+ if (!data)
+ break;
+ }
+ else if ((num_data + strlen(line)) >= alloc_data)
+ {
+ unsigned char *tdata = realloc(data, alloc_data + 1024);
+ // Expanded buffer
+
+ if (!tdata)
+ break;
+
+ 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);
+ }
+ }
+
+ _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
+
+
+ snprintf(buffer, bufsize, "%s/", dirname);
+ bufptr = buffer + strlen(buffer);
+
+ while (*filename && bufptr < bufend)
+ {
+ if (_cups_isalnum(*filename) || *filename == '-' || *filename == '.')
+ *bufptr++ = *filename;
+ else
+ *bufptr++ = '_';
+
+ filename ++;
+ }
+
+ if (bufptr < bufend && filename[-1] != '.')
+ *bufptr++ = '.';
+
+ strlcpy(bufptr, ext, (size_t)(bufend - bufptr + 1));
+
+ return (buffer);
+}
+
+
+//
+// 'http_x509_add_ext()' - Add an extension to a certificate.
+//
+
+static int // O - 1 on success, 0 on failure
+http_x509_add_ext(X509 *cert, // I - Certificate
+ int nid, // I - Extension ID
+ const char *value) // I - Value
+{
+ int ret; // Return value
+ X509_EXTENSION *ex = NULL; // Extension
+ X509V3_CTX ctx; // Certificate context
+
+
+ DEBUG_printf(("3http_x509_add_ext(cert=%p, nid=%d, value=\"%s\")", (void *)cert, nid, value));
+
+ // Don't use a configuration database...
+ X509V3_set_ctx_nodb(&ctx);
+
+ // Self-signed certificates use the same issuer and subject...
+ X509V3_set_ctx(&ctx, /*issuer*/cert, /*subject*/cert, /*req*/NULL, /*crl*/NULL, /*flags*/0);
+
+ // Create and add the extension...
+ if ((ex = X509V3_EXT_conf_nid(/*conf*/NULL, &ctx, nid, value)) == NULL)
+ {
+ DEBUG_puts("4http_x509_add_ext: Unable to create extension, returning false.");
+ return (0);
+ }
+
+ ret = X509_add_ext(cert, ex, -1) != 0;
+
+ DEBUG_printf(("4http_x509_add_ext: X509_add_ext returned %s.", ret ? "true" : "false"));
+
+ // Free the extension and return...
+ X509_EXTENSION_free(ex);
+
+ return (ret);
+}
+
+
+//
+// 'http_x509_add_san()' - Add a subjectAltName to GENERAL_NAMES used for
+// the extension to an X.509 certificate.
+//
+
+static void
+http_x509_add_san(GENERAL_NAMES *gens, // I - Concatenation of DNS names
+ const char *name) // I - Hostname
+{
+ GENERAL_NAME *gen_dns = GENERAL_NAME_new();
+ // DNS: name
+ ASN1_IA5STRING *ia5 = ASN1_IA5STRING_new();
+ // Hostname string
+
+
+ // Set the strings and push it on the GENERAL_NAMES list...
+ ASN1_STRING_set(ia5, name, strlen(name));
+ GENERAL_NAME_set0_value(gen_dns, GEN_DNS, ia5);
+ sk_GENERAL_NAME_push(gens, gen_dns);
+}