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