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