From 192553088b1c2795dfc3d822895a250fcf5d9a46 Mon Sep 17 00:00:00 2001 From: Michael R Sweet Date: Wed, 30 Oct 2024 09:49:44 -0400 Subject: [PATCH] GNU TLS updates. --- cups/cups.h | 1 + cups/testcreds.c | 20 ++++++ cups/tls-gnutls.c | 158 +++++++++++++++++++++++++++++++++------------ cups/tls-openssl.c | 40 +++++++++++- cups/tls.c | 15 ++++- 5 files changed, 190 insertions(+), 44 deletions(-) diff --git a/cups/cups.h b/cups/cups.h index a81262428..72cd74379 100644 --- a/cups/cups.h +++ b/cups/cups.h @@ -388,6 +388,7 @@ extern http_t *cupsConnectDestBlock(cups_dest_t *dest, unsigned flags, int msec # endif // __BLOCKS__ extern char *cupsCopyCredentials(const char *path, const char *common_name) _CUPS_PUBLIC; extern char *cupsCopyCredentialsKey(const char *path, const char *common_name) _CUPS_PUBLIC; +extern char *cupsCopyCredentialsPublicKey(const char *path, const char *common_name) _CUPS_PUBLIC; extern char *cupsCopyCredentialsRequest(const char *path, const char *common_name) _CUPS_PUBLIC; extern int cupsCopyDest(cups_dest_t *dest, int num_dests, cups_dest_t **dests) _CUPS_PUBLIC; extern int cupsCopyDestConflicts(http_t *http, cups_dest_t *dest, cups_dinfo_t *info, int num_options, cups_option_t *options, const char *new_option, const char *new_value, int *num_conflicts, cups_option_t **conflicts, int *num_resolved, cups_option_t **resolved) _CUPS_PUBLIC; diff --git a/cups/testcreds.c b/cups/testcreds.c index 5018df874..1e7626a4c 100644 --- a/cups/testcreds.c +++ b/cups/testcreds.c @@ -466,6 +466,16 @@ do_unit_tests(void) if (data) { + char *pubkey; // Public key + + testBegin("cupsCopyCredentialsPublicKey()"); + if ((pubkey = cupsCopyCredentialsPublicKey(TEST_CERT_PATH, "altprinter")) != NULL) + testEnd(true); + else + testEndMessage(false, "%s", cupsGetErrorString()); + + free(pubkey); + testBegin("cupsSignCredentialsRequest(altprinter w/alt names)"); if (cupsSignCredentialsRequest(TEST_CERT_PATH, "altprinter", data, "_site_", CUPS_CREDPURPOSE_ALL, CUPS_CREDUSAGE_ALL, /*cb*/NULL, /*cb_data*/NULL, time(NULL) + 30 * 86400)) { @@ -500,6 +510,16 @@ do_unit_tests(void) if (data) { + char *pubkey; // Public key + + testBegin("cupsCopyCredentialsPublicKey()"); + if ((pubkey = cupsCopyCredentialsPublicKey(TEST_CERT_PATH, "altprinter")) != NULL) + testEnd(true); + else + testEndMessage(false, "%s", cupsGetErrorString()); + + free(pubkey); + testBegin("cupsSignCredentialsRequest(altprinter w/o alt names)"); if (cupsSignCredentialsRequest(TEST_CERT_PATH, "altprinter", data, "_site_", CUPS_CREDPURPOSE_ALL, CUPS_CREDUSAGE_ALL, /*cb*/NULL, /*cb_data*/NULL, time(NULL) + 30 * 86400)) { diff --git a/cups/tls-gnutls.c b/cups/tls-gnutls.c index db5801ca8..dcfea91e8 100644 --- a/cups/tls-gnutls.c +++ b/cups/tls-gnutls.c @@ -178,11 +178,13 @@ cupsCreateCredentials( bool ret = false; // Return value gnutls_x509_crt_t crt = NULL; // New certificate gnutls_x509_privkey_t key = NULL; // Encryption private key + gnutls_pubkey_t pubkey = NULL; // Encryption public key gnutls_x509_crt_t root_crt = NULL;// Root certificate gnutls_x509_privkey_t root_key = NULL;// Root private key char defpath[1024], // Default path crtfile[1024], // Certificate filename keyfile[1024], // Private key filename + pubfile[1024], // Public key filename *root_crtdata, // Root certificate data *root_keydata; // Root private key data unsigned gnutls_usage = 0;// GNU TLS keyUsage bits @@ -208,6 +210,27 @@ cupsCreateCredentials( http_make_path(crtfile, sizeof(crtfile), path, common_name, "crt"); http_make_path(keyfile, sizeof(keyfile), path, common_name, "key"); + http_make_path(pubfile, sizeof(pubfile), path, common_name, "pub"); + + // Key usage flags... + if (usage & CUPS_CREDUSAGE_DIGITAL_SIGNATURE) + gnutls_usage |= GNUTLS_KEY_DIGITAL_SIGNATURE; + if (usage & CUPS_CREDUSAGE_NON_REPUDIATION) + gnutls_usage |= GNUTLS_KEY_NON_REPUDIATION; + if (usage & CUPS_CREDUSAGE_KEY_ENCIPHERMENT) + gnutls_usage |= GNUTLS_KEY_KEY_ENCIPHERMENT; + if (usage & CUPS_CREDUSAGE_DATA_ENCIPHERMENT) + gnutls_usage |= GNUTLS_KEY_DATA_ENCIPHERMENT; + if (usage & CUPS_CREDUSAGE_KEY_AGREEMENT) + gnutls_usage |= GNUTLS_KEY_KEY_AGREEMENT; + if (usage & CUPS_CREDUSAGE_KEY_CERT_SIGN) + gnutls_usage |= GNUTLS_KEY_KEY_CERT_SIGN; + if (usage & CUPS_CREDUSAGE_CRL_SIGN) + gnutls_usage |= GNUTLS_KEY_CRL_SIGN; + if (usage & CUPS_CREDUSAGE_ENCIPHER_ONLY) + gnutls_usage |= GNUTLS_KEY_ENCIPHER_ONLY; + if (usage & CUPS_CREDUSAGE_DECIPHER_ONLY) + gnutls_usage |= GNUTLS_KEY_DECIPHER_ONLY; // Create the encryption key... DEBUG_puts("1cupsCreateCredentials: Creating key pair."); @@ -238,6 +261,39 @@ cupsCreateCredentials( goto done; } + bytes = sizeof(buffer); + + if ((err = gnutls_pubkey_init(&pubkey)) < 0) + { + DEBUG_printf("1cupsCreateCredentials: Unable to create public key: %s", gnutls_strerror(err)); + _cupsSetError(IPP_STATUS_ERROR_INTERNAL, gnutls_strerror(err), 0); + goto done; + } + else if ((err = gnutls_pubkey_import_privkey(pubkey, key, gnutls_usage, /*flags*/0)) < 0) + { + DEBUG_printf("1cupsCreateCredentials: Unable to import public key: %s", gnutls_strerror(err)); + _cupsSetError(IPP_STATUS_ERROR_INTERNAL, gnutls_strerror(err), 0); + goto done; + } + else if ((err = gnutls_pubkey_export(pubkey, GNUTLS_X509_FMT_PEM, buffer, &bytes)) < 0) + { + DEBUG_printf("1cupsCreateCredentials: Unable to export public key: %s", gnutls_strerror(err)); + _cupsSetError(IPP_STATUS_ERROR_INTERNAL, gnutls_strerror(err), 0); + goto done; + } + else if ((fp = cupsFileOpen(pubfile, "w")) != NULL) + { + DEBUG_printf("1cupsCreateCredentials: Writing public key to \"%s\".", keyfile); + cupsFileWrite(fp, (char *)buffer, bytes); + cupsFileClose(fp); + } + else + { + DEBUG_printf("1cupsCreateCredentials: Unable to create public key file \"%s\": %s", keyfile, strerror(errno)); + _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0); + goto done; + } + // Create the certificate... DEBUG_puts("1cupsCreateCredentials: Generating X.509 certificate."); @@ -310,25 +366,6 @@ cupsCreateCredentials( if (purpose & CUPS_CREDPURPOSE_OCSP_SIGNING) gnutls_x509_crt_set_key_purpose_oid(crt, GNUTLS_KP_OCSP_SIGNING, 0); - if (usage & CUPS_CREDUSAGE_DIGITAL_SIGNATURE) - gnutls_usage |= GNUTLS_KEY_DIGITAL_SIGNATURE; - if (usage & CUPS_CREDUSAGE_NON_REPUDIATION) - gnutls_usage |= GNUTLS_KEY_NON_REPUDIATION; - if (usage & CUPS_CREDUSAGE_KEY_ENCIPHERMENT) - gnutls_usage |= GNUTLS_KEY_KEY_ENCIPHERMENT; - if (usage & CUPS_CREDUSAGE_DATA_ENCIPHERMENT) - gnutls_usage |= GNUTLS_KEY_DATA_ENCIPHERMENT; - if (usage & CUPS_CREDUSAGE_KEY_AGREEMENT) - gnutls_usage |= GNUTLS_KEY_KEY_AGREEMENT; - if (usage & CUPS_CREDUSAGE_KEY_CERT_SIGN) - gnutls_usage |= GNUTLS_KEY_KEY_CERT_SIGN; - if (usage & CUPS_CREDUSAGE_CRL_SIGN) - gnutls_usage |= GNUTLS_KEY_CRL_SIGN; - if (usage & CUPS_CREDUSAGE_ENCIPHER_ONLY) - gnutls_usage |= GNUTLS_KEY_ENCIPHER_ONLY; - if (usage & CUPS_CREDUSAGE_DECIPHER_ONLY) - gnutls_usage |= GNUTLS_KEY_DECIPHER_ONLY; - gnutls_x509_crt_set_key_usage(crt, gnutls_usage); gnutls_x509_crt_set_version(crt, 3); @@ -423,6 +460,8 @@ cupsCreateCredentials( gnutls_x509_crt_deinit(crt); if (key) gnutls_x509_privkey_deinit(key); + if (pubkey) + gnutls_pubkey_deinit(pubkey); return (ret); } @@ -502,9 +541,11 @@ cupsCreateCredentialsRequest( bool ret = false; // Return value gnutls_x509_crq_t crq = NULL; // Certificate request gnutls_x509_privkey_t key = NULL; // Private/public key pair + gnutls_pubkey_t pubkey = NULL; // Encryption public key char defpath[1024], // Default path csrfile[1024], // Certificate signing request filename - keyfile[1024]; // Private key filename + keyfile[1024], // Private key filename + pubfile[1024]; // Public key filename unsigned gnutls_usage = 0;// GNU TLS keyUsage bits cups_file_t *fp; // Key/cert file unsigned char buffer[8192]; // Buffer for key/cert data @@ -526,6 +567,27 @@ cupsCreateCredentialsRequest( http_make_path(csrfile, sizeof(csrfile), path, common_name, "csr"); http_make_path(keyfile, sizeof(keyfile), path, common_name, "ktm"); + http_make_path(pubfile, sizeof(pubfile), path, common_name, "pub"); + + // Key usage flags... + if (usage & CUPS_CREDUSAGE_DIGITAL_SIGNATURE) + gnutls_usage |= GNUTLS_KEY_DIGITAL_SIGNATURE; + if (usage & CUPS_CREDUSAGE_NON_REPUDIATION) + gnutls_usage |= GNUTLS_KEY_NON_REPUDIATION; + if (usage & CUPS_CREDUSAGE_KEY_ENCIPHERMENT) + gnutls_usage |= GNUTLS_KEY_KEY_ENCIPHERMENT; + if (usage & CUPS_CREDUSAGE_DATA_ENCIPHERMENT) + gnutls_usage |= GNUTLS_KEY_DATA_ENCIPHERMENT; + if (usage & CUPS_CREDUSAGE_KEY_AGREEMENT) + gnutls_usage |= GNUTLS_KEY_KEY_AGREEMENT; + if (usage & CUPS_CREDUSAGE_KEY_CERT_SIGN) + gnutls_usage |= GNUTLS_KEY_KEY_CERT_SIGN; + if (usage & CUPS_CREDUSAGE_CRL_SIGN) + gnutls_usage |= GNUTLS_KEY_CRL_SIGN; + if (usage & CUPS_CREDUSAGE_ENCIPHER_ONLY) + gnutls_usage |= GNUTLS_KEY_ENCIPHER_ONLY; + if (usage & CUPS_CREDUSAGE_DECIPHER_ONLY) + gnutls_usage |= GNUTLS_KEY_DECIPHER_ONLY; // Create the encryption key... DEBUG_puts("1cupsCreateCredentialsRequest: Creating key pair."); @@ -555,7 +617,40 @@ cupsCreateCredentialsRequest( goto done; } - // Create the certificate... + bytes = sizeof(buffer); + + if ((err = gnutls_pubkey_init(&pubkey)) < 0) + { + DEBUG_printf("1cupsCreateCredentials: Unable to create public key: %s", gnutls_strerror(err)); + _cupsSetError(IPP_STATUS_ERROR_INTERNAL, gnutls_strerror(err), 0); + goto done; + } + else if ((err = gnutls_pubkey_import_privkey(pubkey, key, gnutls_usage, /*flags*/0)) < 0) + { + DEBUG_printf("1cupsCreateCredentials: Unable to import public key: %s", gnutls_strerror(err)); + _cupsSetError(IPP_STATUS_ERROR_INTERNAL, gnutls_strerror(err), 0); + goto done; + } + else if ((err = gnutls_pubkey_export(pubkey, GNUTLS_X509_FMT_PEM, buffer, &bytes)) < 0) + { + DEBUG_printf("1cupsCreateCredentials: Unable to export public key: %s", gnutls_strerror(err)); + _cupsSetError(IPP_STATUS_ERROR_INTERNAL, gnutls_strerror(err), 0); + goto done; + } + else if ((fp = cupsFileOpen(pubfile, "w")) != NULL) + { + DEBUG_printf("1cupsCreateCredentials: Writing public key to \"%s\".", keyfile); + cupsFileWrite(fp, (char *)buffer, bytes); + cupsFileClose(fp); + } + else + { + DEBUG_printf("1cupsCreateCredentials: Unable to create public key file \"%s\": %s", keyfile, strerror(errno)); + _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0); + goto done; + } + + // Create the certificate signing request... DEBUG_puts("1cupsCreateCredentialsRequest: Generating X.509 certificate request."); if (!organization) @@ -613,25 +708,6 @@ cupsCreateCredentialsRequest( if (purpose & CUPS_CREDPURPOSE_OCSP_SIGNING) gnutls_x509_crq_set_key_purpose_oid(crq, GNUTLS_KP_OCSP_SIGNING, 0); - if (usage & CUPS_CREDUSAGE_DIGITAL_SIGNATURE) - gnutls_usage |= GNUTLS_KEY_DIGITAL_SIGNATURE; - if (usage & CUPS_CREDUSAGE_NON_REPUDIATION) - gnutls_usage |= GNUTLS_KEY_NON_REPUDIATION; - if (usage & CUPS_CREDUSAGE_KEY_ENCIPHERMENT) - gnutls_usage |= GNUTLS_KEY_KEY_ENCIPHERMENT; - if (usage & CUPS_CREDUSAGE_DATA_ENCIPHERMENT) - gnutls_usage |= GNUTLS_KEY_DATA_ENCIPHERMENT; - if (usage & CUPS_CREDUSAGE_KEY_AGREEMENT) - gnutls_usage |= GNUTLS_KEY_KEY_AGREEMENT; - if (usage & CUPS_CREDUSAGE_KEY_CERT_SIGN) - gnutls_usage |= GNUTLS_KEY_KEY_CERT_SIGN; - if (usage & CUPS_CREDUSAGE_CRL_SIGN) - gnutls_usage |= GNUTLS_KEY_CRL_SIGN; - if (usage & CUPS_CREDUSAGE_ENCIPHER_ONLY) - gnutls_usage |= GNUTLS_KEY_ENCIPHER_ONLY; - if (usage & CUPS_CREDUSAGE_DECIPHER_ONLY) - gnutls_usage |= GNUTLS_KEY_DECIPHER_ONLY; - gnutls_x509_crq_set_key_usage(crq, gnutls_usage); gnutls_x509_crq_set_version(crq, 3); @@ -669,6 +745,8 @@ cupsCreateCredentialsRequest( gnutls_x509_crq_deinit(crq); if (key) gnutls_x509_privkey_deinit(key); + if (pubkey) + gnutls_pubkey_deinit(pubkey); return (ret); } diff --git a/cups/tls-openssl.c b/cups/tls-openssl.c index b4652ca36..8a5c5a4cc 100644 --- a/cups/tls-openssl.c +++ b/cups/tls-openssl.c @@ -181,6 +181,7 @@ cupsCreateCredentials( char defpath[1024], // Default path crtfile[1024], // Certificate filename keyfile[1024], // Private key filename + pubfile[1024], // Public key filename root_crtfile[1024], // Root certificate filename root_keyfile[1024]; // Root private key filename time_t curtime; // Current time @@ -235,7 +236,7 @@ cupsCreateCredentials( X509_set_notBefore(cert, notBefore); ASN1_TIME_free(notBefore); - notAfter = ASN1_TIME_new(); + notAfter = ASN1_TIME_new(); ASN1_TIME_set(notAfter, expiration_date); X509_set_notAfter(cert, notAfter); ASN1_TIME_free(notAfter); @@ -358,6 +359,7 @@ cupsCreateCredentials( // Save them... http_make_path(crtfile, sizeof(crtfile), path, common_name, "crt"); http_make_path(keyfile, sizeof(keyfile), path, common_name, "key"); + http_make_path(pubfile, sizeof(pubfile), path, common_name, "pub"); if ((bio = BIO_new_file(keyfile, "wb")) == NULL) { @@ -374,6 +376,21 @@ cupsCreateCredentials( BIO_free(bio); + if ((bio = BIO_new_file(pubfile, "wb")) == NULL) + { + _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0); + goto done; + } + + if (!PEM_write_bio_PUBKEY(bio, pkey)) + { + _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to write public key."), true); + BIO_free(bio); + goto done; + } + + BIO_free(bio); + if ((bio = BIO_new_file(crtfile, "wb")) == NULL) { _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0); @@ -490,7 +507,8 @@ cupsCreateCredentialsRequest( char temp[1024], // Temporary directory name *tempptr, // Pointer into temporary string csrfile[1024], // Certificate signing request filename - keyfile[1024]; // Private key filename + keyfile[1024], // Private key filename + pubfile[1024]; // Public key filename STACK_OF(X509_EXTENSION) *exts; // Extensions unsigned i; // Looping var cups_credpurpose_t purpose_bit; // Current purpose @@ -511,6 +529,7 @@ cupsCreateCredentialsRequest( http_make_path(csrfile, sizeof(csrfile), path, common_name, "csr"); http_make_path(keyfile, sizeof(keyfile), path, common_name, "ktm"); + http_make_path(pubfile, sizeof(pubfile), path, common_name, "pub"); // Create the encryption key... DEBUG_puts("1cupsCreateCredentialsRequest: Creating key pair."); @@ -593,6 +612,21 @@ cupsCreateCredentialsRequest( BIO_free(bio); + if ((bio = BIO_new_file(pubfile, "wb")) == NULL) + { + _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0); + goto done; + } + + if (!PEM_write_bio_PUBKEY(bio, pkey)) + { + _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to write public key."), true); + BIO_free(bio); + goto done; + } + + BIO_free(bio); + if ((bio = BIO_new_file(csrfile, "wb")) == NULL) { _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0); @@ -996,7 +1030,7 @@ cupsSignCredentialsRequest( ASN1_INTEGER *serial; // Serial number ASN1_TIME *notBefore, // Initial date *notAfter; // Expiration date - BIO *bio; // Output file + BIO *bio; // Input/output file char temp[1024]; // Temporary string int i, j, // Looping vars num_exts; // Number of extensions diff --git a/cups/tls.c b/cups/tls.c index 5ae71bf0f..02863fe5e 100644 --- a/cups/tls.c +++ b/cups/tls.c @@ -102,11 +102,24 @@ cupsCopyCredentialsKey( } +// +// 'cupsCopyCredentialsPublicKey()' - Copy the public key for a X.509 certificate request. +// + +char * // O - PEM-encoded public key +cupsCopyCredentialsPublicKey( + const char *path, // I - Directory path for certificate/key store or `NULL` for default + const char *common_name) // I - Common name +{ + return (http_copy_file(path, common_name, "pub")); +} + + // // 'cupsCopyCredentialsRequest()' - Copy the X.509 certificate signing request to a string. // -char * +char * // O - PEM-encoded X.509 certificate signing request cupsCopyCredentialsRequest( const char *path, // I - Directory path for certificate/key store or `NULL` for default const char *common_name) // I - Common name