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