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