]> git.ipfire.org Git - thirdparty/cups.git/blob - cups/tls-darwin.c
Set CERTTOOL_EXPIRATION_DAYS environment variable for certtool, to get certs
[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 * 'httpCredentialsAreTrusted()' - Return whether the credentials are trusted.
451 *
452 * @since CUPS 2.0@
453 */
454
455 int /* O - 1 if trusted, 0 if not/unknown */
456 httpCredentialsAreTrusted(
457 cups_array_t *credentials, /* I - Credentials */
458 const char *common_name) /* I - Common name for trust lookup */
459 {
460 SecCertificateRef secCert; /* Certificate reference */
461 int trusted = 1; /* Trusted? */
462 int save = 0; /* Save credentials? */
463 cups_array_t *tcreds = NULL; /* Trusted credentials */
464 _cups_globals_t *cg = _cupsGlobals();
465 /* Per-thread globals */
466
467
468 if (!common_name)
469 return (0);
470
471 if ((secCert = http_cdsa_create_credential((http_credential_t *)cupsArrayFirst(credentials))) == NULL)
472 return (0);
473
474 /*
475 * Look this common name up in the default keychains...
476 */
477
478 httpLoadCredentials(NULL, &tcreds, common_name);
479
480 if (tcreds)
481 {
482 char credentials_str[1024], /* String for incoming credentials */
483 tcreds_str[1024]; /* String for saved credentials */
484
485 httpCredentialsString(credentials, credentials_str, sizeof(credentials_str));
486 httpCredentialsString(tcreds, tcreds_str, sizeof(tcreds_str));
487
488 if (strcmp(credentials_str, tcreds_str))
489 {
490 /*
491 * Credentials don't match, let's look at the expiration date of the new
492 * credentials and allow if the new ones have a later expiration...
493 */
494
495 if (httpCredentialsGetExpiration(credentials) <= httpCredentialsGetExpiration(tcreds) ||
496 !httpCredentialsIsValidName(credentials, common_name))
497 {
498 /*
499 * Either the new credentials are not newly issued, or the common name
500 * does not match the issued certificate...
501 */
502
503 trusted = 0;
504 }
505 else
506 {
507 /*
508 * Flag that we should save the new credentials...
509 */
510
511 save = 1;
512 }
513 }
514
515 httpFreeCredentials(tcreds);
516 }
517 else if (!httpCredentialsIsValidName(credentials, common_name))
518 trusted = 0;
519 else
520 save = 1;
521
522 if (!cg->expired_certs && !SecCertificateIsValid(secCert, CFAbsoluteTimeGetCurrent()))
523 trusted = 0;
524 else if (!cg->any_root && cupsArrayCount(credentials) == 1)
525 trusted = 0;
526
527 if (trusted && save)
528 httpSaveCredentials(NULL, credentials, common_name);
529
530 CFRelease(secCert);
531
532 return (trusted);
533 }
534
535
536 /*
537 * 'httpCredentialsGetExpiration()' - Return the expiration date of the credentials.
538 *
539 * @since CUPS 2.0@
540 */
541
542 time_t /* O - Expiration date of credentials */
543 httpCredentialsGetExpiration(
544 cups_array_t *credentials) /* I - Credentials */
545 {
546 SecCertificateRef secCert; /* Certificate reference */
547 time_t expiration; /* Expiration date */
548
549
550 if ((secCert = http_cdsa_create_credential((http_credential_t *)cupsArrayFirst(credentials))) == NULL)
551 return (0);
552
553 expiration = (time_t)(SecCertificateNotValidAfter(secCert) + kCFAbsoluteTimeIntervalSince1970);
554
555 CFRelease(secCert);
556
557 return (expiration);
558 }
559
560
561 /*
562 * 'httpCredentialsIsValidName()' - Return whether the credentials are valid for the given name.
563 *
564 * @since CUPS 2.0@
565 */
566
567 int /* O - 1 if valid, 0 otherwise */
568 httpCredentialsIsValidName(
569 cups_array_t *credentials, /* I - Credentials */
570 const char *common_name) /* I - Name to check */
571 {
572 SecCertificateRef secCert; /* Certificate reference */
573 CFStringRef cfcert_name = NULL;
574 /* Certificate's common name (CF string) */
575 char cert_name[256]; /* Certificate's common name (C string) */
576 int valid = 1; /* Valid name? */
577
578
579 if ((secCert = http_cdsa_create_credential((http_credential_t *)cupsArrayFirst(credentials))) == NULL)
580 return (0);
581
582 /*
583 * Compare the common names...
584 */
585
586 if ((cfcert_name = SecCertificateCopySubjectSummary(secCert)) == NULL)
587 {
588 /*
589 * Can't get common name, cannot be valid...
590 */
591
592 valid = 0;
593 }
594 else if (CFStringGetCString(cfcert_name, cert_name, sizeof(cert_name), kCFStringEncodingUTF8) &&
595 _cups_strcasecmp(common_name, cert_name))
596 {
597 /*
598 * Not an exact match for the common name, check for wildcard certs...
599 */
600
601 const char *domain = strchr(common_name, '.');
602 /* Domain in common name */
603
604 if (strncmp(cert_name, "*.", 2) || !domain || _cups_strcasecmp(domain, cert_name + 1))
605 {
606 /*
607 * Not a wildcard match.
608 */
609
610 /* TODO: Check subject alternate names */
611 valid = 0;
612 }
613 }
614
615 if (cfcert_name)
616 CFRelease(cfcert_name);
617
618 CFRelease(secCert);
619
620 return (valid);
621 }
622
623
624 /*
625 * 'httpCredentialsString()' - Return a string representing the credentials.
626 *
627 * @since CUPS 2.0@
628 */
629
630 size_t /* O - Total size of credentials string */
631 httpCredentialsString(
632 cups_array_t *credentials, /* I - Credentials */
633 char *buffer, /* I - Buffer or @code NULL@ */
634 size_t bufsize) /* I - Size of buffer */
635 {
636 http_credential_t *first; /* First certificate */
637 SecCertificateRef secCert; /* Certificate reference */
638
639
640 DEBUG_printf(("httpCredentialsString(credentials=%p, buffer=%p, bufsize=" CUPS_LLFMT ")", credentials, buffer, CUPS_LLCAST bufsize));
641
642 if (!buffer)
643 return (0);
644
645 if (buffer && bufsize > 0)
646 *buffer = '\0';
647
648 if ((first = (http_credential_t *)cupsArrayFirst(credentials)) != NULL &&
649 (secCert = http_cdsa_create_credential(first)) != NULL)
650 {
651 CFStringRef cf_name; /* CF common name string */
652 char name[256]; /* Common name associated with cert */
653 time_t expiration; /* Expiration date of cert */
654 _cups_md5_state_t md5_state; /* MD5 state */
655 unsigned char md5_digest[16]; /* MD5 result */
656
657 if ((cf_name = SecCertificateCopySubjectSummary(secCert)) != NULL)
658 {
659 CFStringGetCString(cf_name, name, (CFIndex)sizeof(name), kCFStringEncodingUTF8);
660 CFRelease(cf_name);
661 }
662 else
663 strlcpy(name, "unknown", sizeof(name));
664
665 expiration = (time_t)(SecCertificateNotValidAfter(secCert) + kCFAbsoluteTimeIntervalSince1970);
666
667 _cupsMD5Init(&md5_state);
668 _cupsMD5Append(&md5_state, first->data, (int)first->datalen);
669 _cupsMD5Finish(&md5_state, md5_digest);
670
671 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]);
672
673 CFRelease(secCert);
674 }
675
676 DEBUG_printf(("1httpCredentialsString: Returning \"%s\".", buffer));
677
678 return (strlen(buffer));
679 }
680
681
682 /*
683 * '_httpFreeCredentials()' - Free internal credentials.
684 */
685
686 void
687 _httpFreeCredentials(
688 http_tls_credentials_t credentials) /* I - Internal credentials */
689 {
690 if (!credentials)
691 return;
692
693 CFRelease(credentials);
694 }
695
696
697 /*
698 * 'httpLoadCredentials()' - Load X.509 credentials from a keychain file.
699 *
700 * @since CUPS 2.0@
701 */
702
703 int /* O - 0 on success, -1 on error */
704 httpLoadCredentials(
705 const char *path, /* I - Keychain/PKCS#12 path */
706 cups_array_t **credentials, /* IO - Credentials */
707 const char *common_name) /* I - Common name for credentials */
708 {
709 #ifdef HAVE_SECKEYCHAINOPEN
710 OSStatus err; /* Error info */
711 char filename[1024]; /* Filename for keychain */
712 SecKeychainRef keychain = NULL;/* Keychain reference */
713 SecIdentitySearchRef search = NULL; /* Search reference */
714 SecCertificateRef cert = NULL; /* Certificate */
715 CFDataRef data; /* Certificate data */
716 SecPolicyRef policy = NULL; /* Policy ref */
717 CFStringRef cfcommon_name = NULL;
718 /* Server name */
719 CFMutableDictionaryRef query = NULL; /* Query qualifiers */
720 CFArrayRef list = NULL; /* Keychain list */
721
722
723 DEBUG_printf(("httpLoadCredentials(path=\"%s\", credentials=%p, common_name=\"%s\")", path, credentials, common_name));
724
725 if (!credentials)
726 return (-1);
727
728 *credentials = NULL;
729
730 if (!path)
731 {
732 const char *home = getenv("HOME"); /* HOME environment variable */
733
734 if (getuid() && home)
735 snprintf(filename, sizeof(filename), "%s/Library/Keychains/login.keychain", home);
736 else
737 strlcpy(filename, "/Library/Keychains/System.keychain", sizeof(filename));
738
739 path = filename;
740
741 DEBUG_printf(("1httpLoadCredentials: Using default path \"%s\".", path));
742 }
743
744
745 if ((err = SecKeychainOpen(path, &keychain)) != noErr)
746 goto cleanup;
747
748 cfcommon_name = CFStringCreateWithCString(kCFAllocatorDefault, common_name, kCFStringEncodingUTF8);
749
750 policy = SecPolicyCreateSSL(1, cfcommon_name);
751
752 if (cfcommon_name)
753 CFRelease(cfcommon_name);
754
755 if (!policy)
756 goto cleanup;
757
758 if (!(query = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)))
759 goto cleanup;
760
761 list = CFArrayCreate(kCFAllocatorDefault, (const void **)&keychain, 1, &kCFTypeArrayCallBacks);
762
763 CFDictionaryAddValue(query, kSecClass, kSecClassCertificate);
764 CFDictionaryAddValue(query, kSecMatchPolicy, policy);
765 CFDictionaryAddValue(query, kSecReturnRef, kCFBooleanTrue);
766 CFDictionaryAddValue(query, kSecMatchLimit, kSecMatchLimitOne);
767 CFDictionaryAddValue(query, kSecMatchSearchList, list);
768
769 CFRelease(list);
770
771 err = SecItemCopyMatching(query, (CFTypeRef *)&cert);
772
773 if (err)
774 goto cleanup;
775
776 if (CFGetTypeID(cert) != SecCertificateGetTypeID())
777 goto cleanup;
778
779 if ((data = SecCertificateCopyData(cert)) != NULL)
780 {
781 DEBUG_printf(("1httpLoadCredentials: Adding %d byte certificate blob.", (int)CFDataGetLength(data)));
782
783 *credentials = cupsArrayNew(NULL, NULL);
784 httpAddCredential(*credentials, CFDataGetBytePtr(data), (size_t)CFDataGetLength(data));
785 CFRelease(data);
786 }
787
788 cleanup :
789
790 if (keychain)
791 CFRelease(keychain);
792 if (search)
793 CFRelease(search);
794 if (cert)
795 CFRelease(cert);
796 if (policy)
797 CFRelease(policy);
798 if (query)
799 CFRelease(query);
800
801 DEBUG_printf(("1httpLoadCredentials: Returning %d.", *credentials ? 0 : -1));
802
803 return (*credentials ? 0 : -1);
804
805 #else
806 (void)path;
807 (void)credentials;
808 (void)common_name;
809
810 return (-1);
811 #endif /* HAVE_SECKEYCHAINOPEN */
812 }
813
814
815 /*
816 * 'httpSaveCredentials()' - Save X.509 credentials to a keychain file.
817 *
818 * @since CUPS 2.0@
819 */
820
821 int /* O - -1 on error, 0 on success */
822 httpSaveCredentials(
823 const char *path, /* I - Keychain/PKCS#12 path */
824 cups_array_t *credentials, /* I - Credentials */
825 const char *common_name) /* I - Common name for credentials */
826 {
827 #ifdef HAVE_SECKEYCHAINOPEN
828 int ret = -1; /* Return value */
829 OSStatus err; /* Error info */
830 char filename[1024]; /* Filename for keychain */
831 SecKeychainRef keychain = NULL;/* Keychain reference */
832 SecIdentitySearchRef search = NULL; /* Search reference */
833 SecCertificateRef cert = NULL; /* Certificate */
834 CFStringRef cfcommon_name = NULL;
835 /* Server name */
836 CFMutableDictionaryRef attrs = NULL; /* Attributes for add */
837 CFArrayRef list = NULL; /* Keychain list */
838
839
840 DEBUG_printf(("httpSaveCredentials(path=\"%s\", credentials=%p, common_name=\"%s\")", path, credentials, common_name));
841 if (!credentials)
842 goto cleanup;
843
844 if ((cert = http_cdsa_create_credential((http_credential_t *)cupsArrayFirst(credentials))) == NULL)
845 {
846 DEBUG_puts("1httpSaveCredentials: Unable to create certificate.");
847 goto cleanup;
848 }
849
850 if (!path)
851 {
852 const char *home = getenv("HOME"); /* HOME environment variable */
853
854 if (getuid() && home)
855 snprintf(filename, sizeof(filename), "%s/Library/Keychains/login.keychain", home);
856 else
857 strlcpy(filename, "/Library/Keychains/System.keychain", sizeof(filename));
858
859 path = filename;
860
861 DEBUG_printf(("1httpSaveCredentials: Using default path \"%s\".", path));
862 }
863
864 if ((err = SecKeychainOpen(path, &keychain)) != noErr)
865 {
866 DEBUG_printf(("1httpSaveCredentials: SecKeychainOpen returned %d.", (int)err));
867 goto cleanup;
868 }
869
870 if ((list = CFArrayCreate(kCFAllocatorDefault, (const void **)&keychain, 1, &kCFTypeArrayCallBacks)) == NULL)
871 {
872 DEBUG_puts("1httpSaveCredentials: Unable to create list of keychains.");
873 goto cleanup;
874 }
875
876 if ((cfcommon_name = CFStringCreateWithCString(kCFAllocatorDefault, common_name, kCFStringEncodingUTF8)) == NULL)
877 {
878 DEBUG_puts("1httpSaveCredentials: Unable to create common name string.");
879 goto cleanup;
880 }
881
882 if ((attrs = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)) == NULL)
883 {
884 DEBUG_puts("1httpSaveCredentials: Unable to create dictionary.");
885 goto cleanup;
886 }
887
888 CFDictionaryAddValue(attrs, kSecClass, kSecClassCertificate);
889 CFDictionaryAddValue(attrs, kSecAttrLabel, cfcommon_name);
890 CFDictionaryAddValue(attrs, kSecAttrSubject, cfcommon_name);
891 CFDictionaryAddValue(attrs, kSecValueRef, cert);
892 CFDictionaryAddValue(attrs, kSecMatchSearchList, list);
893
894 /* Note: SecItemAdd consumes "attrs"... */
895 if ((err = SecItemAdd(attrs, NULL)) == noErr)
896 ret = 0;
897
898 DEBUG_printf(("1httpSaveCredentials: SecItemAdd returned %d.", (int)err));
899
900 cleanup :
901
902 if (cfcommon_name)
903 CFRelease(cfcommon_name);
904 if (list)
905 CFRelease(list);
906 if (keychain)
907 CFRelease(keychain);
908 if (search)
909 CFRelease(search);
910 if (cert)
911 CFRelease(cert);
912
913 DEBUG_printf(("1httpSaveCredentials: Returning %d.", ret));
914
915 return (ret);
916
917 #else
918 (void)path;
919 (void)credentials;
920 (void)common_name;
921
922 return (-1);
923 #endif /* HAVE_SECKEYCHAINOPEN */
924 }
925
926
927 /*
928 * '_httpTLSInitialize()' - Initialize the TLS stack.
929 */
930
931 void
932 _httpTLSInitialize(void)
933 {
934 /*
935 * Nothing to do...
936 */
937 }
938
939
940 /*
941 * '_httpTLSPending()' - Return the number of pending TLS-encrypted bytes.
942 */
943
944 size_t
945 _httpTLSPending(http_t *http) /* I - HTTP connection */
946 {
947 size_t bytes; /* Bytes that are available */
948
949
950 if (!SSLGetBufferedReadSize(http->tls, &bytes))
951 return (bytes);
952
953 return (0);
954 }
955
956
957 /*
958 * '_httpTLSRead()' - Read from a SSL/TLS connection.
959 */
960
961 int /* O - Bytes read */
962 _httpTLSRead(http_t *http, /* I - HTTP connection */
963 char *buf, /* I - Buffer to store data */
964 int len) /* I - Length of buffer */
965 {
966 int result; /* Return value */
967 OSStatus error; /* Error info */
968 size_t processed; /* Number of bytes processed */
969
970
971 error = SSLRead(http->tls, buf, (size_t)len, &processed);
972 DEBUG_printf(("6_httpTLSRead: error=%d, processed=%d", (int)error,
973 (int)processed));
974 switch (error)
975 {
976 case 0 :
977 result = (int)processed;
978 break;
979
980 case errSSLWouldBlock :
981 if (processed)
982 result = (int)processed;
983 else
984 {
985 result = -1;
986 errno = EINTR;
987 }
988 break;
989
990 case errSSLClosedGraceful :
991 default :
992 if (processed)
993 result = (int)processed;
994 else
995 {
996 result = -1;
997 errno = EPIPE;
998 }
999 break;
1000 }
1001
1002 return (result);
1003 }
1004
1005
1006 /*
1007 * '_httpTLSStart()' - Set up SSL/TLS support on a connection.
1008 */
1009
1010 int /* O - 0 on success, -1 on failure */
1011 _httpTLSStart(http_t *http) /* I - HTTP connection */
1012 {
1013 char hostname[256], /* Hostname */
1014 *hostptr; /* Pointer into hostname */
1015 _cups_globals_t *cg = _cupsGlobals();
1016 /* Pointer to library globals */
1017 OSStatus error; /* Error code */
1018 const char *message = NULL;/* Error message */
1019 cups_array_t *credentials; /* Credentials array */
1020 cups_array_t *names; /* CUPS distinguished names */
1021 CFArrayRef dn_array; /* CF distinguished names array */
1022 CFIndex count; /* Number of credentials */
1023 CFDataRef data; /* Certificate data */
1024 int i; /* Looping var */
1025 http_credential_t *credential; /* Credential data */
1026
1027
1028 DEBUG_printf(("7_httpTLSStart(http=%p)", http));
1029
1030 #ifdef HAVE_SECKEYCHAINOPEN
1031 if (http->mode == _HTTP_MODE_SERVER && !tls_keychain)
1032 #else
1033 if (http->mode == _HTTP_MODE_SERVER)
1034 #endif /* HAVE_SECKEYCHAINOPEN */
1035 {
1036 DEBUG_puts("4_httpTLSStart: cupsSetServerCredentials not called.");
1037 http->error = errno = EINVAL;
1038 http->status = HTTP_STATUS_ERROR;
1039 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Server credentials not set."), 1);
1040
1041 return (-1);
1042 }
1043
1044 if ((http->tls = SSLCreateContext(kCFAllocatorDefault, http->mode == _HTTP_MODE_CLIENT ? kSSLClientSide : kSSLServerSide, kSSLStreamType)) == NULL)
1045 {
1046 DEBUG_puts("4_httpTLSStart: SSLCreateContext failed.");
1047 http->error = errno = ENOMEM;
1048 http->status = HTTP_STATUS_ERROR;
1049 _cupsSetHTTPError(HTTP_STATUS_ERROR);
1050
1051 return (-1);
1052 }
1053
1054 error = SSLSetConnection(http->tls, http);
1055 DEBUG_printf(("4_httpTLSStart: SSLSetConnection, error=%d", (int)error));
1056
1057 if (!error)
1058 {
1059 error = SSLSetIOFuncs(http->tls, http_cdsa_read, http_cdsa_write);
1060 DEBUG_printf(("4_httpTLSStart: SSLSetIOFuncs, error=%d", (int)error));
1061 }
1062
1063 if (!error)
1064 {
1065 error = SSLSetSessionOption(http->tls, kSSLSessionOptionBreakOnServerAuth,
1066 true);
1067 DEBUG_printf(("4_httpTLSStart: SSLSetSessionOption, error=%d",
1068 (int)error));
1069 }
1070
1071 if (!error && http->mode == _HTTP_MODE_CLIENT)
1072 {
1073 /*
1074 * Client: set client-side credentials, if any...
1075 */
1076
1077 if (cg->client_cert_cb)
1078 {
1079 error = SSLSetSessionOption(http->tls,
1080 kSSLSessionOptionBreakOnCertRequested, true);
1081 DEBUG_printf(("4_httpTLSStart: kSSLSessionOptionBreakOnCertRequested, "
1082 "error=%d", (int)error));
1083 }
1084 else
1085 {
1086 error = http_cdsa_set_credentials(http);
1087 DEBUG_printf(("4_httpTLSStart: http_cdsa_set_credentials, error=%d",
1088 (int)error));
1089 }
1090 }
1091 else if (!error)
1092 {
1093 /*
1094 * Server: find/create a certificate for TLS...
1095 */
1096
1097 if (http->fields[HTTP_FIELD_HOST][0])
1098 {
1099 /*
1100 * Use hostname for TLS upgrade...
1101 */
1102
1103 strlcpy(hostname, http->fields[HTTP_FIELD_HOST], sizeof(hostname));
1104 }
1105 else
1106 {
1107 /*
1108 * Resolve hostname from connection address...
1109 */
1110
1111 http_addr_t addr; /* Connection address */
1112 socklen_t addrlen; /* Length of address */
1113
1114 addrlen = sizeof(addr);
1115 if (getsockname(http->fd, (struct sockaddr *)&addr, &addrlen))
1116 {
1117 DEBUG_printf(("4_httpTLSStart: Unable to get socket address: %s", strerror(errno)));
1118 hostname[0] = '\0';
1119 }
1120 else if (httpAddrLocalhost(&addr))
1121 hostname[0] = '\0';
1122 else
1123 {
1124 httpAddrLookup(&addr, hostname, sizeof(hostname));
1125 DEBUG_printf(("4_httpTLSStart: Resolved socket address to \"%s\".", hostname));
1126 }
1127 }
1128
1129 #ifdef HAVE_SECKEYCHAINOPEN
1130 if (isdigit(hostname[0] & 255) || hostname[0] == '[')
1131 hostname[0] = '\0'; /* Don't allow numeric addresses */
1132
1133 if (hostname[0])
1134 http->tls_credentials = http_cdsa_copy_server(hostname);
1135 else if (tls_common_name)
1136 http->tls_credentials = http_cdsa_copy_server(tls_common_name);
1137
1138 if (!http->tls_credentials && tls_auto_create && (hostname[0] || tls_common_name))
1139 {
1140 DEBUG_printf(("4_httpTLSStart: Auto-create credentials for \"%s\".", hostname[0] ? hostname : tls_common_name));
1141
1142 if (!cupsMakeServerCredentials(tls_keypath, hostname[0] ? hostname : tls_common_name, 0, NULL, time(NULL) + 365 * 86400))
1143 {
1144 DEBUG_puts("4_httpTLSStart: cupsMakeServerCredentials failed.");
1145 http->error = errno = EINVAL;
1146 http->status = HTTP_STATUS_ERROR;
1147 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to create server credentials."), 1);
1148
1149 return (-1);
1150 }
1151
1152 http->tls_credentials = http_cdsa_copy_server(hostname[0] ? hostname : tls_common_name);
1153 }
1154 #endif /* HAVE_SECKEYCHAINOPEN */
1155
1156 if (!http->tls_credentials)
1157 {
1158 DEBUG_puts("4_httpTLSStart: Unable to find server credentials.");
1159 http->error = errno = EINVAL;
1160 http->status = HTTP_STATUS_ERROR;
1161 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to find server credentials."), 1);
1162
1163 return (-1);
1164 }
1165
1166 error = SSLSetCertificate(http->tls, http->tls_credentials);
1167
1168 DEBUG_printf(("4_httpTLSStart: SSLSetCertificate, error=%d", (int)error));
1169 }
1170
1171 DEBUG_printf(("4_httpTLSStart: tls_credentials=%p", http->tls_credentials));
1172
1173 /*
1174 * Let the server know which hostname/domain we are trying to connect to
1175 * in case it wants to serve up a certificate with a matching common name.
1176 */
1177
1178 if (!error && http->mode == _HTTP_MODE_CLIENT)
1179 {
1180 /*
1181 * Client: get the hostname to use for TLS...
1182 */
1183
1184 if (httpAddrLocalhost(http->hostaddr))
1185 {
1186 strlcpy(hostname, "localhost", sizeof(hostname));
1187 }
1188 else
1189 {
1190 /*
1191 * Otherwise make sure the hostname we have does not end in a trailing dot.
1192 */
1193
1194 strlcpy(hostname, http->hostname, sizeof(hostname));
1195 if ((hostptr = hostname + strlen(hostname) - 1) >= hostname &&
1196 *hostptr == '.')
1197 *hostptr = '\0';
1198 }
1199
1200 error = SSLSetPeerDomainName(http->tls, hostname, strlen(hostname));
1201
1202 DEBUG_printf(("4_httpTLSStart: SSLSetPeerDomainName, error=%d", (int)error));
1203 }
1204
1205 if (!error)
1206 {
1207 int done = 0; /* Are we done yet? */
1208
1209 while (!error && !done)
1210 {
1211 error = SSLHandshake(http->tls);
1212
1213 DEBUG_printf(("4_httpTLSStart: SSLHandshake returned %d.", (int)error));
1214
1215 switch (error)
1216 {
1217 case noErr :
1218 done = 1;
1219 break;
1220
1221 case errSSLWouldBlock :
1222 error = noErr; /* Force a retry */
1223 usleep(1000); /* in 1 millisecond */
1224 break;
1225
1226 case errSSLServerAuthCompleted :
1227 error = 0;
1228 if (cg->server_cert_cb)
1229 {
1230 error = httpCopyCredentials(http, &credentials);
1231 if (!error)
1232 {
1233 error = (cg->server_cert_cb)(http, http->tls, credentials,
1234 cg->server_cert_data);
1235 httpFreeCredentials(credentials);
1236 }
1237
1238 DEBUG_printf(("4_httpTLSStart: Server certificate callback "
1239 "returned %d.", (int)error));
1240 }
1241 break;
1242
1243 case errSSLClientCertRequested :
1244 error = 0;
1245
1246 if (cg->client_cert_cb)
1247 {
1248 names = NULL;
1249 if (!(error = SSLCopyDistinguishedNames(http->tls, &dn_array)) &&
1250 dn_array)
1251 {
1252 if ((names = cupsArrayNew(NULL, NULL)) != NULL)
1253 {
1254 for (i = 0, count = CFArrayGetCount(dn_array); i < count; i++)
1255 {
1256 data = (CFDataRef)CFArrayGetValueAtIndex(dn_array, i);
1257
1258 if ((credential = malloc(sizeof(*credential))) != NULL)
1259 {
1260 credential->datalen = (size_t)CFDataGetLength(data);
1261 if ((credential->data = malloc(credential->datalen)))
1262 {
1263 memcpy((void *)credential->data, CFDataGetBytePtr(data),
1264 credential->datalen);
1265 cupsArrayAdd(names, credential);
1266 }
1267 else
1268 free(credential);
1269 }
1270 }
1271 }
1272
1273 CFRelease(dn_array);
1274 }
1275
1276 if (!error)
1277 {
1278 error = (cg->client_cert_cb)(http, http->tls, names,
1279 cg->client_cert_data);
1280
1281 DEBUG_printf(("4_httpTLSStart: Client certificate callback "
1282 "returned %d.", (int)error));
1283 }
1284
1285 httpFreeCredentials(names);
1286 }
1287 break;
1288
1289 case errSSLUnknownRootCert :
1290 message = _("Unable to establish a secure connection to host "
1291 "(untrusted certificate).");
1292 break;
1293
1294 case errSSLNoRootCert :
1295 message = _("Unable to establish a secure connection to host "
1296 "(self-signed certificate).");
1297 break;
1298
1299 case errSSLCertExpired :
1300 message = _("Unable to establish a secure connection to host "
1301 "(expired certificate).");
1302 break;
1303
1304 case errSSLCertNotYetValid :
1305 message = _("Unable to establish a secure connection to host "
1306 "(certificate not yet valid).");
1307 break;
1308
1309 case errSSLHostNameMismatch :
1310 message = _("Unable to establish a secure connection to host "
1311 "(host name mismatch).");
1312 break;
1313
1314 case errSSLXCertChainInvalid :
1315 message = _("Unable to establish a secure connection to host "
1316 "(certificate chain invalid).");
1317 break;
1318
1319 case errSSLConnectionRefused :
1320 message = _("Unable to establish a secure connection to host "
1321 "(peer dropped connection before responding).");
1322 break;
1323
1324 default :
1325 break;
1326 }
1327 }
1328 }
1329
1330 if (error)
1331 {
1332 http->error = error;
1333 http->status = HTTP_STATUS_ERROR;
1334 errno = ECONNREFUSED;
1335
1336 CFRelease(http->tls);
1337 http->tls = NULL;
1338
1339 /*
1340 * If an error string wasn't set by the callbacks use a generic one...
1341 */
1342
1343 if (!message)
1344 #ifdef HAVE_CSSMERRORSTRING
1345 message = cssmErrorString(error);
1346 #else
1347 message = _("Unable to establish a secure connection to host.");
1348 #endif /* HAVE_CSSMERRORSTRING */
1349
1350 _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI, message, 1);
1351
1352 return (-1);
1353 }
1354
1355 return (0);
1356 }
1357
1358
1359 /*
1360 * '_httpTLSStop()' - Shut down SSL/TLS on a connection.
1361 */
1362
1363 void
1364 _httpTLSStop(http_t *http) /* I - HTTP connection */
1365 {
1366 while (SSLClose(http->tls) == errSSLWouldBlock)
1367 usleep(1000);
1368
1369 CFRelease(http->tls);
1370
1371 if (http->tls_credentials)
1372 CFRelease(http->tls_credentials);
1373
1374 http->tls = NULL;
1375 http->tls_credentials = NULL;
1376 }
1377
1378
1379 /*
1380 * '_httpTLSWrite()' - Write to a SSL/TLS connection.
1381 */
1382
1383 int /* O - Bytes written */
1384 _httpTLSWrite(http_t *http, /* I - HTTP connection */
1385 const char *buf, /* I - Buffer holding data */
1386 int len) /* I - Length of buffer */
1387 {
1388 ssize_t result; /* Return value */
1389 OSStatus error; /* Error info */
1390 size_t processed; /* Number of bytes processed */
1391
1392
1393 DEBUG_printf(("2_httpTLSWrite(http=%p, buf=%p, len=%d)", http, buf, len));
1394
1395 error = SSLWrite(http->tls, buf, (size_t)len, &processed);
1396
1397 switch (error)
1398 {
1399 case 0 :
1400 result = (int)processed;
1401 break;
1402
1403 case errSSLWouldBlock :
1404 if (processed)
1405 {
1406 result = (int)processed;
1407 }
1408 else
1409 {
1410 result = -1;
1411 errno = EINTR;
1412 }
1413 break;
1414
1415 case errSSLClosedGraceful :
1416 default :
1417 if (processed)
1418 {
1419 result = (int)processed;
1420 }
1421 else
1422 {
1423 result = -1;
1424 errno = EPIPE;
1425 }
1426 break;
1427 }
1428
1429 DEBUG_printf(("3_httpTLSWrite: Returning %d.", (int)result));
1430
1431 return ((int)result);
1432 }
1433
1434
1435 #ifdef HAVE_SECKEYCHAINOPEN
1436 /*
1437 * 'http_cdsa_copy_server()' - Find and copy server credentials from the keychain.
1438 */
1439
1440 static CFArrayRef /* O - Array of certificates or NULL */
1441 http_cdsa_copy_server(
1442 const char *common_name) /* I - Server's hostname */
1443 {
1444 OSStatus err; /* Error info */
1445 SecIdentitySearchRef search = NULL; /* Search reference */
1446 SecIdentityRef identity = NULL;/* Identity */
1447 CFArrayRef certificates = NULL;
1448 /* Certificate array */
1449 SecPolicyRef policy = NULL; /* Policy ref */
1450 CFStringRef cfcommon_name = NULL;
1451 /* Server name */
1452 CFMutableDictionaryRef query = NULL; /* Query qualifiers */
1453 CFArrayRef list = NULL; /* Keychain list */
1454
1455
1456 cfcommon_name = CFStringCreateWithCString(kCFAllocatorDefault, common_name, kCFStringEncodingUTF8);
1457
1458 policy = SecPolicyCreateSSL(1, cfcommon_name);
1459
1460 if (cfcommon_name)
1461 CFRelease(cfcommon_name);
1462
1463 if (!policy)
1464 goto cleanup;
1465
1466 if (!(query = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)))
1467 goto cleanup;
1468
1469 list = CFArrayCreate(kCFAllocatorDefault, (const void **)&tls_keychain, 1, &kCFTypeArrayCallBacks);
1470
1471 CFDictionaryAddValue(query, kSecClass, kSecClassIdentity);
1472 CFDictionaryAddValue(query, kSecMatchPolicy, policy);
1473 CFDictionaryAddValue(query, kSecReturnRef, kCFBooleanTrue);
1474 CFDictionaryAddValue(query, kSecMatchLimit, kSecMatchLimitOne);
1475 CFDictionaryAddValue(query, kSecMatchSearchList, list);
1476
1477 CFRelease(list);
1478
1479 err = SecItemCopyMatching(query, (CFTypeRef *)&identity);
1480
1481 if (err)
1482 goto cleanup;
1483
1484 if (CFGetTypeID(identity) != SecIdentityGetTypeID())
1485 goto cleanup;
1486
1487 if ((certificates = CFArrayCreate(NULL, (const void **)&identity, 1, &kCFTypeArrayCallBacks)) == NULL)
1488 goto cleanup;
1489
1490 cleanup :
1491
1492 if (search)
1493 CFRelease(search);
1494 if (identity)
1495 CFRelease(identity);
1496
1497 if (policy)
1498 CFRelease(policy);
1499 if (query)
1500 CFRelease(query);
1501
1502 return (certificates);
1503 }
1504 #endif /* HAVE_SECKEYCHAINOPEN */
1505
1506
1507 /*
1508 * 'http_cdsa_read()' - Read function for the CDSA library.
1509 */
1510
1511 static OSStatus /* O - -1 on error, 0 on success */
1512 http_cdsa_read(
1513 SSLConnectionRef connection, /* I - SSL/TLS connection */
1514 void *data, /* I - Data buffer */
1515 size_t *dataLength) /* IO - Number of bytes */
1516 {
1517 OSStatus result; /* Return value */
1518 ssize_t bytes; /* Number of bytes read */
1519 http_t *http; /* HTTP connection */
1520
1521
1522 http = (http_t *)connection;
1523
1524 if (!http->blocking)
1525 {
1526 /*
1527 * Make sure we have data before we read...
1528 */
1529
1530 while (!_httpWait(http, http->wait_value, 0))
1531 {
1532 if (http->timeout_cb && (*http->timeout_cb)(http, http->timeout_data))
1533 continue;
1534
1535 http->error = ETIMEDOUT;
1536 return (-1);
1537 }
1538 }
1539
1540 do
1541 {
1542 bytes = recv(http->fd, data, *dataLength, 0);
1543 }
1544 while (bytes == -1 && (errno == EINTR || errno == EAGAIN));
1545
1546 if ((size_t)bytes == *dataLength)
1547 {
1548 result = 0;
1549 }
1550 else if (bytes > 0)
1551 {
1552 *dataLength = (size_t)bytes;
1553 result = errSSLWouldBlock;
1554 }
1555 else
1556 {
1557 *dataLength = 0;
1558
1559 if (bytes == 0)
1560 result = errSSLClosedGraceful;
1561 else if (errno == EAGAIN)
1562 result = errSSLWouldBlock;
1563 else
1564 result = errSSLClosedAbort;
1565 }
1566
1567 return (result);
1568 }
1569
1570
1571 /*
1572 * 'http_cdsa_set_credentials()' - Set the TLS credentials.
1573 */
1574
1575 static int /* O - Status of connection */
1576 http_cdsa_set_credentials(http_t *http) /* I - HTTP connection */
1577 {
1578 _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals */
1579 OSStatus error = 0; /* Error code */
1580 http_tls_credentials_t credentials = NULL;
1581 /* TLS credentials */
1582
1583
1584 DEBUG_printf(("7http_tls_set_credentials(%p)", http));
1585
1586 /*
1587 * Prefer connection specific credentials...
1588 */
1589
1590 if ((credentials = http->tls_credentials) == NULL)
1591 credentials = cg->tls_credentials;
1592
1593 if (credentials)
1594 {
1595 error = SSLSetCertificate(http->tls, credentials);
1596 DEBUG_printf(("4http_tls_set_credentials: SSLSetCertificate, error=%d",
1597 (int)error));
1598 }
1599 else
1600 DEBUG_puts("4http_tls_set_credentials: No credentials to set.");
1601
1602 return (error);
1603 }
1604
1605
1606 /*
1607 * 'http_cdsa_write()' - Write function for the CDSA library.
1608 */
1609
1610 static OSStatus /* O - -1 on error, 0 on success */
1611 http_cdsa_write(
1612 SSLConnectionRef connection, /* I - SSL/TLS connection */
1613 const void *data, /* I - Data buffer */
1614 size_t *dataLength) /* IO - Number of bytes */
1615 {
1616 OSStatus result; /* Return value */
1617 ssize_t bytes; /* Number of bytes read */
1618 http_t *http; /* HTTP connection */
1619
1620
1621 http = (http_t *)connection;
1622
1623 do
1624 {
1625 bytes = write(http->fd, data, *dataLength);
1626 }
1627 while (bytes == -1 && (errno == EINTR || errno == EAGAIN));
1628
1629 if ((size_t)bytes == *dataLength)
1630 {
1631 result = 0;
1632 }
1633 else if (bytes >= 0)
1634 {
1635 *dataLength = (size_t)bytes;
1636 result = errSSLWouldBlock;
1637 }
1638 else
1639 {
1640 *dataLength = 0;
1641
1642 if (errno == EAGAIN)
1643 result = errSSLWouldBlock;
1644 else
1645 result = errSSLClosedAbort;
1646 }
1647
1648 return (result);
1649 }
1650
1651
1652 /*
1653 * End of "$Id$".
1654 */