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