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