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