]> git.ipfire.org Git - thirdparty/cups.git/blob - cups/tls-gnutls.c
Update HTTP implementation to support field values larger than HTTP_MAX_VALUE
[thirdparty/cups.git] / cups / tls-gnutls.c
1 /*
2 * TLS support code for CUPS using GNU TLS.
3 *
4 * Copyright 2007-2018 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 int min_version, /* I - Minimum TLS version */
1222 int max_version) /* I - Maximum TLS version */
1223 {
1224 if (!(options & _HTTP_TLS_SET_DEFAULT) || tls_options < 0)
1225 {
1226 tls_options = options;
1227 tls_min_version = min_version;
1228 tls_max_version = max_version;
1229 }
1230 }
1231
1232
1233 /*
1234 * '_httpTLSStart()' - Set up SSL/TLS support on a connection.
1235 */
1236
1237 int /* O - 0 on success, -1 on failure */
1238 _httpTLSStart(http_t *http) /* I - Connection to server */
1239 {
1240 char hostname[256], /* Hostname */
1241 *hostptr; /* Pointer into hostname */
1242 int status; /* Status of handshake */
1243 gnutls_certificate_credentials_t *credentials;
1244 /* TLS credentials */
1245 char priority_string[2048];
1246 /* Priority string */
1247 int version; /* Current version */
1248 static const char * const versions[] =/* SSL/TLS versions */
1249 {
1250 "VERS-SSL3.0",
1251 "VERS-TLS1.0",
1252 "VERS-TLS1.1",
1253 "VERS-TLS1.2",
1254 "VERS-TLS1.3",
1255 "VERS-TLS-ALL"
1256 };
1257
1258
1259 DEBUG_printf(("3_httpTLSStart(http=%p)", http));
1260
1261 if (tls_options < 0)
1262 {
1263 DEBUG_puts("4_httpTLSStart: Setting defaults.");
1264 _cupsSetDefaults();
1265 DEBUG_printf(("4_httpTLSStart: tls_options=%x", tls_options));
1266 }
1267
1268 if (http->mode == _HTTP_MODE_SERVER && !tls_keypath)
1269 {
1270 DEBUG_puts("4_httpTLSStart: cupsSetServerCredentials not called.");
1271 http->error = errno = EINVAL;
1272 http->status = HTTP_STATUS_ERROR;
1273 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Server credentials not set."), 1);
1274
1275 return (-1);
1276 }
1277
1278 credentials = (gnutls_certificate_credentials_t *)
1279 malloc(sizeof(gnutls_certificate_credentials_t));
1280 if (credentials == NULL)
1281 {
1282 DEBUG_printf(("8_httpStartTLS: Unable to allocate credentials: %s",
1283 strerror(errno)));
1284 http->error = errno;
1285 http->status = HTTP_STATUS_ERROR;
1286 _cupsSetHTTPError(HTTP_STATUS_ERROR);
1287
1288 return (-1);
1289 }
1290
1291 gnutls_certificate_allocate_credentials(credentials);
1292 status = gnutls_init(&http->tls, http->mode == _HTTP_MODE_CLIENT ? GNUTLS_CLIENT : GNUTLS_SERVER);
1293 if (!status)
1294 status = gnutls_set_default_priority(http->tls);
1295
1296 if (status)
1297 {
1298 http->error = EIO;
1299 http->status = HTTP_STATUS_ERROR;
1300
1301 DEBUG_printf(("4_httpTLSStart: Unable to initialize common TLS parameters: %s", gnutls_strerror(status)));
1302 _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI, gnutls_strerror(status), 0);
1303
1304 gnutls_deinit(http->tls);
1305 gnutls_certificate_free_credentials(*credentials);
1306 free(credentials);
1307 http->tls = NULL;
1308
1309 return (-1);
1310 }
1311
1312 if (http->mode == _HTTP_MODE_CLIENT)
1313 {
1314 /*
1315 * Client: get the hostname to use for TLS...
1316 */
1317
1318 if (httpAddrLocalhost(http->hostaddr))
1319 {
1320 strlcpy(hostname, "localhost", sizeof(hostname));
1321 }
1322 else
1323 {
1324 /*
1325 * Otherwise make sure the hostname we have does not end in a trailing dot.
1326 */
1327
1328 strlcpy(hostname, http->hostname, sizeof(hostname));
1329 if ((hostptr = hostname + strlen(hostname) - 1) >= hostname &&
1330 *hostptr == '.')
1331 *hostptr = '\0';
1332 }
1333
1334 status = gnutls_server_name_set(http->tls, GNUTLS_NAME_DNS, hostname, strlen(hostname));
1335 }
1336 else
1337 {
1338 /*
1339 * Server: get certificate and private key...
1340 */
1341
1342 char crtfile[1024], /* Certificate file */
1343 keyfile[1024]; /* Private key file */
1344 int have_creds = 0; /* Have credentials? */
1345
1346 if (http->fields[HTTP_FIELD_HOST])
1347 {
1348 /*
1349 * Use hostname for TLS upgrade...
1350 */
1351
1352 strlcpy(hostname, http->fields[HTTP_FIELD_HOST], sizeof(hostname));
1353 }
1354 else
1355 {
1356 /*
1357 * Resolve hostname from connection address...
1358 */
1359
1360 http_addr_t addr; /* Connection address */
1361 socklen_t addrlen; /* Length of address */
1362
1363 addrlen = sizeof(addr);
1364 if (getsockname(http->fd, (struct sockaddr *)&addr, &addrlen))
1365 {
1366 DEBUG_printf(("4_httpTLSStart: Unable to get socket address: %s", strerror(errno)));
1367 hostname[0] = '\0';
1368 }
1369 else if (httpAddrLocalhost(&addr))
1370 hostname[0] = '\0';
1371 else
1372 {
1373 httpAddrLookup(&addr, hostname, sizeof(hostname));
1374 DEBUG_printf(("4_httpTLSStart: Resolved socket address to \"%s\".", hostname));
1375 }
1376 }
1377
1378 if (isdigit(hostname[0] & 255) || hostname[0] == '[')
1379 hostname[0] = '\0'; /* Don't allow numeric addresses */
1380
1381 if (hostname[0])
1382 {
1383 /*
1384 * First look in the CUPS keystore...
1385 */
1386
1387 http_gnutls_make_path(crtfile, sizeof(crtfile), tls_keypath, hostname, "crt");
1388 http_gnutls_make_path(keyfile, sizeof(keyfile), tls_keypath, hostname, "key");
1389
1390 if (access(crtfile, R_OK) || access(keyfile, R_OK))
1391 {
1392 /*
1393 * No CUPS-managed certs, look for CA certs...
1394 */
1395
1396 char cacrtfile[1024], cakeyfile[1024]; /* CA cert files */
1397
1398 snprintf(cacrtfile, sizeof(cacrtfile), "/etc/letsencrypt/live/%s/fullchain.pem", hostname);
1399 snprintf(cakeyfile, sizeof(cakeyfile), "/etc/letsencrypt/live/%s/privkey.pem", hostname);
1400
1401 if ((access(cacrtfile, R_OK) || access(cakeyfile, R_OK)) && (hostptr = strchr(hostname, '.')) != NULL)
1402 {
1403 /*
1404 * Try just domain name...
1405 */
1406
1407 hostptr ++;
1408 if (strchr(hostptr, '.'))
1409 {
1410 snprintf(cacrtfile, sizeof(cacrtfile), "/etc/letsencrypt/live/%s/fullchain.pem", hostptr);
1411 snprintf(cakeyfile, sizeof(cakeyfile), "/etc/letsencrypt/live/%s/privkey.pem", hostptr);
1412 }
1413 }
1414
1415 if (!access(cacrtfile, R_OK) && !access(cakeyfile, R_OK))
1416 {
1417 /*
1418 * Use the CA certs...
1419 */
1420
1421 strlcpy(crtfile, cacrtfile, sizeof(crtfile));
1422 strlcpy(keyfile, cakeyfile, sizeof(keyfile));
1423 }
1424 }
1425
1426 have_creds = !access(crtfile, R_OK) && !access(keyfile, R_OK);
1427 }
1428 else if (tls_common_name)
1429 {
1430 /*
1431 * First look in the CUPS keystore...
1432 */
1433
1434 http_gnutls_make_path(crtfile, sizeof(crtfile), tls_keypath, tls_common_name, "crt");
1435 http_gnutls_make_path(keyfile, sizeof(keyfile), tls_keypath, tls_common_name, "key");
1436
1437 if (access(crtfile, R_OK) || access(keyfile, R_OK))
1438 {
1439 /*
1440 * No CUPS-managed certs, look for CA certs...
1441 */
1442
1443 char cacrtfile[1024], cakeyfile[1024]; /* CA cert files */
1444
1445 snprintf(cacrtfile, sizeof(cacrtfile), "/etc/letsencrypt/live/%s/fullchain.pem", tls_common_name);
1446 snprintf(cakeyfile, sizeof(cakeyfile), "/etc/letsencrypt/live/%s/privkey.pem", tls_common_name);
1447
1448 if ((access(cacrtfile, R_OK) || access(cakeyfile, R_OK)) && (hostptr = strchr(tls_common_name, '.')) != NULL)
1449 {
1450 /*
1451 * Try just domain name...
1452 */
1453
1454 hostptr ++;
1455 if (strchr(hostptr, '.'))
1456 {
1457 snprintf(cacrtfile, sizeof(cacrtfile), "/etc/letsencrypt/live/%s/fullchain.pem", hostptr);
1458 snprintf(cakeyfile, sizeof(cakeyfile), "/etc/letsencrypt/live/%s/privkey.pem", hostptr);
1459 }
1460 }
1461
1462 if (!access(cacrtfile, R_OK) && !access(cakeyfile, R_OK))
1463 {
1464 /*
1465 * Use the CA certs...
1466 */
1467
1468 strlcpy(crtfile, cacrtfile, sizeof(crtfile));
1469 strlcpy(keyfile, cakeyfile, sizeof(keyfile));
1470 }
1471 }
1472
1473 have_creds = !access(crtfile, R_OK) && !access(keyfile, R_OK);
1474 }
1475
1476 if (!have_creds && tls_auto_create && (hostname[0] || tls_common_name))
1477 {
1478 DEBUG_printf(("4_httpTLSStart: Auto-create credentials for \"%s\".", hostname[0] ? hostname : tls_common_name));
1479
1480 if (!cupsMakeServerCredentials(tls_keypath, hostname[0] ? hostname : tls_common_name, 0, NULL, time(NULL) + 365 * 86400))
1481 {
1482 DEBUG_puts("4_httpTLSStart: cupsMakeServerCredentials failed.");
1483 http->error = errno = EINVAL;
1484 http->status = HTTP_STATUS_ERROR;
1485 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to create server credentials."), 1);
1486
1487 return (-1);
1488 }
1489 }
1490
1491 DEBUG_printf(("4_httpTLSStart: Using certificate \"%s\" and private key \"%s\".", crtfile, keyfile));
1492
1493 if (!status)
1494 status = gnutls_certificate_set_x509_key_file(*credentials, crtfile, keyfile, GNUTLS_X509_FMT_PEM);
1495 }
1496
1497 if (!status)
1498 status = gnutls_credentials_set(http->tls, GNUTLS_CRD_CERTIFICATE, *credentials);
1499
1500 if (status)
1501 {
1502 http->error = EIO;
1503 http->status = HTTP_STATUS_ERROR;
1504
1505 DEBUG_printf(("4_httpTLSStart: Unable to complete client/server setup: %s", gnutls_strerror(status)));
1506 _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI, gnutls_strerror(status), 0);
1507
1508 gnutls_deinit(http->tls);
1509 gnutls_certificate_free_credentials(*credentials);
1510 free(credentials);
1511 http->tls = NULL;
1512
1513 return (-1);
1514 }
1515
1516 strlcpy(priority_string, "NORMAL", sizeof(priority_string));
1517
1518 if (tls_max_version < _HTTP_TLS_MAX)
1519 {
1520 /*
1521 * Require specific TLS versions...
1522 */
1523
1524 strlcat(priority_string, ":-VERS-TLS-ALL", sizeof(priority_string));
1525 for (version = tls_min_version; version <= tls_max_version; version ++)
1526 {
1527 strlcat(priority_string, ":+", sizeof(priority_string));
1528 strlcat(priority_string, versions[version], sizeof(priority_string));
1529 }
1530 }
1531 else if (tls_min_version == _HTTP_TLS_SSL3)
1532 {
1533 /*
1534 * Allow all versions of TLS and SSL/3.0...
1535 */
1536
1537 strlcat(priority_string, ":+VERS-TLS-ALL:+VERS-SSL3.0", sizeof(priority_string));
1538 }
1539 else
1540 {
1541 /*
1542 * Require a minimum version...
1543 */
1544
1545 strlcat(priority_string, ":+VERS-TLS-ALL", sizeof(priority_string));
1546 for (version = 0; version < tls_min_version; version ++)
1547 {
1548 strlcat(priority_string, ":-", sizeof(priority_string));
1549 strlcat(priority_string, versions[version], sizeof(priority_string));
1550 }
1551 }
1552
1553 if (tls_options & _HTTP_TLS_ALLOW_RC4)
1554 strlcat(priority_string, ":+ARCFOUR-128", sizeof(priority_string));
1555 else
1556 strlcat(priority_string, ":!ARCFOUR-128", sizeof(priority_string));
1557
1558 strlcat(priority_string, ":!ANON-DH", sizeof(priority_string));
1559
1560 if (tls_options & _HTTP_TLS_DENY_CBC)
1561 strlcat(priority_string, ":!AES-128-CBC:!AES-256-CBC:!CAMELLIA-128-CBC:!CAMELLIA-256-CBC:!3DES-CBC", sizeof(priority_string));
1562
1563 #ifdef HAVE_GNUTLS_PRIORITY_SET_DIRECT
1564 gnutls_priority_set_direct(http->tls, priority_string, NULL);
1565
1566 #else
1567 gnutls_priority_t priority; /* Priority */
1568
1569 gnutls_priority_init(&priority, priority_string, NULL);
1570 gnutls_priority_set(http->tls, priority);
1571 gnutls_priority_deinit(priority);
1572 #endif /* HAVE_GNUTLS_PRIORITY_SET_DIRECT */
1573
1574 gnutls_transport_set_ptr(http->tls, (gnutls_transport_ptr_t)http);
1575 gnutls_transport_set_pull_function(http->tls, http_gnutls_read);
1576 #ifdef HAVE_GNUTLS_TRANSPORT_SET_PULL_TIMEOUT_FUNCTION
1577 gnutls_transport_set_pull_timeout_function(http->tls, (gnutls_pull_timeout_func)httpWait);
1578 #endif /* HAVE_GNUTLS_TRANSPORT_SET_PULL_TIMEOUT_FUNCTION */
1579 gnutls_transport_set_push_function(http->tls, http_gnutls_write);
1580
1581 while ((status = gnutls_handshake(http->tls)) != GNUTLS_E_SUCCESS)
1582 {
1583 DEBUG_printf(("5_httpStartTLS: gnutls_handshake returned %d (%s)",
1584 status, gnutls_strerror(status)));
1585
1586 if (gnutls_error_is_fatal(status))
1587 {
1588 http->error = EIO;
1589 http->status = HTTP_STATUS_ERROR;
1590
1591 _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI, gnutls_strerror(status), 0);
1592
1593 gnutls_deinit(http->tls);
1594 gnutls_certificate_free_credentials(*credentials);
1595 free(credentials);
1596 http->tls = NULL;
1597
1598 return (-1);
1599 }
1600 }
1601
1602 http->tls_credentials = credentials;
1603
1604 return (0);
1605 }
1606
1607
1608 /*
1609 * '_httpTLSStop()' - Shut down SSL/TLS on a connection.
1610 */
1611
1612 void
1613 _httpTLSStop(http_t *http) /* I - Connection to server */
1614 {
1615 int error; /* Error code */
1616
1617
1618 error = gnutls_bye(http->tls, http->mode == _HTTP_MODE_CLIENT ? GNUTLS_SHUT_RDWR : GNUTLS_SHUT_WR);
1619 if (error != GNUTLS_E_SUCCESS)
1620 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, gnutls_strerror(errno), 0);
1621
1622 gnutls_deinit(http->tls);
1623 http->tls = NULL;
1624
1625 if (http->tls_credentials)
1626 {
1627 gnutls_certificate_free_credentials(*(http->tls_credentials));
1628 free(http->tls_credentials);
1629 http->tls_credentials = NULL;
1630 }
1631 }
1632
1633
1634 /*
1635 * '_httpTLSWrite()' - Write to a SSL/TLS connection.
1636 */
1637
1638 int /* O - Bytes written */
1639 _httpTLSWrite(http_t *http, /* I - Connection to server */
1640 const char *buf, /* I - Buffer holding data */
1641 int len) /* I - Length of buffer */
1642 {
1643 ssize_t result; /* Return value */
1644
1645
1646 DEBUG_printf(("2http_write_ssl(http=%p, buf=%p, len=%d)", http, buf, len));
1647
1648 result = gnutls_record_send(http->tls, buf, (size_t)len);
1649
1650 if (result < 0 && !errno)
1651 {
1652 /*
1653 * Convert GNU TLS error to errno value...
1654 */
1655
1656 switch (result)
1657 {
1658 case GNUTLS_E_INTERRUPTED :
1659 errno = EINTR;
1660 break;
1661
1662 case GNUTLS_E_AGAIN :
1663 errno = EAGAIN;
1664 break;
1665
1666 default :
1667 errno = EPIPE;
1668 break;
1669 }
1670
1671 result = -1;
1672 }
1673
1674 DEBUG_printf(("3http_write_ssl: Returning %d.", (int)result));
1675
1676 return ((int)result);
1677 }