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
29static int		tls_auto_create = 0;
30					/* Auto-create self-signed certs? */
31static char		*tls_common_name = NULL;
32					/* Default common name */
33static gnutls_x509_crl_t tls_crl = NULL;/* Certificate revocation list */
34static char		*tls_keypath = NULL;
35					/* Server cert keychain path */
36static _cups_mutex_t	tls_mutex = _CUPS_MUTEX_INITIALIZER;
37					/* Mutex for keychain/certs */
38static int		tls_options = -1;/* Options for TLS connections */
39
40
41/*
42 * Local functions...
43 */
44
45static gnutls_x509_crt_t http_gnutls_create_credential(http_credential_t *credential);
46static const char	*http_gnutls_default_path(char *buffer, size_t bufsize);
47static void		http_gnutls_load_crl(void);
48static const char	*http_gnutls_make_path(char *buffer, size_t bufsize, const char *dirname, const char *filename, const char *ext);
49static ssize_t		http_gnutls_read(gnutls_transport_ptr_t ptr, void *data, size_t length);
50static 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
59int					/* O - 1 on success, 0 on failure */
60cupsMakeServerCredentials(
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
236int					/* O - 1 on success, 0 on failure */
237cupsSetServerCredentials(
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
297int					/* O - Status of call (0 = success) */
298httpCopyCredentials(
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
337http_tls_credentials_t			/* O - Internal 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
351void
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
365int					/* O - 1 if valid, 0 otherwise */
366httpCredentialsAreValidForName(
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
424http_trust_t				/* O - Level of trust */
425httpCredentialsGetTrust(
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
600time_t					/* O - Expiration date of credentials */
601httpCredentialsGetExpiration(
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
625size_t					/* O - Total size of credentials string */
626httpCredentialsString(
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
681int					/* O - 0 on success, -1 on error */
682httpLoadCredentials(
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
812int					/* O - -1 on error, 0 on success */
813httpSaveCredentials(
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
867static gnutls_x509_crt_t			/* O - Certificate */
868http_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
906static const char *			/* O - Path or NULL on error */
907http_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
950static void
951http_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
1045static const char *			/* O - Filename */
1046http_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
1083static ssize_t				/* O - Number of bytes read or -1 on error */
1084http_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
1123static ssize_t				/* O - Number of bytes written or -1 on error */
1124http_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
1145void
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
1160size_t					/* O - Bytes available */
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
1171int					/* O - Bytes read */
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
1213int					/* O - Status of connection */
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
1226void
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
1238int					/* O - 0 on success, -1 on failure */
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
1577void
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
1603int					/* O - Bytes written */
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