]> git.ipfire.org Git - thirdparty/cups.git/blame - cups/tls-darwin.c
Allow more paths in sandbox.
[thirdparty/cups.git] / cups / tls-darwin.c
CommitLineData
2c85b752
MS
1/*
2 * "$Id$"
3 *
4 * TLS support code for CUPS on OS X.
5 *
7e86f2f6 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
dafebafd 18/**** This file is included from http.c ****/
2c85b752
MS
19
20/*
dafebafd 21 * Include necessary headers...
2c85b752
MS
22 */
23
dafebafd
MS
24#include <spawn.h>
25
26extern char **environ;
27
28
c0459938
MS
29/*
30 * Local globals...
31 */
32
eb66bc71 33#ifdef HAVE_SECKEYCHAINOPEN
c0459938
MS
34static int tls_auto_create = 0;
35 /* Auto-create self-signed certs? */
36static char *tls_common_name = NULL;
37 /* Default common name */
41e0907c
MS
38static SecKeychainRef tls_keychain = NULL;
39 /* Server cert keychain */
40static char *tls_keypath = NULL;
41 /* Server cert keychain path */
42static _cups_mutex_t tls_mutex = _CUPS_MUTEX_INITIALIZER;
43 /* Mutex for keychain/certs */
eb66bc71 44#endif /* HAVE_SECKEYCHAINOPEN */
c0459938
MS
45
46
dafebafd
MS
47/*
48 * Local functions...
49 */
2c85b752 50
eb66bc71 51#ifdef HAVE_SECKEYCHAINOPEN
41e0907c 52static CFArrayRef http_cdsa_copy_server(const char *common_name);
eb66bc71 53#endif /* HAVE_SECKEYCHAINOPEN */
41e0907c 54static OSStatus http_cdsa_read(SSLConnectionRef connection, void *data, size_t *dataLength);
88f1e9c8 55static int http_cdsa_set_credentials(http_t *http);
41e0907c 56static OSStatus http_cdsa_write(SSLConnectionRef connection, const void *data, size_t *dataLength);
2c85b752
MS
57
58
3af9ac9e
MS
59/*
60 * 'cupsMakeServerCredentials()' - Make a self-signed certificate and private key pair.
61 *
62 * @since CUPS 2.0@
63 */
64
65int /* O - 1 on success, 0 on failure */
66cupsMakeServerCredentials(
f93b32b6 67 const char *path, /* I - Keychain path or @code NULL@ for default */
3af9ac9e
MS
68 const char *common_name, /* I - Common name */
69 int num_alt_names, /* I - Number of subject alternate names */
70 const char **alt_names, /* I - Subject Alternate Names */
71 time_t expiration_date) /* I - Expiration date */
72{
eb66bc71 73#if defined(HAVE_SECGENERATESELFSIGNEDCERTIFICATE) && defined(HAVE_SECKEYCHAINOPEN)
f93b32b6 74 char filename[1024]; /* Default keychain path */
41e0907c
MS
75 int status = 0; /* Return status */
76 OSStatus err; /* Error code (if any) */
77 CFStringRef cfcommon_name = NULL;
78 /* CF string for server name */
79 SecIdentityRef ident = NULL; /* Identity */
80 SecKeyRef publicKey = NULL,
81 /* Public key */
82 privateKey = NULL;
83 /* Private key */
84 CFMutableDictionaryRef keyParams = NULL;
85 /* Key generation parameters */
86
87
172bdf5d
MS
88 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));
89
3af9ac9e
MS
90 (void)num_alt_names;
91 (void)alt_names;
92 (void)expiration_date;
93
f93b32b6
MS
94 if (!path)
95 {
96 const char *home = getenv("HOME"); /* HOME environment variable */
97
98 if (getuid() && home)
99 snprintf(filename, sizeof(filename), "%s/Library/Keychains/login.keychain", home);
100 else
101 strlcpy(filename, "/Library/Keychains/System.keychain", sizeof(filename));
102
103 path = filename;
104
105 DEBUG_printf(("1cupsMakeServerCredentials: Using default path \"%s\".", path));
106 }
107
108 cfcommon_name = CFStringCreateWithCString(kCFAllocatorDefault, common_name, kCFStringEncodingUTF8);
41e0907c
MS
109 if (!cfcommon_name)
110 goto cleanup;
111
112 /*
113 * Create a public/private key pair...
114 */
115
f93b32b6 116 keyParams = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
41e0907c
MS
117 if (!keyParams)
118 goto cleanup;
119
120 CFDictionaryAddValue(keyParams, kSecAttrKeyType, kSecAttrKeyTypeRSA);
121 CFDictionaryAddValue(keyParams, kSecAttrKeySizeInBits, CFSTR("2048"));
f93b32b6 122 CFDictionaryAddValue(keyParams, kSecAttrLabel, CFSTR("CUPS Self-Signed Certificate"));
41e0907c
MS
123
124 err = SecKeyGeneratePair(keyParams, &publicKey, &privateKey);
125 if (err != noErr)
126 goto cleanup;
127
128 /*
129 * Create a self-signed certificate using the public/private key pair...
130 */
131
132 CFIndex usageInt = kSecKeyUsageAll;
133 CFNumberRef usage = CFNumberCreate(alloc, kCFNumberCFIndexType, &usageInt);
134 CFDictionaryRef certParams = CFDictionaryCreateMutable(kCFAllocatorDefault,
135kSecCSRBasicContraintsPathLen, CFINT(0), kSecSubjectAltName, cfcommon_name, kSecCertificateKeyUsage, usage, NULL, NULL);
136 CFRelease(usage);
137
138 const void *ca_o[] = { kSecOidOrganization, CFSTR("") };
139 const void *ca_cn[] = { kSecOidCommonName, cfcommon_name };
140 CFArrayRef ca_o_dn = CFArrayCreate(kCFAllocatorDefault, ca_o, 2, NULL);
141 CFArrayRef ca_cn_dn = CFArrayCreate(kCFAllocatorDefault, ca_cn, 2, NULL);
142 const void *ca_dn_array[2];
143
144 ca_dn_array[0] = CFArrayCreate(kCFAllocatorDefault, (const void **)&ca_o_dn, 1, NULL);
145 ca_dn_array[1] = CFArrayCreate(kCFAllocatorDefault, (const void **)&ca_cn_dn, 1, NULL);
146
147 CFArrayRef subject = CFArrayCreate(kCFAllocatorDefault, ca_dn_array, 2, NULL);
148 SecCertificateRef cert = SecGenerateSelfSignedCertificate(subject, certParams, publicKey, privateKey);
149 CFRelease(subject);
150 CFRelease(certParams);
151
152 if (!cert)
153 goto cleanup;
154
155 ident = SecIdentityCreate(kCFAllocatorDefault, cert, privateKey);
156
157 if (ident)
158 status = 1;
159
160 /*
161 * Cleanup and return...
162 */
163
164cleanup:
165
166 if (cfcommon_name)
167 CFRelease(cfcommon_name);
168
169 if (keyParams)
170 CFRelease(keyParams);
171
172 if (ident)
173 CFRelease(ident);
174
175 if (cert)
176 CFRelease(cert);
177
178 if (publicKey)
179 CFRelease(publicKey);
180
181 if (privateKey)
182 CFRelease(publicKey);
183
184 return (status);
185
eb66bc71 186#else /* !(HAVE_SECGENERATESELFSIGNEDCERTIFICATE && HAVE_SECKEYCHAINOPEN) */
41e0907c 187 int pid, /* Process ID of command */
7d58a105
MS
188 status, /* Status of command */
189 i; /* Looping var */
41e0907c
MS
190 char command[1024], /* Command */
191 *argv[4], /* Command-line arguments */
7d58a105
MS
192 *envp[1000], /* Environment variables */
193 days[32], /* CERTTOOL_EXPIRATION_DAYS env var */
41e0907c 194 keychain[1024], /* Keychain argument */
f93b32b6
MS
195 infofile[1024], /* Type-in information for cert */
196 filename[1024]; /* Default keychain path */
41e0907c
MS
197 cups_file_t *fp; /* Seed/info file */
198
199
172bdf5d
MS
200 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));
201
41e0907c
MS
202 (void)num_alt_names;
203 (void)alt_names;
41e0907c 204
f93b32b6
MS
205 if (!path)
206 {
207 const char *home = getenv("HOME"); /* HOME environment variable */
208
209 if (getuid() && home)
210 snprintf(filename, sizeof(filename), "%s/Library/Keychains/login.keychain", home);
211 else
212 strlcpy(filename, "/Library/Keychains/System.keychain", sizeof(filename));
213
214 path = filename;
215
216 DEBUG_printf(("1cupsMakeServerCredentials: Using default path \"%s\".", path));
217 }
218
41e0907c
MS
219 /*
220 * Run the "certtool" command to generate a self-signed certificate...
221 */
222
223 if (!cupsFileFind("certtool", getenv("PATH"), 1, command, sizeof(command)))
224 return (-1);
225
226 /*
227 * Create a file with the certificate information fields...
228 *
229 * Note: This assumes that the default questions are asked by the certtool
230 * command...
231 */
232
233 if ((fp = cupsTempFile2(infofile, sizeof(infofile))) == NULL)
234 return (-1);
235
236 cupsFilePrintf(fp,
237 "CUPS Self-Signed Certificate\n"
238 /* Enter key and certificate label */
239 "r\n" /* Generate RSA key pair */
240 "2048\n" /* Key size in bits */
241 "y\n" /* OK (y = yes) */
242 "b\n" /* Usage (b=signing/encryption) */
243 "s\n" /* Sign with SHA1 */
244 "y\n" /* OK (y = yes) */
245 "%s\n" /* Common name */
246 "\n" /* Country (default) */
247 "\n" /* Organization (default) */
248 "\n" /* Organizational unit (default) */
249 "\n" /* State/Province (default) */
250 "\n" /* Email address */
251 "y\n", /* OK (y = yes) */
252 common_name);
253 cupsFileClose(fp);
254
255 snprintf(keychain, sizeof(keychain), "k=%s", path);
256
257 argv[0] = "certtool";
258 argv[1] = "c";
259 argv[2] = keychain;
260 argv[3] = NULL;
261
7d58a105
MS
262 snprintf(days, sizeof(days), "CERTTOOL_EXPIRATION_DAYS=%d", (int)((expiration_date - time(NULL) + 86399) / 86400));
263 envp[0] = days;
264 for (i = 0; i < (int)(sizeof(envp) / sizeof(envp[0]) - 2) && environ[i]; i ++)
265 envp[i + 1] = environ[i];
266 envp[i] = NULL;
267
41e0907c
MS
268 posix_spawn_file_actions_t actions; /* File actions */
269
270 posix_spawn_file_actions_init(&actions);
271 posix_spawn_file_actions_addclose(&actions, 0);
272 posix_spawn_file_actions_addopen(&actions, 0, infofile, O_RDONLY, 0);
f93b32b6
MS
273 posix_spawn_file_actions_addclose(&actions, 1);
274 posix_spawn_file_actions_addopen(&actions, 1, "/dev/null", O_WRONLY, 0);
275 posix_spawn_file_actions_addclose(&actions, 2);
276 posix_spawn_file_actions_addopen(&actions, 2, "/dev/null", O_WRONLY, 0);
41e0907c 277
7d58a105 278 if (posix_spawn(&pid, command, &actions, NULL, argv, envp))
41e0907c
MS
279 {
280 unlink(infofile);
281 return (-1);
282 }
283
284 posix_spawn_file_actions_destroy(&actions);
285
286 unlink(infofile);
287
288 while (waitpid(pid, &status, 0) < 0)
289 if (errno != EINTR)
290 {
291 status = -1;
292 break;
293 }
294
295 return (!status);
eb66bc71 296#endif /* HAVE_SECGENERATESELFSIGNEDCERTIFICATE && HAVE_SECKEYCHAINOPEN */
3af9ac9e
MS
297}
298
299
300/*
301 * 'cupsSetServerCredentials()' - Set the default server credentials.
302 *
303 * Note: The server credentials are used by all threads in the running process.
304 * This function is threadsafe.
305 *
306 * @since CUPS 2.0@
307 */
308
309int /* O - 1 on success, 0 on failure */
310cupsSetServerCredentials(
f93b32b6 311 const char *path, /* I - Keychain path or @code NULL@ for default */
3af9ac9e
MS
312 const char *common_name, /* I - Default common name for server */
313 int auto_create) /* I - 1 = automatically create self-signed certificates */
314{
a27a134a
MS
315 DEBUG_printf(("cupsSetServerCredentials(path=\"%s\", common_name=\"%s\", auto_create=%d)", path, common_name, auto_create));
316
eb66bc71 317#ifdef HAVE_SECKEYCHAINOPEN
f93b32b6 318 char filename[1024]; /* Filename for keychain */
c0459938 319 SecKeychainRef keychain = NULL;/* Temporary keychain */
3af9ac9e 320
c0459938 321
f93b32b6
MS
322 if (!path)
323 {
324 const char *home = getenv("HOME"); /* HOME environment variable */
325
326 if (getuid() && home)
327 snprintf(filename, sizeof(filename), "%s/Library/Keychains/login.keychain", home);
328 else
329 strlcpy(filename, "/Library/Keychains/System.keychain", sizeof(filename));
330
331 path = filename;
332
333 DEBUG_printf(("1cupsSetServerCredentials: Using default path \"%s\".", path));
334 }
335
c0459938
MS
336 if (SecKeychainOpen(path, &keychain) != noErr)
337 {
338 /* TODO: Set cups last error string */
a27a134a 339 DEBUG_puts("1cupsSetServerCredentials: Unable to open keychain, returning 0.");
c0459938
MS
340 return (0);
341 }
342
343 _cupsMutexLock(&tls_mutex);
344
345 /*
346 * Close any keychain that is currently open...
347 */
348
349 if (tls_keychain)
350 CFRelease(tls_keychain);
351
41e0907c
MS
352 if (tls_keypath)
353 _cupsStrFree(tls_keypath);
354
c0459938
MS
355 if (tls_common_name)
356 _cupsStrFree(tls_common_name);
357
358 /*
359 * Save the new keychain...
360 */
361
362 tls_keychain = keychain;
41e0907c 363 tls_keypath = _cupsStrAlloc(path);
c0459938
MS
364 tls_auto_create = auto_create;
365 tls_common_name = _cupsStrAlloc(common_name);
366
367 _cupsMutexUnlock(&tls_mutex);
368
a27a134a 369 DEBUG_puts("1cupsSetServerCredentials: Opened keychain, returning 1.");
c0459938 370 return (1);
eb66bc71
MS
371
372#else
a27a134a 373 DEBUG_puts("1cupsSetServerCredentials: No keychain support compiled in, returning 0.");
eb66bc71
MS
374 return (0);
375#endif /* HAVE_SECKEYCHAINOPEN */
3af9ac9e
MS
376}
377
2c85b752
MS
378
379/*
380 * 'httpCopyCredentials()' - Copy the credentials associated with the peer in
381 * an encrypted connection.
382 *
383 * @since CUPS 1.5/OS X 10.7@
384 */
385
386int /* O - Status of call (0 = success) */
387httpCopyCredentials(
388 http_t *http, /* I - Connection to server */
389 cups_array_t **credentials) /* O - Array of credentials */
390{
391 OSStatus error; /* Error code */
392 SecTrustRef peerTrust; /* Peer trust reference */
393 CFIndex count; /* Number of credentials */
394 SecCertificateRef secCert; /* Certificate reference */
395 CFDataRef data; /* Certificate data */
396 int i; /* Looping var */
397
398
376d7c69
MS
399 DEBUG_printf(("httpCopyCredentials(http=%p, credentials=%p)", http, credentials));
400
2c85b752
MS
401 if (credentials)
402 *credentials = NULL;
403
404 if (!http || !http->tls || !credentials)
405 return (-1);
406
407 if (!(error = SSLCopyPeerTrust(http->tls, &peerTrust)) && peerTrust)
408 {
376d7c69
MS
409 DEBUG_printf(("2httpCopyCredentials: Peer provided %d certificates.", (int)SecTrustGetCertificateCount(peerTrust)));
410
2c85b752
MS
411 if ((*credentials = cupsArrayNew(NULL, NULL)) != NULL)
412 {
413 count = SecTrustGetCertificateCount(peerTrust);
414
415 for (i = 0; i < count; i ++)
416 {
417 secCert = SecTrustGetCertificateAtIndex(peerTrust, i);
376d7c69
MS
418
419#ifdef DEBUG
420 CFStringRef cf_name = SecCertificateCopySubjectSummary(secCert);
421 char name[1024];
422 if (cf_name)
423 CFStringGetCString(cf_name, name, sizeof(name), kCFStringEncodingUTF8);
424 else
425 strlcpy(name, "unknown", sizeof(name));
426
427 DEBUG_printf(("2httpCopyCredentials: Certificate %d name is \"%s\".", i, name));
428#endif /* DEBUG */
429
88f1e9c8 430 if ((data = SecCertificateCopyData(secCert)) != NULL)
2c85b752 431 {
376d7c69
MS
432 DEBUG_printf(("2httpCopyCredentials: Adding %d byte certificate blob.", (int)CFDataGetLength(data)));
433
7e86f2f6 434 httpAddCredential(*credentials, CFDataGetBytePtr(data), (size_t)CFDataGetLength(data));
2c85b752
MS
435 CFRelease(data);
436 }
437 }
438 }
439
440 CFRelease(peerTrust);
441 }
442
443 return (error);
444}
445
446
9653cfdf
MS
447/*
448 * 'http_cdsa_create_credential()' - Create a single credential in the internal format.
449 */
450
451static SecCertificateRef /* O - Certificate */
452http_cdsa_create_credential(
453 http_credential_t *credential) /* I - Credential */
454{
455 if (!credential)
456 return (NULL);
457
458 return (SecCertificateCreateWithBytes(kCFAllocatorDefault, credential->data, (CFIndex)credential->datalen));
459}
460
461
2c85b752
MS
462/*
463 * '_httpCreateCredentials()' - Create credentials in the internal format.
464 */
465
466http_tls_credentials_t /* O - Internal credentials */
467_httpCreateCredentials(
468 cups_array_t *credentials) /* I - Array of credentials */
469{
470 CFMutableArrayRef peerCerts; /* Peer credentials reference */
471 SecCertificateRef secCert; /* Certificate reference */
2c85b752
MS
472 http_credential_t *credential; /* Credential data */
473
474
475 if (!credentials)
476 return (NULL);
477
478 if ((peerCerts = CFArrayCreateMutable(kCFAllocatorDefault,
479 cupsArrayCount(credentials),
480 &kCFTypeArrayCallBacks)) == NULL)
481 return (NULL);
482
483 for (credential = (http_credential_t *)cupsArrayFirst(credentials);
484 credential;
485 credential = (http_credential_t *)cupsArrayNext(credentials))
486 {
9653cfdf 487 if ((secCert = http_cdsa_create_credential(credential)) != NULL)
2c85b752 488 {
9653cfdf
MS
489 CFArrayAppendValue(peerCerts, secCert);
490 CFRelease(secCert);
2c85b752
MS
491 }
492 }
493
494 return (peerCerts);
495}
496
497
3af9ac9e 498/*
524c65e6 499 * 'httpCredentialsAreValidForName()' - Return whether the credentials are valid for the given name.
3af9ac9e
MS
500 *
501 * @since CUPS 2.0@
502 */
503
524c65e6
MS
504int /* O - 1 if valid, 0 otherwise */
505httpCredentialsAreValidForName(
506 cups_array_t *credentials, /* I - Credentials */
507 const char *common_name) /* I - Name to check */
508{
509 SecCertificateRef secCert; /* Certificate reference */
510 CFStringRef cfcert_name = NULL;
511 /* Certificate's common name (CF string) */
512 char cert_name[256]; /* Certificate's common name (C string) */
513 int valid = 1; /* Valid name? */
514
515
516 if ((secCert = http_cdsa_create_credential((http_credential_t *)cupsArrayFirst(credentials))) == NULL)
517 return (0);
518
519 /*
520 * Compare the common names...
521 */
522
523 if ((cfcert_name = SecCertificateCopySubjectSummary(secCert)) == NULL)
524 {
525 /*
526 * Can't get common name, cannot be valid...
527 */
528
529 valid = 0;
530 }
531 else if (CFStringGetCString(cfcert_name, cert_name, sizeof(cert_name), kCFStringEncodingUTF8) &&
532 _cups_strcasecmp(common_name, cert_name))
533 {
534 /*
535 * Not an exact match for the common name, check for wildcard certs...
536 */
537
538 const char *domain = strchr(common_name, '.');
539 /* Domain in common name */
540
541 if (strncmp(cert_name, "*.", 2) || !domain || _cups_strcasecmp(domain, cert_name + 1))
542 {
543 /*
544 * Not a wildcard match.
545 */
546
547 /* TODO: Check subject alternate names */
548 valid = 0;
549 }
550 }
551
552 if (cfcert_name)
553 CFRelease(cfcert_name);
554
555 CFRelease(secCert);
556
557 return (valid);
558}
559
560
561/*
562 * 'httpCredentialsGetTrust()' - Return the trust of credentials.
563 *
564 * @since CUPS 2.0@
565 */
566
567http_trust_t /* O - Level of trust */
568httpCredentialsGetTrust(
376d7c69
MS
569 cups_array_t *credentials, /* I - Credentials */
570 const char *common_name) /* I - Common name for trust lookup */
3af9ac9e 571{
9653cfdf 572 SecCertificateRef secCert; /* Certificate reference */
524c65e6
MS
573 http_trust_t trust = HTTP_TRUST_OK;
574 /* Trusted? */
376d7c69 575 cups_array_t *tcreds = NULL; /* Trusted credentials */
9653cfdf
MS
576 _cups_globals_t *cg = _cupsGlobals();
577 /* Per-thread globals */
3af9ac9e 578
9653cfdf 579
376d7c69 580 if (!common_name)
524c65e6 581 return (HTTP_TRUST_UNKNOWN);
376d7c69 582
9653cfdf 583 if ((secCert = http_cdsa_create_credential((http_credential_t *)cupsArrayFirst(credentials))) == NULL)
524c65e6 584 return (HTTP_TRUST_UNKNOWN);
9653cfdf 585
376d7c69 586 /*
88f1e9c8 587 * Look this common name up in the default keychains...
376d7c69
MS
588 */
589
88f1e9c8 590 httpLoadCredentials(NULL, &tcreds, common_name);
376d7c69
MS
591
592 if (tcreds)
593 {
594 char credentials_str[1024], /* String for incoming credentials */
595 tcreds_str[1024]; /* String for saved credentials */
596
597 httpCredentialsString(credentials, credentials_str, sizeof(credentials_str));
598 httpCredentialsString(tcreds, tcreds_str, sizeof(tcreds_str));
599
600 if (strcmp(credentials_str, tcreds_str))
601 {
602 /*
603 * Credentials don't match, let's look at the expiration date of the new
604 * credentials and allow if the new ones have a later expiration...
605 */
606
607 if (httpCredentialsGetExpiration(credentials) <= httpCredentialsGetExpiration(tcreds) ||
524c65e6 608 !httpCredentialsAreValidForName(credentials, common_name))
376d7c69
MS
609 {
610 /*
611 * Either the new credentials are not newly issued, or the common name
612 * does not match the issued certificate...
613 */
614
524c65e6 615 trust = HTTP_TRUST_INVALID;
376d7c69 616 }
524c65e6 617 else if (httpCredentialsGetExpiration(tcreds) < time(NULL))
376d7c69
MS
618 {
619 /*
524c65e6 620 * Save the renewed credentials...
376d7c69
MS
621 */
622
524c65e6
MS
623 trust = HTTP_TRUST_RENEWED;
624
625 httpSaveCredentials(NULL, credentials, common_name);
376d7c69
MS
626 }
627 }
628
629 httpFreeCredentials(tcreds);
630 }
f51f3773 631 else if (cg->validate_certs && !httpCredentialsAreValidForName(credentials, common_name))
524c65e6 632 trust = HTTP_TRUST_INVALID;
376d7c69 633
9653cfdf 634 if (!cg->expired_certs && !SecCertificateIsValid(secCert, CFAbsoluteTimeGetCurrent()))
524c65e6 635 trust = HTTP_TRUST_EXPIRED;
25731360 636 else if (!cg->any_root && cupsArrayCount(credentials) == 1)
524c65e6 637 trust = HTTP_TRUST_INVALID;
376d7c69 638
9653cfdf
MS
639 CFRelease(secCert);
640
524c65e6 641 return (trust);
3af9ac9e
MS
642}
643
644
645/*
646 * 'httpCredentialsGetExpiration()' - Return the expiration date of the credentials.
647 *
648 * @since CUPS 2.0@
649 */
650
651time_t /* O - Expiration date of credentials */
652httpCredentialsGetExpiration(
653 cups_array_t *credentials) /* I - Credentials */
654{
9653cfdf
MS
655 SecCertificateRef secCert; /* Certificate reference */
656 time_t expiration; /* Expiration date */
3af9ac9e 657
9653cfdf
MS
658
659 if ((secCert = http_cdsa_create_credential((http_credential_t *)cupsArrayFirst(credentials))) == NULL)
660 return (0);
661
376d7c69 662 expiration = (time_t)(SecCertificateNotValidAfter(secCert) + kCFAbsoluteTimeIntervalSince1970);
9653cfdf
MS
663
664 CFRelease(secCert);
665
666 return (expiration);
3af9ac9e
MS
667}
668
669
72d05bc9
MS
670/*
671 * 'httpCredentialsString()' - Return a string representing the credentials.
672 *
673 * @since CUPS 2.0@
674 */
675
676size_t /* O - Total size of credentials string */
677httpCredentialsString(
678 cups_array_t *credentials, /* I - Credentials */
679 char *buffer, /* I - Buffer or @code NULL@ */
680 size_t bufsize) /* I - Size of buffer */
681{
376d7c69 682 http_credential_t *first; /* First certificate */
9653cfdf 683 SecCertificateRef secCert; /* Certificate reference */
9653cfdf
MS
684
685
376d7c69
MS
686 DEBUG_printf(("httpCredentialsString(credentials=%p, buffer=%p, bufsize=" CUPS_LLFMT ")", credentials, buffer, CUPS_LLCAST bufsize));
687
9653cfdf
MS
688 if (!buffer)
689 return (0);
72d05bc9
MS
690
691 if (buffer && bufsize > 0)
692 *buffer = '\0';
693
376d7c69
MS
694 if ((first = (http_credential_t *)cupsArrayFirst(credentials)) != NULL &&
695 (secCert = http_cdsa_create_credential(first)) != NULL)
9653cfdf 696 {
376d7c69
MS
697 CFStringRef cf_name; /* CF common name string */
698 char name[256]; /* Common name associated with cert */
699 time_t expiration; /* Expiration date of cert */
700 _cups_md5_state_t md5_state; /* MD5 state */
701 unsigned char md5_digest[16]; /* MD5 result */
702
703 if ((cf_name = SecCertificateCopySubjectSummary(secCert)) != NULL)
9653cfdf 704 {
376d7c69
MS
705 CFStringGetCString(cf_name, name, (CFIndex)sizeof(name), kCFStringEncodingUTF8);
706 CFRelease(cf_name);
9653cfdf 707 }
376d7c69
MS
708 else
709 strlcpy(name, "unknown", sizeof(name));
710
711 expiration = (time_t)(SecCertificateNotValidAfter(secCert) + kCFAbsoluteTimeIntervalSince1970);
712
713 _cupsMD5Init(&md5_state);
714 _cupsMD5Append(&md5_state, first->data, (int)first->datalen);
715 _cupsMD5Finish(&md5_state, md5_digest);
716
717 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]);
9653cfdf
MS
718
719 CFRelease(secCert);
720 }
721
376d7c69
MS
722 DEBUG_printf(("1httpCredentialsString: Returning \"%s\".", buffer));
723
9653cfdf 724 return (strlen(buffer));
72d05bc9
MS
725}
726
727
2c85b752
MS
728/*
729 * '_httpFreeCredentials()' - Free internal credentials.
730 */
731
732void
733_httpFreeCredentials(
734 http_tls_credentials_t credentials) /* I - Internal credentials */
735{
736 if (!credentials)
737 return;
738
739 CFRelease(credentials);
740}
741
742
72d05bc9
MS
743/*
744 * 'httpLoadCredentials()' - Load X.509 credentials from a keychain file.
745 *
746 * @since CUPS 2.0@
747 */
748
dafebafd 749int /* O - 0 on success, -1 on error */
72d05bc9 750httpLoadCredentials(
f93b32b6 751 const char *path, /* I - Keychain path or @code NULL@ for default */
72d05bc9
MS
752 cups_array_t **credentials, /* IO - Credentials */
753 const char *common_name) /* I - Common name for credentials */
754{
88f1e9c8 755#ifdef HAVE_SECKEYCHAINOPEN
dafebafd 756 OSStatus err; /* Error info */
88f1e9c8 757 char filename[1024]; /* Filename for keychain */
dafebafd
MS
758 SecKeychainRef keychain = NULL;/* Keychain reference */
759 SecIdentitySearchRef search = NULL; /* Search reference */
88f1e9c8
MS
760 SecCertificateRef cert = NULL; /* Certificate */
761 CFDataRef data; /* Certificate data */
dafebafd
MS
762 SecPolicyRef policy = NULL; /* Policy ref */
763 CFStringRef cfcommon_name = NULL;
764 /* Server name */
765 CFMutableDictionaryRef query = NULL; /* Query qualifiers */
766 CFArrayRef list = NULL; /* Keychain list */
767
768
88f1e9c8
MS
769 DEBUG_printf(("httpLoadCredentials(path=\"%s\", credentials=%p, common_name=\"%s\")", path, credentials, common_name));
770
771 if (!credentials)
772 return (-1);
773
774 *credentials = NULL;
775
776 if (!path)
777 {
778 const char *home = getenv("HOME"); /* HOME environment variable */
779
780 if (getuid() && home)
781 snprintf(filename, sizeof(filename), "%s/Library/Keychains/login.keychain", home);
782 else
783 strlcpy(filename, "/Library/Keychains/System.keychain", sizeof(filename));
784
785 path = filename;
786
787 DEBUG_printf(("1httpLoadCredentials: Using default path \"%s\".", path));
788 }
789
88f1e9c8 790 if ((err = SecKeychainOpen(path, &keychain)) != noErr)
dafebafd
MS
791 goto cleanup;
792
793 cfcommon_name = CFStringCreateWithCString(kCFAllocatorDefault, common_name, kCFStringEncodingUTF8);
794
795 policy = SecPolicyCreateSSL(1, cfcommon_name);
796
797 if (cfcommon_name)
798 CFRelease(cfcommon_name);
799
800 if (!policy)
801 goto cleanup;
802
803 if (!(query = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)))
804 goto cleanup;
805
88f1e9c8 806 list = CFArrayCreate(kCFAllocatorDefault, (const void **)&keychain, 1, &kCFTypeArrayCallBacks);
dafebafd 807
88f1e9c8 808 CFDictionaryAddValue(query, kSecClass, kSecClassCertificate);
dafebafd
MS
809 CFDictionaryAddValue(query, kSecMatchPolicy, policy);
810 CFDictionaryAddValue(query, kSecReturnRef, kCFBooleanTrue);
811 CFDictionaryAddValue(query, kSecMatchLimit, kSecMatchLimitOne);
812 CFDictionaryAddValue(query, kSecMatchSearchList, list);
813
814 CFRelease(list);
815
88f1e9c8 816 err = SecItemCopyMatching(query, (CFTypeRef *)&cert);
dafebafd
MS
817
818 if (err)
819 goto cleanup;
820
88f1e9c8 821 if (CFGetTypeID(cert) != SecCertificateGetTypeID())
dafebafd
MS
822 goto cleanup;
823
88f1e9c8
MS
824 if ((data = SecCertificateCopyData(cert)) != NULL)
825 {
826 DEBUG_printf(("1httpLoadCredentials: Adding %d byte certificate blob.", (int)CFDataGetLength(data)));
827
828 *credentials = cupsArrayNew(NULL, NULL);
829 httpAddCredential(*credentials, CFDataGetBytePtr(data), (size_t)CFDataGetLength(data));
830 CFRelease(data);
831 }
dafebafd
MS
832
833 cleanup :
834
835 if (keychain)
836 CFRelease(keychain);
837 if (search)
838 CFRelease(search);
88f1e9c8
MS
839 if (cert)
840 CFRelease(cert);
dafebafd
MS
841 if (policy)
842 CFRelease(policy);
843 if (query)
844 CFRelease(query);
845
88f1e9c8
MS
846 DEBUG_printf(("1httpLoadCredentials: Returning %d.", *credentials ? 0 : -1));
847
848 return (*credentials ? 0 : -1);
849
850#else
851 (void)path;
852 (void)credentials;
853 (void)common_name;
854
855 return (-1);
856#endif /* HAVE_SECKEYCHAINOPEN */
72d05bc9
MS
857}
858
859
72d05bc9
MS
860/*
861 * 'httpSaveCredentials()' - Save X.509 credentials to a keychain file.
862 *
863 * @since CUPS 2.0@
864 */
865
866int /* O - -1 on error, 0 on success */
867httpSaveCredentials(
f93b32b6 868 const char *path, /* I - Keychain path or @code NULL@ for default */
72d05bc9
MS
869 cups_array_t *credentials, /* I - Credentials */
870 const char *common_name) /* I - Common name for credentials */
871{
eb66bc71 872#ifdef HAVE_SECKEYCHAINOPEN
88f1e9c8 873 int ret = -1; /* Return value */
41e0907c 874 OSStatus err; /* Error info */
88f1e9c8
MS
875 char filename[1024]; /* Filename for keychain */
876 SecKeychainRef keychain = NULL;/* Keychain reference */
41e0907c 877 SecIdentitySearchRef search = NULL; /* Search reference */
88f1e9c8 878 SecCertificateRef cert = NULL; /* Certificate */
88f1e9c8 879 CFMutableDictionaryRef attrs = NULL; /* Attributes for add */
41e0907c
MS
880 CFArrayRef list = NULL; /* Keychain list */
881
882
88f1e9c8
MS
883 DEBUG_printf(("httpSaveCredentials(path=\"%s\", credentials=%p, common_name=\"%s\")", path, credentials, common_name));
884 if (!credentials)
41e0907c
MS
885 goto cleanup;
886
524c65e6
MS
887 if (!httpCredentialsAreValidForName(credentials, common_name))
888 {
889 DEBUG_puts("1httpSaveCredentials: Common name does not match.");
890 return (-1);
891 }
892
88f1e9c8
MS
893 if ((cert = http_cdsa_create_credential((http_credential_t *)cupsArrayFirst(credentials))) == NULL)
894 {
895 DEBUG_puts("1httpSaveCredentials: Unable to create certificate.");
41e0907c 896 goto cleanup;
88f1e9c8 897 }
41e0907c 898
88f1e9c8 899 if (!path)
2c85b752 900 {
88f1e9c8 901 const char *home = getenv("HOME"); /* HOME environment variable */
2c85b752 902
88f1e9c8
MS
903 if (getuid() && home)
904 snprintf(filename, sizeof(filename), "%s/Library/Keychains/login.keychain", home);
905 else
906 strlcpy(filename, "/Library/Keychains/System.keychain", sizeof(filename));
2c85b752 907
88f1e9c8
MS
908 path = filename;
909
910 DEBUG_printf(("1httpSaveCredentials: Using default path \"%s\".", path));
2c85b752
MS
911 }
912
88f1e9c8 913 if ((err = SecKeychainOpen(path, &keychain)) != noErr)
2c85b752 914 {
88f1e9c8
MS
915 DEBUG_printf(("1httpSaveCredentials: SecKeychainOpen returned %d.", (int)err));
916 goto cleanup;
2c85b752 917 }
2c85b752 918
88f1e9c8 919 if ((list = CFArrayCreate(kCFAllocatorDefault, (const void **)&keychain, 1, &kCFTypeArrayCallBacks)) == NULL)
2c85b752 920 {
88f1e9c8
MS
921 DEBUG_puts("1httpSaveCredentials: Unable to create list of keychains.");
922 goto cleanup;
2c85b752 923 }
88f1e9c8 924
88f1e9c8
MS
925 if ((attrs = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)) == NULL)
926 {
927 DEBUG_puts("1httpSaveCredentials: Unable to create dictionary.");
928 goto cleanup;
2c85b752
MS
929 }
930
88f1e9c8 931 CFDictionaryAddValue(attrs, kSecClass, kSecClassCertificate);
88f1e9c8
MS
932 CFDictionaryAddValue(attrs, kSecValueRef, cert);
933 CFDictionaryAddValue(attrs, kSecMatchSearchList, list);
2c85b752 934
88f1e9c8 935 /* Note: SecItemAdd consumes "attrs"... */
524c65e6 936 err = SecItemAdd(attrs, NULL);
88f1e9c8 937 DEBUG_printf(("1httpSaveCredentials: SecItemAdd returned %d.", (int)err));
2c85b752 938
88f1e9c8 939 cleanup :
2c85b752 940
88f1e9c8
MS
941 if (list)
942 CFRelease(list);
943 if (keychain)
944 CFRelease(keychain);
945 if (search)
946 CFRelease(search);
947 if (cert)
948 CFRelease(cert);
2c85b752 949
88f1e9c8 950 DEBUG_printf(("1httpSaveCredentials: Returning %d.", ret));
2c85b752 951
88f1e9c8 952 return (ret);
2c85b752 953
88f1e9c8
MS
954#else
955 (void)path;
956 (void)credentials;
957 (void)common_name;
2c85b752 958
88f1e9c8
MS
959 return (-1);
960#endif /* HAVE_SECKEYCHAINOPEN */
2c85b752
MS
961}
962
963
964/*
25731360 965 * '_httpTLSInitialize()' - Initialize the TLS stack.
2c85b752
MS
966 */
967
25731360
MS
968void
969_httpTLSInitialize(void)
2c85b752
MS
970{
971 /*
972 * Nothing to do...
973 */
974}
975
976
977/*
25731360 978 * '_httpTLSPending()' - Return the number of pending TLS-encrypted bytes.
2c85b752
MS
979 */
980
25731360
MS
981size_t
982_httpTLSPending(http_t *http) /* I - HTTP connection */
2c85b752
MS
983{
984 size_t bytes; /* Bytes that are available */
985
986
987 if (!SSLGetBufferedReadSize(http->tls, &bytes))
988 return (bytes);
989
990 return (0);
991}
992
993
994/*
25731360 995 * '_httpTLSRead()' - Read from a SSL/TLS connection.
2c85b752
MS
996 */
997
25731360
MS
998int /* O - Bytes read */
999_httpTLSRead(http_t *http, /* I - HTTP connection */
2c85b752
MS
1000 char *buf, /* I - Buffer to store data */
1001 int len) /* I - Length of buffer */
1002{
1003 int result; /* Return value */
1004 OSStatus error; /* Error info */
1005 size_t processed; /* Number of bytes processed */
1006
1007
7e86f2f6 1008 error = SSLRead(http->tls, buf, (size_t)len, &processed);
25731360 1009 DEBUG_printf(("6_httpTLSRead: error=%d, processed=%d", (int)error,
2c85b752
MS
1010 (int)processed));
1011 switch (error)
1012 {
1013 case 0 :
1014 result = (int)processed;
1015 break;
1016
1017 case errSSLWouldBlock :
1018 if (processed)
1019 result = (int)processed;
1020 else
1021 {
1022 result = -1;
1023 errno = EINTR;
1024 }
1025 break;
1026
1027 case errSSLClosedGraceful :
1028 default :
1029 if (processed)
1030 result = (int)processed;
1031 else
1032 {
1033 result = -1;
1034 errno = EPIPE;
1035 }
1036 break;
1037 }
1038
1039 return (result);
1040}
1041
1042
2c85b752 1043/*
25731360 1044 * '_httpTLSStart()' - Set up SSL/TLS support on a connection.
2c85b752
MS
1045 */
1046
25731360
MS
1047int /* O - 0 on success, -1 on failure */
1048_httpTLSStart(http_t *http) /* I - HTTP connection */
2c85b752
MS
1049{
1050 char hostname[256], /* Hostname */
1051 *hostptr; /* Pointer into hostname */
1052 _cups_globals_t *cg = _cupsGlobals();
1053 /* Pointer to library globals */
1054 OSStatus error; /* Error code */
1055 const char *message = NULL;/* Error message */
1056 cups_array_t *credentials; /* Credentials array */
1057 cups_array_t *names; /* CUPS distinguished names */
1058 CFArrayRef dn_array; /* CF distinguished names array */
1059 CFIndex count; /* Number of credentials */
1060 CFDataRef data; /* Certificate data */
1061 int i; /* Looping var */
1062 http_credential_t *credential; /* Credential data */
1063
1064
25731360 1065 DEBUG_printf(("7_httpTLSStart(http=%p)", http));
2c85b752 1066
c913d726 1067#ifdef HAVE_SECKEYCHAINOPEN
41e0907c 1068 if (http->mode == _HTTP_MODE_SERVER && !tls_keychain)
c913d726
MS
1069#else
1070 if (http->mode == _HTTP_MODE_SERVER)
1071#endif /* HAVE_SECKEYCHAINOPEN */
2c85b752 1072 {
25731360 1073 DEBUG_puts("4_httpTLSStart: cupsSetServerCredentials not called.");
41e0907c
MS
1074 http->error = errno = EINVAL;
1075 http->status = HTTP_STATUS_ERROR;
1076 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Server credentials not set."), 1);
2c85b752 1077
41e0907c 1078 return (-1);
2c85b752
MS
1079 }
1080
72d05bc9 1081 if ((http->tls = SSLCreateContext(kCFAllocatorDefault, http->mode == _HTTP_MODE_CLIENT ? kSSLClientSide : kSSLServerSide, kSSLStreamType)) == NULL)
2c85b752 1082 {
25731360 1083 DEBUG_puts("4_httpTLSStart: SSLCreateContext failed.");
2c85b752
MS
1084 http->error = errno = ENOMEM;
1085 http->status = HTTP_STATUS_ERROR;
1086 _cupsSetHTTPError(HTTP_STATUS_ERROR);
1087
1088 return (-1);
1089 }
1090
1091 error = SSLSetConnection(http->tls, http);
25731360 1092 DEBUG_printf(("4_httpTLSStart: SSLSetConnection, error=%d", (int)error));
2c85b752
MS
1093
1094 if (!error)
1095 {
1096 error = SSLSetIOFuncs(http->tls, http_cdsa_read, http_cdsa_write);
25731360 1097 DEBUG_printf(("4_httpTLSStart: SSLSetIOFuncs, error=%d", (int)error));
2c85b752
MS
1098 }
1099
1100 if (!error)
1101 {
1102 error = SSLSetSessionOption(http->tls, kSSLSessionOptionBreakOnServerAuth,
1103 true);
25731360 1104 DEBUG_printf(("4_httpTLSStart: SSLSetSessionOption, error=%d",
2c85b752
MS
1105 (int)error));
1106 }
1107
41e0907c 1108 if (!error && http->mode == _HTTP_MODE_CLIENT)
2c85b752 1109 {
41e0907c
MS
1110 /*
1111 * Client: set client-side credentials, if any...
1112 */
1113
2c85b752
MS
1114 if (cg->client_cert_cb)
1115 {
1116 error = SSLSetSessionOption(http->tls,
1117 kSSLSessionOptionBreakOnCertRequested, true);
25731360 1118 DEBUG_printf(("4_httpTLSStart: kSSLSessionOptionBreakOnCertRequested, "
2c85b752
MS
1119 "error=%d", (int)error));
1120 }
1121 else
1122 {
88f1e9c8
MS
1123 error = http_cdsa_set_credentials(http);
1124 DEBUG_printf(("4_httpTLSStart: http_cdsa_set_credentials, error=%d",
2c85b752
MS
1125 (int)error));
1126 }
1127 }
41e0907c
MS
1128 else if (!error)
1129 {
1130 /*
1131 * Server: find/create a certificate for TLS...
1132 */
1133
1134 if (http->fields[HTTP_FIELD_HOST][0])
1135 {
1136 /*
1137 * Use hostname for TLS upgrade...
1138 */
1139
1140 strlcpy(hostname, http->fields[HTTP_FIELD_HOST], sizeof(hostname));
1141 }
1142 else
1143 {
1144 /*
1145 * Resolve hostname from connection address...
1146 */
1147
1148 http_addr_t addr; /* Connection address */
1149 socklen_t addrlen; /* Length of address */
1150
1151 addrlen = sizeof(addr);
1152 if (getsockname(http->fd, (struct sockaddr *)&addr, &addrlen))
1153 {
25731360 1154 DEBUG_printf(("4_httpTLSStart: Unable to get socket address: %s", strerror(errno)));
41e0907c
MS
1155 hostname[0] = '\0';
1156 }
1157 else if (httpAddrLocalhost(&addr))
1158 hostname[0] = '\0';
1159 else
a27a134a
MS
1160 {
1161 httpAddrLookup(&addr, hostname, sizeof(hostname));
25731360 1162 DEBUG_printf(("4_httpTLSStart: Resolved socket address to \"%s\".", hostname));
a27a134a 1163 }
41e0907c
MS
1164 }
1165
eb66bc71 1166#ifdef HAVE_SECKEYCHAINOPEN
a27a134a
MS
1167 if (isdigit(hostname[0] & 255) || hostname[0] == '[')
1168 hostname[0] = '\0'; /* Don't allow numeric addresses */
1169
41e0907c
MS
1170 if (hostname[0])
1171 http->tls_credentials = http_cdsa_copy_server(hostname);
1172 else if (tls_common_name)
1173 http->tls_credentials = http_cdsa_copy_server(tls_common_name);
1174
1175 if (!http->tls_credentials && tls_auto_create && (hostname[0] || tls_common_name))
1176 {
25731360 1177 DEBUG_printf(("4_httpTLSStart: Auto-create credentials for \"%s\".", hostname[0] ? hostname : tls_common_name));
41e0907c
MS
1178
1179 if (!cupsMakeServerCredentials(tls_keypath, hostname[0] ? hostname : tls_common_name, 0, NULL, time(NULL) + 365 * 86400))
1180 {
25731360 1181 DEBUG_puts("4_httpTLSStart: cupsMakeServerCredentials failed.");
41e0907c
MS
1182 http->error = errno = EINVAL;
1183 http->status = HTTP_STATUS_ERROR;
1184 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to create server credentials."), 1);
1185
1186 return (-1);
1187 }
1188
1189 http->tls_credentials = http_cdsa_copy_server(hostname[0] ? hostname : tls_common_name);
1190 }
eb66bc71 1191#endif /* HAVE_SECKEYCHAINOPEN */
41e0907c
MS
1192
1193 if (!http->tls_credentials)
1194 {
25731360 1195 DEBUG_puts("4_httpTLSStart: Unable to find server credentials.");
41e0907c
MS
1196 http->error = errno = EINVAL;
1197 http->status = HTTP_STATUS_ERROR;
1198 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to find server credentials."), 1);
1199
1200 return (-1);
1201 }
1202
1203 error = SSLSetCertificate(http->tls, http->tls_credentials);
1204
25731360 1205 DEBUG_printf(("4_httpTLSStart: SSLSetCertificate, error=%d", (int)error));
41e0907c
MS
1206 }
1207
25731360 1208 DEBUG_printf(("4_httpTLSStart: tls_credentials=%p", http->tls_credentials));
2c85b752
MS
1209
1210 /*
1211 * Let the server know which hostname/domain we are trying to connect to
1212 * in case it wants to serve up a certificate with a matching common name.
1213 */
1214
41e0907c 1215 if (!error && http->mode == _HTTP_MODE_CLIENT)
2c85b752 1216 {
41e0907c
MS
1217 /*
1218 * Client: get the hostname to use for TLS...
1219 */
1220
1221 if (httpAddrLocalhost(http->hostaddr))
1222 {
1223 strlcpy(hostname, "localhost", sizeof(hostname));
1224 }
1225 else
1226 {
1227 /*
1228 * Otherwise make sure the hostname we have does not end in a trailing dot.
1229 */
1230
1231 strlcpy(hostname, http->hostname, sizeof(hostname));
1232 if ((hostptr = hostname + strlen(hostname) - 1) >= hostname &&
1233 *hostptr == '.')
1234 *hostptr = '\0';
1235 }
1236
2c85b752
MS
1237 error = SSLSetPeerDomainName(http->tls, hostname, strlen(hostname));
1238
25731360 1239 DEBUG_printf(("4_httpTLSStart: SSLSetPeerDomainName, error=%d", (int)error));
2c85b752
MS
1240 }
1241
1242 if (!error)
1243 {
1244 int done = 0; /* Are we done yet? */
1245
1246 while (!error && !done)
1247 {
1248 error = SSLHandshake(http->tls);
1249
25731360 1250 DEBUG_printf(("4_httpTLSStart: SSLHandshake returned %d.", (int)error));
2c85b752
MS
1251
1252 switch (error)
1253 {
1254 case noErr :
1255 done = 1;
1256 break;
1257
1258 case errSSLWouldBlock :
1259 error = noErr; /* Force a retry */
1260 usleep(1000); /* in 1 millisecond */
1261 break;
1262
1263 case errSSLServerAuthCompleted :
1264 error = 0;
1265 if (cg->server_cert_cb)
1266 {
1267 error = httpCopyCredentials(http, &credentials);
1268 if (!error)
1269 {
1270 error = (cg->server_cert_cb)(http, http->tls, credentials,
1271 cg->server_cert_data);
1272 httpFreeCredentials(credentials);
1273 }
1274
25731360 1275 DEBUG_printf(("4_httpTLSStart: Server certificate callback "
2c85b752
MS
1276 "returned %d.", (int)error));
1277 }
1278 break;
1279
1280 case errSSLClientCertRequested :
1281 error = 0;
1282
1283 if (cg->client_cert_cb)
1284 {
1285 names = NULL;
1286 if (!(error = SSLCopyDistinguishedNames(http->tls, &dn_array)) &&
1287 dn_array)
1288 {
1289 if ((names = cupsArrayNew(NULL, NULL)) != NULL)
1290 {
1291 for (i = 0, count = CFArrayGetCount(dn_array); i < count; i++)
1292 {
1293 data = (CFDataRef)CFArrayGetValueAtIndex(dn_array, i);
1294
1295 if ((credential = malloc(sizeof(*credential))) != NULL)
1296 {
7e86f2f6 1297 credential->datalen = (size_t)CFDataGetLength(data);
2c85b752
MS
1298 if ((credential->data = malloc(credential->datalen)))
1299 {
1300 memcpy((void *)credential->data, CFDataGetBytePtr(data),
1301 credential->datalen);
1302 cupsArrayAdd(names, credential);
1303 }
1304 else
1305 free(credential);
1306 }
1307 }
1308 }
1309
1310 CFRelease(dn_array);
1311 }
1312
1313 if (!error)
1314 {
1315 error = (cg->client_cert_cb)(http, http->tls, names,
1316 cg->client_cert_data);
1317
25731360 1318 DEBUG_printf(("4_httpTLSStart: Client certificate callback "
2c85b752
MS
1319 "returned %d.", (int)error));
1320 }
1321
1322 httpFreeCredentials(names);
1323 }
1324 break;
1325
1326 case errSSLUnknownRootCert :
1327 message = _("Unable to establish a secure connection to host "
1328 "(untrusted certificate).");
1329 break;
1330
1331 case errSSLNoRootCert :
1332 message = _("Unable to establish a secure connection to host "
1333 "(self-signed certificate).");
1334 break;
1335
1336 case errSSLCertExpired :
1337 message = _("Unable to establish a secure connection to host "
1338 "(expired certificate).");
1339 break;
1340
1341 case errSSLCertNotYetValid :
1342 message = _("Unable to establish a secure connection to host "
1343 "(certificate not yet valid).");
1344 break;
1345
1346 case errSSLHostNameMismatch :
1347 message = _("Unable to establish a secure connection to host "
1348 "(host name mismatch).");
1349 break;
1350
1351 case errSSLXCertChainInvalid :
1352 message = _("Unable to establish a secure connection to host "
1353 "(certificate chain invalid).");
1354 break;
1355
1356 case errSSLConnectionRefused :
1357 message = _("Unable to establish a secure connection to host "
1358 "(peer dropped connection before responding).");
1359 break;
1360
1361 default :
1362 break;
1363 }
1364 }
1365 }
1366
1367 if (error)
1368 {
1369 http->error = error;
1370 http->status = HTTP_STATUS_ERROR;
1371 errno = ECONNREFUSED;
1372
1373 CFRelease(http->tls);
1374 http->tls = NULL;
1375
1376 /*
1377 * If an error string wasn't set by the callbacks use a generic one...
1378 */
1379
1380 if (!message)
1381#ifdef HAVE_CSSMERRORSTRING
1382 message = cssmErrorString(error);
1383#else
1384 message = _("Unable to establish a secure connection to host.");
1385#endif /* HAVE_CSSMERRORSTRING */
1386
1387 _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI, message, 1);
1388
1389 return (-1);
1390 }
1391
1392 return (0);
1393}
1394
1395
1396/*
25731360 1397 * '_httpTLSStop()' - Shut down SSL/TLS on a connection.
2c85b752
MS
1398 */
1399
25731360
MS
1400void
1401_httpTLSStop(http_t *http) /* I - HTTP connection */
2c85b752
MS
1402{
1403 while (SSLClose(http->tls) == errSSLWouldBlock)
1404 usleep(1000);
1405
1406 CFRelease(http->tls);
1407
1408 if (http->tls_credentials)
1409 CFRelease(http->tls_credentials);
1410
1411 http->tls = NULL;
1412 http->tls_credentials = NULL;
1413}
1414
1415
1416/*
25731360 1417 * '_httpTLSWrite()' - Write to a SSL/TLS connection.
2c85b752
MS
1418 */
1419
25731360
MS
1420int /* O - Bytes written */
1421_httpTLSWrite(http_t *http, /* I - HTTP connection */
2c85b752
MS
1422 const char *buf, /* I - Buffer holding data */
1423 int len) /* I - Length of buffer */
1424{
1425 ssize_t result; /* Return value */
1426 OSStatus error; /* Error info */
1427 size_t processed; /* Number of bytes processed */
1428
1429
25731360 1430 DEBUG_printf(("2_httpTLSWrite(http=%p, buf=%p, len=%d)", http, buf, len));
2c85b752 1431
7e86f2f6 1432 error = SSLWrite(http->tls, buf, (size_t)len, &processed);
2c85b752
MS
1433
1434 switch (error)
1435 {
1436 case 0 :
1437 result = (int)processed;
1438 break;
1439
1440 case errSSLWouldBlock :
1441 if (processed)
1442 {
1443 result = (int)processed;
1444 }
1445 else
1446 {
1447 result = -1;
1448 errno = EINTR;
1449 }
1450 break;
1451
1452 case errSSLClosedGraceful :
1453 default :
1454 if (processed)
1455 {
1456 result = (int)processed;
1457 }
1458 else
1459 {
1460 result = -1;
1461 errno = EPIPE;
1462 }
1463 break;
1464 }
1465
25731360 1466 DEBUG_printf(("3_httpTLSWrite: Returning %d.", (int)result));
2c85b752
MS
1467
1468 return ((int)result);
1469}
1470
1471
88f1e9c8 1472#ifdef HAVE_SECKEYCHAINOPEN
2c85b752 1473/*
88f1e9c8 1474 * 'http_cdsa_copy_server()' - Find and copy server credentials from the keychain.
2c85b752
MS
1475 */
1476
88f1e9c8
MS
1477static CFArrayRef /* O - Array of certificates or NULL */
1478http_cdsa_copy_server(
1479 const char *common_name) /* I - Server's hostname */
2c85b752 1480{
88f1e9c8
MS
1481 OSStatus err; /* Error info */
1482 SecIdentitySearchRef search = NULL; /* Search reference */
1483 SecIdentityRef identity = NULL;/* Identity */
1484 CFArrayRef certificates = NULL;
1485 /* Certificate array */
1486 SecPolicyRef policy = NULL; /* Policy ref */
1487 CFStringRef cfcommon_name = NULL;
1488 /* Server name */
1489 CFMutableDictionaryRef query = NULL; /* Query qualifiers */
1490 CFArrayRef list = NULL; /* Keychain list */
2c85b752 1491
2c85b752 1492
88f1e9c8 1493 cfcommon_name = CFStringCreateWithCString(kCFAllocatorDefault, common_name, kCFStringEncodingUTF8);
2c85b752 1494
88f1e9c8
MS
1495 policy = SecPolicyCreateSSL(1, cfcommon_name);
1496
1497 if (cfcommon_name)
1498 CFRelease(cfcommon_name);
1499
1500 if (!policy)
1501 goto cleanup;
1502
1503 if (!(query = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)))
1504 goto cleanup;
1505
f93b32b6
MS
1506 _cupsMutexLock(&tls_mutex);
1507
88f1e9c8
MS
1508 list = CFArrayCreate(kCFAllocatorDefault, (const void **)&tls_keychain, 1, &kCFTypeArrayCallBacks);
1509
1510 CFDictionaryAddValue(query, kSecClass, kSecClassIdentity);
1511 CFDictionaryAddValue(query, kSecMatchPolicy, policy);
1512 CFDictionaryAddValue(query, kSecReturnRef, kCFBooleanTrue);
1513 CFDictionaryAddValue(query, kSecMatchLimit, kSecMatchLimitOne);
1514 CFDictionaryAddValue(query, kSecMatchSearchList, list);
1515
1516 CFRelease(list);
1517
1518 err = SecItemCopyMatching(query, (CFTypeRef *)&identity);
1519
f93b32b6
MS
1520 _cupsMutexUnlock(&tls_mutex);
1521
88f1e9c8
MS
1522 if (err)
1523 goto cleanup;
1524
1525 if (CFGetTypeID(identity) != SecIdentityGetTypeID())
1526 goto cleanup;
1527
1528 if ((certificates = CFArrayCreate(NULL, (const void **)&identity, 1, &kCFTypeArrayCallBacks)) == NULL)
1529 goto cleanup;
1530
1531 cleanup :
1532
1533 if (search)
1534 CFRelease(search);
1535 if (identity)
1536 CFRelease(identity);
1537
1538 if (policy)
1539 CFRelease(policy);
1540 if (query)
1541 CFRelease(query);
1542
1543 return (certificates);
2c85b752 1544}
88f1e9c8 1545#endif /* HAVE_SECKEYCHAINOPEN */
2c85b752
MS
1546
1547
1548/*
88f1e9c8 1549 * 'http_cdsa_read()' - Read function for the CDSA library.
2c85b752
MS
1550 */
1551
88f1e9c8
MS
1552static OSStatus /* O - -1 on error, 0 on success */
1553http_cdsa_read(
1554 SSLConnectionRef connection, /* I - SSL/TLS connection */
1555 void *data, /* I - Data buffer */
1556 size_t *dataLength) /* IO - Number of bytes */
2c85b752 1557{
88f1e9c8
MS
1558 OSStatus result; /* Return value */
1559 ssize_t bytes; /* Number of bytes read */
1560 http_t *http; /* HTTP connection */
2c85b752 1561
2c85b752 1562
88f1e9c8 1563 http = (http_t *)connection;
2c85b752 1564
88f1e9c8 1565 if (!http->blocking)
2c85b752
MS
1566 {
1567 /*
88f1e9c8 1568 * Make sure we have data before we read...
2c85b752
MS
1569 */
1570
88f1e9c8
MS
1571 while (!_httpWait(http, http->wait_value, 0))
1572 {
1573 if (http->timeout_cb && (*http->timeout_cb)(http, http->timeout_data))
1574 continue;
1575
1576 http->error = ETIMEDOUT;
1577 return (-1);
1578 }
2c85b752
MS
1579 }
1580
88f1e9c8 1581 do
2c85b752 1582 {
88f1e9c8 1583 bytes = recv(http->fd, data, *dataLength, 0);
2c85b752 1584 }
88f1e9c8 1585 while (bytes == -1 && (errno == EINTR || errno == EAGAIN));
2c85b752 1586
88f1e9c8
MS
1587 if ((size_t)bytes == *dataLength)
1588 {
1589 result = 0;
1590 }
1591 else if (bytes > 0)
1592 {
1593 *dataLength = (size_t)bytes;
1594 result = errSSLWouldBlock;
1595 }
1596 else
1597 {
1598 *dataLength = 0;
2c85b752 1599
88f1e9c8
MS
1600 if (bytes == 0)
1601 result = errSSLClosedGraceful;
1602 else if (errno == EAGAIN)
1603 result = errSSLWouldBlock;
1604 else
1605 result = errSSLClosedAbort;
1606 }
2c85b752 1607
88f1e9c8
MS
1608 return (result);
1609}
2c85b752 1610
2c85b752 1611
88f1e9c8
MS
1612/*
1613 * 'http_cdsa_set_credentials()' - Set the TLS credentials.
1614 */
2c85b752 1615
88f1e9c8
MS
1616static int /* O - Status of connection */
1617http_cdsa_set_credentials(http_t *http) /* I - HTTP connection */
1618{
1619 _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals */
1620 OSStatus error = 0; /* Error code */
1621 http_tls_credentials_t credentials = NULL;
1622 /* TLS credentials */
2c85b752 1623
2c85b752 1624
88f1e9c8 1625 DEBUG_printf(("7http_tls_set_credentials(%p)", http));
2c85b752 1626
88f1e9c8
MS
1627 /*
1628 * Prefer connection specific credentials...
1629 */
2c85b752 1630
88f1e9c8
MS
1631 if ((credentials = http->tls_credentials) == NULL)
1632 credentials = cg->tls_credentials;
2c85b752 1633
88f1e9c8
MS
1634 if (credentials)
1635 {
1636 error = SSLSetCertificate(http->tls, credentials);
1637 DEBUG_printf(("4http_tls_set_credentials: SSLSetCertificate, error=%d",
1638 (int)error));
2c85b752 1639 }
88f1e9c8
MS
1640 else
1641 DEBUG_puts("4http_tls_set_credentials: No credentials to set.");
1642
1643 return (error);
1644}
1645
1646
1647/*
1648 * 'http_cdsa_write()' - Write function for the CDSA library.
1649 */
1650
1651static OSStatus /* O - -1 on error, 0 on success */
1652http_cdsa_write(
1653 SSLConnectionRef connection, /* I - SSL/TLS connection */
1654 const void *data, /* I - Data buffer */
1655 size_t *dataLength) /* IO - Number of bytes */
1656{
1657 OSStatus result; /* Return value */
1658 ssize_t bytes; /* Number of bytes read */
1659 http_t *http; /* HTTP connection */
2c85b752 1660
2c85b752 1661
88f1e9c8
MS
1662 http = (http_t *)connection;
1663
1664 do
2c85b752 1665 {
88f1e9c8
MS
1666 bytes = write(http->fd, data, *dataLength);
1667 }
1668 while (bytes == -1 && (errno == EINTR || errno == EAGAIN));
1669
1670 if ((size_t)bytes == *dataLength)
1671 {
1672 result = 0;
1673 }
1674 else if (bytes >= 0)
1675 {
1676 *dataLength = (size_t)bytes;
1677 result = errSSLWouldBlock;
2c85b752
MS
1678 }
1679 else
88f1e9c8
MS
1680 {
1681 *dataLength = 0;
2c85b752 1682
88f1e9c8
MS
1683 if (errno == EAGAIN)
1684 result = errSSLWouldBlock;
1685 else
1686 result = errSSLClosedAbort;
1687 }
1688
1689 return (result);
2c85b752 1690}
2c85b752
MS
1691
1692
1693/*
1694 * End of "$Id$".
1695 */