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