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