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