]> git.ipfire.org Git - thirdparty/cups.git/blob - cups/tls-gnutls.c
License change: Apache License, Version 2.0.
[thirdparty/cups.git] / cups / tls-gnutls.c
1 /*
2 * TLS support code for CUPS using GNU TLS.
3 *
4 * Copyright 2007-2017 by Apple Inc.
5 * Copyright 1997-2007 by Easy Software Products, all rights reserved.
6 *
7 * Licensed under Apache License v2.0. See the file "LICENSE" for more information.
8 */
9
10 /**** This file is included from tls.c ****/
11
12 /*
13 * Include necessary headers...
14 */
15
16 #include <sys/stat.h>
17
18
19 /*
20 * Local globals...
21 */
22
23 static int tls_auto_create = 0;
24 /* Auto-create self-signed certs? */
25 static char *tls_common_name = NULL;
26 /* Default common name */
27 static gnutls_x509_crl_t tls_crl = NULL;/* Certificate revocation list */
28 static char *tls_keypath = NULL;
29 /* Server cert keychain path */
30 static _cups_mutex_t tls_mutex = _CUPS_MUTEX_INITIALIZER;
31 /* Mutex for keychain/certs */
32 static int tls_options = -1,/* Options for TLS connections */
33 tls_min_version = _HTTP_TLS_1_0,
34 tls_max_version = _HTTP_TLS_MAX;
35
36
37 /*
38 * Local functions...
39 */
40
41 static gnutls_x509_crt_t http_gnutls_create_credential(http_credential_t *credential);
42 static const char *http_gnutls_default_path(char *buffer, size_t bufsize);
43 static void http_gnutls_load_crl(void);
44 static const char *http_gnutls_make_path(char *buffer, size_t bufsize, const char *dirname, const char *filename, const char *ext);
45 static ssize_t http_gnutls_read(gnutls_transport_ptr_t ptr, void *data, size_t length);
46 static ssize_t http_gnutls_write(gnutls_transport_ptr_t ptr, const void *data, size_t length);
47
48
49 /*
50 * 'cupsMakeServerCredentials()' - Make a self-signed certificate and private key pair.
51 *
52 * @since CUPS 2.0/OS 10.10@
53 */
54
55 int /* O - 1 on success, 0 on failure */
56 cupsMakeServerCredentials(
57 const char *path, /* I - Path to keychain/directory */
58 const char *common_name, /* I - Common name */
59 int num_alt_names, /* I - Number of subject alternate names */
60 const char **alt_names, /* I - Subject Alternate Names */
61 time_t expiration_date) /* I - Expiration date */
62 {
63 gnutls_x509_crt_t crt; /* Self-signed certificate */
64 gnutls_x509_privkey_t key; /* Encryption private key */
65 char temp[1024], /* Temporary directory name */
66 crtfile[1024], /* Certificate filename */
67 keyfile[1024]; /* Private key filename */
68 cups_lang_t *language; /* Default language info */
69 cups_file_t *fp; /* Key/cert file */
70 unsigned char buffer[8192]; /* Buffer for x509 data */
71 size_t bytes; /* Number of bytes of data */
72 unsigned char serial[4]; /* Serial number buffer */
73 time_t curtime; /* Current time */
74 int result; /* Result of GNU TLS calls */
75
76
77 DEBUG_printf(("cupsMakeServerCredentials(path=\"%s\", common_name=\"%s\", num_alt_names=%d, alt_names=%p, expiration_date=%d)", path, common_name, num_alt_names, alt_names, (int)expiration_date));
78
79 /*
80 * Filenames...
81 */
82
83 if (!path)
84 path = http_gnutls_default_path(temp, sizeof(temp));
85
86 if (!path || !common_name)
87 {
88 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
89 return (0);
90 }
91
92 http_gnutls_make_path(crtfile, sizeof(crtfile), path, common_name, "crt");
93 http_gnutls_make_path(keyfile, sizeof(keyfile), path, common_name, "key");
94
95 /*
96 * Create the encryption key...
97 */
98
99 DEBUG_puts("1cupsMakeServerCredentials: Creating key pair.");
100
101 gnutls_x509_privkey_init(&key);
102 gnutls_x509_privkey_generate(key, GNUTLS_PK_RSA, 2048, 0);
103
104 DEBUG_puts("1cupsMakeServerCredentials: Key pair created.");
105
106 /*
107 * Save it...
108 */
109
110 bytes = sizeof(buffer);
111
112 if ((result = gnutls_x509_privkey_export(key, GNUTLS_X509_FMT_PEM, buffer, &bytes)) < 0)
113 {
114 DEBUG_printf(("1cupsMakeServerCredentials: Unable to export private key: %s", gnutls_strerror(result)));
115 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, gnutls_strerror(result), 0);
116 gnutls_x509_privkey_deinit(key);
117 return (0);
118 }
119 else if ((fp = cupsFileOpen(keyfile, "w")) != NULL)
120 {
121 DEBUG_printf(("1cupsMakeServerCredentials: Writing private key to \"%s\".", keyfile));
122 cupsFileWrite(fp, (char *)buffer, bytes);
123 cupsFileClose(fp);
124 }
125 else
126 {
127 DEBUG_printf(("1cupsMakeServerCredentials: Unable to create private key file \"%s\": %s", keyfile, strerror(errno)));
128 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
129 gnutls_x509_privkey_deinit(key);
130 return (0);
131 }
132
133 /*
134 * Create the self-signed certificate...
135 */
136
137 DEBUG_puts("1cupsMakeServerCredentials: Generating self-signed X.509 certificate.");
138
139 language = cupsLangDefault();
140 curtime = time(NULL);
141 serial[0] = curtime >> 24;
142 serial[1] = curtime >> 16;
143 serial[2] = curtime >> 8;
144 serial[3] = curtime;
145
146 gnutls_x509_crt_init(&crt);
147 if (strlen(language->language) == 5)
148 gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_COUNTRY_NAME, 0,
149 language->language + 3, 2);
150 else
151 gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_COUNTRY_NAME, 0,
152 "US", 2);
153 gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_COMMON_NAME, 0,
154 common_name, strlen(common_name));
155 gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_ORGANIZATION_NAME, 0,
156 common_name, strlen(common_name));
157 gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_ORGANIZATIONAL_UNIT_NAME,
158 0, "Unknown", 7);
159 gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_STATE_OR_PROVINCE_NAME, 0,
160 "Unknown", 7);
161 gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_LOCALITY_NAME, 0,
162 "Unknown", 7);
163 /* gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_PKCS9_EMAIL, 0,
164 ServerAdmin, strlen(ServerAdmin));*/
165 gnutls_x509_crt_set_key(crt, key);
166 gnutls_x509_crt_set_serial(crt, serial, sizeof(serial));
167 gnutls_x509_crt_set_activation_time(crt, curtime);
168 gnutls_x509_crt_set_expiration_time(crt, curtime + 10 * 365 * 86400);
169 gnutls_x509_crt_set_ca_status(crt, 0);
170 if (num_alt_names > 0)
171 gnutls_x509_crt_set_subject_alternative_name(crt, GNUTLS_SAN_DNSNAME, alt_names[0]);
172 gnutls_x509_crt_set_key_purpose_oid(crt, GNUTLS_KP_TLS_WWW_SERVER, 0);
173 gnutls_x509_crt_set_key_usage(crt, GNUTLS_KEY_KEY_ENCIPHERMENT);
174 gnutls_x509_crt_set_version(crt, 3);
175
176 bytes = sizeof(buffer);
177 if (gnutls_x509_crt_get_key_id(crt, 0, buffer, &bytes) >= 0)
178 gnutls_x509_crt_set_subject_key_id(crt, buffer, bytes);
179
180 gnutls_x509_crt_sign(crt, crt, key);
181
182 /*
183 * Save it...
184 */
185
186 bytes = sizeof(buffer);
187 if ((result = gnutls_x509_crt_export(crt, GNUTLS_X509_FMT_PEM, buffer, &bytes)) < 0)
188 {
189 DEBUG_printf(("1cupsMakeServerCredentials: Unable to export public key and X.509 certificate: %s", gnutls_strerror(result)));
190 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, gnutls_strerror(result), 0);
191 gnutls_x509_crt_deinit(crt);
192 gnutls_x509_privkey_deinit(key);
193 return (0);
194 }
195 else if ((fp = cupsFileOpen(crtfile, "w")) != NULL)
196 {
197 DEBUG_printf(("1cupsMakeServerCredentials: Writing public key and X.509 certificate to \"%s\".", crtfile));
198 cupsFileWrite(fp, (char *)buffer, bytes);
199 cupsFileClose(fp);
200 }
201 else
202 {
203 DEBUG_printf(("1cupsMakeServerCredentials: Unable to create public key and X.509 certificate file \"%s\": %s", crtfile, strerror(errno)));
204 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
205 gnutls_x509_crt_deinit(crt);
206 gnutls_x509_privkey_deinit(key);
207 return (0);
208 }
209
210 /*
211 * Cleanup...
212 */
213
214 gnutls_x509_crt_deinit(crt);
215 gnutls_x509_privkey_deinit(key);
216
217 DEBUG_puts("1cupsMakeServerCredentials: Successfully created credentials.");
218
219 return (1);
220 }
221
222
223 /*
224 * 'cupsSetServerCredentials()' - Set the default server credentials.
225 *
226 * Note: The server credentials are used by all threads in the running process.
227 * This function is threadsafe.
228 *
229 * @since CUPS 2.0/OS 10.10@
230 */
231
232 int /* O - 1 on success, 0 on failure */
233 cupsSetServerCredentials(
234 const char *path, /* I - Path to keychain/directory */
235 const char *common_name, /* I - Default common name for server */
236 int auto_create) /* I - 1 = automatically create self-signed certificates */
237 {
238 char temp[1024]; /* Default path buffer */
239
240
241 DEBUG_printf(("cupsSetServerCredentials(path=\"%s\", common_name=\"%s\", auto_create=%d)", path, common_name, auto_create));
242
243 /*
244 * Use defaults as needed...
245 */
246
247 if (!path)
248 path = http_gnutls_default_path(temp, sizeof(temp));
249
250 /*
251 * Range check input...
252 */
253
254 if (!path || !common_name)
255 {
256 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
257 return (0);
258 }
259
260 _cupsMutexLock(&tls_mutex);
261
262 /*
263 * Free old values...
264 */
265
266 if (tls_keypath)
267 _cupsStrFree(tls_keypath);
268
269 if (tls_common_name)
270 _cupsStrFree(tls_common_name);
271
272 /*
273 * Save the new values...
274 */
275
276 tls_keypath = _cupsStrAlloc(path);
277 tls_auto_create = auto_create;
278 tls_common_name = _cupsStrAlloc(common_name);
279
280 _cupsMutexUnlock(&tls_mutex);
281
282 return (1);
283 }
284
285
286 /*
287 * 'httpCopyCredentials()' - Copy the credentials associated with the peer in
288 * an encrypted connection.
289 *
290 * @since CUPS 1.5/macOS 10.7@
291 */
292
293 int /* O - Status of call (0 = success) */
294 httpCopyCredentials(
295 http_t *http, /* I - Connection to server */
296 cups_array_t **credentials) /* O - Array of credentials */
297 {
298 unsigned count; /* Number of certificates */
299 const gnutls_datum_t *certs; /* Certificates */
300
301
302 DEBUG_printf(("httpCopyCredentials(http=%p, credentials=%p)", http, credentials));
303
304 if (credentials)
305 *credentials = NULL;
306
307 if (!http || !http->tls || !credentials)
308 return (-1);
309
310 *credentials = cupsArrayNew(NULL, NULL);
311 certs = gnutls_certificate_get_peers(http->tls, &count);
312
313 DEBUG_printf(("1httpCopyCredentials: certs=%p, count=%u", certs, count));
314
315 if (certs && count)
316 {
317 while (count > 0)
318 {
319 httpAddCredential(*credentials, certs->data, certs->size);
320 certs ++;
321 count --;
322 }
323 }
324
325 return (0);
326 }
327
328
329 /*
330 * '_httpCreateCredentials()' - Create credentials in the internal format.
331 */
332
333 http_tls_credentials_t /* O - Internal credentials */
334 _httpCreateCredentials(
335 cups_array_t *credentials) /* I - Array of credentials */
336 {
337 (void)credentials;
338
339 return (NULL);
340 }
341
342
343 /*
344 * '_httpFreeCredentials()' - Free internal credentials.
345 */
346
347 void
348 _httpFreeCredentials(
349 http_tls_credentials_t credentials) /* I - Internal credentials */
350 {
351 (void)credentials;
352 }
353
354
355 /*
356 * 'httpCredentialsAreValidForName()' - Return whether the credentials are valid for the given name.
357 *
358 * @since CUPS 2.0/OS 10.10@
359 */
360
361 int /* O - 1 if valid, 0 otherwise */
362 httpCredentialsAreValidForName(
363 cups_array_t *credentials, /* I - Credentials */
364 const char *common_name) /* I - Name to check */
365 {
366 gnutls_x509_crt_t cert; /* Certificate */
367 int result = 0; /* Result */
368
369
370 cert = http_gnutls_create_credential((http_credential_t *)cupsArrayFirst(credentials));
371 if (cert)
372 {
373 result = gnutls_x509_crt_check_hostname(cert, common_name) != 0;
374
375 if (result)
376 {
377 int i, /* Looping var */
378 count; /* Number of revoked certificates */
379 unsigned char cserial[1024], /* Certificate serial number */
380 rserial[1024]; /* Revoked serial number */
381 size_t cserial_size, /* Size of cert serial number */
382 rserial_size; /* Size of revoked serial number */
383
384 _cupsMutexLock(&tls_mutex);
385
386 count = gnutls_x509_crl_get_crt_count(tls_crl);
387
388 if (count > 0)
389 {
390 cserial_size = sizeof(cserial);
391 gnutls_x509_crt_get_serial(cert, cserial, &cserial_size);
392
393 for (i = 0; i < count; i ++)
394 {
395 rserial_size = sizeof(rserial);
396 if (!gnutls_x509_crl_get_crt_serial(tls_crl, (unsigned)i, rserial, &rserial_size, NULL) && cserial_size == rserial_size && !memcmp(cserial, rserial, rserial_size))
397 {
398 result = 0;
399 break;
400 }
401 }
402 }
403
404 _cupsMutexUnlock(&tls_mutex);
405 }
406
407 gnutls_x509_crt_deinit(cert);
408 }
409
410 return (result);
411 }
412
413
414 /*
415 * 'httpCredentialsGetTrust()' - Return the trust of credentials.
416 *
417 * @since CUPS 2.0/OS 10.10@
418 */
419
420 http_trust_t /* O - Level of trust */
421 httpCredentialsGetTrust(
422 cups_array_t *credentials, /* I - Credentials */
423 const char *common_name) /* I - Common name for trust lookup */
424 {
425 http_trust_t trust = HTTP_TRUST_OK;
426 /* Trusted? */
427 gnutls_x509_crt_t cert; /* Certificate */
428 cups_array_t *tcreds = NULL; /* Trusted credentials */
429 _cups_globals_t *cg = _cupsGlobals();
430 /* Per-thread globals */
431
432
433 if (!common_name)
434 {
435 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("No common name specified."), 1);
436 return (HTTP_TRUST_UNKNOWN);
437 }
438
439 if ((cert = http_gnutls_create_credential((http_credential_t *)cupsArrayFirst(credentials))) == NULL)
440 {
441 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to create credentials from array."), 1);
442 return (HTTP_TRUST_UNKNOWN);
443 }
444
445 if (cg->any_root < 0)
446 {
447 _cupsSetDefaults();
448 http_gnutls_load_crl();
449 }
450
451 /*
452 * Look this common name up in the default keychains...
453 */
454
455 httpLoadCredentials(NULL, &tcreds, common_name);
456
457 if (tcreds)
458 {
459 char credentials_str[1024], /* String for incoming credentials */
460 tcreds_str[1024]; /* String for saved credentials */
461
462 httpCredentialsString(credentials, credentials_str, sizeof(credentials_str));
463 httpCredentialsString(tcreds, tcreds_str, sizeof(tcreds_str));
464
465 if (strcmp(credentials_str, tcreds_str))
466 {
467 /*
468 * Credentials don't match, let's look at the expiration date of the new
469 * credentials and allow if the new ones have a later expiration...
470 */
471
472 if (!cg->trust_first)
473 {
474 /*
475 * Do not trust certificates on first use...
476 */
477
478 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Trust on first use is disabled."), 1);
479
480 trust = HTTP_TRUST_INVALID;
481 }
482 else if (httpCredentialsGetExpiration(credentials) <= httpCredentialsGetExpiration(tcreds))
483 {
484 /*
485 * The new credentials are not newly issued...
486 */
487
488 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("New credentials are older than stored credentials."), 1);
489
490 trust = HTTP_TRUST_INVALID;
491 }
492 else if (!httpCredentialsAreValidForName(credentials, common_name))
493 {
494 /*
495 * The common name does not match the issued certificate...
496 */
497
498 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("New credentials are not valid for name."), 1);
499
500 trust = HTTP_TRUST_INVALID;
501 }
502 else if (httpCredentialsGetExpiration(tcreds) < time(NULL))
503 {
504 /*
505 * Save the renewed credentials...
506 */
507
508 trust = HTTP_TRUST_RENEWED;
509
510 httpSaveCredentials(NULL, credentials, common_name);
511 }
512 }
513
514 httpFreeCredentials(tcreds);
515 }
516 else if (cg->validate_certs && !httpCredentialsAreValidForName(credentials, common_name))
517 {
518 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("No stored credentials, not valid for name."), 1);
519 trust = HTTP_TRUST_INVALID;
520 }
521 else if (!cg->trust_first)
522 {
523 /*
524 * See if we have a site CA certificate we can compare...
525 */
526
527 if (!httpLoadCredentials(NULL, &tcreds, "site"))
528 {
529 if (cupsArrayCount(credentials) != (cupsArrayCount(tcreds) + 1))
530 {
531 /*
532 * Certificate isn't directly generated from the CA cert...
533 */
534
535 trust = HTTP_TRUST_INVALID;
536 }
537 else
538 {
539 /*
540 * Do a tail comparison of the two certificates...
541 */
542
543 http_credential_t *a, *b; /* Certificates */
544
545 for (a = (http_credential_t *)cupsArrayFirst(tcreds), b = (http_credential_t *)cupsArrayIndex(credentials, 1);
546 a && b;
547 a = (http_credential_t *)cupsArrayNext(tcreds), b = (http_credential_t *)cupsArrayNext(credentials))
548 if (a->datalen != b->datalen || memcmp(a->data, b->data, a->datalen))
549 break;
550
551 if (a || b)
552 trust = HTTP_TRUST_INVALID;
553 }
554
555 if (trust != HTTP_TRUST_OK)
556 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Credentials do not validate against site CA certificate."), 1);
557 }
558 else
559 {
560 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Trust on first use is disabled."), 1);
561 trust = HTTP_TRUST_INVALID;
562 }
563 }
564
565 if (trust == HTTP_TRUST_OK && !cg->expired_certs)
566 {
567 time_t curtime; /* Current date/time */
568
569 time(&curtime);
570 if (curtime < gnutls_x509_crt_get_activation_time(cert) ||
571 curtime > gnutls_x509_crt_get_expiration_time(cert))
572 {
573 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Credentials have expired."), 1);
574 trust = HTTP_TRUST_EXPIRED;
575 }
576 }
577
578 if (trust == HTTP_TRUST_OK && !cg->any_root && cupsArrayCount(credentials) == 1)
579 {
580 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Self-signed credentials are blocked."), 1);
581 trust = HTTP_TRUST_INVALID;
582 }
583
584 gnutls_x509_crt_deinit(cert);
585
586 return (trust);
587 }
588
589
590 /*
591 * 'httpCredentialsGetExpiration()' - Return the expiration date of the credentials.
592 *
593 * @since CUPS 2.0/OS 10.10@
594 */
595
596 time_t /* O - Expiration date of credentials */
597 httpCredentialsGetExpiration(
598 cups_array_t *credentials) /* I - Credentials */
599 {
600 gnutls_x509_crt_t cert; /* Certificate */
601 time_t result = 0; /* Result */
602
603
604 cert = http_gnutls_create_credential((http_credential_t *)cupsArrayFirst(credentials));
605 if (cert)
606 {
607 result = gnutls_x509_crt_get_expiration_time(cert);
608 gnutls_x509_crt_deinit(cert);
609 }
610
611 return (result);
612 }
613
614
615 /*
616 * 'httpCredentialsString()' - Return a string representing the credentials.
617 *
618 * @since CUPS 2.0/OS 10.10@
619 */
620
621 size_t /* O - Total size of credentials string */
622 httpCredentialsString(
623 cups_array_t *credentials, /* I - Credentials */
624 char *buffer, /* I - Buffer or @code NULL@ */
625 size_t bufsize) /* I - Size of buffer */
626 {
627 http_credential_t *first; /* First certificate */
628 gnutls_x509_crt_t cert; /* Certificate */
629
630
631 DEBUG_printf(("httpCredentialsString(credentials=%p, buffer=%p, bufsize=" CUPS_LLFMT ")", credentials, buffer, CUPS_LLCAST bufsize));
632
633 if (!buffer)
634 return (0);
635
636 if (buffer && bufsize > 0)
637 *buffer = '\0';
638
639 if ((first = (http_credential_t *)cupsArrayFirst(credentials)) != NULL &&
640 (cert = http_gnutls_create_credential(first)) != NULL)
641 {
642 char name[256]; /* Common name associated with cert */
643 size_t namelen; /* Length of name */
644 time_t expiration; /* Expiration date of cert */
645 unsigned char md5_digest[16]; /* MD5 result */
646
647 namelen = sizeof(name) - 1;
648 if (gnutls_x509_crt_get_dn_by_oid(cert, GNUTLS_OID_X520_COMMON_NAME, 0, 0, name, &namelen) >= 0)
649 name[namelen] = '\0';
650 else
651 strlcpy(name, "unknown", sizeof(name));
652
653 expiration = gnutls_x509_crt_get_expiration_time(cert);
654
655 cupsHashData("md5", first->data, first->datalen, md5_digest, sizeof(md5_digest));
656
657 snprintf(buffer, bufsize, "%s / %s / %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X", name, httpGetDateString(expiration), 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]);
658
659 gnutls_x509_crt_deinit(cert);
660 }
661
662 DEBUG_printf(("1httpCredentialsString: Returning \"%s\".", buffer));
663
664 return (strlen(buffer));
665 }
666
667
668 /*
669 * 'httpLoadCredentials()' - Load X.509 credentials from a keychain file.
670 *
671 * @since CUPS 2.0/OS 10.10@
672 */
673
674 int /* O - 0 on success, -1 on error */
675 httpLoadCredentials(
676 const char *path, /* I - Keychain/PKCS#12 path */
677 cups_array_t **credentials, /* IO - Credentials */
678 const char *common_name) /* I - Common name for credentials */
679 {
680 cups_file_t *fp; /* Certificate file */
681 char filename[1024], /* filename.crt */
682 temp[1024], /* Temporary string */
683 line[256]; /* Base64-encoded line */
684 unsigned char *data = NULL; /* Buffer for cert data */
685 size_t alloc_data = 0, /* Bytes allocated */
686 num_data = 0; /* Bytes used */
687 int decoded; /* Bytes decoded */
688 int in_certificate = 0;
689 /* In a certificate? */
690
691
692 if (!credentials || !common_name)
693 return (-1);
694
695 if (!path)
696 path = http_gnutls_default_path(temp, sizeof(temp));
697 if (!path)
698 return (-1);
699
700 http_gnutls_make_path(filename, sizeof(filename), path, common_name, "crt");
701
702 if ((fp = cupsFileOpen(filename, "r")) == NULL)
703 return (-1);
704
705 while (cupsFileGets(fp, line, sizeof(line)))
706 {
707 if (!strcmp(line, "-----BEGIN CERTIFICATE-----"))
708 {
709 if (in_certificate)
710 {
711 /*
712 * Missing END CERTIFICATE...
713 */
714
715 httpFreeCredentials(*credentials);
716 *credentials = NULL;
717 break;
718 }
719
720 in_certificate = 1;
721 }
722 else if (!strcmp(line, "-----END CERTIFICATE-----"))
723 {
724 if (!in_certificate || !num_data)
725 {
726 /*
727 * Missing data...
728 */
729
730 httpFreeCredentials(*credentials);
731 *credentials = NULL;
732 break;
733 }
734
735 if (!*credentials)
736 *credentials = cupsArrayNew(NULL, NULL);
737
738 if (httpAddCredential(*credentials, data, num_data))
739 {
740 httpFreeCredentials(*credentials);
741 *credentials = NULL;
742 break;
743 }
744
745 num_data = 0;
746 in_certificate = 0;
747 }
748 else if (in_certificate)
749 {
750 if (alloc_data == 0)
751 {
752 data = malloc(2048);
753 alloc_data = 2048;
754
755 if (!data)
756 break;
757 }
758 else if ((num_data + strlen(line)) >= alloc_data)
759 {
760 unsigned char *tdata = realloc(data, alloc_data + 1024);
761 /* Expanded buffer */
762
763 if (!tdata)
764 {
765 httpFreeCredentials(*credentials);
766 *credentials = NULL;
767 break;
768 }
769
770 data = tdata;
771 alloc_data += 1024;
772 }
773
774 decoded = alloc_data - num_data;
775 httpDecode64_2((char *)data + num_data, &decoded, line);
776 num_data += (size_t)decoded;
777 }
778 }
779
780 cupsFileClose(fp);
781
782 if (in_certificate)
783 {
784 /*
785 * Missing END CERTIFICATE...
786 */
787
788 httpFreeCredentials(*credentials);
789 *credentials = NULL;
790 }
791
792 if (data)
793 free(data);
794
795 return (*credentials ? 0 : -1);
796 }
797
798
799 /*
800 * 'httpSaveCredentials()' - Save X.509 credentials to a keychain file.
801 *
802 * @since CUPS 2.0/OS 10.10@
803 */
804
805 int /* O - -1 on error, 0 on success */
806 httpSaveCredentials(
807 const char *path, /* I - Keychain/PKCS#12 path */
808 cups_array_t *credentials, /* I - Credentials */
809 const char *common_name) /* I - Common name for credentials */
810 {
811 cups_file_t *fp; /* Certificate file */
812 char filename[1024], /* filename.crt */
813 nfilename[1024],/* filename.crt.N */
814 temp[1024], /* Temporary string */
815 line[256]; /* Base64-encoded line */
816 const unsigned char *ptr; /* Pointer into certificate */
817 ssize_t remaining; /* Bytes left */
818 http_credential_t *cred; /* Current credential */
819
820
821 if (!credentials || !common_name)
822 return (-1);
823
824 if (!path)
825 path = http_gnutls_default_path(temp, sizeof(temp));
826 if (!path)
827 return (-1);
828
829 http_gnutls_make_path(filename, sizeof(filename), path, common_name, "crt");
830 snprintf(nfilename, sizeof(nfilename), "%s.N", filename);
831
832 if ((fp = cupsFileOpen(nfilename, "w")) == NULL)
833 return (-1);
834
835 fchmod(cupsFileNumber(fp), 0600);
836
837 for (cred = (http_credential_t *)cupsArrayFirst(credentials);
838 cred;
839 cred = (http_credential_t *)cupsArrayNext(credentials))
840 {
841 cupsFilePuts(fp, "-----BEGIN CERTIFICATE-----\n");
842 for (ptr = cred->data, remaining = (ssize_t)cred->datalen; remaining > 0; remaining -= 45, ptr += 45)
843 {
844 httpEncode64_2(line, sizeof(line), (char *)ptr, remaining > 45 ? 45 : remaining);
845 cupsFilePrintf(fp, "%s\n", line);
846 }
847 cupsFilePuts(fp, "-----END CERTIFICATE-----\n");
848 }
849
850 cupsFileClose(fp);
851
852 return (rename(nfilename, filename));
853 }
854
855
856 /*
857 * 'http_gnutls_create_credential()' - Create a single credential in the internal format.
858 */
859
860 static gnutls_x509_crt_t /* O - Certificate */
861 http_gnutls_create_credential(
862 http_credential_t *credential) /* I - Credential */
863 {
864 int result; /* Result from GNU TLS */
865 gnutls_x509_crt_t cert; /* Certificate */
866 gnutls_datum_t datum; /* Data record */
867
868
869 DEBUG_printf(("3http_gnutls_create_credential(credential=%p)", credential));
870
871 if (!credential)
872 return (NULL);
873
874 if ((result = gnutls_x509_crt_init(&cert)) < 0)
875 {
876 DEBUG_printf(("4http_gnutls_create_credential: init error: %s", gnutls_strerror(result)));
877 return (NULL);
878 }
879
880 datum.data = credential->data;
881 datum.size = credential->datalen;
882
883 if ((result = gnutls_x509_crt_import(cert, &datum, GNUTLS_X509_FMT_DER)) < 0)
884 {
885 DEBUG_printf(("4http_gnutls_create_credential: import error: %s", gnutls_strerror(result)));
886
887 gnutls_x509_crt_deinit(cert);
888 return (NULL);
889 }
890
891 return (cert);
892 }
893
894
895 /*
896 * 'http_gnutls_default_path()' - Get the default credential store path.
897 */
898
899 static const char * /* O - Path or NULL on error */
900 http_gnutls_default_path(char *buffer,/* I - Path buffer */
901 size_t bufsize)/* I - Size of path buffer */
902 {
903 const char *home = getenv("HOME"); /* HOME environment variable */
904
905
906 if (getuid() && home)
907 {
908 snprintf(buffer, bufsize, "%s/.cups", home);
909 if (access(buffer, 0))
910 {
911 DEBUG_printf(("1http_gnutls_default_path: Making directory \"%s\".", buffer));
912 if (mkdir(buffer, 0700))
913 {
914 DEBUG_printf(("1http_gnutls_default_path: Failed to make directory: %s", strerror(errno)));
915 return (NULL);
916 }
917 }
918
919 snprintf(buffer, bufsize, "%s/.cups/ssl", home);
920 if (access(buffer, 0))
921 {
922 DEBUG_printf(("1http_gnutls_default_path: Making directory \"%s\".", buffer));
923 if (mkdir(buffer, 0700))
924 {
925 DEBUG_printf(("1http_gnutls_default_path: Failed to make directory: %s", strerror(errno)));
926 return (NULL);
927 }
928 }
929 }
930 else
931 strlcpy(buffer, CUPS_SERVERROOT "/ssl", bufsize);
932
933 DEBUG_printf(("1http_gnutls_default_path: Using default path \"%s\".", buffer));
934
935 return (buffer);
936 }
937
938
939 /*
940 * 'http_gnutls_load_crl()' - Load the certificate revocation list, if any.
941 */
942
943 static void
944 http_gnutls_load_crl(void)
945 {
946 _cupsMutexLock(&tls_mutex);
947
948 if (!gnutls_x509_crl_init(&tls_crl))
949 {
950 cups_file_t *fp; /* CRL file */
951 char filename[1024], /* site.crl */
952 line[256]; /* Base64-encoded line */
953 unsigned char *data = NULL; /* Buffer for cert data */
954 size_t alloc_data = 0, /* Bytes allocated */
955 num_data = 0; /* Bytes used */
956 int decoded; /* Bytes decoded */
957 gnutls_datum_t datum; /* Data record */
958
959
960 http_gnutls_make_path(filename, sizeof(filename), CUPS_SERVERROOT, "site", "crl");
961
962 if ((fp = cupsFileOpen(filename, "r")) != NULL)
963 {
964 while (cupsFileGets(fp, line, sizeof(line)))
965 {
966 if (!strcmp(line, "-----BEGIN X509 CRL-----"))
967 {
968 if (num_data)
969 {
970 /*
971 * Missing END X509 CRL...
972 */
973
974 break;
975 }
976 }
977 else if (!strcmp(line, "-----END X509 CRL-----"))
978 {
979 if (!num_data)
980 {
981 /*
982 * Missing data...
983 */
984
985 break;
986 }
987
988 datum.data = data;
989 datum.size = num_data;
990
991 gnutls_x509_crl_import(tls_crl, &datum, GNUTLS_X509_FMT_PEM);
992
993 num_data = 0;
994 }
995 else
996 {
997 if (alloc_data == 0)
998 {
999 data = malloc(2048);
1000 alloc_data = 2048;
1001
1002 if (!data)
1003 break;
1004 }
1005 else if ((num_data + strlen(line)) >= alloc_data)
1006 {
1007 unsigned char *tdata = realloc(data, alloc_data + 1024);
1008 /* Expanded buffer */
1009
1010 if (!tdata)
1011 break;
1012
1013 data = tdata;
1014 alloc_data += 1024;
1015 }
1016
1017 decoded = alloc_data - num_data;
1018 httpDecode64_2((char *)data + num_data, &decoded, line);
1019 num_data += (size_t)decoded;
1020 }
1021 }
1022
1023 cupsFileClose(fp);
1024
1025 if (data)
1026 free(data);
1027 }
1028 }
1029
1030 _cupsMutexUnlock(&tls_mutex);
1031 }
1032
1033
1034 /*
1035 * 'http_gnutls_make_path()' - Format a filename for a certificate or key file.
1036 */
1037
1038 static const char * /* O - Filename */
1039 http_gnutls_make_path(
1040 char *buffer, /* I - Filename buffer */
1041 size_t bufsize, /* I - Size of buffer */
1042 const char *dirname, /* I - Directory */
1043 const char *filename, /* I - Filename (usually hostname) */
1044 const char *ext) /* I - Extension */
1045 {
1046 char *bufptr, /* Pointer into buffer */
1047 *bufend = buffer + bufsize - 1; /* End of buffer */
1048
1049
1050 snprintf(buffer, bufsize, "%s/", dirname);
1051 bufptr = buffer + strlen(buffer);
1052
1053 while (*filename && bufptr < bufend)
1054 {
1055 if (_cups_isalnum(*filename) || *filename == '-' || *filename == '.')
1056 *bufptr++ = *filename;
1057 else
1058 *bufptr++ = '_';
1059
1060 filename ++;
1061 }
1062
1063 if (bufptr < bufend)
1064 *bufptr++ = '.';
1065
1066 strlcpy(bufptr, ext, (size_t)(bufend - bufptr + 1));
1067
1068 return (buffer);
1069 }
1070
1071
1072 /*
1073 * 'http_gnutls_read()' - Read function for the GNU TLS library.
1074 */
1075
1076 static ssize_t /* O - Number of bytes read or -1 on error */
1077 http_gnutls_read(
1078 gnutls_transport_ptr_t ptr, /* I - Connection to server */
1079 void *data, /* I - Buffer */
1080 size_t length) /* I - Number of bytes to read */
1081 {
1082 http_t *http; /* HTTP connection */
1083 ssize_t bytes; /* Bytes read */
1084
1085
1086 DEBUG_printf(("6http_gnutls_read(ptr=%p, data=%p, length=%d)", ptr, data, (int)length));
1087
1088 http = (http_t *)ptr;
1089
1090 if (!http->blocking)
1091 {
1092 /*
1093 * Make sure we have data before we read...
1094 */
1095
1096 while (!_httpWait(http, http->wait_value, 0))
1097 {
1098 if (http->timeout_cb && (*http->timeout_cb)(http, http->timeout_data))
1099 continue;
1100
1101 http->error = ETIMEDOUT;
1102 return (-1);
1103 }
1104 }
1105
1106 bytes = recv(http->fd, data, length, 0);
1107 DEBUG_printf(("6http_gnutls_read: bytes=%d", (int)bytes));
1108 return (bytes);
1109 }
1110
1111
1112 /*
1113 * 'http_gnutls_write()' - Write function for the GNU TLS library.
1114 */
1115
1116 static ssize_t /* O - Number of bytes written or -1 on error */
1117 http_gnutls_write(
1118 gnutls_transport_ptr_t ptr, /* I - Connection to server */
1119 const void *data, /* I - Data buffer */
1120 size_t length) /* I - Number of bytes to write */
1121 {
1122 ssize_t bytes; /* Bytes written */
1123
1124
1125 DEBUG_printf(("6http_gnutls_write(ptr=%p, data=%p, length=%d)", ptr, data,
1126 (int)length));
1127 bytes = send(((http_t *)ptr)->fd, data, length, 0);
1128 DEBUG_printf(("http_gnutls_write: bytes=%d", (int)bytes));
1129
1130 return (bytes);
1131 }
1132
1133
1134 /*
1135 * '_httpTLSInitialize()' - Initialize the TLS stack.
1136 */
1137
1138 void
1139 _httpTLSInitialize(void)
1140 {
1141 /*
1142 * Initialize GNU TLS...
1143 */
1144
1145 gnutls_global_init();
1146 }
1147
1148
1149 /*
1150 * '_httpTLSPending()' - Return the number of pending TLS-encrypted bytes.
1151 */
1152
1153 size_t /* O - Bytes available */
1154 _httpTLSPending(http_t *http) /* I - HTTP connection */
1155 {
1156 return (gnutls_record_check_pending(http->tls));
1157 }
1158
1159
1160 /*
1161 * '_httpTLSRead()' - Read from a SSL/TLS connection.
1162 */
1163
1164 int /* O - Bytes read */
1165 _httpTLSRead(http_t *http, /* I - Connection to server */
1166 char *buf, /* I - Buffer to store data */
1167 int len) /* I - Length of buffer */
1168 {
1169 ssize_t result; /* Return value */
1170
1171
1172 result = gnutls_record_recv(http->tls, buf, (size_t)len);
1173
1174 if (result < 0 && !errno)
1175 {
1176 /*
1177 * Convert GNU TLS error to errno value...
1178 */
1179
1180 switch (result)
1181 {
1182 case GNUTLS_E_INTERRUPTED :
1183 errno = EINTR;
1184 break;
1185
1186 case GNUTLS_E_AGAIN :
1187 errno = EAGAIN;
1188 break;
1189
1190 default :
1191 errno = EPIPE;
1192 break;
1193 }
1194
1195 result = -1;
1196 }
1197
1198 return ((int)result);
1199 }
1200
1201
1202 /*
1203 * '_httpTLSSetCredentials()' - Set the TLS credentials.
1204 */
1205
1206 int /* O - Status of connection */
1207 _httpTLSSetCredentials(http_t *http) /* I - Connection to server */
1208 {
1209 (void)http;
1210
1211 return (0);
1212 }
1213
1214
1215 /*
1216 * '_httpTLSSetOptions()' - Set TLS protocol and cipher suite options.
1217 */
1218
1219 void
1220 _httpTLSSetOptions(int options) /* I - Options */
1221 {
1222 if (!(options & _HTTP_TLS_SET_DEFAULT) || tls_options < 0)
1223 {
1224 tls_options = options;
1225 tls_min_version = min_version;
1226 tls_max_version = max_version;
1227 }
1228 }
1229
1230
1231 /*
1232 * '_httpTLSStart()' - Set up SSL/TLS support on a connection.
1233 */
1234
1235 int /* O - 0 on success, -1 on failure */
1236 _httpTLSStart(http_t *http) /* I - Connection to server */
1237 {
1238 char hostname[256], /* Hostname */
1239 *hostptr; /* Pointer into hostname */
1240 int status; /* Status of handshake */
1241 gnutls_certificate_credentials_t *credentials;
1242 /* TLS credentials */
1243 char priority_string[2048];
1244 /* Priority string */
1245 int version; /* Current version */
1246 static const char * const versions[] =/* SSL/TLS versions */
1247 {
1248 "VERS-SSL3.0",
1249 "VERS-TLS1.0",
1250 "VERS-TLS1.1",
1251 "VERS-TLS1.2",
1252 "VERS-TLS1.3",
1253 "VERS-TLS-ALL"
1254 };
1255
1256
1257 DEBUG_printf(("3_httpTLSStart(http=%p)", http));
1258
1259 if (tls_options < 0)
1260 {
1261 DEBUG_puts("4_httpTLSStart: Setting defaults.");
1262 _cupsSetDefaults();
1263 DEBUG_printf(("4_httpTLSStart: tls_options=%x", tls_options));
1264 }
1265
1266 if (http->mode == _HTTP_MODE_SERVER && !tls_keypath)
1267 {
1268 DEBUG_puts("4_httpTLSStart: cupsSetServerCredentials not called.");
1269 http->error = errno = EINVAL;
1270 http->status = HTTP_STATUS_ERROR;
1271 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Server credentials not set."), 1);
1272
1273 return (-1);
1274 }
1275
1276 credentials = (gnutls_certificate_credentials_t *)
1277 malloc(sizeof(gnutls_certificate_credentials_t));
1278 if (credentials == NULL)
1279 {
1280 DEBUG_printf(("8_httpStartTLS: Unable to allocate credentials: %s",
1281 strerror(errno)));
1282 http->error = errno;
1283 http->status = HTTP_STATUS_ERROR;
1284 _cupsSetHTTPError(HTTP_STATUS_ERROR);
1285
1286 return (-1);
1287 }
1288
1289 gnutls_certificate_allocate_credentials(credentials);
1290 status = gnutls_init(&http->tls, http->mode == _HTTP_MODE_CLIENT ? GNUTLS_CLIENT : GNUTLS_SERVER);
1291 if (!status)
1292 status = gnutls_set_default_priority(http->tls);
1293
1294 if (status)
1295 {
1296 http->error = EIO;
1297 http->status = HTTP_STATUS_ERROR;
1298
1299 DEBUG_printf(("4_httpTLSStart: Unable to initialize common TLS parameters: %s", gnutls_strerror(status)));
1300 _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI, gnutls_strerror(status), 0);
1301
1302 gnutls_deinit(http->tls);
1303 gnutls_certificate_free_credentials(*credentials);
1304 free(credentials);
1305 http->tls = NULL;
1306
1307 return (-1);
1308 }
1309
1310 if (http->mode == _HTTP_MODE_CLIENT)
1311 {
1312 /*
1313 * Client: get the hostname to use for TLS...
1314 */
1315
1316 if (httpAddrLocalhost(http->hostaddr))
1317 {
1318 strlcpy(hostname, "localhost", sizeof(hostname));
1319 }
1320 else
1321 {
1322 /*
1323 * Otherwise make sure the hostname we have does not end in a trailing dot.
1324 */
1325
1326 strlcpy(hostname, http->hostname, sizeof(hostname));
1327 if ((hostptr = hostname + strlen(hostname) - 1) >= hostname &&
1328 *hostptr == '.')
1329 *hostptr = '\0';
1330 }
1331
1332 status = gnutls_server_name_set(http->tls, GNUTLS_NAME_DNS, hostname, strlen(hostname));
1333 }
1334 else
1335 {
1336 /*
1337 * Server: get certificate and private key...
1338 */
1339
1340 char crtfile[1024], /* Certificate file */
1341 keyfile[1024]; /* Private key file */
1342 int have_creds = 0; /* Have credentials? */
1343
1344 if (http->fields[HTTP_FIELD_HOST][0])
1345 {
1346 /*
1347 * Use hostname for TLS upgrade...
1348 */
1349
1350 strlcpy(hostname, http->fields[HTTP_FIELD_HOST], sizeof(hostname));
1351 }
1352 else
1353 {
1354 /*
1355 * Resolve hostname from connection address...
1356 */
1357
1358 http_addr_t addr; /* Connection address */
1359 socklen_t addrlen; /* Length of address */
1360
1361 addrlen = sizeof(addr);
1362 if (getsockname(http->fd, (struct sockaddr *)&addr, &addrlen))
1363 {
1364 DEBUG_printf(("4_httpTLSStart: Unable to get socket address: %s", strerror(errno)));
1365 hostname[0] = '\0';
1366 }
1367 else if (httpAddrLocalhost(&addr))
1368 hostname[0] = '\0';
1369 else
1370 {
1371 httpAddrLookup(&addr, hostname, sizeof(hostname));
1372 DEBUG_printf(("4_httpTLSStart: Resolved socket address to \"%s\".", hostname));
1373 }
1374 }
1375
1376 if (isdigit(hostname[0] & 255) || hostname[0] == '[')
1377 hostname[0] = '\0'; /* Don't allow numeric addresses */
1378
1379 if (hostname[0])
1380 {
1381 /*
1382 * First look in the CUPS keystore...
1383 */
1384
1385 http_gnutls_make_path(crtfile, sizeof(crtfile), tls_keypath, hostname, "crt");
1386 http_gnutls_make_path(keyfile, sizeof(keyfile), tls_keypath, hostname, "key");
1387
1388 if (access(crtfile, R_OK) || access(keyfile, R_OK))
1389 {
1390 /*
1391 * No CUPS-managed certs, look for CA certs...
1392 */
1393
1394 char cacrtfile[1024], cakeyfile[1024]; /* CA cert files */
1395
1396 snprintf(cacrtfile, sizeof(cacrtfile), "/etc/letsencrypt/live/%s/fullchain.pem", hostname);
1397 snprintf(cakeyfile, sizeof(cakeyfile), "/etc/letsencrypt/live/%s/privkey.pem", hostname);
1398
1399 if ((access(cacrtfile, R_OK) || access(cakeyfile, R_OK)) && (hostptr = strchr(hostname, '.')) != NULL)
1400 {
1401 /*
1402 * Try just domain name...
1403 */
1404
1405 hostptr ++;
1406 if (strchr(hostptr, '.'))
1407 {
1408 snprintf(cacrtfile, sizeof(cacrtfile), "/etc/letsencrypt/live/%s/fullchain.pem", hostptr);
1409 snprintf(cakeyfile, sizeof(cakeyfile), "/etc/letsencrypt/live/%s/privkey.pem", hostptr);
1410 }
1411 }
1412
1413 if (!access(cacrtfile, R_OK) && !access(cakeyfile, R_OK))
1414 {
1415 /*
1416 * Use the CA certs...
1417 */
1418
1419 strlcpy(crtfile, cacrtfile, sizeof(crtfile));
1420 strlcpy(keyfile, cakeyfile, sizeof(keyfile));
1421 }
1422 }
1423
1424 have_creds = !access(crtfile, R_OK) && !access(keyfile, R_OK);
1425 }
1426 else if (tls_common_name)
1427 {
1428 /*
1429 * First look in the CUPS keystore...
1430 */
1431
1432 http_gnutls_make_path(crtfile, sizeof(crtfile), tls_keypath, tls_common_name, "crt");
1433 http_gnutls_make_path(keyfile, sizeof(keyfile), tls_keypath, tls_common_name, "key");
1434
1435 if (access(crtfile, R_OK) || access(keyfile, R_OK))
1436 {
1437 /*
1438 * No CUPS-managed certs, look for CA certs...
1439 */
1440
1441 char cacrtfile[1024], cakeyfile[1024]; /* CA cert files */
1442
1443 snprintf(cacrtfile, sizeof(cacrtfile), "/etc/letsencrypt/live/%s/fullchain.pem", tls_common_name);
1444 snprintf(cakeyfile, sizeof(cakeyfile), "/etc/letsencrypt/live/%s/privkey.pem", tls_common_name);
1445
1446 if ((access(cacrtfile, R_OK) || access(cakeyfile, R_OK)) && (hostptr = strchr(tls_common_name, '.')) != NULL)
1447 {
1448 /*
1449 * Try just domain name...
1450 */
1451
1452 hostptr ++;
1453 if (strchr(hostptr, '.'))
1454 {
1455 snprintf(cacrtfile, sizeof(cacrtfile), "/etc/letsencrypt/live/%s/fullchain.pem", hostptr);
1456 snprintf(cakeyfile, sizeof(cakeyfile), "/etc/letsencrypt/live/%s/privkey.pem", hostptr);
1457 }
1458 }
1459
1460 if (!access(cacrtfile, R_OK) && !access(cakeyfile, R_OK))
1461 {
1462 /*
1463 * Use the CA certs...
1464 */
1465
1466 strlcpy(crtfile, cacrtfile, sizeof(crtfile));
1467 strlcpy(keyfile, cakeyfile, sizeof(keyfile));
1468 }
1469 }
1470
1471 have_creds = !access(crtfile, R_OK) && !access(keyfile, R_OK);
1472 }
1473
1474 if (!have_creds && tls_auto_create && (hostname[0] || tls_common_name))
1475 {
1476 DEBUG_printf(("4_httpTLSStart: Auto-create credentials for \"%s\".", hostname[0] ? hostname : tls_common_name));
1477
1478 if (!cupsMakeServerCredentials(tls_keypath, hostname[0] ? hostname : tls_common_name, 0, NULL, time(NULL) + 365 * 86400))
1479 {
1480 DEBUG_puts("4_httpTLSStart: cupsMakeServerCredentials failed.");
1481 http->error = errno = EINVAL;
1482 http->status = HTTP_STATUS_ERROR;
1483 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to create server credentials."), 1);
1484
1485 return (-1);
1486 }
1487 }
1488
1489 DEBUG_printf(("4_httpTLSStart: Using certificate \"%s\" and private key \"%s\".", crtfile, keyfile));
1490
1491 if (!status)
1492 status = gnutls_certificate_set_x509_key_file(*credentials, crtfile, keyfile, GNUTLS_X509_FMT_PEM);
1493 }
1494
1495 if (!status)
1496 status = gnutls_credentials_set(http->tls, GNUTLS_CRD_CERTIFICATE, *credentials);
1497
1498 if (status)
1499 {
1500 http->error = EIO;
1501 http->status = HTTP_STATUS_ERROR;
1502
1503 DEBUG_printf(("4_httpTLSStart: Unable to complete client/server setup: %s", gnutls_strerror(status)));
1504 _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI, gnutls_strerror(status), 0);
1505
1506 gnutls_deinit(http->tls);
1507 gnutls_certificate_free_credentials(*credentials);
1508 free(credentials);
1509 http->tls = NULL;
1510
1511 return (-1);
1512 }
1513
1514 strlcpy(priority_string, "NORMAL", sizeof(priority_string));
1515
1516 if (tls_max_version < _HTTP_TLS_MAX)
1517 {
1518 /*
1519 * Require specific TLS versions...
1520 */
1521
1522 strlcat(priority_string, ":-VERS-TLS-ALL", sizeof(priority_string));
1523 for (version = tls_min_version; version <= tls_max_version; version ++)
1524 {
1525 strlcat(priority_string, ":+", sizeof(priority_string));
1526 strlcat(priority_string, versions[version], sizeof(priority_string));
1527 }
1528 }
1529 else if (tls_min_version == _HTTP_TLS_SSL3)
1530 {
1531 /*
1532 * Allow all versions of TLS and SSL/3.0...
1533 */
1534
1535 strlcat(priority_string, ":+VERS-TLS-ALL:+VERS-SSL3.0", sizeof(priority_string));
1536 }
1537 else
1538 {
1539 /*
1540 * Require a minimum version...
1541 */
1542
1543 strlcat(priority_string, ":+VERS-TLS-ALL", sizeof(priority_string));
1544 for (version = 0; version < tls_min_version; version ++)
1545 {
1546 strlcat(priority_string, ":-", sizeof(priority_string));
1547 strlcat(priority_string, versions[version], sizeof(priority_string));
1548 }
1549 }
1550
1551 if (tls_options & _HTTP_TLS_ALLOW_RC4)
1552 strlcat(priority_string, ":+ARCFOUR-128", sizeof(priority_string));
1553 else
1554 strlcat(priority_string, ":!ARCFOUR-128", sizeof(priority_string));
1555
1556 strlcat(priority_string, ":!ANON-DH", sizeof(priority_string));
1557
1558 if (tls_options & _HTTP_TLS_DENY_CBC)
1559 strlcat(priority_string, ":!AES-128-CBC:!AES-256-CBC:!CAMELLIA-128-CBC:!CAMELLIA-256-CBC:!3DES-CBC", sizeof(priority_string));
1560
1561 #ifdef HAVE_GNUTLS_PRIORITY_SET_DIRECT
1562 gnutls_priority_set_direct(http->tls, priority_string, NULL);
1563
1564 #else
1565 gnutls_priority_t priority; /* Priority */
1566
1567 gnutls_priority_init(&priority, priority_string, NULL);
1568 gnutls_priority_set(http->tls, priority);
1569 gnutls_priority_deinit(priority);
1570 #endif /* HAVE_GNUTLS_PRIORITY_SET_DIRECT */
1571
1572 gnutls_transport_set_ptr(http->tls, (gnutls_transport_ptr_t)http);
1573 gnutls_transport_set_pull_function(http->tls, http_gnutls_read);
1574 #ifdef HAVE_GNUTLS_TRANSPORT_SET_PULL_TIMEOUT_FUNCTION
1575 gnutls_transport_set_pull_timeout_function(http->tls, (gnutls_pull_timeout_func)httpWait);
1576 #endif /* HAVE_GNUTLS_TRANSPORT_SET_PULL_TIMEOUT_FUNCTION */
1577 gnutls_transport_set_push_function(http->tls, http_gnutls_write);
1578
1579 while ((status = gnutls_handshake(http->tls)) != GNUTLS_E_SUCCESS)
1580 {
1581 DEBUG_printf(("5_httpStartTLS: gnutls_handshake returned %d (%s)",
1582 status, gnutls_strerror(status)));
1583
1584 if (gnutls_error_is_fatal(status))
1585 {
1586 http->error = EIO;
1587 http->status = HTTP_STATUS_ERROR;
1588
1589 _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI, gnutls_strerror(status), 0);
1590
1591 gnutls_deinit(http->tls);
1592 gnutls_certificate_free_credentials(*credentials);
1593 free(credentials);
1594 http->tls = NULL;
1595
1596 return (-1);
1597 }
1598 }
1599
1600 http->tls_credentials = credentials;
1601
1602 return (0);
1603 }
1604
1605
1606 /*
1607 * '_httpTLSStop()' - Shut down SSL/TLS on a connection.
1608 */
1609
1610 void
1611 _httpTLSStop(http_t *http) /* I - Connection to server */
1612 {
1613 int error; /* Error code */
1614
1615
1616 error = gnutls_bye(http->tls, http->mode == _HTTP_MODE_CLIENT ? GNUTLS_SHUT_RDWR : GNUTLS_SHUT_WR);
1617 if (error != GNUTLS_E_SUCCESS)
1618 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, gnutls_strerror(errno), 0);
1619
1620 gnutls_deinit(http->tls);
1621 http->tls = NULL;
1622
1623 if (http->tls_credentials)
1624 {
1625 gnutls_certificate_free_credentials(*(http->tls_credentials));
1626 free(http->tls_credentials);
1627 http->tls_credentials = NULL;
1628 }
1629 }
1630
1631
1632 /*
1633 * '_httpTLSWrite()' - Write to a SSL/TLS connection.
1634 */
1635
1636 int /* O - Bytes written */
1637 _httpTLSWrite(http_t *http, /* I - Connection to server */
1638 const char *buf, /* I - Buffer holding data */
1639 int len) /* I - Length of buffer */
1640 {
1641 ssize_t result; /* Return value */
1642
1643
1644 DEBUG_printf(("2http_write_ssl(http=%p, buf=%p, len=%d)", http, buf, len));
1645
1646 result = gnutls_record_send(http->tls, buf, (size_t)len);
1647
1648 if (result < 0 && !errno)
1649 {
1650 /*
1651 * Convert GNU TLS error to errno value...
1652 */
1653
1654 switch (result)
1655 {
1656 case GNUTLS_E_INTERRUPTED :
1657 errno = EINTR;
1658 break;
1659
1660 case GNUTLS_E_AGAIN :
1661 errno = EAGAIN;
1662 break;
1663
1664 default :
1665 errno = EPIPE;
1666 break;
1667 }
1668
1669 result = -1;
1670 }
1671
1672 DEBUG_printf(("3http_write_ssl: Returning %d.", (int)result));
1673
1674 return ((int)result);
1675 }