%s: %s ", papplClientGetLocString(client, _PAPPL_LOC("Level")), papplClientGetLocString(client, _PAPPL_LOC("Good (2048-bit RSA)")), papplClientGetLocString(client, _PAPPL_LOC("Better (4096-bit RSA)")), papplClientGetLocString(client, _PAPPL_LOC("Best (384-bit ECC)")), papplClientGetLocString(client, _PAPPL_LOC("EMail (contact)")), system->contact.email, papplClientGetLocString(client, _PAPPL_LOC("Organization")), system->organization ? system->organization : "", papplClientGetLocString(client, _PAPPL_LOC("Organization/business name")), papplClientGetLocString(client, _PAPPL_LOC("Organization Unit")), system->org_unit ? system->org_unit : "", papplClientGetLocString(client, _PAPPL_LOC("Unit, department, etc.")), papplClientGetLocString(client, _PAPPL_LOC("City/Locality")), papplClientGetLocString(client, _PAPPL_LOC("City/town name")), papplClientGetLocString(client, _PAPPL_LOC("Use My Position")), papplClientGetLocString(client, _PAPPL_LOC("State/Province")), papplClientGetLocString(client, _PAPPL_LOC("State/province name")), papplClientGetLocString(client, _PAPPL_LOC("Country or Region")), papplClientGetLocString(client, _PAPPL_LOC("Choose")));
+ " %s: %s ", papplClientGetLocString(client, _PAPPL_LOC("Level")), papplClientGetLocString(client, _PAPPL_LOC("Good (2048-bit RSA)")), papplClientGetLocString(client, _PAPPL_LOC("Better (3072-bit RSA)")), papplClientGetLocString(client, _PAPPL_LOC("Better (4096-bit RSA)")), papplClientGetLocString(client, _PAPPL_LOC("Better (256-bit ECC)")), papplClientGetLocString(client, _PAPPL_LOC("Best (384-bit ECC)")), papplClientGetLocString(client, _PAPPL_LOC("Best (521-bit ECC)")), papplClientGetLocString(client, _PAPPL_LOC("EMail (contact)")), system->contact.email, papplClientGetLocString(client, _PAPPL_LOC("Organization")), system->organization ? system->organization : "", papplClientGetLocString(client, _PAPPL_LOC("Organization/business name")), papplClientGetLocString(client, _PAPPL_LOC("Organization Unit")), system->org_unit ? system->org_unit : "", papplClientGetLocString(client, _PAPPL_LOC("Unit, department, etc.")), papplClientGetLocString(client, _PAPPL_LOC("City/Locality")), papplClientGetLocString(client, _PAPPL_LOC("City/town name")), papplClientGetLocString(client, _PAPPL_LOC("Use My Position")), papplClientGetLocString(client, _PAPPL_LOC("State/Province")), papplClientGetLocString(client, _PAPPL_LOC("State/province name")), papplClientGetLocString(client, _PAPPL_LOC("Country or Region")), papplClientGetLocString(client, _PAPPL_LOC("Choose")));
for (i = 0; i < (int)(sizeof(countries) / sizeof(countries[0])); i ++)
papplClientHTMLPrintf(client, "%s ", countries[i][0], papplClientGetLocString(client, countries[i][1]));
@@ -2100,6 +2116,162 @@ get_networks(
#endif // _WIN32
}
+#ifdef HAVE_OPENSSL
+//
+// 'openssl_add_ext()' - Add an extension.
+//
+
+static bool // O - `true` on success, `false` on error
+openssl_add_ext(
+ STACK_OF(X509_EXTENSION) *exts, // I - Stack of extensions
+ int nid, // I - Extension ID
+ const char *value) // I - Value
+{
+ X509_EXTENSION *ext = NULL; // Extension
+
+
+ // Create and add the extension...
+ if ((ext = X509V3_EXT_conf_nid(/*conf*/NULL, /*ctx*/NULL, nid, value)) == NULL)
+ return (false);
+
+ sk_X509_EXTENSION_push(exts, ext);
+
+ return (true);
+}
+
+
+//
+// 'openssl_create_key()' - Create a suitable key pair for a certificate/signing request.
+//
+
+static EVP_PKEY * // O - Key pair
+openssl_create_key(
+ pappl_system_t *system, // I - System
+ _pappl_credtype_t type) // I - Type of key
+{
+ EVP_PKEY *pkey; // Key pair
+ EVP_PKEY_CTX *ctx; // Key generation context
+ int algid; // Algorithm NID
+ int bits = 0; // Bits
+ int curveid = 0; // Curve NID
+
+
+ switch (type)
+ {
+ case _PAPPL_CREDTYPE_ECDSA_P256_SHA256 :
+ algid = EVP_PKEY_EC;
+ curveid = NID_secp256k1;
+ break;
+
+ case _PAPPL_CREDTYPE_ECDSA_P384_SHA256 :
+ algid = EVP_PKEY_EC;
+ curveid = NID_secp384r1;
+ break;
+
+ case _PAPPL_CREDTYPE_ECDSA_P521_SHA256 :
+ algid = EVP_PKEY_EC;
+ curveid = NID_secp521r1;
+ break;
+
+ case _PAPPL_CREDTYPE_RSA_2048_SHA256 :
+ algid = EVP_PKEY_RSA;
+ bits = 2048;
+ break;
+
+ default :
+ case _PAPPL_CREDTYPE_RSA_3072_SHA256 :
+ algid = EVP_PKEY_RSA;
+ bits = 3072;
+ break;
+
+ case _PAPPL_CREDTYPE_RSA_4096_SHA256 :
+ algid = EVP_PKEY_RSA;
+ bits = 4096;
+ break;
+ }
+
+ pkey = NULL;
+
+ if ((ctx = EVP_PKEY_CTX_new_id(algid, NULL)) == NULL)
+ papplLog(system, PAPPL_LOGLEVEL_ERROR, "Unable to create private key context.");
+ else if (EVP_PKEY_keygen_init(ctx) <= 0)
+ papplLog(system, PAPPL_LOGLEVEL_ERROR, "Unable to initialize private key context.");
+ else if (bits && EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, bits) <= 0)
+ papplLog(system, PAPPL_LOGLEVEL_ERROR, "Unable to configure private key context.");
+ else if (curveid && EVP_PKEY_CTX_set_ec_paramgen_curve_nid(ctx, curveid) <= 0)
+ papplLog(system, PAPPL_LOGLEVEL_ERROR, "Unable to configure private key context.");
+ else if (EVP_PKEY_keygen(ctx, &pkey) <= 0)
+ papplLog(system, PAPPL_LOGLEVEL_ERROR, "Unable to create private key.");
+
+ EVP_PKEY_CTX_free(ctx);
+
+ return (pkey);
+}
+
+
+//
+// 'openssl_create_name()' - Create an X.509 name value for a certificate/signing request.
+//
+
+static X509_NAME * // O - X.509 name value
+openssl_create_name(
+ const char *organization, // I - Organization or `NULL` to use common name
+ const char *org_unit, // I - Organizational unit or `NULL` for none
+ const char *locality, // I - City/town or `NULL` for "Unknown"
+ const char *state_province, // I - State/province or `NULL` for "Unknown"
+ const char *country, // I - Country or `NULL` for locale-based default
+ const char *common_name, // I - Common name
+ const char *email) // I - Email address or `NULL` for none
+{
+ X509_NAME *name; // Subject/issuer name
+ cups_lang_t *language; // Default language info
+ const char *langname; // Language name
+
+
+ language = cupsLangDefault();
+ langname = cupsLangGetName(language);
+ name = X509_NAME_new();
+ if (country)
+ X509_NAME_add_entry_by_txt(name, SN_countryName, MBSTRING_ASC, (unsigned char *)country, -1, -1, 0);
+ else if (strlen(langname) == 5)
+ X509_NAME_add_entry_by_txt(name, SN_countryName, MBSTRING_ASC, (unsigned char *)langname + 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 *)(organization ? organization : common_name), -1, -1, 0);
+ X509_NAME_add_entry_by_txt(name, SN_organizationalUnitName, MBSTRING_ASC, (unsigned char *)(org_unit ? org_unit : ""), -1, -1, 0);
+ X509_NAME_add_entry_by_txt(name, SN_stateOrProvinceName, MBSTRING_ASC, (unsigned char *)(state_province ? state_province : "Unknown"), -1, -1, 0);
+ X509_NAME_add_entry_by_txt(name, SN_localityName, MBSTRING_ASC, (unsigned char *)(locality ? locality : "Unknown"), -1, -1, 0);
+ if (email)
+ X509_NAME_add_entry_by_txt(name, "emailAddress", MBSTRING_ASC, (unsigned char *)email, -1, -1, 0);
+
+ return (name);
+}
+
+
+//
+// 'openssl_create_san()' - Create a list of subjectAltName values for a certificate/signing request.
+//
+
+static X509_EXTENSION * // O - Extension
+openssl_create_san(
+ const char *hostname, // I - Hostname
+ const char *localname) // I - Local hostname
+{
+ char temp[2048]; // Temporary string
+
+
+ // Add the hostname and (if set) local name...
+ if (localname && *localname)
+ snprintf(temp, sizeof(temp), "DNS:%s,DNS:%s", hostname, localname);
+ else
+ snprintf(temp, sizeof(temp), "DNS:%s", hostname);
+
+ // Return the stack
+ return (X509V3_EXT_conf_nid(/*conf*/NULL, /*ctx*/NULL, NID_subject_alt_name, temp));
+}
+#endif // HAVE_OPENSSL
+
//
// 'system_device_cb()' - Device callback for the "add printer" chooser.
@@ -2450,26 +2622,25 @@ tls_make_certificate(
*state, // State/province
*country; // Country
int duration; // Duration in years
- int num_alt_names = 1; // Alternate names
- char alt_names[4][256]; // Subject alternate names
char hostname[256], // Hostname
+ localname[256], // Hostname.local
*domain, // Domain name
basedir[256], // CUPS directory
ssldir[256], // CUPS "ssl" directory
crtfile[1024], // Certificate file
keyfile[1024]; // Private key file
+ _pappl_credtype_t credtype; // Type of credentials
+ time_t curtime; // Current time
# ifdef HAVE_OPENSSL
bool result = false; // Result of operations
EVP_PKEY *pkey; // Private key
- BIGNUM *rsaexp; // Public exponent for RSA keys
- RSA *rsa = NULL; // RSA key pair
- EC_KEY *ecdsa = NULL; // ECDSA key pair
X509 *cert; // Certificate
- char dns_name[1024]; // DNS: prefixed hostname
- X509_EXTENSION *san_ext; // Extension for subjectAltName
- ASN1_OCTET_STRING *san_asn1; // ASN1 string
- time_t curtime; // Current time
+ ASN1_INTEGER *serial; // Serial number
+ ASN1_TIME *notBefore, // Initial date
+ *notAfter; // Expiration date
X509_NAME *name; // Subject/issuer name
+ STACK_OF(X509_EXTENSION) *exts; // Extensions
+ X509_EXTENSION *ext; // Current extension
BIO *bio; // Output file
# else // HAVE_GNUTLS
gnutls_x509_crt_t crt; // Self-signed certificate
@@ -2477,7 +2648,7 @@ tls_make_certificate(
cups_file_t *fp; // Key/cert file
unsigned char buffer[8192]; // Buffer for key/cert data
size_t bytes; // Number of bytes of data
- unsigned char serial[4]; // Serial number buffer
+ unsigned char serial[8]; // Serial number buffer
int status; // GNU TLS status
# endif // HAVE_OPENSSL
@@ -2499,6 +2670,35 @@ tls_make_certificate(
papplLogClient(client, PAPPL_LOGLEVEL_ERROR, "Missing 'level' form field.");
return (false);
}
+ else if (!strcmp(level, "rsa-2048"))
+ {
+ credtype = _PAPPL_CREDTYPE_RSA_2048_SHA256;
+ }
+ else if (!strcmp(level, "rsa-3072"))
+ {
+ credtype = _PAPPL_CREDTYPE_RSA_3072_SHA256;
+ }
+ else if (!strcmp(level, "rsa-4096"))
+ {
+ credtype = _PAPPL_CREDTYPE_RSA_4096_SHA256;
+ }
+ else if (!strcmp(level, "ecdsa-p256"))
+ {
+ credtype = _PAPPL_CREDTYPE_ECDSA_P256_SHA256;
+ }
+ else if (!strcmp(level, "ecdsa-p384"))
+ {
+ credtype = _PAPPL_CREDTYPE_ECDSA_P384_SHA256;
+ }
+ else if (!strcmp(level, "ecdsa-p521"))
+ {
+ credtype = _PAPPL_CREDTYPE_ECDSA_P521_SHA256;
+ }
+ else
+ {
+ papplLogClient(client, PAPPL_LOGLEVEL_ERROR, "Unknown 'level' form field '%s'.", level);
+ return (false);
+ }
if ((email = cupsGetOption("email", num_form, form)) == NULL)
{
@@ -2536,24 +2736,27 @@ tls_make_certificate(
return (false);
}
- // Get all of the names this system is known by...
+ curtime = time(NULL);
+
+ // Get the name this system is known by...
papplSystemGetHostName(system, hostname, sizeof(hostname));
- if ((domain = strchr(hostname, '.')) != NULL)
- {
- // If the domain name is not hostname.local or hostname.lan, make that the
- // second Subject Alternate Name...
- if (strcmp(domain, ".local") && strcmp(domain, ".lan"))
- papplCopyString(alt_names[num_alt_names ++], hostname, sizeof(alt_names[0]));
- *domain = '\0';
+ if (strstr(hostname, ".local"))
+ {
+ // Hostname is of the form "HOSTNAME.local"...
+ localname[0] = '\0';
}
+ else
+ {
+ // Hostname is not of the form "HOSTNAME.local"...
+ char *localptr; // Pointer into localname
- // then add hostname as the first alternate name...
- papplCopyString(alt_names[0], hostname, sizeof(alt_names[0]));
-
- // and finish up with hostname.lan and hostname.local as the final alternates...
- snprintf(alt_names[num_alt_names ++], sizeof(alt_names[0]), "%s.lan", hostname);
- snprintf(alt_names[num_alt_names ++], sizeof(alt_names[0]), "%s.local", hostname);
+ papplCopyString(localname, hostname, sizeof(localname));
+ if ((localptr = strchr(localname, '.')) != NULL)
+ papplCopyString(localptr, ".local", sizeof(localname) - (size_t)(localptr - localname));
+ else
+ papplCopyString(localname + strlen(localname), ".local", sizeof(localname) - strlen(localname));
+ }
// Store the certificate and private key in the CUPS "ssl" directory...
home = getuid() ? getenv("HOME") : NULL;
@@ -2584,47 +2787,12 @@ tls_make_certificate(
# ifdef HAVE_OPENSSL
// Create the paired encryption keys...
- if ((pkey = EVP_PKEY_new()) == NULL)
+ if ((pkey = openssl_create_key(system, credtype)) == NULL)
{
papplLogClient(client, PAPPL_LOGLEVEL_ERROR, "Unable to create private key.");
return (false);
}
- if (!strcmp(level, "rsa-2048"))
- {
- // 2048-bit RSA key...
- rsaexp = BN_new();
- BN_set_word(rsaexp, RSA_F4);
- rsa = RSA_new();
- RSA_generate_key_ex(rsa, 2048, rsaexp, NULL);
- BN_free(rsaexp);
- }
- else if (!strcmp(level, "rsa-4096"))
- {
- // 4096-bit RSA key...
- rsaexp = BN_new();
- BN_set_word(rsaexp, RSA_F4);
- rsa = RSA_new();
- RSA_generate_key_ex(rsa, 4096, rsaexp, NULL);
- BN_free(rsaexp);
- }
- else
- {
- // 384-bit ECDSA key...
- ecdsa = EC_KEY_new_by_curve_name(NID_secp384r1);
- }
-
- if (!rsa && !ecdsa)
- {
- papplLogClient(client, PAPPL_LOGLEVEL_ERROR, "Unable to create RSA/ECDSA key pair.");
- return (false);
- }
-
- if (rsa)
- EVP_PKEY_assign_RSA(pkey, rsa);
- else
- EVP_PKEY_assign_EC_KEY(pkey, ecdsa);
-
// Create the self-signed certificate...
if ((cert = X509_new()) == NULL)
{
@@ -2633,44 +2801,57 @@ tls_make_certificate(
return (false);
}
- 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, curtime + duration * 365 * 86400);
+ X509_set_notAfter(cert, notAfter);
+ ASN1_TIME_free(notAfter);
+
+ serial = ASN1_INTEGER_new();
+ ASN1_INTEGER_set(serial, (long)curtime);
+ X509_set_serialNumber(cert, serial);
+ ASN1_INTEGER_free(serial);
- ASN1_TIME_set(X509_get_notBefore(cert), curtime);
- ASN1_TIME_set(X509_get_notAfter(cert), curtime * duration * 365 * 86400);
- ASN1_INTEGER_set(X509_get_serialNumber(cert), (int)curtime);
X509_set_pubkey(cert, pkey);
- name = X509_get_subject_name(cert);
- X509_NAME_add_entry_by_txt(name, "C", MBSTRING_ASC, (unsigned char *)country, -1, -1, 0);
- X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, (unsigned char *)hostname, -1, -1, 0);
- X509_NAME_add_entry_by_txt(name, "emailAddress", MBSTRING_ASC, (unsigned char *)email, -1, -1, 0);
- X509_NAME_add_entry_by_txt(name, "L", MBSTRING_ASC, (unsigned char *)city, -1, -1, 0);
- X509_NAME_add_entry_by_txt(name, "O", MBSTRING_ASC, (unsigned char *)organization, -1, -1, 0);
- X509_NAME_add_entry_by_txt(name, "OU", MBSTRING_ASC, (unsigned char *)org_unit, -1, -1, 0);
- X509_NAME_add_entry_by_txt(name, "ST", MBSTRING_ASC, (unsigned char *)state, -1, -1, 0);
+ name = openssl_create_name(organization, org_unit, city, state, country, hostname, email);
+ X509_set_subject_name(cert, name);
X509_set_issuer_name(cert, name);
- for (i = 0; i < num_alt_names; i ++)
- {
- // The subjectAltName value for DNS names starts with a DNS: prefix...
- snprintf(dns_name, sizeof(dns_name), "DNS: %s", alt_names[i]);
+ X509_NAME_free(name);
- if ((san_asn1 = ASN1_OCTET_STRING_new()) == NULL)
- break;
+ exts = sk_X509_EXTENSION_new_null();
+
+ // Add extension with DNS names and free buffer for GENERAL_NAME
+ if ((ext = openssl_create_san(hostname, localname)) == NULL)
+ goto done;
+
+ sk_X509_EXTENSION_push(exts, ext);
+
+ // Add other extensions required for TLS...
+ openssl_add_ext(exts, NID_basic_constraints, "critical,CA:FALSE,pathlen:0");
+ openssl_add_ext(exts, NID_key_usage, "critical,digitalSignature,keyEncipherment");
+ openssl_add_ext(exts, NID_ext_key_usage, "1.3.6.1.5.5.7.3.1"); // serverAuth OID
+ openssl_add_ext(exts, NID_subject_key_identifier, "hash");
+ openssl_add_ext(exts, NID_authority_key_identifier, "keyid,issuer");
- ASN1_OCTET_STRING_set(san_asn1, (unsigned char *)dns_name, (int)strlen(dns_name));
- if (!X509_EXTENSION_create_by_NID(&san_ext, NID_subject_alt_name, 0, san_asn1))
+ while ((ext = sk_X509_EXTENSION_pop(exts)) != NULL)
+ {
+ if (!X509_add_ext(cert, ext, -1))
{
- ASN1_OCTET_STRING_free(san_asn1);
- break;
+ sk_X509_EXTENSION_pop_free(exts, X509_EXTENSION_free);
+ goto done;
}
-
- X509_add_ext(cert, san_ext, -1);
- X509_EXTENSION_free(san_ext);
- ASN1_OCTET_STRING_free(san_asn1);
}
+ X509_set_version(cert, 2); // v3
+
+ // Sign with our private key...
X509_sign(cert, pkey, EVP_sha256());
// Save them...
@@ -2720,12 +2901,27 @@ tls_make_certificate(
// Create the paired encryption keys...
gnutls_x509_privkey_init(&key);
- if (!strcmp(level, "rsa-2048"))
- gnutls_x509_privkey_generate(key, GNUTLS_PK_RSA, 2048, 0);
- else if (!strcmp(level, "rsa-4096"))
- gnutls_x509_privkey_generate(key, GNUTLS_PK_RSA, 4096, 0);
- else
- gnutls_x509_privkey_generate(key, GNUTLS_PK_ECDSA, 384, 0);
+ switch (credtype)
+ {
+ case _PAPPL_CREDTYPE_RSA_2048 :
+ gnutls_x509_privkey_generate(key, GNUTLS_PK_RSA, 2048, 0);
+ break;
+ case _PAPPL_CREDTYPE_RSA_3072 :
+ gnutls_x509_privkey_generate(key, GNUTLS_PK_RSA, 3072, 0);
+ break;
+ case _PAPPL_CREDTYPE_RSA_4096 :
+ gnutls_x509_privkey_generate(key, GNUTLS_PK_RSA, 4096, 0);
+ break;
+ case _PAPPL_CREDTYPE_ECDSA_P256 :
+ gnutls_x509_privkey_generate(key, GNUTLS_PK_ECDSA, 256, 0);
+ break;
+ case _PAPPL_CREDTYPE_ECDSA_P384 :
+ gnutls_x509_privkey_generate(key, GNUTLS_PK_ECDSA, 384, 0);
+ break;
+ case _PAPPL_CREDTYPE_ECDSA_P521 :
+ gnutls_x509_privkey_generate(key, GNUTLS_PK_ECDSA, 521, 0);
+ break;
+ }
// Save the private key...
bytes = sizeof(buffer);
@@ -2749,11 +2945,14 @@ tls_make_certificate(
}
// Create the self-signed certificate...
- i = (int)(time(NULL) / 60);
- serial[0] = (unsigned char)(i >> 24);
- serial[1] = (unsigned char)(i >> 16);
- serial[2] = (unsigned char)(i >> 8);
- serial[3] = (unsigned char)i;
+ serial[0] = (unsigned char)(curtime >> 56);
+ serial[1] = (unsigned char)(curtime >> 48);
+ serial[2] = (unsigned char)(curtime >> 40);
+ serial[3] = (unsigned char)(curtime >> 32);
+ serial[4] = (unsigned char)(curtime >> 24);
+ serial[5] = (unsigned char)(curtime >> 16);
+ serial[6] = (unsigned char)(curtime >> 8);
+ serial[7] = (unsigned char)(curtime);
gnutls_x509_crt_init(&crt);
gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_COUNTRY_NAME, 0, country, (unsigned)strlen(country));
@@ -2765,12 +2964,12 @@ tls_make_certificate(
gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_PKCS9_EMAIL, 0, email, (unsigned)strlen(email));
gnutls_x509_crt_set_key(crt, key);
gnutls_x509_crt_set_serial(crt, serial, sizeof(serial));
- gnutls_x509_crt_set_activation_time(crt, time(NULL));
- gnutls_x509_crt_set_expiration_time(crt, time(NULL) + duration * 365 * 86400);
+ gnutls_x509_crt_set_activation_time(crt, curtime);
+ gnutls_x509_crt_set_expiration_time(crt, curtime + duration * 365 * 86400);
gnutls_x509_crt_set_ca_status(crt, 0);
- gnutls_x509_crt_set_subject_alt_name(crt, GNUTLS_SAN_DNSNAME, alt_names[0], (unsigned)strlen(alt_names[0]), GNUTLS_FSAN_SET);
- for (i = 1; i < num_alt_names; i ++)
- gnutls_x509_crt_set_subject_alt_name(crt, GNUTLS_SAN_DNSNAME, alt_names[i], (unsigned)strlen(alt_names[i]), GNUTLS_FSAN_APPEND);
+ gnutls_x509_crt_set_subject_alt_name(crt, GNUTLS_SAN_DNSNAME, hostname, (unsigned)strlen(hostname), GNUTLS_FSAN_SET);
+ if (localname[0])
+ gnutls_x509_crt_set_subject_alt_name(crt, GNUTLS_SAN_DNSNAME, localname, (unsigned)strlen(localname), GNUTLS_FSAN_APPEND);
gnutls_x509_crt_set_key_purpose_oid(crt, GNUTLS_KP_TLS_WWW_SERVER, 0);
gnutls_x509_crt_set_key_usage(crt, GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT);
gnutls_x509_crt_set_version(crt, 3);
@@ -2814,15 +3013,15 @@ tls_make_certificate(
# define symlink(src,dst) CreateSymbolicLinkA(dst,src,SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE)
# endif // _WIN32
- for (i = 1; i < num_alt_names; i ++)
+ if (localname[0])
{
char altfile[1024]; // Alternate cert/key filename
- snprintf(altfile, sizeof(altfile), "%s/%s.key", ssldir, alt_names[i]);
+ snprintf(altfile, sizeof(altfile), "%s/%s.key", ssldir, localname);
unlink(altfile);
symlink(keyfile, altfile);
- snprintf(altfile, sizeof(altfile), "%s/%s.crt", ssldir, alt_names[i]);
+ snprintf(altfile, sizeof(altfile), "%s/%s.crt", ssldir, localname);
unlink(altfile);
symlink(crtfile, altfile);
}
@@ -2853,20 +3052,17 @@ tls_make_certsignreq(
*state, // State/province
*country; // Country
char hostname[256], // Hostname
+ localname[256], // Hostname.local
crqfile[1024], // Certificate request file
keyfile[1024]; // Private key file
+ _pappl_credtype_t credtype; // Type of credentials
# ifdef HAVE_OPENSSL
bool result = false; // Result of operations
EVP_PKEY *pkey; // Private key
- BIGNUM *rsaexp; // Public exponent for RSA keys
- RSA *rsa = NULL; // RSA key pair
- EC_KEY *ecdsa = NULL; // ECDSA key pair
X509_REQ *crq; // Certificate request
- char dns_name[1024]; // DNS: prefixed hostname
- STACK_OF(X509_EXTENSION) *san_exts; // Extensions
- X509_EXTENSION *san_ext; // Extension for subjectAltName
- ASN1_OCTET_STRING *san_asn1; // ASN1 string
X509_NAME *name; // Subject/issuer name
+ STACK_OF(X509_EXTENSION) *exts; // Extensions
+ X509_EXTENSION *ext; // Current extension
BIO *bio; // Output file
# else
gnutls_x509_crq_t crq; // Certificate request
@@ -2886,6 +3082,35 @@ tls_make_certsignreq(
papplLogClient(client, PAPPL_LOGLEVEL_ERROR, "Missing 'level' form field.");
return (false);
}
+ else if (!strcmp(level, "rsa-2048"))
+ {
+ credtype = _PAPPL_CREDTYPE_RSA_2048_SHA256;
+ }
+ else if (!strcmp(level, "rsa-3072"))
+ {
+ credtype = _PAPPL_CREDTYPE_RSA_3072_SHA256;
+ }
+ else if (!strcmp(level, "rsa-4096"))
+ {
+ credtype = _PAPPL_CREDTYPE_RSA_4096_SHA256;
+ }
+ else if (!strcmp(level, "ecdsa-p256"))
+ {
+ credtype = _PAPPL_CREDTYPE_ECDSA_P256_SHA256;
+ }
+ else if (!strcmp(level, "ecdsa-p384"))
+ {
+ credtype = _PAPPL_CREDTYPE_ECDSA_P384_SHA256;
+ }
+ else if (!strcmp(level, "ecdsa-p521"))
+ {
+ credtype = _PAPPL_CREDTYPE_ECDSA_P521_SHA256;
+ }
+ else
+ {
+ papplLogClient(client, PAPPL_LOGLEVEL_ERROR, "Unknown 'level' form field '%s'.", level);
+ return (false);
+ }
if ((email = cupsGetOption("email", num_form, form)) == NULL)
{
@@ -2923,54 +3148,39 @@ tls_make_certsignreq(
return (false);
}
- // Store the certificate request and private key in the spool directory...
- snprintf(keyfile, sizeof(keyfile), "%s/%s.key", system->directory, papplSystemGetHostName(system, hostname, sizeof(hostname)));
- snprintf(crqfile, sizeof(crqfile), "%s/%s.csr", system->directory, hostname);
- snprintf(crqpath, crqsize, "/%s.csr", hostname);
-
-# ifdef HAVE_OPENSSL
- // Create the paired encryption keys...
- if ((pkey = EVP_PKEY_new()) == NULL)
- {
- papplLogClient(client, PAPPL_LOGLEVEL_ERROR, "Unable to create private key.");
- return (false);
- }
+ // Get the name this system is known by...
+ papplSystemGetHostName(system, hostname, sizeof(hostname));
- if (!strcmp(level, "rsa-2048"))
- {
- // 2048-bit RSA key...
- rsaexp = BN_new();
- BN_set_word(rsaexp, RSA_F4);
- rsa = RSA_new();
- RSA_generate_key_ex(rsa, 2048, rsaexp, NULL);
- BN_free(rsaexp);
- }
- else if (!strcmp(level, "rsa-4096"))
+ if (strstr(hostname, ".local"))
{
- // 4096-bit RSA key...
- rsaexp = BN_new();
- BN_set_word(rsaexp, RSA_F4);
- rsa = RSA_new();
- RSA_generate_key_ex(rsa, 4096, rsaexp, NULL);
- BN_free(rsaexp);
+ // Hostname is of the form "HOSTNAME.local"...
+ localname[0] = '\0';
}
else
{
- // 384-bit ECDSA key...
- ecdsa = EC_KEY_new_by_curve_name(NID_secp384r1);
+ // Hostname is not of the form "HOSTNAME.local"...
+ char *localptr; // Pointer into localname
+
+ papplCopyString(localname, hostname, sizeof(localname));
+ if ((localptr = strchr(localname, '.')) != NULL)
+ papplCopyString(localptr, ".local", sizeof(localname) - (size_t)(localptr - localname));
+ else
+ papplCopyString(localname + strlen(localname), ".local", sizeof(localname) - strlen(localname));
}
- if (!rsa && !ecdsa)
+ // Store the certificate request and private key in the spool directory...
+ snprintf(keyfile, sizeof(keyfile), "%s/%s.key", system->directory, hostname);
+ snprintf(crqfile, sizeof(crqfile), "%s/%s.csr", system->directory, hostname);
+ snprintf(crqpath, crqsize, "/%s.csr", hostname);
+
+# ifdef HAVE_OPENSSL
+ // Create the paired encryption keys...
+ if ((pkey = openssl_create_key(system, credtype)) == NULL)
{
- papplLogClient(client, PAPPL_LOGLEVEL_ERROR, "Unable to create RSA/ECDSA key pair.");
+ papplLogClient(client, PAPPL_LOGLEVEL_ERROR, "Unable to create private key.");
return (false);
}
- if (rsa)
- EVP_PKEY_assign_RSA(pkey, rsa);
- else
- EVP_PKEY_assign_EC_KEY(pkey, ecdsa);
-
// Create the certificate request...
if ((crq = X509_REQ_new()) == NULL)
{
@@ -2981,38 +3191,23 @@ tls_make_certsignreq(
X509_REQ_set_pubkey(crq, pkey);
- name = X509_REQ_get_subject_name(crq);
- X509_NAME_add_entry_by_txt(name, "C", MBSTRING_ASC, (unsigned char *)country, -1, -1, 0);
- X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, (unsigned char *)hostname, -1, -1, 0);
- X509_NAME_add_entry_by_txt(name, "emailAddress", MBSTRING_ASC, (unsigned char *)email, -1, -1, 0);
- X509_NAME_add_entry_by_txt(name, "L", MBSTRING_ASC, (unsigned char *)city, -1, -1, 0);
- X509_NAME_add_entry_by_txt(name, "O", MBSTRING_ASC, (unsigned char *)organization, -1, -1, 0);
- X509_NAME_add_entry_by_txt(name, "OU", MBSTRING_ASC, (unsigned char *)org_unit, -1, -1, 0);
- X509_NAME_add_entry_by_txt(name, "ST", MBSTRING_ASC, (unsigned char *)state, -1, -1, 0);
+ name = openssl_create_name(organization, org_unit, city, state, country, hostname, email);
- // The subjectAltName value for DNS names starts with a DNS: prefix...
- snprintf(dns_name, sizeof(dns_name), "DNS: %s", hostname);
+ X509_REQ_set_subject_name(crq, name);
+ X509_NAME_free(name);
- if ((san_asn1 = ASN1_OCTET_STRING_new()) == NULL)
- goto done;
+ // Add extension with DNS names and free buffer for GENERAL_NAME
+ exts = sk_X509_EXTENSION_new_null();
- ASN1_OCTET_STRING_set(san_asn1, (unsigned char *)dns_name, (int)strlen(dns_name));
- if (!X509_EXTENSION_create_by_NID(&san_ext, NID_subject_alt_name, 0, san_asn1))
- {
- ASN1_OCTET_STRING_free(san_asn1);
+ if ((ext = openssl_create_san(hostname, localname)) == NULL)
goto done;
- }
- if ((san_exts = sk_X509_EXTENSION_new_null()) != NULL)
- {
- sk_X509_EXTENSION_push(san_exts, san_ext);
- X509_REQ_add_extensions(crq, san_exts);
- sk_X509_EXTENSION_free(san_exts);
- }
+ sk_X509_EXTENSION_push(exts, ext);
- X509_EXTENSION_free(san_ext);
- ASN1_OCTET_STRING_free(san_asn1);
+ openssl_add_ext(exts, NID_key_usage, "critical,digitalSignature,keyEncipherment");
+ openssl_add_ext(exts, NID_ext_key_usage, "1.3.6.1.5.5.7.3.1"); // serverAuth OID
+ X509_REQ_add_extensions(crq, exts);
X509_REQ_sign(crq, pkey, EVP_sha256());
// Save them...
@@ -3061,12 +3256,27 @@ tls_make_certsignreq(
// Create the paired encryption keys...
gnutls_x509_privkey_init(&key);
- if (!strcmp(level, "rsa-2048"))
- gnutls_x509_privkey_generate(key, GNUTLS_PK_RSA, 2048, 0);
- else if (!strcmp(level, "rsa-4096"))
- gnutls_x509_privkey_generate(key, GNUTLS_PK_RSA, 4096, 0);
- else
- gnutls_x509_privkey_generate(key, GNUTLS_PK_ECDSA, 384, 0);
+ switch (credtype)
+ {
+ case _PAPPL_CREDTYPE_RSA_2048 :
+ gnutls_x509_privkey_generate(key, GNUTLS_PK_RSA, 2048, 0);
+ break;
+ case _PAPPL_CREDTYPE_RSA_3072 :
+ gnutls_x509_privkey_generate(key, GNUTLS_PK_RSA, 3072, 0);
+ break;
+ case _PAPPL_CREDTYPE_RSA_4096 :
+ gnutls_x509_privkey_generate(key, GNUTLS_PK_RSA, 4096, 0);
+ break;
+ case _PAPPL_CREDTYPE_ECDSA_P256 :
+ gnutls_x509_privkey_generate(key, GNUTLS_PK_ECDSA, 256, 0);
+ break;
+ case _PAPPL_CREDTYPE_ECDSA_P384 :
+ gnutls_x509_privkey_generate(key, GNUTLS_PK_ECDSA, 384, 0);
+ break;
+ case _PAPPL_CREDTYPE_ECDSA_P521 :
+ gnutls_x509_privkey_generate(key, GNUTLS_PK_ECDSA, 521, 0);
+ break;
+ }
// Save the private key...
bytes = sizeof(buffer);
@@ -3100,6 +3310,8 @@ tls_make_certsignreq(
gnutls_x509_crq_set_dn_by_oid(crq, GNUTLS_OID_PKCS9_EMAIL, 0, email, (unsigned)strlen(email));
gnutls_x509_crq_set_key(crq, key);
gnutls_x509_crq_set_subject_alt_name(crq, GNUTLS_SAN_DNSNAME, hostname, (unsigned)strlen(hostname), GNUTLS_FSAN_SET);
+ if (localname[0])
+ gnutls_x509_crt_set_subject_alt_name(crt, GNUTLS_SAN_DNSNAME, localname, (unsigned)strlen(localname), GNUTLS_FSAN_APPEND);
gnutls_x509_crq_set_key_purpose_oid(crq, GNUTLS_KP_TLS_WWW_SERVER, 0);
gnutls_x509_crq_set_key_usage(crq, GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT);
gnutls_x509_crq_set_version(crq, 3);