]> git.ipfire.org Git - thirdparty/cups.git/blame - cups/tls-gnutls.c
Import CUPS v2.0rc1
[thirdparty/cups.git] / cups / tls-gnutls.c
CommitLineData
1a18c85c 1/*
5d2cc5d3 2 * "$Id: tls-gnutls.c 12125 2014-08-28 15:49:29Z msweet $"
1a18c85c
MS
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 *
5d2cc5d3 54 * @since CUPS 2.0/OS 10.10@
1a18c85c
MS
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 *
5d2cc5d3 231 * @since CUPS 2.0/OS 10.10@
1a18c85c
MS
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 *
5d2cc5d3 360 * @since CUPS 2.0/OS 10.10@
1a18c85c
MS
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 *
5d2cc5d3 386 * @since CUPS 2.0/OS 10.10@
1a18c85c
MS
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 *
5d2cc5d3 478 * @since CUPS 2.0/OS 10.10@
1a18c85c
MS
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 *
5d2cc5d3 503 * @since CUPS 2.0/OS 10.10@
1a18c85c
MS
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 *
5d2cc5d3 559 * @since CUPS 2.0/OS 10.10@
1a18c85c
MS
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 *
5d2cc5d3 685 * @since CUPS 2.0/OS 10.10@
1a18c85c
MS
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);
5d2cc5d3 1189#ifdef HAVE_GNUTLS_TRANSPORT_SET_PULL_TIMEOUT_FUNCTION
1a18c85c 1190 gnutls_transport_set_pull_timeout_function(http->tls, (gnutls_pull_timeout_func)httpWait);
5d2cc5d3 1191#endif /* HAVE_GNUTLS_TRANSPORT_SET_PULL_TIMEOUT_FUNCTION */
1a18c85c
MS
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/*
5d2cc5d3 1294 * End of "$Id: tls-gnutls.c 12125 2014-08-28 15:49:29Z msweet $".
1a18c85c 1295 */