/src/libcups/cups/tls-openssl.c
Line | Count | Source |
1 | | // |
2 | | // TLS support code for CUPS using OpenSSL/LibreSSL. |
3 | | // |
4 | | // Note: This file is included from tls.c |
5 | | // |
6 | | // Copyright © 2020-2025 by OpenPrinting |
7 | | // Copyright © 2007-2019 by Apple Inc. |
8 | | // Copyright © 1997-2007 by Easy Software Products, all rights reserved. |
9 | | // |
10 | | // Licensed under Apache License v2.0. See the file "LICENSE" for more |
11 | | // information. |
12 | | // |
13 | | |
14 | | #include <openssl/x509v3.h> |
15 | | #include <openssl/evp.h> |
16 | | #include <openssl/objects.h> |
17 | | #include <openssl/obj_mac.h> |
18 | | |
19 | | |
20 | | // |
21 | | // Local functions... |
22 | | // |
23 | | |
24 | | static long http_bio_ctrl(BIO *h, int cmd, long arg1, void *arg2); |
25 | | static int http_bio_free(BIO *data); |
26 | | static int http_bio_new(BIO *h); |
27 | | static int http_bio_puts(BIO *h, const char *str); |
28 | | static int http_bio_read(BIO *h, char *buf, int size); |
29 | | static int http_bio_write(BIO *h, const char *buf, int num); |
30 | | |
31 | | static bool openssl_add_ext(STACK_OF(X509_EXTENSION) *exts, int nid, const char *value); |
32 | | static X509_NAME *openssl_create_name(const char *organization, const char *org_unit, const char *locality, const char *state_province, const char *country, const char *common_name, const char *email); |
33 | | static EVP_PKEY *openssl_create_key(cups_credtype_t type); |
34 | | static X509_EXTENSION *openssl_create_san(const char *common_name, size_t num_alt_names, const char * const *alt_names); |
35 | | static time_t openssl_get_date(X509 *cert, int which); |
36 | | //static void openssl_load_crl(void); |
37 | | static STACK_OF(X509 *) openssl_load_x509(const char *credentials); |
38 | | |
39 | | |
40 | | // |
41 | | // Local globals... |
42 | | // |
43 | | |
44 | | static BIO_METHOD *tls_bio_method = NULL; |
45 | | // OpenSSL BIO method |
46 | | static const char * const tls_purpose_oids[] = |
47 | | { // OIDs for each key purpose value |
48 | | "1.3.6.1.5.5.7.3.1", // serverAuth |
49 | | "1.3.6.1.5.5.7.3.2", // clientAuth |
50 | | "1.3.6.1.5.5.7.3.3", // codeSigning |
51 | | "1.3.6.1.5.5.7.3.4", // emailProtection |
52 | | "1.3.6.1.5.5.7.3.8", // timeStamping |
53 | | "1.3.6.1.5.5.7.3.9" // OCSPSigning |
54 | | }; |
55 | | static const char * const tls_usage_strings[] = |
56 | | { // Strings for each key usage value |
57 | | "digitalSignature", |
58 | | "nonRepudiation", |
59 | | "keyEncipherment", |
60 | | "dataEncipherment", |
61 | | "keyAgreement", |
62 | | "keyCertSign", |
63 | | "cRLSign", |
64 | | "encipherOnly", |
65 | | "decipherOnly" |
66 | | }; |
67 | | |
68 | | |
69 | | // |
70 | | // 'cupsAreCredentialsValidForName()' - Return whether the credentials are valid |
71 | | // for the given name. |
72 | | // |
73 | | |
74 | | bool // O - `true` if valid, `false` otherwise |
75 | | cupsAreCredentialsValidForName( |
76 | | const char *common_name, // I - Name to check |
77 | | const char *credentials) // I - Credentials |
78 | 0 | { |
79 | 0 | STACK_OF(X509) *certs; // Certificate chain |
80 | 0 | bool result = false; // Result |
81 | | |
82 | |
|
83 | 0 | DEBUG_printf("cupsAreCredentialsValidForName(common_name=\"%s\", credentials=\"%s\")", common_name, credentials); |
84 | | |
85 | | // Range check input... |
86 | 0 | if (!common_name || !credentials) |
87 | 0 | return (false); |
88 | | |
89 | | // Load the credentials... |
90 | 0 | if ((certs = openssl_load_x509(credentials)) != NULL) |
91 | 0 | { |
92 | | // Check the hostname against the primary certificate... |
93 | 0 | X509 *cert = sk_X509_value(certs, 0); |
94 | | // Primary certificate |
95 | 0 | char subjectName[256]; // Common name from certificate |
96 | 0 | STACK_OF(GENERAL_NAME) *names = NULL; |
97 | | // subjectAltName values |
98 | |
|
99 | 0 | DEBUG_printf("1cupsAreCredentialsValidForName: certs=%p(num=%d), cert=%p", certs, sk_X509_num(certs), cert); |
100 | |
|
101 | 0 | X509_NAME_get_text_by_NID(X509_get_subject_name(cert), NID_commonName, subjectName, sizeof(subjectName)); |
102 | 0 | DEBUG_printf("1cupsAreCredentialsValidForName: subjectName=\"%s\"", subjectName); |
103 | |
|
104 | 0 | if (!_cups_strcasecmp(common_name, subjectName)) |
105 | 0 | { |
106 | 0 | DEBUG_puts("1cupsAreCredentialsValidForName: Match."); |
107 | 0 | result = true; |
108 | 0 | } |
109 | |
|
110 | | #ifdef DEBUG |
111 | | char issuerName[256]; |
112 | | X509_NAME_get_text_by_NID(X509_get_issuer_name(cert), NID_commonName, issuerName, sizeof(issuerName)); |
113 | | DEBUG_printf("1cupsAreCredentialsValidForName: issuerName=\"%s\"", issuerName); |
114 | | #endif // DEBUG |
115 | |
|
116 | 0 | if (!result) |
117 | 0 | { |
118 | 0 | names = X509_get_ext_d2i(cert, NID_subject_alt_name, /*crit*/NULL, /*idx*/NULL); |
119 | 0 | DEBUG_printf("1cupsAreCredentialsValidForName: names=%p", names); |
120 | 0 | } |
121 | |
|
122 | 0 | if (names) |
123 | 0 | { |
124 | | // Got subjectAltName values, look at them... |
125 | 0 | int i, // Looping var |
126 | 0 | count; // Number of values |
127 | |
|
128 | 0 | for (i = 0, count = sk_GENERAL_NAME_num(names); i < count && !result; i ++) |
129 | 0 | { |
130 | 0 | const GENERAL_NAME *name = sk_GENERAL_NAME_value(names, i); |
131 | | // subjectAltName value |
132 | |
|
133 | 0 | if (!name) |
134 | 0 | continue; |
135 | | |
136 | 0 | DEBUG_printf("1cupsAreCredentialsValidForName: subjectAltName[%d/%d].type=%d", i + 1, count, name->type); |
137 | 0 | if (name->type == GEN_DNS) |
138 | 0 | { |
139 | | // Match a DNS name... |
140 | 0 | char *dNSName; // DNS name value |
141 | |
|
142 | 0 | if (ASN1_STRING_to_UTF8((unsigned char **)&dNSName, name->d.dNSName) > 0) |
143 | 0 | { |
144 | 0 | DEBUG_printf("1cupsAreCredentialsValidForName: subjectAltName[%d/%d].dNSName=\"%s\"", i + 1, count, dNSName); |
145 | |
|
146 | 0 | if (!_cups_strcasecmp(common_name, dNSName)) |
147 | 0 | { |
148 | | // Direct name match... |
149 | 0 | DEBUG_puts("1cupsAreCredentialsValidForName: Match."); |
150 | 0 | result = true; |
151 | 0 | } |
152 | 0 | else if (!strncmp(dNSName, "*.", 2)) |
153 | 0 | { |
154 | | // Compare wildcard... |
155 | 0 | const char *domain_name = strchr(common_name, '.'); |
156 | | // Domain name of common name |
157 | 0 | if (domain_name && !_cups_strcasecmp(domain_name, dNSName + 1)) |
158 | 0 | { |
159 | 0 | DEBUG_puts("1cupsAreCredentialsValidForName: Match."); |
160 | 0 | result = true; |
161 | 0 | } |
162 | 0 | } |
163 | |
|
164 | 0 | OPENSSL_free(dNSName); |
165 | 0 | } |
166 | 0 | } |
167 | 0 | } |
168 | |
|
169 | 0 | GENERAL_NAMES_free(names); |
170 | 0 | } |
171 | |
|
172 | 0 | sk_X509_free(certs); |
173 | 0 | } |
174 | |
|
175 | 0 | return (result); |
176 | 0 | } |
177 | | |
178 | | |
179 | | // |
180 | | // 'cupsCreateCredentials()' - Make an X.509 certificate and private key pair. |
181 | | // |
182 | | // This function creates an X.509 certificate and private key pair. The |
183 | | // certificate and key are stored in the directory "path" or, if "path" is |
184 | | // `NULL`, in a per-user or system-wide (when running as root) certificate/key |
185 | | // store. The generated certificate is signed by the named root certificate or, |
186 | | // if "root_name" is `NULL`, a site-wide default root certificate. When |
187 | | // "root_name" is `NULL` and there is no site-wide default root certificate, a |
188 | | // self-signed certificate is generated instead. |
189 | | // |
190 | | // The "ca_cert" argument specifies whether a CA certificate should be created. |
191 | | // |
192 | | // The "purpose" argument specifies the purpose(s) used for the credentials as a |
193 | | // bitwise OR of the following constants: |
194 | | // |
195 | | // - `CUPS_CREDPURPOSE_SERVER_AUTH` for validating TLS servers, |
196 | | // - `CUPS_CREDPURPOSE_CLIENT_AUTH` for validating TLS clients, |
197 | | // - `CUPS_CREDPURPOSE_CODE_SIGNING` for validating compiled code, |
198 | | // - `CUPS_CREDPURPOSE_EMAIL_PROTECTION` for validating email messages, |
199 | | // - `CUPS_CREDPURPOSE_TIME_STAMPING` for signing timestamps to objects, and/or |
200 | | // - `CUPS_CREDPURPOSE_OCSP_SIGNING` for Online Certificate Status Protocol |
201 | | // message signing. |
202 | | // |
203 | | // The "type" argument specifies the type of credentials using one of the |
204 | | // following constants: |
205 | | // |
206 | | // - `CUPS_CREDTYPE_DEFAULT`: default type (RSA-3072 or P-384), |
207 | | // - `CUPS_CREDTYPE_RSA_2048_SHA256`: RSA with 2048-bit keys and SHA-256 hash, |
208 | | // - `CUPS_CREDTYPE_RSA_3072_SHA256`: RSA with 3072-bit keys and SHA-256 hash, |
209 | | // - `CUPS_CREDTYPE_RSA_4096_SHA256`: RSA with 4096-bit keys and SHA-256 hash, |
210 | | // - `CUPS_CREDTYPE_ECDSA_P256_SHA256`: ECDSA using the P-256 curve with SHA-256 hash, |
211 | | // - `CUPS_CREDTYPE_ECDSA_P384_SHA256`: ECDSA using the P-384 curve with SHA-256 hash, or |
212 | | // - `CUPS_CREDTYPE_ECDSA_P521_SHA256`: ECDSA using the P-521 curve with SHA-256 hash. |
213 | | // |
214 | | // The "usage" argument specifies the usage(s) for the credentials as a bitwise |
215 | | // OR of the following constants: |
216 | | // |
217 | | // - `CUPS_CREDUSAGE_DIGITAL_SIGNATURE`: digital signatures, |
218 | | // - `CUPS_CREDUSAGE_NON_REPUDIATION`: non-repudiation/content commitment, |
219 | | // - `CUPS_CREDUSAGE_KEY_ENCIPHERMENT`: key encipherment, |
220 | | // - `CUPS_CREDUSAGE_DATA_ENCIPHERMENT`: data encipherment, |
221 | | // - `CUPS_CREDUSAGE_KEY_AGREEMENT`: key agreement, |
222 | | // - `CUPS_CREDUSAGE_KEY_CERT_SIGN`: key certicate signing, |
223 | | // - `CUPS_CREDUSAGE_CRL_SIGN`: certificate revocation list signing, |
224 | | // - `CUPS_CREDUSAGE_ENCIPHER_ONLY`: encipherment only, |
225 | | // - `CUPS_CREDUSAGE_DECIPHER_ONLY`: decipherment only, |
226 | | // - `CUPS_CREDUSAGE_DEFAULT_CA`: defaults for CA certificates, |
227 | | // - `CUPS_CREDUSAGE_DEFAULT_TLS`: defaults for TLS certificates, and/or |
228 | | // - `CUPS_CREDUSAGE_ALL`: all usages. |
229 | | // |
230 | | // The "organization", "org_unit", "locality", "state_province", and "country" |
231 | | // arguments specify information about the identity and geolocation of the |
232 | | // issuer. |
233 | | // |
234 | | // The "common_name" argument specifies the common name and the "num_alt_names" |
235 | | // and "alt_names" arguments specify a list of DNS hostnames for the |
236 | | // certificate. |
237 | | // |
238 | | // The "expiration_date" argument specifies the expiration date and time as a |
239 | | // Unix `time_t` value in seconds. |
240 | | // |
241 | | |
242 | | bool // O - `true` on success, `false` on failure |
243 | | cupsCreateCredentials( |
244 | | const char *path, // I - Directory path for certificate/key store or `NULL` for default |
245 | | bool ca_cert, // I - `true` to create a CA certificate, `false` for a client/server certificate |
246 | | cups_credpurpose_t purpose, // I - Credential purposes |
247 | | cups_credtype_t type, // I - Credential type |
248 | | cups_credusage_t usage, // I - Credential usages |
249 | | const char *organization, // I - Organization or `NULL` to use common name |
250 | | const char *org_unit, // I - Organizational unit or `NULL` for none |
251 | | const char *locality, // I - City/town or `NULL` for "Unknown" |
252 | | const char *state_province, // I - State/province or `NULL` for "Unknown" |
253 | | const char *country, // I - Country or `NULL` for locale-based default |
254 | | const char *common_name, // I - Common name |
255 | | const char *email, // I - Email address or `NULL` for none |
256 | | size_t num_alt_names, // I - Number of subject alternate names |
257 | | const char * const *alt_names, // I - Subject Alternate Names |
258 | | const char *root_name, // I - Root certificate/domain name or `NULL` for site/self-signed |
259 | | time_t expiration_date) // I - Expiration date |
260 | 0 | { |
261 | 0 | bool result = false; // Return value |
262 | 0 | EVP_PKEY *pkey; // Key pair |
263 | 0 | X509 *cert; // Certificate |
264 | 0 | X509 *root_cert = NULL; // Root certificate, if any |
265 | 0 | EVP_PKEY *root_key = NULL; // Root private key, if any |
266 | 0 | char defpath[1024], // Default path |
267 | 0 | crtfile[1024], // Certificate filename |
268 | 0 | keyfile[1024], // Private key filename |
269 | 0 | pubfile[1024], // Public key filename |
270 | 0 | root_crtfile[1024], // Root certificate filename |
271 | 0 | root_keyfile[1024]; // Root private key filename |
272 | 0 | time_t curtime; // Current time |
273 | 0 | X509_NAME *name; // Subject/issuer name |
274 | 0 | ASN1_INTEGER *serial; // Serial number |
275 | 0 | ASN1_TIME *notBefore, // Initial date |
276 | 0 | *notAfter; // Expiration date |
277 | 0 | BIO *bio; // Output file |
278 | 0 | char temp[1024], // Temporary string |
279 | 0 | *tempptr; // Pointer into temporary string |
280 | 0 | STACK_OF(X509_EXTENSION) *exts; // Extensions |
281 | 0 | X509_EXTENSION *ext; // Current extension |
282 | 0 | unsigned i; // Looping var |
283 | 0 | cups_credpurpose_t purpose_bit; // Current purpose |
284 | 0 | cups_credusage_t usage_bit; // Current usage |
285 | | |
286 | |
|
287 | 0 | DEBUG_printf("cupsCreateCredentials(path=\"%s\", ca_cert=%s, purpose=0x%x, type=%d, usage=0x%x, organization=\"%s\", org_unit=\"%s\", locality=\"%s\", state_province=\"%s\", country=\"%s\", common_name=\"%s\", num_alt_names=%u, alt_names=%p, root_name=\"%s\", expiration_date=%ld)", path, ca_cert ? "true" : "false", purpose, type, usage, organization, org_unit, locality, state_province, country, common_name, (unsigned)num_alt_names, alt_names, root_name, (long)expiration_date); |
288 | | |
289 | | // Filenames... |
290 | 0 | if (!path) |
291 | 0 | path = http_default_path(defpath, sizeof(defpath)); |
292 | |
|
293 | 0 | if (!path || !common_name) |
294 | 0 | { |
295 | 0 | _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0); |
296 | 0 | return (false); |
297 | 0 | } |
298 | | |
299 | | // Create the encryption key... |
300 | 0 | DEBUG_puts("1cupsCreateCredentials: Creating key pair."); |
301 | |
|
302 | 0 | if ((pkey = openssl_create_key(type)) == NULL) |
303 | 0 | return (false); |
304 | | |
305 | 0 | DEBUG_puts("1cupsCreateCredentials: Key pair created."); |
306 | | |
307 | | // Create the X.509 certificate... |
308 | 0 | DEBUG_puts("1cupsCreateCredentials: Generating X.509 certificate."); |
309 | |
|
310 | 0 | if ((cert = X509_new()) == NULL) |
311 | 0 | { |
312 | 0 | EVP_PKEY_free(pkey); |
313 | 0 | _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to create X.509 certificate."), true); |
314 | 0 | return (false); |
315 | 0 | } |
316 | | |
317 | 0 | curtime = time(NULL); |
318 | |
|
319 | 0 | notBefore = ASN1_TIME_new(); |
320 | 0 | ASN1_TIME_set(notBefore, curtime); |
321 | 0 | X509_set_notBefore(cert, notBefore); |
322 | 0 | ASN1_TIME_free(notBefore); |
323 | |
|
324 | 0 | notAfter = ASN1_TIME_new(); |
325 | 0 | ASN1_TIME_set(notAfter, expiration_date); |
326 | 0 | X509_set_notAfter(cert, notAfter); |
327 | 0 | ASN1_TIME_free(notAfter); |
328 | |
|
329 | 0 | serial = ASN1_INTEGER_new(); |
330 | 0 | ASN1_INTEGER_set(serial, (long)curtime); |
331 | 0 | X509_set_serialNumber(cert, serial); |
332 | 0 | ASN1_INTEGER_free(serial); |
333 | |
|
334 | 0 | X509_set_pubkey(cert, pkey); |
335 | |
|
336 | 0 | name = openssl_create_name(organization, org_unit, locality, state_province, country, common_name, email); |
337 | |
|
338 | 0 | X509_set_subject_name(cert, name); |
339 | | |
340 | | // Try loading a root certificate... |
341 | 0 | http_make_path(root_crtfile, sizeof(root_crtfile), path, root_name ? root_name : "_site_", "crt"); |
342 | 0 | http_make_path(root_keyfile, sizeof(root_keyfile), path, root_name ? root_name : "_site_", "key"); |
343 | |
|
344 | 0 | if (!ca_cert && !access(root_crtfile, 0) && !access(root_keyfile, 0)) |
345 | 0 | { |
346 | 0 | if ((bio = BIO_new_file(root_crtfile, "rb")) != NULL) |
347 | 0 | { |
348 | 0 | PEM_read_bio_X509(bio, &root_cert, /*cb*/NULL, /*u*/NULL); |
349 | 0 | BIO_free(bio); |
350 | |
|
351 | 0 | if ((bio = BIO_new_file(root_keyfile, "rb")) != NULL) |
352 | 0 | { |
353 | 0 | PEM_read_bio_PrivateKey(bio, &root_key, /*cb*/NULL, /*u*/NULL); |
354 | 0 | BIO_free(bio); |
355 | 0 | } |
356 | |
|
357 | 0 | if (!root_key) |
358 | 0 | { |
359 | | // Only use root certificate if we have the key... |
360 | 0 | X509_free(root_cert); |
361 | 0 | root_cert = NULL; |
362 | 0 | } |
363 | 0 | } |
364 | |
|
365 | 0 | if (!root_cert || !root_key) |
366 | 0 | { |
367 | 0 | _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to load X.509 CA certificate and private key."), true); |
368 | 0 | goto done; |
369 | 0 | } |
370 | 0 | } |
371 | | |
372 | 0 | if (root_cert) |
373 | 0 | X509_set_issuer_name(cert, X509_get_subject_name(root_cert)); |
374 | 0 | else |
375 | 0 | X509_set_issuer_name(cert, name); |
376 | |
|
377 | 0 | X509_NAME_free(name); |
378 | |
|
379 | 0 | exts = sk_X509_EXTENSION_new_null(); |
380 | |
|
381 | 0 | if (ca_cert) |
382 | 0 | { |
383 | | // Add extensions that are required to make Chrome happy... |
384 | 0 | openssl_add_ext(exts, NID_basic_constraints, "critical,CA:TRUE,pathlen:0"); |
385 | 0 | } |
386 | 0 | else |
387 | 0 | { |
388 | | // Add extension with DNS names and free buffer for GENERAL_NAME |
389 | 0 | if ((ext = openssl_create_san(common_name, num_alt_names, alt_names)) == NULL) |
390 | 0 | goto done; |
391 | | |
392 | 0 | sk_X509_EXTENSION_push(exts, ext); |
393 | | |
394 | | // Add extensions that are required to make Chrome happy... |
395 | 0 | openssl_add_ext(exts, NID_basic_constraints, "critical,CA:FALSE,pathlen:0"); |
396 | 0 | } |
397 | | |
398 | 0 | cupsCopyString(temp, "critical", sizeof(temp)); |
399 | 0 | for (tempptr = temp + strlen(temp), i = 0, usage_bit = CUPS_CREDUSAGE_DIGITAL_SIGNATURE; i < (sizeof(tls_usage_strings) / sizeof(tls_usage_strings[0])); i ++, usage_bit *= 2) |
400 | 0 | { |
401 | 0 | if (!(usage & usage_bit)) |
402 | 0 | continue; |
403 | | |
404 | 0 | snprintf(tempptr, sizeof(temp) - (size_t)(tempptr - temp), ",%s", tls_usage_strings[i]); |
405 | |
|
406 | 0 | tempptr += strlen(tempptr); |
407 | 0 | } |
408 | 0 | openssl_add_ext(exts, NID_key_usage, temp); |
409 | |
|
410 | 0 | temp[0] = '\0'; |
411 | 0 | for (tempptr = temp, i = 0, purpose_bit = CUPS_CREDPURPOSE_SERVER_AUTH; i < (sizeof(tls_purpose_oids) / sizeof(tls_purpose_oids[0])); i ++, purpose_bit *= 2) |
412 | 0 | { |
413 | 0 | if (!(purpose & purpose_bit)) |
414 | 0 | continue; |
415 | | |
416 | 0 | if (tempptr == temp) |
417 | 0 | cupsCopyString(temp, tls_purpose_oids[i], sizeof(temp)); |
418 | 0 | else |
419 | 0 | snprintf(tempptr, sizeof(temp) - (size_t)(tempptr - temp), ",%s", tls_purpose_oids[i]); |
420 | |
|
421 | 0 | tempptr += strlen(tempptr); |
422 | 0 | } |
423 | 0 | openssl_add_ext(exts, NID_ext_key_usage, temp); |
424 | |
|
425 | 0 | openssl_add_ext(exts, NID_subject_key_identifier, "hash"); |
426 | 0 | openssl_add_ext(exts, NID_authority_key_identifier, "keyid,issuer"); |
427 | |
|
428 | 0 | while ((ext = sk_X509_EXTENSION_pop(exts)) != NULL) |
429 | 0 | { |
430 | 0 | if (!X509_add_ext(cert, ext, -1)) |
431 | 0 | { |
432 | 0 | sk_X509_EXTENSION_pop_free(exts, X509_EXTENSION_free); |
433 | 0 | goto done; |
434 | 0 | } |
435 | 0 | } |
436 | | |
437 | 0 | X509_set_version(cert, 2); // v3 |
438 | |
|
439 | 0 | if (root_key) |
440 | 0 | X509_sign(cert, root_key, EVP_sha256()); |
441 | 0 | else |
442 | 0 | X509_sign(cert, pkey, EVP_sha256()); |
443 | | |
444 | | // Save them... |
445 | 0 | http_make_path(crtfile, sizeof(crtfile), path, common_name, "crt"); |
446 | 0 | http_make_path(keyfile, sizeof(keyfile), path, common_name, "key"); |
447 | 0 | http_make_path(pubfile, sizeof(pubfile), path, common_name, "pub"); |
448 | |
|
449 | 0 | if ((bio = BIO_new_file(keyfile, "wb")) == NULL) |
450 | 0 | { |
451 | 0 | _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0); |
452 | 0 | goto done; |
453 | 0 | } |
454 | | |
455 | 0 | if (!PEM_write_bio_PrivateKey(bio, pkey, NULL, NULL, 0, NULL, NULL)) |
456 | 0 | { |
457 | 0 | _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to write private key."), true); |
458 | 0 | BIO_free(bio); |
459 | 0 | goto done; |
460 | 0 | } |
461 | | |
462 | 0 | BIO_free(bio); |
463 | |
|
464 | 0 | if ((bio = BIO_new_file(pubfile, "wb")) == NULL) |
465 | 0 | { |
466 | 0 | _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0); |
467 | 0 | goto done; |
468 | 0 | } |
469 | | |
470 | 0 | if (!PEM_write_bio_PUBKEY(bio, pkey)) |
471 | 0 | { |
472 | 0 | _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to write public key."), true); |
473 | 0 | BIO_free(bio); |
474 | 0 | goto done; |
475 | 0 | } |
476 | | |
477 | 0 | BIO_free(bio); |
478 | |
|
479 | 0 | if ((bio = BIO_new_file(crtfile, "wb")) == NULL) |
480 | 0 | { |
481 | 0 | _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0); |
482 | 0 | goto done; |
483 | 0 | } |
484 | | |
485 | 0 | if (!PEM_write_bio_X509(bio, cert)) |
486 | 0 | { |
487 | 0 | _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to write X.509 certificate."), true); |
488 | 0 | BIO_free(bio); |
489 | 0 | goto done; |
490 | 0 | } |
491 | | |
492 | 0 | if (root_cert) |
493 | 0 | PEM_write_bio_X509(bio, root_cert); |
494 | |
|
495 | 0 | BIO_free(bio); |
496 | |
|
497 | 0 | result = true; |
498 | 0 | DEBUG_puts("1cupsCreateCredentials: Successfully created credentials."); |
499 | | |
500 | | // Cleanup... |
501 | 0 | done: |
502 | |
|
503 | 0 | X509_free(cert); |
504 | 0 | EVP_PKEY_free(pkey); |
505 | |
|
506 | 0 | if (root_cert) |
507 | 0 | X509_free(root_cert); |
508 | 0 | if (root_key) |
509 | 0 | EVP_PKEY_free(root_key); |
510 | |
|
511 | 0 | return (result); |
512 | 0 | } |
513 | | |
514 | | |
515 | | // |
516 | | // 'cupsCreateCredentialsRequest()' - Make an X.509 Certificate Signing Request. |
517 | | // |
518 | | // This function creates an X.509 certificate signing request (CSR) and |
519 | | // associated private key. The CSR and key are stored in the directory "path" |
520 | | // or, if "path" is `NULL`, in a per-user or system-wide (when running as root) |
521 | | // certificate/key store. |
522 | | // |
523 | | // The "purpose" argument specifies the purpose(s) used for the credentials as a |
524 | | // bitwise OR of the following constants: |
525 | | // |
526 | | // - `CUPS_CREDPURPOSE_SERVER_AUTH` for validating TLS servers, |
527 | | // - `CUPS_CREDPURPOSE_CLIENT_AUTH` for validating TLS clients, |
528 | | // - `CUPS_CREDPURPOSE_CODE_SIGNING` for validating compiled code, |
529 | | // - `CUPS_CREDPURPOSE_EMAIL_PROTECTION` for validating email messages, |
530 | | // - `CUPS_CREDPURPOSE_TIME_STAMPING` for signing timestamps to objects, and/or |
531 | | // - `CUPS_CREDPURPOSE_OCSP_SIGNING` for Online Certificate Status Protocol |
532 | | // message signing. |
533 | | // |
534 | | // The "type" argument specifies the type of credentials using one of the |
535 | | // following constants: |
536 | | // |
537 | | // - `CUPS_CREDTYPE_DEFAULT`: default type (RSA-3072 or P-384), |
538 | | // - `CUPS_CREDTYPE_RSA_2048_SHA256`: RSA with 2048-bit keys and SHA-256 hash, |
539 | | // - `CUPS_CREDTYPE_RSA_3072_SHA256`: RSA with 3072-bit keys and SHA-256 hash, |
540 | | // - `CUPS_CREDTYPE_RSA_4096_SHA256`: RSA with 4096-bit keys and SHA-256 hash, |
541 | | // - `CUPS_CREDTYPE_ECDSA_P256_SHA256`: ECDSA using the P-256 curve with SHA-256 hash, |
542 | | // - `CUPS_CREDTYPE_ECDSA_P384_SHA256`: ECDSA using the P-384 curve with SHA-256 hash, or |
543 | | // - `CUPS_CREDTYPE_ECDSA_P521_SHA256`: ECDSA using the P-521 curve with SHA-256 hash. |
544 | | // |
545 | | // The "usage" argument specifies the usage(s) for the credentials as a bitwise |
546 | | // OR of the following constants: |
547 | | // |
548 | | // - `CUPS_CREDUSAGE_DIGITAL_SIGNATURE`: digital signatures, |
549 | | // - `CUPS_CREDUSAGE_NON_REPUDIATION`: non-repudiation/content commitment, |
550 | | // - `CUPS_CREDUSAGE_KEY_ENCIPHERMENT`: key encipherment, |
551 | | // - `CUPS_CREDUSAGE_DATA_ENCIPHERMENT`: data encipherment, |
552 | | // - `CUPS_CREDUSAGE_KEY_AGREEMENT`: key agreement, |
553 | | // - `CUPS_CREDUSAGE_KEY_CERT_SIGN`: key certicate signing, |
554 | | // - `CUPS_CREDUSAGE_CRL_SIGN`: certificate revocation list signing, |
555 | | // - `CUPS_CREDUSAGE_ENCIPHER_ONLY`: encipherment only, |
556 | | // - `CUPS_CREDUSAGE_DECIPHER_ONLY`: decipherment only, |
557 | | // - `CUPS_CREDUSAGE_DEFAULT_CA`: defaults for CA certificates, |
558 | | // - `CUPS_CREDUSAGE_DEFAULT_TLS`: defaults for TLS certificates, and/or |
559 | | // - `CUPS_CREDUSAGE_ALL`: all usages. |
560 | | // |
561 | | // The "organization", "org_unit", "locality", "state_province", and "country" |
562 | | // arguments specify information about the identity and geolocation of the |
563 | | // issuer. |
564 | | // |
565 | | // The "common_name" argument specifies the common name and the "num_alt_names" |
566 | | // and "alt_names" arguments specify a list of DNS hostnames for the |
567 | | // certificate. |
568 | | // |
569 | | |
570 | | bool // O - `true` on success, `false` on error |
571 | | cupsCreateCredentialsRequest( |
572 | | const char *path, // I - Directory path for certificate/key store or `NULL` for default |
573 | | cups_credpurpose_t purpose, // I - Credential purposes |
574 | | cups_credtype_t type, // I - Credential type |
575 | | cups_credusage_t usage, // I - Credential usages |
576 | | const char *organization, // I - Organization or `NULL` to use common name |
577 | | const char *org_unit, // I - Organizational unit or `NULL` for none |
578 | | const char *locality, // I - City/town or `NULL` for "Unknown" |
579 | | const char *state_province, // I - State/province or `NULL` for "Unknown" |
580 | | const char *country, // I - Country or `NULL` for locale-based default |
581 | | const char *common_name, // I - Common name |
582 | | const char *email, // I - Email address or `NULL` for none |
583 | | size_t num_alt_names, // I - Number of subject alternate names |
584 | | const char * const *alt_names) // I - Subject Alternate Names |
585 | 0 | { |
586 | 0 | bool ret = false; // Return value |
587 | 0 | EVP_PKEY *pkey; // Key pair |
588 | 0 | X509_REQ *csr; // Certificate signing request |
589 | 0 | X509_NAME *name; // Subject/issuer name |
590 | 0 | X509_EXTENSION *ext; // X509 extension |
591 | 0 | BIO *bio; // Output file |
592 | 0 | char temp[1024], // Temporary directory name |
593 | 0 | *tempptr, // Pointer into temporary string |
594 | 0 | csrfile[1024], // Certificate signing request filename |
595 | 0 | keyfile[1024], // Private key filename |
596 | 0 | pubfile[1024]; // Public key filename |
597 | 0 | STACK_OF(X509_EXTENSION) *exts; // Extensions |
598 | 0 | unsigned i; // Looping var |
599 | 0 | cups_credpurpose_t purpose_bit; // Current purpose |
600 | 0 | cups_credusage_t usage_bit; // Current usage |
601 | | |
602 | |
|
603 | 0 | DEBUG_printf("cupsCreateCredentialsRequest(path=\"%s\", purpose=0x%x, type=%d, usage=0x%x, organization=\"%s\", org_unit=\"%s\", locality=\"%s\", state_province=\"%s\", country=\"%s\", common_name=\"%s\", num_alt_names=%u, alt_names=%p)", path, purpose, type, usage, organization, org_unit, locality, state_province, country, common_name, (unsigned)num_alt_names, alt_names); |
604 | | |
605 | | // Filenames... |
606 | 0 | if (!path) |
607 | 0 | path = http_default_path(temp, sizeof(temp)); |
608 | |
|
609 | 0 | if (!path || !common_name) |
610 | 0 | { |
611 | 0 | _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0); |
612 | 0 | return (false); |
613 | 0 | } |
614 | | |
615 | 0 | http_make_path(csrfile, sizeof(csrfile), path, common_name, "csr"); |
616 | 0 | http_make_path(keyfile, sizeof(keyfile), path, common_name, "ktm"); |
617 | 0 | http_make_path(pubfile, sizeof(pubfile), path, common_name, "pub"); |
618 | | |
619 | | // Create the encryption key... |
620 | 0 | DEBUG_puts("1cupsCreateCredentialsRequest: Creating key pair."); |
621 | |
|
622 | 0 | if ((pkey = openssl_create_key(type)) == NULL) |
623 | 0 | return (false); |
624 | | |
625 | 0 | DEBUG_puts("1cupsCreateCredentialsRequest: Key pair created."); |
626 | | |
627 | | // Create the X.509 certificate... |
628 | 0 | DEBUG_puts("1cupsCreateCredentialsRequest: Generating self-signed X.509 certificate."); |
629 | |
|
630 | 0 | if ((csr = X509_REQ_new()) == NULL) |
631 | 0 | { |
632 | 0 | _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to create X.509 certificate signing request."), true); |
633 | 0 | goto done; |
634 | 0 | } |
635 | | |
636 | 0 | X509_REQ_set_pubkey(csr, pkey); |
637 | |
|
638 | 0 | if ((name = openssl_create_name(organization, org_unit, locality, state_province, country, common_name, email)) == NULL) |
639 | 0 | goto done; |
640 | | |
641 | 0 | X509_REQ_set_subject_name(csr, name); |
642 | 0 | X509_NAME_free(name); |
643 | | |
644 | | // Add extension with DNS names and free buffer for GENERAL_NAME |
645 | 0 | exts = sk_X509_EXTENSION_new_null(); |
646 | |
|
647 | 0 | if ((ext = openssl_create_san(common_name, num_alt_names, alt_names)) == NULL) |
648 | 0 | goto done; |
649 | | |
650 | 0 | sk_X509_EXTENSION_push(exts, ext); |
651 | |
|
652 | 0 | cupsCopyString(temp, "critical", sizeof(temp)); |
653 | 0 | for (tempptr = temp + strlen(temp), i = 0, usage_bit = CUPS_CREDUSAGE_DIGITAL_SIGNATURE; i < (sizeof(tls_usage_strings) / sizeof(tls_usage_strings[0])); i ++, usage_bit *= 2) |
654 | 0 | { |
655 | 0 | if (!(usage & usage_bit)) |
656 | 0 | continue; |
657 | | |
658 | 0 | snprintf(tempptr, sizeof(temp) - (size_t)(tempptr - temp), ",%s", tls_usage_strings[i]); |
659 | |
|
660 | 0 | tempptr += strlen(tempptr); |
661 | 0 | } |
662 | 0 | openssl_add_ext(exts, NID_key_usage, temp); |
663 | |
|
664 | 0 | temp[0] = '\0'; |
665 | 0 | for (tempptr = temp, i = 0, purpose_bit = CUPS_CREDPURPOSE_SERVER_AUTH; i < (sizeof(tls_purpose_oids) / sizeof(tls_purpose_oids[0])); i ++, purpose_bit *= 2) |
666 | 0 | { |
667 | 0 | if (!(purpose & purpose_bit)) |
668 | 0 | continue; |
669 | | |
670 | 0 | if (tempptr == temp) |
671 | 0 | cupsCopyString(temp, tls_purpose_oids[i], sizeof(temp)); |
672 | 0 | else |
673 | 0 | snprintf(tempptr, sizeof(temp) - (size_t)(tempptr - temp), ",%s", tls_purpose_oids[i]); |
674 | |
|
675 | 0 | tempptr += strlen(tempptr); |
676 | 0 | } |
677 | 0 | openssl_add_ext(exts, NID_ext_key_usage, temp); |
678 | |
|
679 | 0 | X509_REQ_add_extensions(csr, exts); |
680 | 0 | X509_REQ_sign(csr, pkey, EVP_sha256()); |
681 | |
|
682 | 0 | sk_X509_EXTENSION_pop_free(exts, X509_EXTENSION_free); |
683 | | |
684 | | // Save them... |
685 | 0 | if ((bio = BIO_new_file(keyfile, "wb")) == NULL) |
686 | 0 | { |
687 | 0 | _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0); |
688 | 0 | goto done; |
689 | 0 | } |
690 | | |
691 | 0 | if (!PEM_write_bio_PrivateKey(bio, pkey, NULL, NULL, 0, NULL, NULL)) |
692 | 0 | { |
693 | 0 | _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to write private key."), true); |
694 | 0 | BIO_free(bio); |
695 | 0 | goto done; |
696 | 0 | } |
697 | | |
698 | 0 | BIO_free(bio); |
699 | |
|
700 | 0 | if ((bio = BIO_new_file(pubfile, "wb")) == NULL) |
701 | 0 | { |
702 | 0 | _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0); |
703 | 0 | goto done; |
704 | 0 | } |
705 | | |
706 | 0 | if (!PEM_write_bio_PUBKEY(bio, pkey)) |
707 | 0 | { |
708 | 0 | _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to write public key."), true); |
709 | 0 | BIO_free(bio); |
710 | 0 | goto done; |
711 | 0 | } |
712 | | |
713 | 0 | BIO_free(bio); |
714 | |
|
715 | 0 | if ((bio = BIO_new_file(csrfile, "wb")) == NULL) |
716 | 0 | { |
717 | 0 | _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0); |
718 | 0 | goto done; |
719 | 0 | } |
720 | | |
721 | 0 | if (!PEM_write_bio_X509_REQ(bio, csr)) |
722 | 0 | { |
723 | 0 | _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to write X.509 certificate signing request."), true); |
724 | 0 | BIO_free(bio); |
725 | 0 | goto done; |
726 | 0 | } |
727 | | |
728 | 0 | BIO_free(bio); |
729 | |
|
730 | 0 | ret = true; |
731 | 0 | DEBUG_puts("1cupsCreateCredentialsRequest: Successfully created signing request."); |
732 | | |
733 | | // Cleanup... |
734 | 0 | done: |
735 | |
|
736 | 0 | X509_REQ_free(csr); |
737 | 0 | EVP_PKEY_free(pkey); |
738 | |
|
739 | 0 | return (ret); |
740 | 0 | } |
741 | | |
742 | | |
743 | | // |
744 | | // 'cupsGetCredentialsExpiration()' - Return the expiration date of the credentials. |
745 | | // |
746 | | |
747 | | time_t // O - Expiration date of credentials |
748 | | cupsGetCredentialsExpiration( |
749 | | const char *credentials) // I - Credentials |
750 | 0 | { |
751 | 0 | time_t result = 0; // Result |
752 | 0 | STACK_OF(X509) *certs; // Certificate chain |
753 | | |
754 | |
|
755 | 0 | if ((certs = openssl_load_x509(credentials)) != NULL) |
756 | 0 | { |
757 | 0 | result = openssl_get_date(sk_X509_value(certs, 0), 1); |
758 | 0 | sk_X509_free(certs); |
759 | 0 | } |
760 | |
|
761 | 0 | return (result); |
762 | 0 | } |
763 | | |
764 | | |
765 | | // |
766 | | // 'cupsGetCredentialsInfo()' - Return a string describing the credentials. |
767 | | // |
768 | | |
769 | | char * // O - Credentials description or `NULL` on error |
770 | | cupsGetCredentialsInfo( |
771 | | const char *credentials, // I - Credentials |
772 | | char *buffer, // I - Buffer |
773 | | size_t bufsize) // I - Size of buffer |
774 | 0 | { |
775 | 0 | STACK_OF(X509) *certs; // Certificate chain |
776 | 0 | X509 *cert; // Certificate |
777 | | |
778 | | |
779 | | // Range check input... |
780 | 0 | DEBUG_printf("cupsGetCredentialsInfo(credentials=%p, buffer=%p, bufsize=" CUPS_LLFMT ")", credentials, buffer, CUPS_LLCAST bufsize); |
781 | |
|
782 | 0 | if (buffer) |
783 | 0 | *buffer = '\0'; |
784 | |
|
785 | 0 | if (!credentials || !buffer || bufsize < 32) |
786 | 0 | { |
787 | 0 | DEBUG_puts("1cupsGetCredentialsInfo: Returning NULL."); |
788 | 0 | return (NULL); |
789 | 0 | } |
790 | | |
791 | 0 | if ((certs = openssl_load_x509(credentials)) != NULL) |
792 | 0 | { |
793 | 0 | char name[256], // Common name associated with cert |
794 | 0 | issuer[256], // Issuer associated with cert |
795 | 0 | expdate[256]; // Expiration data as string |
796 | 0 | time_t expiration; // Expiration date of cert |
797 | 0 | const char *sigalg; // Signature algorithm |
798 | 0 | unsigned char md5_digest[16]; // MD5 result |
799 | |
|
800 | 0 | DEBUG_printf("2cupsGetCredentialsInfo: certs=%p(%d certificates)", certs, sk_X509_num(certs)); |
801 | 0 | cert = sk_X509_value(certs, 0); |
802 | 0 | DEBUG_printf("2cupsGetCredentialsInfo: cert=%p", cert); |
803 | |
|
804 | 0 | X509_NAME_get_text_by_NID(X509_get_subject_name(cert), NID_commonName, name, sizeof(name)); |
805 | 0 | X509_NAME_get_text_by_NID(X509_get_issuer_name(cert), NID_commonName, issuer, sizeof(issuer)); |
806 | 0 | expiration = openssl_get_date(cert, 1); |
807 | |
|
808 | 0 | switch (X509_get_signature_nid(cert)) |
809 | 0 | { |
810 | 0 | case NID_ecdsa_with_SHA1 : |
811 | 0 | sigalg = "SHA1WithECDSAEncryption"; |
812 | 0 | break; |
813 | 0 | case NID_ecdsa_with_SHA224 : |
814 | 0 | sigalg = "SHA224WithECDSAEncryption"; |
815 | 0 | break; |
816 | 0 | case NID_ecdsa_with_SHA256 : |
817 | 0 | sigalg = "SHA256WithECDSAEncryption"; |
818 | 0 | break; |
819 | 0 | case NID_ecdsa_with_SHA384 : |
820 | 0 | sigalg = "SHA384WithECDSAEncryption"; |
821 | 0 | break; |
822 | 0 | case NID_ecdsa_with_SHA512 : |
823 | 0 | sigalg = "SHA512WithECDSAEncryption"; |
824 | 0 | break; |
825 | 0 | case NID_sha1WithRSAEncryption : |
826 | 0 | sigalg = "SHA1WithRSAEncryption"; |
827 | 0 | break; |
828 | 0 | case NID_sha224WithRSAEncryption : |
829 | 0 | sigalg = "SHA224WithRSAEncryption"; |
830 | 0 | break; |
831 | 0 | case NID_sha256WithRSAEncryption : |
832 | 0 | sigalg = "SHA256WithRSAEncryption"; |
833 | 0 | break; |
834 | 0 | case NID_sha384WithRSAEncryption : |
835 | 0 | sigalg = "SHA384WithRSAEncryption"; |
836 | 0 | break; |
837 | 0 | case NID_sha512WithRSAEncryption : |
838 | 0 | sigalg = "SHA512WithRSAEncryption"; |
839 | 0 | break; |
840 | 0 | default : |
841 | 0 | sigalg = "Unknown"; |
842 | 0 | break; |
843 | 0 | } |
844 | | |
845 | 0 | cupsHashData("md5", credentials, strlen(credentials), md5_digest, sizeof(md5_digest)); |
846 | |
|
847 | 0 | 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, expdate, sizeof(expdate)), 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]); |
848 | 0 | sk_X509_free(certs); |
849 | 0 | } |
850 | | |
851 | 0 | DEBUG_printf("1cupsGetCredentialsInfo: Returning \"%s\".", buffer); |
852 | |
|
853 | 0 | return (buffer); |
854 | 0 | } |
855 | | |
856 | | |
857 | | // |
858 | | // 'cupsGetCredentialsTrust()' - Return the trust of credentials. |
859 | | // |
860 | | // This function determines the level of trust for the supplied credentials. |
861 | | // The "path" parameter specifies the certificate/key store for known |
862 | | // credentials and certificate authorities. The "common_name" parameter |
863 | | // specifies the FQDN of the service being accessed such as |
864 | | // "printer.example.com". The "credentials" parameter provides the credentials |
865 | | // being evaluated, which are usually obtained with the |
866 | | // @link httpCopyPeerCredentials@ function. The "require_ca" parameter |
867 | | // specifies whether a CA-signed certificate is required for trust. |
868 | | // |
869 | | // The `AllowAnyRoot`, `AllowExpiredCerts`, `TrustOnFirstUse`, and |
870 | | // `ValidateCerts` options in the "client.conf" file (or corresponding |
871 | | // preferences file on macOS) control the trust policy, which defaults to |
872 | | // AllowAnyRoot=Yes, AllowExpiredCerts=No, TrustOnFirstUse=Yes, and |
873 | | // ValidateCerts=No. When the "require_ca" parameter is `true` the AllowAnyRoot |
874 | | // and TrustOnFirstUse policies are turned off ("No"). |
875 | | // |
876 | | // The returned trust value can be one of the following: |
877 | | // |
878 | | // - `HTTP_TRUST_OK`: Credentials are OK/trusted |
879 | | // - `HTTP_TRUST_INVALID`: Credentials are invalid |
880 | | // - `HTTP_TRUST_EXPIRED`: Credentials are expired |
881 | | // - `HTTP_TRUST_RENEWED`: Credentials have been renewed |
882 | | // - `HTTP_TRUST_UNKNOWN`: Credentials are unknown/new |
883 | | // |
884 | | |
885 | | http_trust_t // O - Level of trust |
886 | | cupsGetCredentialsTrust( |
887 | | const char *path, // I - Directory path for certificate/key store or `NULL` for default |
888 | | const char *common_name, // I - Common name for trust lookup |
889 | | const char *credentials, // I - Credentials |
890 | | bool require_ca) // I - Require a CA-signed certificate? |
891 | 0 | { |
892 | 0 | http_trust_t trust = HTTP_TRUST_OK; |
893 | | // Trusted? |
894 | 0 | STACK_OF(X509) *certs; // Certificate chain |
895 | 0 | X509 *cert; // Certificate |
896 | 0 | char *tcreds = NULL; // Trusted credentials |
897 | 0 | char defpath[1024]; // Default path |
898 | 0 | _cups_globals_t *cg = _cupsGlobals(); // Per-thread globals |
899 | | |
900 | |
|
901 | 0 | DEBUG_printf("cupsGetCredentialsTrust(path=\"%s\", common_name=\"%s\", credentials=%p, require_ca=%s)", path, common_name, (void *)credentials, require_ca ? "true" : "false"); |
902 | | |
903 | | // Range check input... |
904 | 0 | if (!path) |
905 | 0 | path = http_default_path(defpath, sizeof(defpath)); |
906 | |
|
907 | 0 | if (!path || !credentials || !common_name) |
908 | 0 | { |
909 | 0 | _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), false); |
910 | 0 | DEBUG_printf("1cupsGetCredentialsTrust: Returning %d.", HTTP_TRUST_UNKNOWN); |
911 | 0 | return (HTTP_TRUST_UNKNOWN); |
912 | 0 | } |
913 | | |
914 | | // Load the credentials... |
915 | 0 | if ((certs = openssl_load_x509(credentials)) == NULL) |
916 | 0 | { |
917 | 0 | _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI, _("Unable to import credentials."), true); |
918 | 0 | DEBUG_printf("1cupsGetCredentialsTrust: Returning %d.", HTTP_TRUST_UNKNOWN); |
919 | 0 | return (HTTP_TRUST_UNKNOWN); |
920 | 0 | } |
921 | | |
922 | 0 | cert = sk_X509_value(certs, 0); |
923 | |
|
924 | 0 | if (!cg->client_conf_loaded) |
925 | 0 | { |
926 | 0 | _cupsSetDefaults(); |
927 | | // openssl_load_crl(); |
928 | 0 | } |
929 | | |
930 | | // Look this common name up in the default keychains... |
931 | 0 | if (sk_X509_num(certs) == 1 && (tcreds = cupsCopyCredentials(path, common_name)) != NULL) |
932 | 0 | { |
933 | 0 | char credentials_str[1024], // String for incoming credentials |
934 | 0 | tcreds_str[1024]; // String for saved credentials |
935 | |
|
936 | 0 | cupsGetCredentialsInfo(credentials, credentials_str, sizeof(credentials_str)); |
937 | 0 | cupsGetCredentialsInfo(tcreds, tcreds_str, sizeof(tcreds_str)); |
938 | |
|
939 | 0 | if (strcmp(credentials_str, tcreds_str)) |
940 | 0 | { |
941 | | // Credentials don't match, let's look at the expiration date of the new |
942 | | // credentials and allow if the new ones have a later expiration... |
943 | 0 | if (!cg->trust_first || require_ca) |
944 | 0 | { |
945 | | // Do not trust certificates on first use... |
946 | 0 | _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI, _("Trust on first use is disabled."), true); |
947 | |
|
948 | 0 | trust = HTTP_TRUST_INVALID; |
949 | 0 | } |
950 | 0 | else if (cupsGetCredentialsExpiration(credentials) <= cupsGetCredentialsExpiration(tcreds)) |
951 | 0 | { |
952 | | // The new credentials are not newly issued... |
953 | 0 | _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI, _("New credentials are older than stored credentials."), true); |
954 | |
|
955 | 0 | trust = HTTP_TRUST_INVALID; |
956 | 0 | } |
957 | 0 | else if (!cupsAreCredentialsValidForName(common_name, credentials)) |
958 | 0 | { |
959 | | // The common name does not match the issued certificate... |
960 | 0 | _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI, _("New credentials are not valid for name."), true); |
961 | |
|
962 | 0 | trust = HTTP_TRUST_INVALID; |
963 | 0 | } |
964 | 0 | else if (cupsGetCredentialsExpiration(tcreds) < time(NULL)) |
965 | 0 | { |
966 | | // Save the renewed credentials... |
967 | 0 | trust = HTTP_TRUST_RENEWED; |
968 | |
|
969 | 0 | cupsSaveCredentials(path, common_name, credentials, NULL); |
970 | 0 | } |
971 | 0 | } |
972 | |
|
973 | 0 | free(tcreds); |
974 | 0 | } |
975 | 0 | else if ((cg->validate_certs || require_ca) && !cupsAreCredentialsValidForName(common_name, credentials)) |
976 | 0 | { |
977 | 0 | _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI, _("No stored credentials, not valid for name."), true); |
978 | 0 | trust = HTTP_TRUST_INVALID; |
979 | 0 | } |
980 | 0 | else if (sk_X509_num(certs) > 1) |
981 | 0 | { |
982 | 0 | if (!http_check_roots(credentials)) |
983 | 0 | { |
984 | | // See if we have a site CA certificate we can compare... |
985 | 0 | if ((tcreds = cupsCopyCredentials(path, "_site_")) != NULL) |
986 | 0 | { |
987 | 0 | size_t credslen, // Length of credentials |
988 | 0 | tcredslen; // Length of trust root |
989 | | |
990 | | |
991 | | // Do a tail comparison of the root... |
992 | 0 | credslen = strlen(credentials); |
993 | 0 | tcredslen = strlen(tcreds); |
994 | 0 | if (credslen <= tcredslen || strcmp(credentials + (credslen - tcredslen), tcreds)) |
995 | 0 | { |
996 | | // Certificate isn't directly generated from the CA cert... |
997 | 0 | trust = HTTP_TRUST_INVALID; |
998 | 0 | } |
999 | |
|
1000 | 0 | if (trust != HTTP_TRUST_OK) |
1001 | 0 | _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI, _("Credentials do not validate against site CA certificate."), true); |
1002 | |
|
1003 | 0 | free(tcreds); |
1004 | 0 | } |
1005 | 0 | } |
1006 | 0 | } |
1007 | 0 | else if (require_ca) |
1008 | 0 | { |
1009 | 0 | _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI, _("Credentials are not CA-signed."), true); |
1010 | 0 | trust = HTTP_TRUST_INVALID; |
1011 | 0 | } |
1012 | 0 | else if (!cg->trust_first) |
1013 | 0 | { |
1014 | 0 | _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI, _("Trust on first use is disabled."), true); |
1015 | 0 | trust = HTTP_TRUST_INVALID; |
1016 | 0 | } |
1017 | 0 | else if (!cg->any_root || require_ca) |
1018 | 0 | { |
1019 | 0 | _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI, _("Self-signed credentials are blocked."), true); |
1020 | 0 | trust = HTTP_TRUST_INVALID; |
1021 | 0 | } |
1022 | |
|
1023 | 0 | if (trust == HTTP_TRUST_OK && !cg->expired_certs) |
1024 | 0 | { |
1025 | 0 | time_t curtime; // Current date/time |
1026 | |
|
1027 | 0 | time(&curtime); |
1028 | |
|
1029 | 0 | DEBUG_printf("1cupsGetCredentialsTrust: curtime=%ld, notBefore=%ld, notAfter=%ld", (long)curtime, (long)openssl_get_date(cert, 0), (long)openssl_get_date(cert, 1)); |
1030 | |
|
1031 | 0 | if ((curtime + 86400) < openssl_get_date(cert, 0) || curtime > openssl_get_date(cert, 1)) |
1032 | 0 | { |
1033 | 0 | _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI, _("Credentials have expired."), true); |
1034 | 0 | trust = HTTP_TRUST_EXPIRED; |
1035 | 0 | } |
1036 | 0 | } |
1037 | |
|
1038 | 0 | sk_X509_free(certs); |
1039 | |
|
1040 | 0 | DEBUG_printf("1cupsGetCredentialsTrust: Returning %d.", trust); |
1041 | |
|
1042 | 0 | return (trust); |
1043 | 0 | } |
1044 | | |
1045 | | |
1046 | | // |
1047 | | // 'cupsSignCredentialsRequest()' - Sign an X.509 certificate signing request to produce an X.509 certificate chain. |
1048 | | // |
1049 | | // This function creates an X.509 certificate from a signing request. The |
1050 | | // certificate is stored in the directory "path" or, if "path" is `NULL`, in a |
1051 | | // per-user or system-wide (when running as root) certificate/key store. The |
1052 | | // generated certificate is signed by the named root certificate or, if |
1053 | | // "root_name" is `NULL`, a site-wide default root certificate. When |
1054 | | // "root_name" is `NULL` and there is no site-wide default root certificate, a |
1055 | | // self-signed certificate is generated instead. |
1056 | | // |
1057 | | // The "allowed_purpose" argument specifies the allowed purpose(s) used for the |
1058 | | // credentials as a bitwise OR of the following constants: |
1059 | | // |
1060 | | // - `CUPS_CREDPURPOSE_SERVER_AUTH` for validating TLS servers, |
1061 | | // - `CUPS_CREDPURPOSE_CLIENT_AUTH` for validating TLS clients, |
1062 | | // - `CUPS_CREDPURPOSE_CODE_SIGNING` for validating compiled code, |
1063 | | // - `CUPS_CREDPURPOSE_EMAIL_PROTECTION` for validating email messages, |
1064 | | // - `CUPS_CREDPURPOSE_TIME_STAMPING` for signing timestamps to objects, and/or |
1065 | | // - `CUPS_CREDPURPOSE_OCSP_SIGNING` for Online Certificate Status Protocol |
1066 | | // message signing. |
1067 | | // |
1068 | | // The "allowed_usage" argument specifies the allowed usage(s) for the |
1069 | | // credentials as a bitwise OR of the following constants: |
1070 | | // |
1071 | | // - `CUPS_CREDUSAGE_DIGITAL_SIGNATURE`: digital signatures, |
1072 | | // - `CUPS_CREDUSAGE_NON_REPUDIATION`: non-repudiation/content commitment, |
1073 | | // - `CUPS_CREDUSAGE_KEY_ENCIPHERMENT`: key encipherment, |
1074 | | // - `CUPS_CREDUSAGE_DATA_ENCIPHERMENT`: data encipherment, |
1075 | | // - `CUPS_CREDUSAGE_KEY_AGREEMENT`: key agreement, |
1076 | | // - `CUPS_CREDUSAGE_KEY_CERT_SIGN`: key certicate signing, |
1077 | | // - `CUPS_CREDUSAGE_CRL_SIGN`: certificate revocation list signing, |
1078 | | // - `CUPS_CREDUSAGE_ENCIPHER_ONLY`: encipherment only, |
1079 | | // - `CUPS_CREDUSAGE_DECIPHER_ONLY`: decipherment only, |
1080 | | // - `CUPS_CREDUSAGE_DEFAULT_CA`: defaults for CA certificates, |
1081 | | // - `CUPS_CREDUSAGE_DEFAULT_TLS`: defaults for TLS certificates, and/or |
1082 | | // - `CUPS_CREDUSAGE_ALL`: all usages. |
1083 | | // |
1084 | | // The "cb" and "cb_data" arguments specify a function and its data that are |
1085 | | // used to validate any subjectAltName values in the signing request: |
1086 | | // |
1087 | | // ``` |
1088 | | // bool san_cb(const char *common_name, const char *alt_name, void *cb_data) { |
1089 | | // ... return true if OK and false if not ... |
1090 | | // } |
1091 | | // ``` |
1092 | | // |
1093 | | // If `NULL`, a default validation function is used that allows "localhost" and |
1094 | | // variations of the common name. |
1095 | | // |
1096 | | // The "expiration_date" argument specifies the expiration date and time as a |
1097 | | // Unix `time_t` value in seconds. |
1098 | | // |
1099 | | |
1100 | | bool // O - `true` on success, `false` on failure |
1101 | | cupsSignCredentialsRequest( |
1102 | | const char *path, // I - Directory path for certificate/key store or `NULL` for default |
1103 | | const char *common_name, // I - Common name to use |
1104 | | const char *request, // I - PEM-encoded CSR |
1105 | | const char *root_name, // I - Root certificate |
1106 | | cups_credpurpose_t allowed_purpose, // I - Allowed credential purpose(s) |
1107 | | cups_credusage_t allowed_usage, // I - Allowed credential usage(s) |
1108 | | cups_cert_san_cb_t cb, // I - subjectAltName callback or `NULL` to allow just .local |
1109 | | void *cb_data, // I - Callback data |
1110 | | time_t expiration_date) // I - Certificate expiration date |
1111 | 0 | { |
1112 | 0 | bool result = false; // Return value |
1113 | 0 | X509 *cert = NULL; // Certificate |
1114 | 0 | X509_REQ *crq = NULL; // Certificate request |
1115 | 0 | X509 *root_cert = NULL; // Root certificate, if any |
1116 | 0 | EVP_PKEY *root_key = NULL; // Root private key, if any |
1117 | 0 | char defpath[1024], // Default path |
1118 | 0 | crtfile[1024], // Certificate filename |
1119 | 0 | root_crtfile[1024], // Root certificate filename |
1120 | 0 | root_keyfile[1024]; // Root private key filename |
1121 | 0 | time_t curtime; // Current time |
1122 | 0 | ASN1_INTEGER *serial; // Serial number |
1123 | 0 | ASN1_TIME *notBefore, // Initial date |
1124 | 0 | *notAfter; // Expiration date |
1125 | 0 | BIO *bio; // Input/output file |
1126 | 0 | char temp[1024]; // Temporary string |
1127 | 0 | int i, j, // Looping vars |
1128 | 0 | num_exts; // Number of extensions |
1129 | 0 | STACK_OF(X509_EXTENSION) *exts = NULL;// Extensions |
1130 | 0 | X509_EXTENSION *ext; // Current extension |
1131 | 0 | cups_credpurpose_t purpose; // Current purpose |
1132 | 0 | cups_credusage_t usage; // Current usage |
1133 | 0 | bool saw_usage = false, // Saw NID_key_usage? |
1134 | 0 | saw_ext_usage = false, // Saw NID_ext_key_usage? |
1135 | 0 | saw_san = false; // Saw NID_subject_alt_name? |
1136 | | |
1137 | |
|
1138 | 0 | DEBUG_printf("cupsSignCredentialsRequest(path=\"%s\", common_name=\"%s\", request=\"%s\", root_name=\"%s\", allowed_purpose=0x%x, allowed_usage=0x%x, cb=%p, cb_data=%p, expiration_date=%ld)", path, common_name, request, root_name, allowed_purpose, allowed_usage, cb, cb_data, (long)expiration_date); |
1139 | | |
1140 | | // Filenames... |
1141 | 0 | if (!path) |
1142 | 0 | path = http_default_path(defpath, sizeof(defpath)); |
1143 | |
|
1144 | 0 | if (!path || !common_name || !request) |
1145 | 0 | { |
1146 | 0 | _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), false); |
1147 | 0 | return (false); |
1148 | 0 | } |
1149 | | |
1150 | 0 | if (!cb) |
1151 | 0 | cb = http_default_san_cb; |
1152 | | |
1153 | | // Import the X.509 certificate request... |
1154 | 0 | DEBUG_puts("1cupsCreateCredentials: Importing X.509 certificate request."); |
1155 | 0 | if ((bio = BIO_new_mem_buf(request, (int)strlen(request))) != NULL) |
1156 | 0 | { |
1157 | 0 | PEM_read_bio_X509_REQ(bio, &crq, /*cb*/NULL, /*u*/NULL); |
1158 | 0 | BIO_free(bio); |
1159 | 0 | } |
1160 | |
|
1161 | 0 | if (!crq) |
1162 | 0 | { |
1163 | 0 | _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to import X.509 certificate request."), true); |
1164 | 0 | return (false); |
1165 | 0 | } |
1166 | | |
1167 | 0 | if (X509_REQ_verify(crq, X509_REQ_get_pubkey(crq)) < 0) |
1168 | 0 | { |
1169 | 0 | _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to verify X.509 certificate request."), true); |
1170 | 0 | goto done; |
1171 | 0 | } |
1172 | | |
1173 | | // Create the X.509 certificate... |
1174 | 0 | DEBUG_puts("1cupsSignCredentialsRequest: Generating X.509 certificate."); |
1175 | |
|
1176 | 0 | if ((cert = X509_new()) == NULL) |
1177 | 0 | { |
1178 | 0 | _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to create X.509 certificate."), true); |
1179 | 0 | goto done; |
1180 | 0 | } |
1181 | | |
1182 | 0 | curtime = time(NULL); |
1183 | |
|
1184 | 0 | notBefore = ASN1_TIME_new(); |
1185 | 0 | ASN1_TIME_set(notBefore, curtime); |
1186 | 0 | X509_set_notBefore(cert, notBefore); |
1187 | 0 | ASN1_TIME_free(notBefore); |
1188 | |
|
1189 | 0 | notAfter = ASN1_TIME_new(); |
1190 | 0 | ASN1_TIME_set(notAfter, expiration_date); |
1191 | 0 | X509_set_notAfter(cert, notAfter); |
1192 | 0 | ASN1_TIME_free(notAfter); |
1193 | |
|
1194 | 0 | serial = ASN1_INTEGER_new(); |
1195 | 0 | ASN1_INTEGER_set(serial, (long)curtime); |
1196 | 0 | X509_set_serialNumber(cert, serial); |
1197 | 0 | ASN1_INTEGER_free(serial); |
1198 | |
|
1199 | 0 | X509_set_pubkey(cert, X509_REQ_get_pubkey(crq)); |
1200 | |
|
1201 | 0 | X509_set_subject_name(cert, X509_REQ_get_subject_name(crq)); |
1202 | 0 | X509_set_version(cert, 2); // v3 |
1203 | | |
1204 | | // Copy/verify extensions... |
1205 | 0 | exts = X509_REQ_get_extensions(crq); |
1206 | 0 | num_exts = sk_X509_EXTENSION_num(exts); |
1207 | |
|
1208 | 0 | for (i = 0; i < num_exts; i ++) |
1209 | 0 | { |
1210 | | // Get the extension object... |
1211 | 0 | bool add_ext = false; // Add this extension? |
1212 | 0 | ASN1_OBJECT *obj; // Extension object |
1213 | 0 | ASN1_OCTET_STRING *extdata; // Extension data string |
1214 | 0 | unsigned char *data = NULL; // Extension data bytes |
1215 | 0 | int datalen; // Length of extension data |
1216 | |
|
1217 | 0 | ext = sk_X509_EXTENSION_value(exts, i); |
1218 | 0 | obj = X509_EXTENSION_get_object(ext); |
1219 | 0 | extdata = X509_EXTENSION_get_data(ext); |
1220 | 0 | datalen = i2d_ASN1_OCTET_STRING(extdata, &data); |
1221 | |
|
1222 | | #ifdef DEBUG |
1223 | | char *tempptr; // Pointer into string |
1224 | | |
1225 | | for (j = 0, tempptr = temp; j < datalen; j ++, tempptr += 2) |
1226 | | snprintf(tempptr, sizeof(temp) - (size_t)(tempptr - temp), "%02X", data[j]); |
1227 | | |
1228 | | DEBUG_printf("1cupsSignCredentialsRequest: EXT%d=%s", OBJ_obj2nid(obj), temp); |
1229 | | #endif // DEBUG |
1230 | |
|
1231 | 0 | switch (OBJ_obj2nid(obj)) |
1232 | 0 | { |
1233 | 0 | case NID_ext_key_usage : |
1234 | 0 | add_ext = true; |
1235 | 0 | saw_ext_usage = true; |
1236 | |
|
1237 | 0 | if (datalen < 12 || data[2] != 0x30 || data[3] != (datalen - 4)) |
1238 | 0 | { |
1239 | 0 | _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad keyUsage extension in X.509 certificate request."), true); |
1240 | 0 | goto done; |
1241 | 0 | } |
1242 | | |
1243 | 0 | for (purpose = 0, j = 4; j < datalen; j += data[j + 1] + 2) |
1244 | 0 | { |
1245 | 0 | if (data[j] != 0x06 || data[j + 1] != 8 || memcmp(data + j + 2, "+\006\001\005\005\007\003", 7)) |
1246 | 0 | { |
1247 | 0 | _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad keyUsage extension in X.509 certificate request."), true); |
1248 | 0 | goto done; |
1249 | 0 | } |
1250 | | |
1251 | 0 | switch (data[j + 9]) |
1252 | 0 | { |
1253 | 0 | case 1 : |
1254 | 0 | purpose |= CUPS_CREDPURPOSE_SERVER_AUTH; |
1255 | 0 | break; |
1256 | 0 | case 2 : |
1257 | 0 | purpose |= CUPS_CREDPURPOSE_CLIENT_AUTH; |
1258 | 0 | break; |
1259 | 0 | case 3 : |
1260 | 0 | purpose |= CUPS_CREDPURPOSE_CODE_SIGNING; |
1261 | 0 | break; |
1262 | 0 | case 4 : |
1263 | 0 | purpose |= CUPS_CREDPURPOSE_EMAIL_PROTECTION; |
1264 | 0 | break; |
1265 | 0 | case 8 : |
1266 | 0 | purpose |= CUPS_CREDPURPOSE_TIME_STAMPING; |
1267 | 0 | break; |
1268 | 0 | case 9 : |
1269 | 0 | purpose |= CUPS_CREDPURPOSE_OCSP_SIGNING; |
1270 | 0 | break; |
1271 | 0 | default : |
1272 | 0 | _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad keyUsage extension in X.509 certificate request."), true); |
1273 | 0 | goto done; |
1274 | 0 | } |
1275 | 0 | } |
1276 | | |
1277 | 0 | DEBUG_printf("1cupsSignCredentialsRequest: purpose=0x%04x", purpose); |
1278 | |
|
1279 | 0 | if (purpose & ~allowed_purpose) |
1280 | 0 | { |
1281 | 0 | _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad keyUsage extension in X.509 certificate request."), true); |
1282 | 0 | goto done; |
1283 | 0 | } |
1284 | 0 | break; |
1285 | | |
1286 | 0 | case NID_key_usage : |
1287 | 0 | add_ext = true; |
1288 | 0 | saw_usage = true; |
1289 | |
|
1290 | 0 | if (datalen < 6 || datalen > 7 || data[2] != 0x03 || data[3] != (datalen - 4)) |
1291 | 0 | { |
1292 | 0 | _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad extKeyUsage extension in X.509 certificate request."), true); |
1293 | 0 | goto done; |
1294 | 0 | } |
1295 | | |
1296 | 0 | usage = 0; |
1297 | 0 | if (data[5] & 0x80) |
1298 | 0 | usage |= CUPS_CREDUSAGE_DIGITAL_SIGNATURE; |
1299 | 0 | if (data[5] & 0x40) |
1300 | 0 | usage |= CUPS_CREDUSAGE_NON_REPUDIATION; |
1301 | 0 | if (data[5] & 0x20) |
1302 | 0 | usage |= CUPS_CREDUSAGE_KEY_ENCIPHERMENT; |
1303 | 0 | if (data[5] & 0x10) |
1304 | 0 | usage |= CUPS_CREDUSAGE_DATA_ENCIPHERMENT; |
1305 | 0 | if (data[5] & 0x08) |
1306 | 0 | usage |= CUPS_CREDUSAGE_KEY_AGREEMENT; |
1307 | 0 | if (data[5] & 0x04) |
1308 | 0 | usage |= CUPS_CREDUSAGE_KEY_CERT_SIGN; |
1309 | 0 | if (data[5] & 0x02) |
1310 | 0 | usage |= CUPS_CREDUSAGE_CRL_SIGN; |
1311 | 0 | if (data[5] & 0x01) |
1312 | 0 | usage |= CUPS_CREDUSAGE_ENCIPHER_ONLY; |
1313 | 0 | if (datalen == 7 && (data[6] & 0x80)) |
1314 | 0 | usage |= CUPS_CREDUSAGE_DECIPHER_ONLY; |
1315 | |
|
1316 | 0 | DEBUG_printf("1cupsSignCredentialsRequest: usage=0x%04x", usage); |
1317 | |
|
1318 | 0 | if (usage & ~allowed_usage) |
1319 | 0 | { |
1320 | 0 | _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad extKeyUsage extension in X.509 certificate request."), true); |
1321 | 0 | goto done; |
1322 | 0 | } |
1323 | 0 | break; |
1324 | | |
1325 | 0 | case NID_subject_alt_name : |
1326 | 0 | add_ext = true; |
1327 | 0 | saw_san = true; |
1328 | |
|
1329 | 0 | if (datalen < 4 || data[2] != 0x30 || data[3] != (datalen - 4)) |
1330 | 0 | { |
1331 | 0 | _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad subjectAltName extension in X.509 certificate request."), true); |
1332 | 0 | goto done; |
1333 | 0 | } |
1334 | | |
1335 | | // Parse the SAN values (there should be an easier/standard OpenSSL API to do this!) |
1336 | 0 | for (j = 4, datalen -= 2; j < datalen; j += data[j + 1] + 2) |
1337 | 0 | { |
1338 | 0 | if (data[j] == 0x82 && data[j + 1]) |
1339 | 0 | { |
1340 | | // GENERAL_STRING for DNS |
1341 | 0 | memcpy(temp, data + j + 2, data[j + 1]); |
1342 | 0 | temp[data[j + 1]] = '\0'; |
1343 | |
|
1344 | 0 | DEBUG_printf("1cupsSignCredentialsRequest: SAN %s", temp); |
1345 | |
|
1346 | 0 | if (!(cb)(common_name, temp, cb_data)) |
1347 | 0 | { |
1348 | 0 | _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Validation of subjectAltName in X.509 certificate request failed."), true); |
1349 | 0 | goto done; |
1350 | 0 | } |
1351 | 0 | } |
1352 | 0 | } |
1353 | 0 | break; |
1354 | 0 | } |
1355 | | |
1356 | 0 | OPENSSL_free(data); |
1357 | | |
1358 | | // If we get this far, the object is OK and we can add it... |
1359 | 0 | if (add_ext && !X509_add_ext(cert, ext, -1)) |
1360 | 0 | goto done; |
1361 | 0 | } |
1362 | | |
1363 | | // Add basic constraints for an "edge" certificate... |
1364 | 0 | if ((ext = X509V3_EXT_conf_nid(/*conf*/NULL, /*ctx*/NULL, NID_basic_constraints, "critical,CA:FALSE,pathlen:0")) == NULL || !X509_add_ext(cert, ext, -1)) |
1365 | 0 | { |
1366 | 0 | _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to add extension to X.509 certificate."), true); |
1367 | 0 | goto done; |
1368 | 0 | } |
1369 | | |
1370 | | // Add key usage extensions as needed... |
1371 | 0 | if (!saw_usage) |
1372 | 0 | { |
1373 | 0 | if ((ext = X509V3_EXT_conf_nid(/*conf*/NULL, /*ctx*/NULL, NID_key_usage, "critical,digitalSignature,keyEncipherment")) == NULL || !X509_add_ext(cert, ext, -1)) |
1374 | 0 | { |
1375 | 0 | _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to add extension to X.509 certificate."), true); |
1376 | 0 | goto done; |
1377 | 0 | } |
1378 | 0 | } |
1379 | | |
1380 | 0 | if (!saw_ext_usage) |
1381 | 0 | { |
1382 | 0 | if ((ext = X509V3_EXT_conf_nid(/*conf*/NULL, /*ctx*/NULL, NID_ext_key_usage, tls_usage_strings[0])) == NULL || !X509_add_ext(cert, ext, -1)) |
1383 | 0 | { |
1384 | 0 | _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to add extension to X.509 certificate."), true); |
1385 | 0 | goto done; |
1386 | 0 | } |
1387 | 0 | } |
1388 | | |
1389 | 0 | if (!saw_san) |
1390 | 0 | { |
1391 | 0 | if ((ext = openssl_create_san(common_name, /*num_alt_names*/0, /*alt_names*/NULL)) == NULL || !X509_add_ext(cert, ext, -1)) |
1392 | 0 | { |
1393 | 0 | _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to add extension to X.509 certificate."), true); |
1394 | 0 | goto done; |
1395 | 0 | } |
1396 | 0 | } |
1397 | | |
1398 | | // Try loading a root certificate... |
1399 | 0 | http_make_path(root_crtfile, sizeof(root_crtfile), path, root_name ? root_name : "_site_", "crt"); |
1400 | 0 | http_make_path(root_keyfile, sizeof(root_keyfile), path, root_name ? root_name : "_site_", "key"); |
1401 | |
|
1402 | 0 | if (!access(root_crtfile, 0) && !access(root_keyfile, 0)) |
1403 | 0 | { |
1404 | 0 | if ((bio = BIO_new_file(root_crtfile, "rb")) != NULL) |
1405 | 0 | { |
1406 | 0 | PEM_read_bio_X509(bio, &root_cert, /*cb*/NULL, /*u*/NULL); |
1407 | 0 | BIO_free(bio); |
1408 | |
|
1409 | 0 | if ((bio = BIO_new_file(root_keyfile, "rb")) != NULL) |
1410 | 0 | { |
1411 | 0 | PEM_read_bio_PrivateKey(bio, &root_key, /*cb*/NULL, /*u*/NULL); |
1412 | 0 | BIO_free(bio); |
1413 | 0 | } |
1414 | |
|
1415 | 0 | if (!root_key) |
1416 | 0 | { |
1417 | | // Only use root certificate if we have the key... |
1418 | 0 | X509_free(root_cert); |
1419 | 0 | root_cert = NULL; |
1420 | 0 | } |
1421 | 0 | } |
1422 | 0 | } |
1423 | |
|
1424 | 0 | if (!root_cert || !root_key) |
1425 | 0 | { |
1426 | 0 | _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to load X.509 CA certificate and private key."), true); |
1427 | 0 | goto done; |
1428 | 0 | } |
1429 | | |
1430 | 0 | X509_set_issuer_name(cert, X509_get_subject_name(root_cert)); |
1431 | 0 | X509_sign(cert, root_key, EVP_sha256()); |
1432 | | |
1433 | | // Save the certificate... |
1434 | 0 | http_make_path(crtfile, sizeof(crtfile), path, common_name, "crt"); |
1435 | |
|
1436 | 0 | if ((bio = BIO_new_file(crtfile, "wb")) == NULL) |
1437 | 0 | { |
1438 | 0 | _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0); |
1439 | 0 | goto done; |
1440 | 0 | } |
1441 | | |
1442 | 0 | if (!PEM_write_bio_X509(bio, cert)) |
1443 | 0 | { |
1444 | 0 | _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to write X.509 certificate."), true); |
1445 | 0 | BIO_free(bio); |
1446 | 0 | goto done; |
1447 | 0 | } |
1448 | | |
1449 | 0 | PEM_write_bio_X509(bio, root_cert); |
1450 | |
|
1451 | 0 | BIO_free(bio); |
1452 | 0 | result = true; |
1453 | 0 | DEBUG_puts("1cupsSignRequest: Successfully created credentials."); |
1454 | | |
1455 | | // Cleanup... |
1456 | 0 | done: |
1457 | |
|
1458 | 0 | if (exts) |
1459 | 0 | sk_X509_EXTENSION_pop_free(exts, X509_EXTENSION_free); |
1460 | 0 | if (crq) |
1461 | 0 | X509_REQ_free(crq); |
1462 | 0 | if (cert) |
1463 | 0 | X509_free(cert); |
1464 | 0 | if (root_cert) |
1465 | 0 | X509_free(root_cert); |
1466 | 0 | if (root_key) |
1467 | 0 | EVP_PKEY_free(root_key); |
1468 | |
|
1469 | 0 | return (result); |
1470 | 0 | } |
1471 | | |
1472 | | |
1473 | | // |
1474 | | // 'httpCopyPeerCredentials()' - Copy the credentials associated with the peer in an encrypted connection. |
1475 | | // |
1476 | | |
1477 | | char * // O - PEM-encoded X.509 certificate chain or `NULL` |
1478 | | httpCopyPeerCredentials(http_t *http) // I - Connection to server |
1479 | 0 | { |
1480 | 0 | char *credentials = NULL; // Return value |
1481 | 0 | size_t alloc_creds = 0; // Allocated size |
1482 | 0 | STACK_OF(X509) *chain; // Certificate chain |
1483 | | |
1484 | |
|
1485 | 0 | DEBUG_printf("httpCopyPeerCredentials(http=%p)", http); |
1486 | |
|
1487 | 0 | if (http && http->tls) |
1488 | 0 | { |
1489 | | // Get the chain of certificates for the remote end... |
1490 | 0 | chain = SSL_get_peer_cert_chain(http->tls); |
1491 | |
|
1492 | 0 | DEBUG_printf("1httpCopyPeerCredentials: chain=%p", chain); |
1493 | |
|
1494 | 0 | if (chain) |
1495 | 0 | { |
1496 | | // Loop through the certificates, adding them to the string... |
1497 | 0 | int i, // Looping var |
1498 | 0 | count; // Number of certs |
1499 | |
|
1500 | 0 | for (i = 0, count = sk_X509_num(chain); i < count; i ++) |
1501 | 0 | { |
1502 | 0 | X509 *cert = sk_X509_value(chain, i); |
1503 | | // Current certificate |
1504 | 0 | BIO *bio = BIO_new(BIO_s_mem()); |
1505 | | // Memory buffer for cert |
1506 | |
|
1507 | 0 | DEBUG_printf("1httpCopyPeerCredentials: chain[%d/%d]=%p", i + 1, count, cert); |
1508 | |
|
1509 | | #ifdef DEBUG |
1510 | | char subjectName[256], issuerName[256]; |
1511 | | X509_NAME_get_text_by_NID(X509_get_subject_name(cert), NID_commonName, subjectName, sizeof(subjectName)); |
1512 | | X509_NAME_get_text_by_NID(X509_get_issuer_name(cert), NID_commonName, issuerName, sizeof(issuerName)); |
1513 | | DEBUG_printf("1httpCopyPeerCredentials: subjectName=\"%s\", issuerName=\"%s\"", subjectName, issuerName); |
1514 | | |
1515 | | STACK_OF(GENERAL_NAME) *names; // subjectAltName values |
1516 | | names = X509_get_ext_d2i(cert, NID_subject_alt_name, /*crit*/NULL, /*idx*/NULL); |
1517 | | DEBUG_printf("1httpCopyPeerCredentials: subjectAltNames=%p(%d)", names, names ? sk_GENERAL_NAME_num(names) : 0); |
1518 | | if (names) |
1519 | | GENERAL_NAMES_free(names); |
1520 | | #endif // DEBUG |
1521 | |
|
1522 | 0 | if (bio) |
1523 | 0 | { |
1524 | 0 | long bytes; // Number of bytes |
1525 | 0 | char *buffer; // Pointer to bytes |
1526 | |
|
1527 | 0 | if (PEM_write_bio_X509(bio, cert)) |
1528 | 0 | { |
1529 | 0 | if ((bytes = BIO_get_mem_data(bio, &buffer)) > 0) |
1530 | 0 | { |
1531 | | // Expand credentials string... |
1532 | 0 | if ((credentials = realloc(credentials, alloc_creds + (size_t)bytes + 1)) != NULL) |
1533 | 0 | { |
1534 | | // Copy PEM-encoded data... |
1535 | 0 | memcpy(credentials + alloc_creds, buffer, bytes); |
1536 | 0 | credentials[alloc_creds + (size_t)bytes] = '\0'; |
1537 | 0 | alloc_creds += (size_t)bytes; |
1538 | 0 | } |
1539 | 0 | } |
1540 | 0 | } |
1541 | |
|
1542 | 0 | BIO_free(bio); |
1543 | |
|
1544 | 0 | if (!credentials) |
1545 | 0 | break; |
1546 | 0 | } |
1547 | 0 | } |
1548 | 0 | } |
1549 | 0 | } |
1550 | |
|
1551 | 0 | DEBUG_printf("1httpCopyPeerCredentials: Returning \"%s\".", credentials); |
1552 | |
|
1553 | 0 | return (credentials); |
1554 | 0 | } |
1555 | | |
1556 | | |
1557 | | // |
1558 | | // '_httpCreateCredentials()' - Create credentials in the internal format. |
1559 | | // |
1560 | | |
1561 | | _http_tls_credentials_t * // O - Internal credentials |
1562 | | _httpCreateCredentials( |
1563 | | const char *credentials, // I - Credentials string |
1564 | | const char *key) // I - Private key string |
1565 | 0 | { |
1566 | 0 | _http_tls_credentials_t *hcreds; // Credentials |
1567 | | |
1568 | |
|
1569 | 0 | DEBUG_printf("_httpCreateCredentials(credentials=\"%s\", key=\"%s\")", credentials, key); |
1570 | |
|
1571 | 0 | if (!credentials || !*credentials || !key || !*key) |
1572 | 0 | return (NULL); |
1573 | | |
1574 | 0 | if ((hcreds = calloc(1, sizeof(_http_tls_credentials_t))) == NULL) |
1575 | 0 | return (NULL); |
1576 | | |
1577 | 0 | hcreds->use = 1; |
1578 | | |
1579 | | // Load the certificates... |
1580 | 0 | if ((hcreds->certs = openssl_load_x509(credentials)) == NULL) |
1581 | 0 | { |
1582 | 0 | _httpFreeCredentials(hcreds); |
1583 | 0 | hcreds = NULL; |
1584 | 0 | } |
1585 | 0 | else |
1586 | 0 | { |
1587 | | // Load the private key... |
1588 | 0 | BIO *bio; // Basic I/O for string |
1589 | |
|
1590 | 0 | if ((bio = BIO_new_mem_buf(key, strlen(key))) == NULL) |
1591 | 0 | { |
1592 | 0 | _httpFreeCredentials(hcreds); |
1593 | 0 | hcreds = NULL; |
1594 | 0 | } |
1595 | |
|
1596 | 0 | if (!PEM_read_bio_PrivateKey(bio, &hcreds->key, NULL, NULL)) |
1597 | 0 | { |
1598 | 0 | _httpFreeCredentials(hcreds); |
1599 | 0 | hcreds = NULL; |
1600 | 0 | } |
1601 | 0 | } |
1602 | |
|
1603 | 0 | DEBUG_printf("1_httpCreateCredentials: Returning %p.", hcreds); |
1604 | |
|
1605 | 0 | return (hcreds); |
1606 | 0 | } |
1607 | | |
1608 | | |
1609 | | // |
1610 | | // '_httpFreeCredentials()' - Free internal credentials. |
1611 | | // |
1612 | | |
1613 | | void |
1614 | | _httpFreeCredentials( |
1615 | | _http_tls_credentials_t *hcreds) // I - Internal credentials |
1616 | 0 | { |
1617 | 0 | if (!hcreds) |
1618 | 0 | return; |
1619 | | |
1620 | 0 | if (hcreds->use) |
1621 | 0 | hcreds->use --; |
1622 | |
|
1623 | 0 | if (hcreds->use) |
1624 | 0 | return; |
1625 | | |
1626 | 0 | sk_X509_free(hcreds->certs); |
1627 | 0 | free(hcreds); |
1628 | 0 | } |
1629 | | |
1630 | | |
1631 | | // |
1632 | | // 'httpGetSecurity()' - Get the TLS version and cipher suite used by a connection. |
1633 | | // |
1634 | | // This function gets the TLS version and cipher suite being used by a |
1635 | | // connection, if any. The string is copied to "buffer" and is of the form |
1636 | | // "TLS/major.minor CipherSuite". If not encrypted, the buffer is cleared to |
1637 | | // the empty string. |
1638 | | // |
1639 | | |
1640 | | const char * // O - Security information or `NULL` if not encrypted |
1641 | | httpGetSecurity(http_t *http, // I - HTTP connection |
1642 | | char *buffer, // I - String buffer |
1643 | | size_t bufsize) // I - Size of buffer |
1644 | 0 | { |
1645 | 0 | const char *cipherName; // Cipher suite name |
1646 | | |
1647 | | |
1648 | | // Range check input... |
1649 | 0 | if (buffer) |
1650 | 0 | *buffer = '\0'; |
1651 | |
|
1652 | 0 | if (!http || !http->tls || !buffer || bufsize < 16) |
1653 | 0 | return (NULL); |
1654 | | |
1655 | | // Record the TLS version and cipher suite... |
1656 | 0 | cipherName = SSL_get_cipher_name(http->tls); |
1657 | |
|
1658 | 0 | switch (SSL_version(http->tls)) |
1659 | 0 | { |
1660 | 0 | default : |
1661 | 0 | snprintf(buffer, bufsize, "TLS/?.? %s", cipherName); |
1662 | 0 | break; |
1663 | | |
1664 | 0 | case TLS1_VERSION : |
1665 | 0 | snprintf(buffer, bufsize, "TLS/1.0 %s", cipherName); |
1666 | 0 | break; |
1667 | | |
1668 | 0 | case TLS1_1_VERSION : |
1669 | 0 | snprintf(buffer, bufsize, "TLS/1.1 %s", cipherName); |
1670 | 0 | break; |
1671 | | |
1672 | 0 | case TLS1_2_VERSION : |
1673 | 0 | snprintf(buffer, bufsize, "TLS/1.2 %s", cipherName); |
1674 | 0 | break; |
1675 | | |
1676 | 0 | # ifdef TLS1_3_VERSION |
1677 | 0 | case TLS1_3_VERSION : |
1678 | 0 | snprintf(buffer, bufsize, "TLS/1.3 %s", cipherName); |
1679 | 0 | break; |
1680 | 0 | # endif // TLS1_3_VERSION |
1681 | 0 | } |
1682 | | |
1683 | 0 | return (buffer); |
1684 | 0 | } |
1685 | | |
1686 | | |
1687 | | // |
1688 | | // '_httpTLSInitialize()' - Initialize the TLS stack. |
1689 | | // |
1690 | | |
1691 | | void |
1692 | | _httpTLSInitialize(void) |
1693 | 0 | { |
1694 | | // OpenSSL no longer requires explicit initialization... |
1695 | 0 | } |
1696 | | |
1697 | | |
1698 | | // |
1699 | | // '_httpTLSPending()' - Return the number of pending TLS-encrypted bytes. |
1700 | | // |
1701 | | |
1702 | | size_t // O - Bytes available |
1703 | | _httpTLSPending(http_t *http) // I - HTTP connection |
1704 | 0 | { |
1705 | 0 | return ((size_t)SSL_pending(http->tls)); |
1706 | 0 | } |
1707 | | |
1708 | | |
1709 | | // |
1710 | | // '_httpTLSRead()' - Read from a SSL/TLS connection. |
1711 | | // |
1712 | | |
1713 | | int // O - Bytes read |
1714 | | _httpTLSRead(http_t *http, // I - Connection to server |
1715 | | char *buf, // I - Buffer to store data |
1716 | | int len) // I - Length of buffer |
1717 | 0 | { |
1718 | 0 | int bytes = SSL_read((SSL *)(http->tls), buf, len); |
1719 | | // Bytes read |
1720 | |
|
1721 | 0 | DEBUG_printf("7_httpTLSRead(http=%p, buf=%p, len=%d) returning %d", (void *)http, (void *)buf, len, bytes); |
1722 | |
|
1723 | 0 | return (bytes); |
1724 | 0 | } |
1725 | | |
1726 | | |
1727 | | // |
1728 | | // '_httpTLSStart()' - Set up SSL/TLS support on a connection. |
1729 | | // |
1730 | | |
1731 | | bool // O - `true` on success, `false` on failure |
1732 | | _httpTLSStart(http_t *http) // I - Connection to server |
1733 | 0 | { |
1734 | 0 | const char *keypath; // Certificate store path |
1735 | 0 | BIO *bio; // Basic input/output context |
1736 | 0 | SSL_CTX *context; // Encryption context |
1737 | 0 | char hostname[256], // Hostname |
1738 | 0 | cipherlist[256]; // List of cipher suites |
1739 | 0 | unsigned long error; // Error code, if any |
1740 | 0 | _cups_globals_t *cg = _cupsGlobals(); // Per-thread globals |
1741 | 0 | static const uint16_t versions[] = // SSL/TLS versions |
1742 | 0 | { |
1743 | 0 | TLS1_VERSION, // No more SSL support in OpenSSL |
1744 | 0 | TLS1_VERSION, // TLS/1.0 |
1745 | 0 | TLS1_1_VERSION, // TLS/1.1 |
1746 | 0 | TLS1_2_VERSION, // TLS/1.2 |
1747 | 0 | #ifdef TLS1_3_VERSION |
1748 | 0 | TLS1_3_VERSION, // TLS/1.3 |
1749 | | TLS1_3_VERSION // TLS/1.3 (max) |
1750 | | #else |
1751 | | TLS1_2_VERSION, // TLS/1.2 |
1752 | | TLS1_2_VERSION // TLS/1.2 (max) |
1753 | | #endif // TLS1_3_VERSION |
1754 | 0 | }; |
1755 | | |
1756 | |
|
1757 | 0 | DEBUG_printf("3_httpTLSStart(http=%p)", http); |
1758 | |
|
1759 | 0 | if (!cg->client_conf_loaded) |
1760 | 0 | { |
1761 | 0 | DEBUG_puts("4_httpTLSStart: Setting defaults."); |
1762 | 0 | _cupsSetDefaults(); |
1763 | 0 | DEBUG_printf("4_httpTLSStart: tls_options=%x", tls_options); |
1764 | 0 | } |
1765 | |
|
1766 | 0 | cupsMutexLock(&tls_mutex); |
1767 | 0 | keypath = tls_keypath; |
1768 | 0 | cupsMutexUnlock(&tls_mutex); |
1769 | |
|
1770 | 0 | if (http->mode == _HTTP_MODE_SERVER && !keypath) |
1771 | 0 | { |
1772 | 0 | DEBUG_puts("4_httpTLSStart: cupsSetServerCredentials not called."); |
1773 | 0 | http->error = errno = EINVAL; |
1774 | 0 | http->status = HTTP_STATUS_ERROR; |
1775 | 0 | _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Server credentials not set."), true); |
1776 | |
|
1777 | 0 | return (false); |
1778 | 0 | } |
1779 | | |
1780 | 0 | if (http->mode == _HTTP_MODE_CLIENT) |
1781 | 0 | { |
1782 | | // Negotiate a TLS connection as a client... |
1783 | 0 | context = SSL_CTX_new(TLS_client_method()); |
1784 | 0 | if (http->tls_credentials) |
1785 | 0 | { |
1786 | 0 | int i, // Looping var |
1787 | 0 | count; // Number of certificates |
1788 | |
|
1789 | 0 | DEBUG_puts("4_httpTLSStart: Using client certificate."); |
1790 | 0 | SSL_CTX_use_certificate(context, sk_X509_value(http->tls_credentials->certs, 0)); |
1791 | 0 | SSL_CTX_use_PrivateKey(context, http->tls_credentials->key); |
1792 | |
|
1793 | 0 | count = sk_X509_num(http->tls_credentials->certs); |
1794 | 0 | for (i = 1; i < count; i ++) |
1795 | 0 | SSL_CTX_add_extra_chain_cert(context, sk_X509_value(http->tls_credentials->certs, i)); |
1796 | 0 | } |
1797 | 0 | } |
1798 | 0 | else |
1799 | 0 | { |
1800 | | // Negotiate a TLS connection as a server |
1801 | 0 | char crtfile[1024], // Certificate file |
1802 | 0 | keyfile[1024]; // Private key file |
1803 | 0 | const char *cn = NULL, // Common name to lookup |
1804 | 0 | *cnptr; // Pointer into common name |
1805 | 0 | bool have_creds = false; // Have credentials? |
1806 | |
|
1807 | 0 | context = SSL_CTX_new(TLS_server_method()); |
1808 | | |
1809 | | // Find the TLS certificate... |
1810 | 0 | cupsMutexLock(&tls_mutex); |
1811 | |
|
1812 | 0 | if (!tls_common_name) |
1813 | 0 | { |
1814 | 0 | cupsMutexUnlock(&tls_mutex); |
1815 | |
|
1816 | 0 | if (http->fields[HTTP_FIELD_HOST]) |
1817 | 0 | { |
1818 | | // Use hostname for TLS upgrade... |
1819 | 0 | cupsCopyString(hostname, http->fields[HTTP_FIELD_HOST], sizeof(hostname)); |
1820 | 0 | } |
1821 | 0 | else |
1822 | 0 | { |
1823 | | // Resolve hostname from connection address... |
1824 | 0 | http_addr_t addr; // Connection address |
1825 | 0 | socklen_t addrlen; // Length of address |
1826 | |
|
1827 | 0 | addrlen = sizeof(addr); |
1828 | 0 | if (getsockname(http->fd, (struct sockaddr *)&addr, &addrlen)) |
1829 | 0 | { |
1830 | | // Unable to get local socket address so use default... |
1831 | 0 | DEBUG_printf("4_httpTLSStart: Unable to get socket address: %s", strerror(errno)); |
1832 | 0 | hostname[0] = '\0'; |
1833 | 0 | } |
1834 | 0 | else if (httpAddrIsLocalhost(&addr)) |
1835 | 0 | { |
1836 | | // Local access top use default... |
1837 | 0 | hostname[0] = '\0'; |
1838 | 0 | } |
1839 | 0 | else |
1840 | 0 | { |
1841 | | // Lookup the socket address... |
1842 | 0 | httpAddrLookup(&addr, hostname, sizeof(hostname)); |
1843 | 0 | DEBUG_printf("4_httpTLSStart: Resolved socket address to \"%s\".", hostname); |
1844 | 0 | } |
1845 | 0 | } |
1846 | |
|
1847 | 0 | if (isdigit(hostname[0] & 255) || hostname[0] == '[') |
1848 | 0 | hostname[0] = '\0'; // Don't allow numeric addresses |
1849 | |
|
1850 | 0 | if (hostname[0]) |
1851 | 0 | cn = hostname; |
1852 | |
|
1853 | 0 | cupsMutexLock(&tls_mutex); |
1854 | 0 | } |
1855 | |
|
1856 | 0 | if (!cn) |
1857 | 0 | cn = tls_common_name; |
1858 | |
|
1859 | 0 | DEBUG_printf("4_httpTLSStart: Using common name \"%s\"...", cn); |
1860 | |
|
1861 | 0 | if (cn) |
1862 | 0 | { |
1863 | | // First look in the CUPS keystore... |
1864 | 0 | http_make_path(crtfile, sizeof(crtfile), tls_keypath, cn, "crt"); |
1865 | 0 | http_make_path(keyfile, sizeof(keyfile), tls_keypath, cn, "key"); |
1866 | |
|
1867 | 0 | if (access(crtfile, R_OK) || access(keyfile, R_OK)) |
1868 | 0 | { |
1869 | | // No CUPS-managed certs, look for CA certs... |
1870 | 0 | char cacrtfile[1024], cakeyfile[1024]; // CA cert files |
1871 | |
|
1872 | 0 | snprintf(cacrtfile, sizeof(cacrtfile), "/etc/letsencrypt/live/%s/fullchain.pem", cn); |
1873 | 0 | snprintf(cakeyfile, sizeof(cakeyfile), "/etc/letsencrypt/live/%s/privkey.pem", cn); |
1874 | |
|
1875 | 0 | if ((access(cacrtfile, R_OK) || access(cakeyfile, R_OK)) && (cnptr = strchr(cn, '.')) != NULL) |
1876 | 0 | { |
1877 | | // Try just domain name... |
1878 | 0 | cnptr ++; |
1879 | 0 | if (strchr(cnptr, '.')) |
1880 | 0 | { |
1881 | 0 | snprintf(cacrtfile, sizeof(cacrtfile), "/etc/letsencrypt/live/%s/fullchain.pem", cnptr); |
1882 | 0 | snprintf(cakeyfile, sizeof(cakeyfile), "/etc/letsencrypt/live/%s/privkey.pem", cnptr); |
1883 | 0 | } |
1884 | 0 | } |
1885 | |
|
1886 | 0 | if (!access(cacrtfile, R_OK) && !access(cakeyfile, R_OK)) |
1887 | 0 | { |
1888 | | // Use the CA certs... |
1889 | 0 | cupsCopyString(crtfile, cacrtfile, sizeof(crtfile)); |
1890 | 0 | cupsCopyString(keyfile, cakeyfile, sizeof(keyfile)); |
1891 | 0 | } |
1892 | 0 | } |
1893 | |
|
1894 | 0 | have_creds = !access(crtfile, R_OK) && !access(keyfile, R_OK); |
1895 | 0 | } |
1896 | |
|
1897 | 0 | if (!have_creds && tls_auto_create && cn) |
1898 | 0 | { |
1899 | 0 | DEBUG_printf("4_httpTLSStart: Auto-create credentials for \"%s\".", cn); |
1900 | |
|
1901 | 0 | if (!cupsCreateCredentials(tls_keypath, false, CUPS_CREDPURPOSE_SERVER_AUTH, CUPS_CREDTYPE_DEFAULT, CUPS_CREDUSAGE_DEFAULT_TLS, NULL, NULL, NULL, NULL, NULL, cn, NULL, 0, NULL, NULL, time(NULL) + 3650 * 86400)) |
1902 | 0 | { |
1903 | 0 | DEBUG_printf("4_httpTLSStart: cupsCreateCredentials failed: %s", cupsGetErrorString()); |
1904 | 0 | http->error = errno = EINVAL; |
1905 | 0 | http->status = HTTP_STATUS_ERROR; |
1906 | 0 | SSL_CTX_free(context); |
1907 | 0 | cupsMutexUnlock(&tls_mutex); |
1908 | |
|
1909 | 0 | return (false); |
1910 | 0 | } |
1911 | 0 | } |
1912 | | |
1913 | 0 | cupsMutexUnlock(&tls_mutex); |
1914 | |
|
1915 | 0 | DEBUG_printf("4_httpTLSStart: Using private key file '%s'.", keyfile); |
1916 | 0 | DEBUG_printf("4_httpTLSStart: Using certificate file '%s'.", crtfile); |
1917 | |
|
1918 | 0 | if (!SSL_CTX_use_PrivateKey_file(context, keyfile, SSL_FILETYPE_PEM) || !SSL_CTX_use_certificate_chain_file(context, crtfile)) |
1919 | 0 | { |
1920 | | // Unable to load private key or certificate... |
1921 | 0 | DEBUG_puts("4_httpTLSStart: Unable to use private key or certificate chain file."); |
1922 | 0 | if ((error = ERR_get_error()) != 0) |
1923 | 0 | _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI, ERR_error_string(error, NULL), 0); |
1924 | |
|
1925 | 0 | http->status = HTTP_STATUS_ERROR; |
1926 | 0 | http->error = EIO; |
1927 | |
|
1928 | 0 | SSL_CTX_free(context); |
1929 | |
|
1930 | 0 | return (false); |
1931 | 0 | } |
1932 | 0 | } |
1933 | | |
1934 | | // Set TLS options... |
1935 | 0 | cupsCopyString(cipherlist, "HIGH:!DH:+DHE", sizeof(cipherlist)); |
1936 | 0 | if ((tls_options & _HTTP_TLS_ALLOW_RC4) && http->mode == _HTTP_MODE_CLIENT) |
1937 | 0 | cupsConcatString(cipherlist, ":+RC4", sizeof(cipherlist)); |
1938 | 0 | else |
1939 | 0 | cupsConcatString(cipherlist, ":!RC4", sizeof(cipherlist)); |
1940 | 0 | if (tls_options & _HTTP_TLS_DENY_CBC) |
1941 | 0 | cupsConcatString(cipherlist, ":!SHA1:!SHA256:!SHA384", sizeof(cipherlist)); |
1942 | 0 | cupsConcatString(cipherlist, ":@STRENGTH", sizeof(cipherlist)); |
1943 | |
|
1944 | 0 | DEBUG_printf("4_httpTLSStart: cipherlist='%s', tls_min_version=%d, tls_max_version=%d", cipherlist, tls_min_version, tls_max_version); |
1945 | |
|
1946 | 0 | SSL_CTX_set_min_proto_version(context, versions[tls_min_version]); |
1947 | 0 | SSL_CTX_set_max_proto_version(context, versions[tls_max_version]); |
1948 | 0 | SSL_CTX_set_cipher_list(context, cipherlist); |
1949 | | |
1950 | | // Setup a TLS session |
1951 | 0 | cupsMutexLock(&tls_mutex); |
1952 | 0 | if (!tls_bio_method) |
1953 | 0 | { |
1954 | 0 | tls_bio_method = BIO_meth_new(BIO_get_new_index(), "http"); |
1955 | 0 | BIO_meth_set_ctrl(tls_bio_method, http_bio_ctrl); |
1956 | 0 | BIO_meth_set_create(tls_bio_method, http_bio_new); |
1957 | 0 | BIO_meth_set_destroy(tls_bio_method, http_bio_free); |
1958 | 0 | BIO_meth_set_read(tls_bio_method, http_bio_read); |
1959 | 0 | BIO_meth_set_puts(tls_bio_method, http_bio_puts); |
1960 | 0 | BIO_meth_set_write(tls_bio_method, http_bio_write); |
1961 | 0 | } |
1962 | |
|
1963 | 0 | bio = BIO_new(tls_bio_method); |
1964 | 0 | cupsMutexUnlock(&tls_mutex); |
1965 | |
|
1966 | 0 | BIO_ctrl(bio, BIO_C_SET_FILE_PTR, 0, (char *)http); |
1967 | |
|
1968 | 0 | http->tls = SSL_new(context); |
1969 | 0 | SSL_set_bio(http->tls, bio, bio); |
1970 | |
|
1971 | 0 | if (http->mode == _HTTP_MODE_CLIENT) |
1972 | 0 | { |
1973 | | // Negotiate as a client... |
1974 | 0 | DEBUG_printf("4_httpTLSStart: Setting server name TLS extension to '%s'...", http->hostname); |
1975 | 0 | SSL_set_tlsext_host_name(http->tls, http->hostname); |
1976 | |
|
1977 | 0 | DEBUG_puts("4_httpTLSStart: Calling SSL_connect..."); |
1978 | 0 | if (SSL_connect(http->tls) < 1) |
1979 | 0 | { |
1980 | | // Failed |
1981 | 0 | if ((error = ERR_get_error()) != 0) |
1982 | 0 | _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI, ERR_error_string(error, NULL), 0); |
1983 | |
|
1984 | 0 | http->status = HTTP_STATUS_ERROR; |
1985 | 0 | http->error = EPIPE; |
1986 | |
|
1987 | 0 | SSL_CTX_free(context); |
1988 | |
|
1989 | 0 | SSL_free(http->tls); |
1990 | 0 | http->tls = NULL; |
1991 | |
|
1992 | 0 | DEBUG_printf("4_httpTLSStart: Returning false (%s)", ERR_error_string(error, NULL)); |
1993 | |
|
1994 | 0 | return (false); |
1995 | 0 | } |
1996 | 0 | } |
1997 | 0 | else |
1998 | 0 | { |
1999 | | // Negotiate as a server... |
2000 | 0 | DEBUG_puts("4_httpTLSStart: Calling SSL_accept..."); |
2001 | 0 | if (SSL_accept(http->tls) < 1) |
2002 | 0 | { |
2003 | | // Failed |
2004 | 0 | if ((error = ERR_get_error()) != 0) |
2005 | 0 | _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI, ERR_error_string(error, NULL), 0); |
2006 | |
|
2007 | 0 | http->status = HTTP_STATUS_ERROR; |
2008 | 0 | http->error = EPIPE; |
2009 | |
|
2010 | 0 | SSL_CTX_free(context); |
2011 | |
|
2012 | 0 | SSL_free(http->tls); |
2013 | 0 | http->tls = NULL; |
2014 | |
|
2015 | 0 | DEBUG_printf("4_httpTLSStart: Returning false (%s)", ERR_error_string(error, NULL)); |
2016 | |
|
2017 | 0 | return (false); |
2018 | 0 | } |
2019 | 0 | } |
2020 | | |
2021 | 0 | DEBUG_puts("4_httpTLSStart: Returning true."); |
2022 | |
|
2023 | 0 | return (true); |
2024 | 0 | } |
2025 | | |
2026 | | |
2027 | | // |
2028 | | // '_httpTLSStop()' - Shut down SSL/TLS on a connection. |
2029 | | // |
2030 | | |
2031 | | void |
2032 | | _httpTLSStop(http_t *http) // I - Connection to server |
2033 | 0 | { |
2034 | 0 | SSL_CTX *context; // Context for encryption |
2035 | | |
2036 | |
|
2037 | 0 | context = SSL_get_SSL_CTX(http->tls); |
2038 | |
|
2039 | 0 | SSL_shutdown(http->tls); |
2040 | 0 | SSL_CTX_free(context); |
2041 | 0 | SSL_free(http->tls); |
2042 | |
|
2043 | 0 | http->tls = NULL; |
2044 | 0 | } |
2045 | | |
2046 | | |
2047 | | // |
2048 | | // '_httpTLSWrite()' - Write to a SSL/TLS connection. |
2049 | | // |
2050 | | |
2051 | | int // O - Bytes written |
2052 | | _httpTLSWrite(http_t *http, // I - Connection to server |
2053 | | const char *buf, // I - Buffer holding data |
2054 | | int len) // I - Length of buffer |
2055 | 0 | { |
2056 | 0 | return (SSL_write(http->tls, buf, len)); |
2057 | 0 | } |
2058 | | |
2059 | | |
2060 | | // |
2061 | | // '_httpUseCredentials()' - Increment the use count for internal credentials. |
2062 | | // |
2063 | | |
2064 | | _http_tls_credentials_t * // O - Internal credentials |
2065 | | _httpUseCredentials( |
2066 | | _http_tls_credentials_t *hcreds) // I - Internal credentials |
2067 | 0 | { |
2068 | 0 | if (hcreds) |
2069 | 0 | hcreds->use ++; |
2070 | |
|
2071 | 0 | return (hcreds); |
2072 | 0 | } |
2073 | | |
2074 | | |
2075 | | // |
2076 | | // 'http_bio_ctrl()' - Control the HTTP connection. |
2077 | | // |
2078 | | |
2079 | | static long // O - Result/data |
2080 | | http_bio_ctrl(BIO *h, // I - BIO data |
2081 | | int cmd, // I - Control command |
2082 | | long arg1, // I - First argument |
2083 | | void *arg2) // I - Second argument |
2084 | 0 | { |
2085 | 0 | DEBUG_printf("8http_bio_ctl(h=%p, cmd=%d, arg1=%ld, arg2=%p)", (void *)h, cmd, arg1, arg2); |
2086 | |
|
2087 | 0 | (void)arg1; |
2088 | |
|
2089 | 0 | switch (cmd) |
2090 | 0 | { |
2091 | 0 | default : |
2092 | 0 | return (0); |
2093 | | |
2094 | 0 | case BIO_CTRL_RESET : |
2095 | 0 | BIO_set_data(h, NULL); |
2096 | 0 | return (0); |
2097 | | |
2098 | 0 | case BIO_C_SET_FILE_PTR : |
2099 | 0 | BIO_set_data(h, arg2); |
2100 | 0 | BIO_set_init(h, 1); |
2101 | 0 | return (1); |
2102 | | |
2103 | 0 | case BIO_C_GET_FILE_PTR : |
2104 | 0 | if (arg2) |
2105 | 0 | { |
2106 | 0 | *((void **)arg2) = BIO_get_data(h); |
2107 | 0 | return (1); |
2108 | 0 | } |
2109 | 0 | else |
2110 | 0 | return (0); |
2111 | | |
2112 | 0 | case BIO_CTRL_DUP : |
2113 | 0 | case BIO_CTRL_FLUSH : |
2114 | 0 | return (1); |
2115 | 0 | } |
2116 | 0 | } |
2117 | | |
2118 | | |
2119 | | // |
2120 | | // 'http_bio_free()' - Free OpenSSL data. |
2121 | | // |
2122 | | |
2123 | | static int // O - 1 on success, 0 on failure |
2124 | | http_bio_free(BIO *h) // I - BIO data |
2125 | 0 | { |
2126 | 0 | DEBUG_printf("8http_bio_free(h=%p)", (void *)h); |
2127 | |
|
2128 | 0 | if (!h) |
2129 | 0 | return (0); |
2130 | | |
2131 | 0 | if (BIO_get_shutdown(h)) |
2132 | 0 | BIO_set_init(h, 0); |
2133 | |
|
2134 | 0 | return (1); |
2135 | 0 | } |
2136 | | |
2137 | | |
2138 | | // |
2139 | | // 'http_bio_new()' - Initialize an OpenSSL BIO structure. |
2140 | | // |
2141 | | |
2142 | | static int // O - 1 on success, 0 on failure |
2143 | | http_bio_new(BIO *h) // I - BIO data |
2144 | 0 | { |
2145 | 0 | DEBUG_printf("8http_bio_new(h=%p)", (void *)h); |
2146 | |
|
2147 | 0 | if (!h) |
2148 | 0 | return (0); |
2149 | | |
2150 | 0 | BIO_set_init(h, 0); |
2151 | 0 | BIO_set_data(h, NULL); |
2152 | |
|
2153 | 0 | return (1); |
2154 | 0 | } |
2155 | | |
2156 | | |
2157 | | // |
2158 | | // 'http_bio_puts()' - Send a string for OpenSSL. |
2159 | | // |
2160 | | |
2161 | | static int // O - Bytes written |
2162 | | http_bio_puts(BIO *h, // I - BIO data |
2163 | | const char *str) // I - String to write |
2164 | 0 | { |
2165 | 0 | DEBUG_printf("8http_bio_puts(h=%p, str=\"%s\")", (void *)h, str); |
2166 | |
|
2167 | | #ifdef WIN32 |
2168 | | return (send(((http_t *)BIO_get_data(h))->fd, str, (int)strlen(str), 0)); |
2169 | | #else |
2170 | 0 | return ((int)send(((http_t *)BIO_get_data(h))->fd, str, strlen(str), 0)); |
2171 | 0 | #endif // WIN32 |
2172 | 0 | } |
2173 | | |
2174 | | |
2175 | | // |
2176 | | // 'http_bio_read()' - Read data for OpenSSL. |
2177 | | // |
2178 | | |
2179 | | static int // O - Bytes read |
2180 | | http_bio_read(BIO *h, // I - BIO data |
2181 | | char *buf, // I - Buffer |
2182 | | int size) // I - Number of bytes to read |
2183 | 0 | { |
2184 | 0 | http_t *http; // HTTP connection |
2185 | 0 | int bytes; // Bytes read |
2186 | | |
2187 | |
|
2188 | 0 | DEBUG_printf("8http_bio_read(h=%p, buf=%p, size=%d)", (void *)h, (void *)buf, size); |
2189 | |
|
2190 | 0 | http = (http_t *)BIO_get_data(h); |
2191 | 0 | DEBUG_printf("9http_bio_read: http=%p", (void *)http); |
2192 | |
|
2193 | 0 | if (!http->blocking || http->timeout_value > 0.0) |
2194 | 0 | { |
2195 | | // Make sure we have data before we read... |
2196 | 0 | while (!_httpWait(http, http->wait_value, false)) |
2197 | 0 | { |
2198 | 0 | if (http->timeout_cb && (*http->timeout_cb)(http, http->timeout_data)) |
2199 | 0 | continue; |
2200 | | |
2201 | | #ifdef WIN32 |
2202 | | http->error = WSAETIMEDOUT; |
2203 | | #else |
2204 | 0 | http->error = ETIMEDOUT; |
2205 | 0 | #endif // WIN32 |
2206 | |
|
2207 | 0 | DEBUG_puts("9http_bio_read: Timeout, returning -1."); |
2208 | 0 | return (-1); |
2209 | 0 | } |
2210 | 0 | } |
2211 | | |
2212 | 0 | bytes = (int)recv(http->fd, buf, (size_t)size, 0); |
2213 | 0 | DEBUG_printf("9http_bio_read: Returning %d.", bytes); |
2214 | |
|
2215 | 0 | return (bytes); |
2216 | 0 | } |
2217 | | |
2218 | | |
2219 | | // |
2220 | | // 'http_bio_write()' - Write data for OpenSSL. |
2221 | | // |
2222 | | |
2223 | | static int // O - Bytes written |
2224 | | http_bio_write(BIO *h, // I - BIO data |
2225 | | const char *buf, // I - Buffer to write |
2226 | | int num) // I - Number of bytes to write |
2227 | 0 | { |
2228 | 0 | int bytes; // Bytes written |
2229 | | |
2230 | |
|
2231 | 0 | DEBUG_printf("8http_bio_write(h=%p, buf=%p, num=%d)", (void *)h, (void *)buf, num); |
2232 | |
|
2233 | 0 | bytes = (int)send(((http_t *)BIO_get_data(h))->fd, buf, (size_t)num, 0); |
2234 | |
|
2235 | 0 | DEBUG_printf("9http_bio_write: Returning %d.", bytes); |
2236 | 0 | return (bytes); |
2237 | 0 | } |
2238 | | |
2239 | | |
2240 | | // |
2241 | | // 'openssl_add_ext()' - Add an extension. |
2242 | | // |
2243 | | |
2244 | | static bool // O - `true` on success, `false` on error |
2245 | | openssl_add_ext( |
2246 | | STACK_OF(X509_EXTENSION) *exts, // I - Stack of extensions |
2247 | | int nid, // I - Extension ID |
2248 | | const char *value) // I - Value |
2249 | 0 | { |
2250 | 0 | X509_EXTENSION *ext = NULL; // Extension |
2251 | | |
2252 | |
|
2253 | 0 | DEBUG_printf("3openssl_add_ext(exts=%p, nid=%d, value=\"%s\")", (void *)exts, nid, value); |
2254 | | |
2255 | | // Create and add the extension... |
2256 | 0 | if ((ext = X509V3_EXT_conf_nid(/*conf*/NULL, /*ctx*/NULL, nid, value)) == NULL) |
2257 | 0 | { |
2258 | 0 | DEBUG_puts("4openssl_add_ext: Unable to create extension, returning false."); |
2259 | 0 | return (false); |
2260 | 0 | } |
2261 | | |
2262 | 0 | sk_X509_EXTENSION_push(exts, ext); |
2263 | |
|
2264 | 0 | return (true); |
2265 | 0 | } |
2266 | | |
2267 | | |
2268 | | // |
2269 | | // 'openssl_create_key()' - Create a suitable key pair for a certificate/signing request. |
2270 | | // |
2271 | | |
2272 | | static EVP_PKEY * // O - Key pair |
2273 | | openssl_create_key( |
2274 | | cups_credtype_t type) // I - Type of key |
2275 | 0 | { |
2276 | 0 | EVP_PKEY *pkey; // Key pair |
2277 | 0 | EVP_PKEY_CTX *ctx; // Key generation context |
2278 | 0 | int algid; // Algorithm NID |
2279 | 0 | int bits = 0; // Bits |
2280 | 0 | int curveid = 0; // Curve NID |
2281 | | |
2282 | |
|
2283 | 0 | switch (type) |
2284 | 0 | { |
2285 | 0 | case CUPS_CREDTYPE_ECDSA_P256_SHA256 : |
2286 | 0 | algid = EVP_PKEY_EC; |
2287 | 0 | curveid = NID_secp256k1; |
2288 | 0 | break; |
2289 | | |
2290 | 0 | case CUPS_CREDTYPE_ECDSA_P384_SHA256 : |
2291 | 0 | algid = EVP_PKEY_EC; |
2292 | 0 | curveid = NID_secp384r1; |
2293 | 0 | break; |
2294 | | |
2295 | 0 | case CUPS_CREDTYPE_ECDSA_P521_SHA256 : |
2296 | 0 | algid = EVP_PKEY_EC; |
2297 | 0 | curveid = NID_secp521r1; |
2298 | 0 | break; |
2299 | | |
2300 | 0 | case CUPS_CREDTYPE_RSA_2048_SHA256 : |
2301 | 0 | algid = EVP_PKEY_RSA; |
2302 | 0 | bits = 2048; |
2303 | 0 | break; |
2304 | | |
2305 | 0 | default : |
2306 | 0 | case CUPS_CREDTYPE_RSA_3072_SHA256 : |
2307 | 0 | algid = EVP_PKEY_RSA; |
2308 | 0 | bits = 3072; |
2309 | 0 | break; |
2310 | | |
2311 | 0 | case CUPS_CREDTYPE_RSA_4096_SHA256 : |
2312 | 0 | algid = EVP_PKEY_RSA; |
2313 | 0 | bits = 4096; |
2314 | 0 | break; |
2315 | 0 | } |
2316 | | |
2317 | 0 | pkey = NULL; |
2318 | |
|
2319 | 0 | if ((ctx = EVP_PKEY_CTX_new_id(algid, NULL)) == NULL) |
2320 | 0 | _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to create private key context."), true); |
2321 | 0 | else if (EVP_PKEY_keygen_init(ctx) <= 0) |
2322 | 0 | _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to initialize private key context."), true); |
2323 | 0 | else if (bits && EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, bits) <= 0) |
2324 | 0 | _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to configure private key context."), true); |
2325 | 0 | else if (curveid && EVP_PKEY_CTX_set_ec_paramgen_curve_nid(ctx, curveid) <= 0) |
2326 | 0 | _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to configure private key context."), true); |
2327 | 0 | else if (EVP_PKEY_keygen(ctx, &pkey) <= 0) |
2328 | 0 | _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to create private key."), true); |
2329 | |
|
2330 | 0 | EVP_PKEY_CTX_free(ctx); |
2331 | |
|
2332 | 0 | return (pkey); |
2333 | 0 | } |
2334 | | |
2335 | | |
2336 | | // |
2337 | | // 'openssl_create_name()' - Create an X.509 name value for a certificate/signing request. |
2338 | | // |
2339 | | |
2340 | | static X509_NAME * // O - X.509 name value |
2341 | | openssl_create_name( |
2342 | | const char *organization, // I - Organization or `NULL` to use common name |
2343 | | const char *org_unit, // I - Organizational unit or `NULL` for none |
2344 | | const char *locality, // I - City/town or `NULL` for "Unknown" |
2345 | | const char *state_province, // I - State/province or `NULL` for "Unknown" |
2346 | | const char *country, // I - Country or `NULL` for locale-based default |
2347 | | const char *common_name, // I - Common name |
2348 | | const char *email) // I - Email address or `NULL` for none |
2349 | 0 | { |
2350 | 0 | X509_NAME *name; // Subject/issuer name |
2351 | 0 | cups_lang_t *language; // Default language info |
2352 | 0 | const char *langname; // Language name |
2353 | | |
2354 | |
|
2355 | 0 | language = cupsLangDefault(); |
2356 | 0 | langname = cupsLangGetName(language); |
2357 | 0 | name = X509_NAME_new(); |
2358 | 0 | if (country) |
2359 | 0 | X509_NAME_add_entry_by_txt(name, SN_countryName, MBSTRING_ASC, (unsigned char *)country, -1, -1, 0); |
2360 | 0 | else if (strlen(langname) == 5) |
2361 | 0 | X509_NAME_add_entry_by_txt(name, SN_countryName, MBSTRING_ASC, (unsigned char *)langname + 3, -1, -1, 0); |
2362 | 0 | else |
2363 | 0 | X509_NAME_add_entry_by_txt(name, SN_countryName, MBSTRING_ASC, (unsigned char *)"US", -1, -1, 0); |
2364 | 0 | X509_NAME_add_entry_by_txt(name, SN_commonName, MBSTRING_ASC, (unsigned char *)common_name, -1, -1, 0); |
2365 | 0 | X509_NAME_add_entry_by_txt(name, SN_organizationName, MBSTRING_ASC, (unsigned char *)(organization ? organization : common_name), -1, -1, 0); |
2366 | 0 | X509_NAME_add_entry_by_txt(name, SN_organizationalUnitName, MBSTRING_ASC, (unsigned char *)(org_unit ? org_unit : ""), -1, -1, 0); |
2367 | 0 | X509_NAME_add_entry_by_txt(name, SN_stateOrProvinceName, MBSTRING_ASC, (unsigned char *)(state_province ? state_province : "Unknown"), -1, -1, 0); |
2368 | 0 | X509_NAME_add_entry_by_txt(name, SN_localityName, MBSTRING_ASC, (unsigned char *)(locality ? locality : "Unknown"), -1, -1, 0); |
2369 | 0 | if (email && *email) |
2370 | 0 | X509_NAME_add_entry_by_txt(name, "emailAddress", MBSTRING_ASC, (unsigned char *)email, -1, -1, 0); |
2371 | |
|
2372 | 0 | return (name); |
2373 | 0 | } |
2374 | | |
2375 | | |
2376 | | // |
2377 | | // 'openssl_create_san()' - Create a list of subjectAltName values for a certificate/signing request. |
2378 | | // |
2379 | | |
2380 | | static X509_EXTENSION * // O - Extension |
2381 | | openssl_create_san( |
2382 | | const char *common_name, // I - Common name |
2383 | | size_t num_alt_names, // I - Number of alternate names |
2384 | | const char * const *alt_names) // I - List of alternate names |
2385 | 0 | { |
2386 | 0 | char temp[2048], // Temporary string |
2387 | 0 | *tempptr; // Pointer into temporary string |
2388 | 0 | size_t i; // Looping var |
2389 | | |
2390 | | |
2391 | | // Add the common name |
2392 | 0 | snprintf(temp, sizeof(temp), "DNS:%s", common_name); |
2393 | 0 | tempptr = temp + strlen(temp); |
2394 | |
|
2395 | 0 | if (strstr(common_name, ".local") == NULL) |
2396 | 0 | { |
2397 | | // Add common_name.local to the list, too... |
2398 | 0 | char localname[256], // hostname.local |
2399 | 0 | *localptr; // Pointer into localname |
2400 | |
|
2401 | 0 | cupsCopyString(localname, common_name, sizeof(localname)); |
2402 | 0 | if ((localptr = strchr(localname, '.')) != NULL) |
2403 | 0 | *localptr = '\0'; |
2404 | |
|
2405 | 0 | snprintf(tempptr, sizeof(temp) - (size_t)(tempptr - temp), ",DNS:%s.local", localname); |
2406 | 0 | tempptr += strlen(tempptr); |
2407 | 0 | } |
2408 | | |
2409 | | // Add any alternate names... |
2410 | 0 | for (i = 0; i < num_alt_names; i ++) |
2411 | 0 | { |
2412 | 0 | if (strcmp(alt_names[i], "localhost")) |
2413 | 0 | { |
2414 | 0 | snprintf(tempptr, sizeof(temp) - (size_t)(tempptr - temp), ",DNS:%s", alt_names[i]); |
2415 | 0 | tempptr += strlen(tempptr); |
2416 | 0 | } |
2417 | 0 | } |
2418 | | |
2419 | | // Return the stack |
2420 | 0 | return (X509V3_EXT_conf_nid(/*conf*/NULL, /*ctx*/NULL, NID_subject_alt_name, temp)); |
2421 | 0 | } |
2422 | | |
2423 | | |
2424 | | // |
2425 | | // 'openssl_get_date()' - Get the notBefore or notAfter date of a certificate. |
2426 | | // |
2427 | | |
2428 | | static time_t // O - UNIX time in seconds |
2429 | | openssl_get_date(X509 *cert, // I - Certificate |
2430 | | int which) // I - 0 for notBefore, 1 for notAfter |
2431 | 0 | { |
2432 | 0 | struct tm exptm; // Expiration date components |
2433 | | |
2434 | |
|
2435 | 0 | if (which) |
2436 | 0 | ASN1_TIME_to_tm(X509_get0_notAfter(cert), &exptm); |
2437 | 0 | else |
2438 | 0 | ASN1_TIME_to_tm(X509_get0_notBefore(cert), &exptm); |
2439 | |
|
2440 | 0 | return (mktime(&exptm)); |
2441 | 0 | } |
2442 | | |
2443 | | |
2444 | | #if 0 |
2445 | | // |
2446 | | // 'openssl_load_crl()' - Load the certificate revocation list, if any. |
2447 | | // |
2448 | | |
2449 | | static void |
2450 | | openssl_load_crl(void) |
2451 | | { |
2452 | | cupsMutexLock(&tls_mutex); |
2453 | | |
2454 | | if (!openssl_x509_crl_init(&tls_crl)) |
2455 | | { |
2456 | | cups_file_t *fp; // CRL file |
2457 | | char filename[1024], // site.crl |
2458 | | line[256]; // Base64-encoded line |
2459 | | unsigned char *data = NULL; // Buffer for cert data |
2460 | | size_t alloc_data = 0, // Bytes allocated |
2461 | | num_data = 0; // Bytes used |
2462 | | int decoded; // Bytes decoded |
2463 | | openssl_datum_t datum; // Data record |
2464 | | |
2465 | | |
2466 | | http_make_path(filename, sizeof(filename), CUPS_SERVERROOT, "site", "crl"); |
2467 | | |
2468 | | if ((fp = cupsFileOpen(filename, "r")) != NULL) |
2469 | | { |
2470 | | while (cupsFileGets(fp, line, sizeof(line))) |
2471 | | { |
2472 | | if (!strcmp(line, "-----BEGIN X509 CRL-----")) |
2473 | | { |
2474 | | if (num_data) |
2475 | | { |
2476 | | /* |
2477 | | * Missing END X509 CRL... |
2478 | | */ |
2479 | | |
2480 | | break; |
2481 | | } |
2482 | | } |
2483 | | else if (!strcmp(line, "-----END X509 CRL-----")) |
2484 | | { |
2485 | | if (!num_data) |
2486 | | { |
2487 | | /* |
2488 | | * Missing data... |
2489 | | */ |
2490 | | |
2491 | | break; |
2492 | | } |
2493 | | |
2494 | | datum.data = data; |
2495 | | datum.size = num_data; |
2496 | | |
2497 | | openssl_x509_crl_import(tls_crl, &datum, GNUTLS_X509_FMT_PEM); |
2498 | | |
2499 | | num_data = 0; |
2500 | | } |
2501 | | else |
2502 | | { |
2503 | | if (alloc_data == 0) |
2504 | | { |
2505 | | data = malloc(2048); |
2506 | | alloc_data = 2048; |
2507 | | |
2508 | | if (!data) |
2509 | | break; |
2510 | | } |
2511 | | else if ((num_data + strlen(line)) >= alloc_data) |
2512 | | { |
2513 | | unsigned char *tdata = realloc(data, alloc_data + 1024); |
2514 | | // Expanded buffer |
2515 | | |
2516 | | if (!tdata) |
2517 | | break; |
2518 | | |
2519 | | data = tdata; |
2520 | | alloc_data += 1024; |
2521 | | } |
2522 | | |
2523 | | decoded = alloc_data - num_data; |
2524 | | httpDecode64((char *)data + num_data, &decoded, line, NULL); |
2525 | | num_data += (size_t)decoded; |
2526 | | } |
2527 | | } |
2528 | | |
2529 | | cupsFileClose(fp); |
2530 | | |
2531 | | if (data) |
2532 | | free(data); |
2533 | | } |
2534 | | } |
2535 | | |
2536 | | cupsMutexUnlock(&tls_mutex); |
2537 | | } |
2538 | | #endif // 0 |
2539 | | |
2540 | | |
2541 | | // |
2542 | | // 'openssl_load_x509()' - Load a stack of X.509 certificates. |
2543 | | // |
2544 | | |
2545 | | static STACK_OF(X509) * // O - Stack of X.509 certificates |
2546 | | openssl_load_x509( |
2547 | | const char *credentials) // I - Credentials string |
2548 | 0 | { |
2549 | 0 | STACK_OF(X509) *certs = NULL; // Certificate chain |
2550 | 0 | X509 *cert = NULL; // Current certificate |
2551 | 0 | BIO *bio; // Basic I/O for string |
2552 | | |
2553 | | |
2554 | | // Range check input... |
2555 | 0 | if (!credentials || !*credentials) |
2556 | 0 | return (NULL); |
2557 | | |
2558 | | // Make a BIO memory buffer for the string... |
2559 | 0 | if ((bio = BIO_new_mem_buf(credentials, strlen(credentials))) == NULL) |
2560 | 0 | return (NULL); |
2561 | | |
2562 | | // Read all the X509 certificates from the string... |
2563 | 0 | while (PEM_read_bio_X509(bio, &cert, NULL, (void *)"")) |
2564 | 0 | { |
2565 | 0 | if (!certs) |
2566 | 0 | { |
2567 | | // Make a new stack of X509 certs... |
2568 | 0 | certs = sk_X509_new_null(); |
2569 | 0 | } |
2570 | |
|
2571 | 0 | if (certs) |
2572 | 0 | { |
2573 | | // Add the X509 certificate... |
2574 | 0 | sk_X509_push(certs, cert); |
2575 | 0 | } |
2576 | 0 | else |
2577 | 0 | { |
2578 | | // Unable to add, free and stop... |
2579 | 0 | X509_free(cert); |
2580 | 0 | break; |
2581 | 0 | } |
2582 | | |
2583 | 0 | cert = NULL; |
2584 | 0 | } |
2585 | |
|
2586 | 0 | BIO_free(bio); |
2587 | |
|
2588 | 0 | return (certs); |
2589 | 0 | } |