]> git.ipfire.org Git - thirdparty/cups.git/blob - cups/tls-darwin.c
Sync up with some build changes.
[thirdparty/cups.git] / cups / tls-darwin.c
1 /*
2 * "$Id$"
3 *
4 * TLS support code for CUPS on OS X.
5 *
6 * Copyright 2007-2014 by Apple Inc.
7 * Copyright 1997-2007 by Easy Software Products, all rights reserved.
8 *
9 * These coded instructions, statements, and computer programs are the
10 * property of Apple Inc. and are protected by Federal copyright
11 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
12 * which should have been included with this file. If this file is
13 * file is missing or damaged, see the license at "http://www.cups.org/".
14 *
15 * This file is subject to the Apple OS-Developed Software exception.
16 */
17
18 /**** This file is included from http.c ****/
19
20 /*
21 * Include necessary headers...
22 */
23
24 #include <spawn.h>
25
26 extern char **environ;
27
28
29 /*
30 * Local globals...
31 */
32
33 #ifdef HAVE_SECKEYCHAINOPEN
34 static int tls_auto_create = 0;
35 /* Auto-create self-signed certs? */
36 static char *tls_common_name = NULL;
37 /* Default common name */
38 static SecKeychainRef tls_keychain = NULL;
39 /* Server cert keychain */
40 static char *tls_keypath = NULL;
41 /* Server cert keychain path */
42 static _cups_mutex_t tls_mutex = _CUPS_MUTEX_INITIALIZER;
43 /* Mutex for keychain/certs */
44 #endif /* HAVE_SECKEYCHAINOPEN */
45
46
47 /*
48 * Local functions...
49 */
50
51 #ifdef HAVE_SECKEYCHAINOPEN
52 static CFArrayRef http_cdsa_copy_server(const char *common_name);
53 #endif /* HAVE_SECKEYCHAINOPEN */
54 static OSStatus http_cdsa_read(SSLConnectionRef connection, void *data, size_t *dataLength);
55 static int http_cdsa_set_credentials(http_t *http);
56 static OSStatus http_cdsa_write(SSLConnectionRef connection, const void *data, size_t *dataLength);
57
58
59 /*
60 * 'cupsMakeServerCredentials()' - Make a self-signed certificate and private key pair.
61 *
62 * @since CUPS 2.0@
63 */
64
65 int /* O - 1 on success, 0 on failure */
66 cupsMakeServerCredentials(
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 {
73 #if defined(HAVE_SECGENERATESELFSIGNEDCERTIFICATE) && defined(HAVE_SECKEYCHAINOPEN)
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
87 (void)num_alt_names;
88 (void)alt_names;
89 (void)expiration_date;
90
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,
122 kSecCSRBasicContraintsPathLen, 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
151 cleanup:
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
173 #else /* !(HAVE_SECGENERATESELFSIGNEDCERTIFICATE && HAVE_SECKEYCHAINOPEN) */
174 int pid, /* Process ID of command */
175 status, /* Status of command */
176 i; /* Looping var */
177 char command[1024], /* Command */
178 *argv[4], /* Command-line arguments */
179 *envp[1000], /* Environment variables */
180 days[32], /* CERTTOOL_EXPIRATION_DAYS env var */
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;
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
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
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
244 if (posix_spawn(&pid, command, &actions, NULL, argv, envp))
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);
262 #endif /* HAVE_SECGENERATESELFSIGNEDCERTIFICATE && HAVE_SECKEYCHAINOPEN */
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
275 int /* O - 1 on success, 0 on failure */
276 cupsSetServerCredentials(
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 {
281 DEBUG_printf(("cupsSetServerCredentials(path=\"%s\", common_name=\"%s\", auto_create=%d)", path, common_name, auto_create));
282
283 #ifdef HAVE_SECKEYCHAINOPEN
284 SecKeychainRef keychain = NULL;/* Temporary keychain */
285
286
287 if (SecKeychainOpen(path, &keychain) != noErr)
288 {
289 /* TODO: Set cups last error string */
290 DEBUG_puts("1cupsSetServerCredentials: Unable to open keychain, returning 0.");
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
303 if (tls_keypath)
304 _cupsStrFree(tls_keypath);
305
306 if (tls_common_name)
307 _cupsStrFree(tls_common_name);
308
309 /*
310 * Save the new keychain...
311 */
312
313 tls_keychain = keychain;
314 tls_keypath = _cupsStrAlloc(path);
315 tls_auto_create = auto_create;
316 tls_common_name = _cupsStrAlloc(common_name);
317
318 _cupsMutexUnlock(&tls_mutex);
319
320 DEBUG_puts("1cupsSetServerCredentials: Opened keychain, returning 1.");
321 return (1);
322
323 #else
324 DEBUG_puts("1cupsSetServerCredentials: No keychain support compiled in, returning 0.");
325 return (0);
326 #endif /* HAVE_SECKEYCHAINOPEN */
327 }
328
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
337 int /* O - Status of call (0 = success) */
338 httpCopyCredentials(
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
350 DEBUG_printf(("httpCopyCredentials(http=%p, credentials=%p)", http, credentials));
351
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 {
360 DEBUG_printf(("2httpCopyCredentials: Peer provided %d certificates.", (int)SecTrustGetCertificateCount(peerTrust)));
361
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);
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
381 if ((data = SecCertificateCopyData(secCert)) != NULL)
382 {
383 DEBUG_printf(("2httpCopyCredentials: Adding %d byte certificate blob.", (int)CFDataGetLength(data)));
384
385 httpAddCredential(*credentials, CFDataGetBytePtr(data), (size_t)CFDataGetLength(data));
386 CFRelease(data);
387 }
388 }
389 }
390
391 CFRelease(peerTrust);
392 }
393
394 return (error);
395 }
396
397
398 /*
399 * 'http_cdsa_create_credential()' - Create a single credential in the internal format.
400 */
401
402 static SecCertificateRef /* O - Certificate */
403 http_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
413 /*
414 * '_httpCreateCredentials()' - Create credentials in the internal format.
415 */
416
417 http_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 */
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 {
438 if ((secCert = http_cdsa_create_credential(credential)) != NULL)
439 {
440 CFArrayAppendValue(peerCerts, secCert);
441 CFRelease(secCert);
442 }
443 }
444
445 return (peerCerts);
446 }
447
448
449 /*
450 * 'httpCredentialsAreValidForName()' - Return whether the credentials are valid for the given name.
451 *
452 * @since CUPS 2.0@
453 */
454
455 int /* O - 1 if valid, 0 otherwise */
456 httpCredentialsAreValidForName(
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
518 http_trust_t /* O - Level of trust */
519 httpCredentialsGetTrust(
520 cups_array_t *credentials, /* I - Credentials */
521 const char *common_name) /* I - Common name for trust lookup */
522 {
523 SecCertificateRef secCert; /* Certificate reference */
524 http_trust_t trust = HTTP_TRUST_OK;
525 /* Trusted? */
526 cups_array_t *tcreds = NULL; /* Trusted credentials */
527 _cups_globals_t *cg = _cupsGlobals();
528 /* Per-thread globals */
529
530
531 if (!common_name)
532 return (HTTP_TRUST_UNKNOWN);
533
534 if ((secCert = http_cdsa_create_credential((http_credential_t *)cupsArrayFirst(credentials))) == NULL)
535 return (HTTP_TRUST_UNKNOWN);
536
537 /*
538 * Look this common name up in the default keychains...
539 */
540
541 httpLoadCredentials(NULL, &tcreds, common_name);
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) ||
559 !httpCredentialsAreValidForName(credentials, common_name))
560 {
561 /*
562 * Either the new credentials are not newly issued, or the common name
563 * does not match the issued certificate...
564 */
565
566 trust = HTTP_TRUST_INVALID;
567 }
568 else if (httpCredentialsGetExpiration(tcreds) < time(NULL))
569 {
570 /*
571 * Save the renewed credentials...
572 */
573
574 trust = HTTP_TRUST_RENEWED;
575
576 httpSaveCredentials(NULL, credentials, common_name);
577 }
578 }
579
580 httpFreeCredentials(tcreds);
581 }
582 else if (cg->validate_certs && !httpCredentialsAreValidForName(credentials, common_name))
583 trust = HTTP_TRUST_INVALID;
584
585 if (!cg->expired_certs && !SecCertificateIsValid(secCert, CFAbsoluteTimeGetCurrent()))
586 trust = HTTP_TRUST_EXPIRED;
587 else if (!cg->any_root && cupsArrayCount(credentials) == 1)
588 trust = HTTP_TRUST_INVALID;
589
590 CFRelease(secCert);
591
592 return (trust);
593 }
594
595
596 /*
597 * 'httpCredentialsGetExpiration()' - Return the expiration date of the credentials.
598 *
599 * @since CUPS 2.0@
600 */
601
602 time_t /* O - Expiration date of credentials */
603 httpCredentialsGetExpiration(
604 cups_array_t *credentials) /* I - Credentials */
605 {
606 SecCertificateRef secCert; /* Certificate reference */
607 time_t expiration; /* Expiration date */
608
609
610 if ((secCert = http_cdsa_create_credential((http_credential_t *)cupsArrayFirst(credentials))) == NULL)
611 return (0);
612
613 expiration = (time_t)(SecCertificateNotValidAfter(secCert) + kCFAbsoluteTimeIntervalSince1970);
614
615 CFRelease(secCert);
616
617 return (expiration);
618 }
619
620
621 /*
622 * 'httpCredentialsString()' - Return a string representing the credentials.
623 *
624 * @since CUPS 2.0@
625 */
626
627 size_t /* O - Total size of credentials string */
628 httpCredentialsString(
629 cups_array_t *credentials, /* I - Credentials */
630 char *buffer, /* I - Buffer or @code NULL@ */
631 size_t bufsize) /* I - Size of buffer */
632 {
633 http_credential_t *first; /* First certificate */
634 SecCertificateRef secCert; /* Certificate reference */
635
636
637 DEBUG_printf(("httpCredentialsString(credentials=%p, buffer=%p, bufsize=" CUPS_LLFMT ")", credentials, buffer, CUPS_LLCAST bufsize));
638
639 if (!buffer)
640 return (0);
641
642 if (buffer && bufsize > 0)
643 *buffer = '\0';
644
645 if ((first = (http_credential_t *)cupsArrayFirst(credentials)) != NULL &&
646 (secCert = http_cdsa_create_credential(first)) != NULL)
647 {
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)
655 {
656 CFStringGetCString(cf_name, name, (CFIndex)sizeof(name), kCFStringEncodingUTF8);
657 CFRelease(cf_name);
658 }
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]);
669
670 CFRelease(secCert);
671 }
672
673 DEBUG_printf(("1httpCredentialsString: Returning \"%s\".", buffer));
674
675 return (strlen(buffer));
676 }
677
678
679 /*
680 * '_httpFreeCredentials()' - Free internal credentials.
681 */
682
683 void
684 _httpFreeCredentials(
685 http_tls_credentials_t credentials) /* I - Internal credentials */
686 {
687 if (!credentials)
688 return;
689
690 CFRelease(credentials);
691 }
692
693
694 /*
695 * 'httpLoadCredentials()' - Load X.509 credentials from a keychain file.
696 *
697 * @since CUPS 2.0@
698 */
699
700 int /* O - 0 on success, -1 on error */
701 httpLoadCredentials(
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 {
706 #ifdef HAVE_SECKEYCHAINOPEN
707 OSStatus err; /* Error info */
708 char filename[1024]; /* Filename for keychain */
709 SecKeychainRef keychain = NULL;/* Keychain reference */
710 SecIdentitySearchRef search = NULL; /* Search reference */
711 SecCertificateRef cert = NULL; /* Certificate */
712 CFDataRef data; /* Certificate data */
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
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)
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
758 list = CFArrayCreate(kCFAllocatorDefault, (const void **)&keychain, 1, &kCFTypeArrayCallBacks);
759
760 CFDictionaryAddValue(query, kSecClass, kSecClassCertificate);
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
768 err = SecItemCopyMatching(query, (CFTypeRef *)&cert);
769
770 if (err)
771 goto cleanup;
772
773 if (CFGetTypeID(cert) != SecCertificateGetTypeID())
774 goto cleanup;
775
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 }
784
785 cleanup :
786
787 if (keychain)
788 CFRelease(keychain);
789 if (search)
790 CFRelease(search);
791 if (cert)
792 CFRelease(cert);
793 if (policy)
794 CFRelease(policy);
795 if (query)
796 CFRelease(query);
797
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 */
809 }
810
811
812 /*
813 * 'httpSaveCredentials()' - Save X.509 credentials to a keychain file.
814 *
815 * @since CUPS 2.0@
816 */
817
818 int /* O - -1 on error, 0 on success */
819 httpSaveCredentials(
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 {
824 #ifdef HAVE_SECKEYCHAINOPEN
825 int ret = -1; /* Return value */
826 OSStatus err; /* Error info */
827 char filename[1024]; /* Filename for keychain */
828 SecKeychainRef keychain = NULL;/* Keychain reference */
829 SecIdentitySearchRef search = NULL; /* Search reference */
830 SecCertificateRef cert = NULL; /* Certificate */
831 CFMutableDictionaryRef attrs = NULL; /* Attributes for add */
832 CFArrayRef list = NULL; /* Keychain list */
833
834
835 DEBUG_printf(("httpSaveCredentials(path=\"%s\", credentials=%p, common_name=\"%s\")", path, credentials, common_name));
836 if (!credentials)
837 goto cleanup;
838
839 if (!httpCredentialsAreValidForName(credentials, common_name))
840 {
841 DEBUG_puts("1httpSaveCredentials: Common name does not match.");
842 return (-1);
843 }
844
845 if ((cert = http_cdsa_create_credential((http_credential_t *)cupsArrayFirst(credentials))) == NULL)
846 {
847 DEBUG_puts("1httpSaveCredentials: Unable to create certificate.");
848 goto cleanup;
849 }
850
851 if (!path)
852 {
853 const char *home = getenv("HOME"); /* HOME environment variable */
854
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));
859
860 path = filename;
861
862 DEBUG_printf(("1httpSaveCredentials: Using default path \"%s\".", path));
863 }
864
865 if ((err = SecKeychainOpen(path, &keychain)) != noErr)
866 {
867 DEBUG_printf(("1httpSaveCredentials: SecKeychainOpen returned %d.", (int)err));
868 goto cleanup;
869 }
870
871 if ((list = CFArrayCreate(kCFAllocatorDefault, (const void **)&keychain, 1, &kCFTypeArrayCallBacks)) == NULL)
872 {
873 DEBUG_puts("1httpSaveCredentials: Unable to create list of keychains.");
874 goto cleanup;
875 }
876
877 if ((attrs = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)) == NULL)
878 {
879 DEBUG_puts("1httpSaveCredentials: Unable to create dictionary.");
880 goto cleanup;
881 }
882
883 CFDictionaryAddValue(attrs, kSecClass, kSecClassCertificate);
884 CFDictionaryAddValue(attrs, kSecValueRef, cert);
885 CFDictionaryAddValue(attrs, kSecMatchSearchList, list);
886
887 /* Note: SecItemAdd consumes "attrs"... */
888 err = SecItemAdd(attrs, NULL);
889 DEBUG_printf(("1httpSaveCredentials: SecItemAdd returned %d.", (int)err));
890
891 cleanup :
892
893 if (list)
894 CFRelease(list);
895 if (keychain)
896 CFRelease(keychain);
897 if (search)
898 CFRelease(search);
899 if (cert)
900 CFRelease(cert);
901
902 DEBUG_printf(("1httpSaveCredentials: Returning %d.", ret));
903
904 return (ret);
905
906 #else
907 (void)path;
908 (void)credentials;
909 (void)common_name;
910
911 return (-1);
912 #endif /* HAVE_SECKEYCHAINOPEN */
913 }
914
915
916 /*
917 * '_httpTLSInitialize()' - Initialize the TLS stack.
918 */
919
920 void
921 _httpTLSInitialize(void)
922 {
923 /*
924 * Nothing to do...
925 */
926 }
927
928
929 /*
930 * '_httpTLSPending()' - Return the number of pending TLS-encrypted bytes.
931 */
932
933 size_t
934 _httpTLSPending(http_t *http) /* I - HTTP connection */
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 /*
947 * '_httpTLSRead()' - Read from a SSL/TLS connection.
948 */
949
950 int /* O - Bytes read */
951 _httpTLSRead(http_t *http, /* I - HTTP connection */
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
960 error = SSLRead(http->tls, buf, (size_t)len, &processed);
961 DEBUG_printf(("6_httpTLSRead: error=%d, processed=%d", (int)error,
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
995 /*
996 * '_httpTLSStart()' - Set up SSL/TLS support on a connection.
997 */
998
999 int /* O - 0 on success, -1 on failure */
1000 _httpTLSStart(http_t *http) /* I - HTTP connection */
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
1017 DEBUG_printf(("7_httpTLSStart(http=%p)", http));
1018
1019 #ifdef HAVE_SECKEYCHAINOPEN
1020 if (http->mode == _HTTP_MODE_SERVER && !tls_keychain)
1021 #else
1022 if (http->mode == _HTTP_MODE_SERVER)
1023 #endif /* HAVE_SECKEYCHAINOPEN */
1024 {
1025 DEBUG_puts("4_httpTLSStart: cupsSetServerCredentials not called.");
1026 http->error = errno = EINVAL;
1027 http->status = HTTP_STATUS_ERROR;
1028 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Server credentials not set."), 1);
1029
1030 return (-1);
1031 }
1032
1033 if ((http->tls = SSLCreateContext(kCFAllocatorDefault, http->mode == _HTTP_MODE_CLIENT ? kSSLClientSide : kSSLServerSide, kSSLStreamType)) == NULL)
1034 {
1035 DEBUG_puts("4_httpTLSStart: SSLCreateContext failed.");
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);
1044 DEBUG_printf(("4_httpTLSStart: SSLSetConnection, error=%d", (int)error));
1045
1046 if (!error)
1047 {
1048 error = SSLSetIOFuncs(http->tls, http_cdsa_read, http_cdsa_write);
1049 DEBUG_printf(("4_httpTLSStart: SSLSetIOFuncs, error=%d", (int)error));
1050 }
1051
1052 if (!error)
1053 {
1054 error = SSLSetSessionOption(http->tls, kSSLSessionOptionBreakOnServerAuth,
1055 true);
1056 DEBUG_printf(("4_httpTLSStart: SSLSetSessionOption, error=%d",
1057 (int)error));
1058 }
1059
1060 if (!error && http->mode == _HTTP_MODE_CLIENT)
1061 {
1062 /*
1063 * Client: set client-side credentials, if any...
1064 */
1065
1066 if (cg->client_cert_cb)
1067 {
1068 error = SSLSetSessionOption(http->tls,
1069 kSSLSessionOptionBreakOnCertRequested, true);
1070 DEBUG_printf(("4_httpTLSStart: kSSLSessionOptionBreakOnCertRequested, "
1071 "error=%d", (int)error));
1072 }
1073 else
1074 {
1075 error = http_cdsa_set_credentials(http);
1076 DEBUG_printf(("4_httpTLSStart: http_cdsa_set_credentials, error=%d",
1077 (int)error));
1078 }
1079 }
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 {
1106 DEBUG_printf(("4_httpTLSStart: Unable to get socket address: %s", strerror(errno)));
1107 hostname[0] = '\0';
1108 }
1109 else if (httpAddrLocalhost(&addr))
1110 hostname[0] = '\0';
1111 else
1112 {
1113 httpAddrLookup(&addr, hostname, sizeof(hostname));
1114 DEBUG_printf(("4_httpTLSStart: Resolved socket address to \"%s\".", hostname));
1115 }
1116 }
1117
1118 #ifdef HAVE_SECKEYCHAINOPEN
1119 if (isdigit(hostname[0] & 255) || hostname[0] == '[')
1120 hostname[0] = '\0'; /* Don't allow numeric addresses */
1121
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 {
1129 DEBUG_printf(("4_httpTLSStart: Auto-create credentials for \"%s\".", hostname[0] ? hostname : tls_common_name));
1130
1131 if (!cupsMakeServerCredentials(tls_keypath, hostname[0] ? hostname : tls_common_name, 0, NULL, time(NULL) + 365 * 86400))
1132 {
1133 DEBUG_puts("4_httpTLSStart: cupsMakeServerCredentials failed.");
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 }
1143 #endif /* HAVE_SECKEYCHAINOPEN */
1144
1145 if (!http->tls_credentials)
1146 {
1147 DEBUG_puts("4_httpTLSStart: Unable to find server credentials.");
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
1157 DEBUG_printf(("4_httpTLSStart: SSLSetCertificate, error=%d", (int)error));
1158 }
1159
1160 DEBUG_printf(("4_httpTLSStart: tls_credentials=%p", http->tls_credentials));
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
1167 if (!error && http->mode == _HTTP_MODE_CLIENT)
1168 {
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
1189 error = SSLSetPeerDomainName(http->tls, hostname, strlen(hostname));
1190
1191 DEBUG_printf(("4_httpTLSStart: SSLSetPeerDomainName, error=%d", (int)error));
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
1202 DEBUG_printf(("4_httpTLSStart: SSLHandshake returned %d.", (int)error));
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
1227 DEBUG_printf(("4_httpTLSStart: Server certificate callback "
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 {
1249 credential->datalen = (size_t)CFDataGetLength(data);
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
1270 DEBUG_printf(("4_httpTLSStart: Client certificate callback "
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 /*
1349 * '_httpTLSStop()' - Shut down SSL/TLS on a connection.
1350 */
1351
1352 void
1353 _httpTLSStop(http_t *http) /* I - HTTP connection */
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 /*
1369 * '_httpTLSWrite()' - Write to a SSL/TLS connection.
1370 */
1371
1372 int /* O - Bytes written */
1373 _httpTLSWrite(http_t *http, /* I - HTTP connection */
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
1382 DEBUG_printf(("2_httpTLSWrite(http=%p, buf=%p, len=%d)", http, buf, len));
1383
1384 error = SSLWrite(http->tls, buf, (size_t)len, &processed);
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
1418 DEBUG_printf(("3_httpTLSWrite: Returning %d.", (int)result));
1419
1420 return ((int)result);
1421 }
1422
1423
1424 #ifdef HAVE_SECKEYCHAINOPEN
1425 /*
1426 * 'http_cdsa_copy_server()' - Find and copy server credentials from the keychain.
1427 */
1428
1429 static CFArrayRef /* O - Array of certificates or NULL */
1430 http_cdsa_copy_server(
1431 const char *common_name) /* I - Server's hostname */
1432 {
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 */
1443
1444
1445 cfcommon_name = CFStringCreateWithCString(kCFAllocatorDefault, common_name, kCFStringEncodingUTF8);
1446
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);
1492 }
1493 #endif /* HAVE_SECKEYCHAINOPEN */
1494
1495
1496 /*
1497 * 'http_cdsa_read()' - Read function for the CDSA library.
1498 */
1499
1500 static OSStatus /* O - -1 on error, 0 on success */
1501 http_cdsa_read(
1502 SSLConnectionRef connection, /* I - SSL/TLS connection */
1503 void *data, /* I - Data buffer */
1504 size_t *dataLength) /* IO - Number of bytes */
1505 {
1506 OSStatus result; /* Return value */
1507 ssize_t bytes; /* Number of bytes read */
1508 http_t *http; /* HTTP connection */
1509
1510
1511 http = (http_t *)connection;
1512
1513 if (!http->blocking)
1514 {
1515 /*
1516 * Make sure we have data before we read...
1517 */
1518
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 }
1527 }
1528
1529 do
1530 {
1531 bytes = recv(http->fd, data, *dataLength, 0);
1532 }
1533 while (bytes == -1 && (errno == EINTR || errno == EAGAIN));
1534
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;
1547
1548 if (bytes == 0)
1549 result = errSSLClosedGraceful;
1550 else if (errno == EAGAIN)
1551 result = errSSLWouldBlock;
1552 else
1553 result = errSSLClosedAbort;
1554 }
1555
1556 return (result);
1557 }
1558
1559
1560 /*
1561 * 'http_cdsa_set_credentials()' - Set the TLS credentials.
1562 */
1563
1564 static int /* O - Status of connection */
1565 http_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 */
1571
1572
1573 DEBUG_printf(("7http_tls_set_credentials(%p)", http));
1574
1575 /*
1576 * Prefer connection specific credentials...
1577 */
1578
1579 if ((credentials = http->tls_credentials) == NULL)
1580 credentials = cg->tls_credentials;
1581
1582 if (credentials)
1583 {
1584 error = SSLSetCertificate(http->tls, credentials);
1585 DEBUG_printf(("4http_tls_set_credentials: SSLSetCertificate, error=%d",
1586 (int)error));
1587 }
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
1599 static OSStatus /* O - -1 on error, 0 on success */
1600 http_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 */
1608
1609
1610 http = (http_t *)connection;
1611
1612 do
1613 {
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;
1626 }
1627 else
1628 {
1629 *dataLength = 0;
1630
1631 if (errno == EAGAIN)
1632 result = errSSLWouldBlock;
1633 else
1634 result = errSSLClosedAbort;
1635 }
1636
1637 return (result);
1638 }
1639
1640
1641 /*
1642 * End of "$Id$".
1643 */