]> git.ipfire.org Git - thirdparty/cups.git/blame - cups/tls-darwin.c
Make sure long-edge feed bounding box is correct.
[thirdparty/cups.git] / cups / tls-darwin.c
CommitLineData
2c85b752
MS
1/*
2 * "$Id$"
3 *
4 * TLS support code for CUPS on OS X.
5 *
7e86f2f6 6 * Copyright 2007-2014 by Apple Inc.
2c85b752
MS
7 * Copyright 1997-2007 by Easy Software Products, all rights reserved.
8 *
9 * These coded instructions, statements, and computer programs are the
10 * property of Apple Inc. and are protected by Federal copyright
11 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
12 * which should have been included with this file. If this file is
13 * file is missing or damaged, see the license at "http://www.cups.org/".
14 *
15 * This file is subject to the Apple OS-Developed Software exception.
16 */
17
dafebafd 18/**** This file is included from http.c ****/
2c85b752
MS
19
20/*
dafebafd 21 * Include necessary headers...
2c85b752
MS
22 */
23
dafebafd
MS
24#include <spawn.h>
25
26extern char **environ;
27
28
c0459938
MS
29/*
30 * Local globals...
31 */
32
eb66bc71 33#ifdef HAVE_SECKEYCHAINOPEN
c0459938
MS
34static int tls_auto_create = 0;
35 /* Auto-create self-signed certs? */
36static char *tls_common_name = NULL;
37 /* Default common name */
41e0907c
MS
38static SecKeychainRef tls_keychain = NULL;
39 /* Server cert keychain */
40static char *tls_keypath = NULL;
41 /* Server cert keychain path */
42static _cups_mutex_t tls_mutex = _CUPS_MUTEX_INITIALIZER;
43 /* Mutex for keychain/certs */
eb66bc71 44#endif /* HAVE_SECKEYCHAINOPEN */
c0459938
MS
45
46
dafebafd
MS
47/*
48 * Local functions...
49 */
2c85b752 50
eb66bc71 51#ifdef HAVE_SECKEYCHAINOPEN
41e0907c 52static CFArrayRef http_cdsa_copy_server(const char *common_name);
eb66bc71 53#endif /* HAVE_SECKEYCHAINOPEN */
41e0907c 54static OSStatus http_cdsa_read(SSLConnectionRef connection, void *data, size_t *dataLength);
88f1e9c8 55static int http_cdsa_set_credentials(http_t *http);
41e0907c 56static OSStatus http_cdsa_write(SSLConnectionRef connection, const void *data, size_t *dataLength);
2c85b752
MS
57
58
3af9ac9e
MS
59/*
60 * 'cupsMakeServerCredentials()' - Make a self-signed certificate and private key pair.
61 *
62 * @since CUPS 2.0@
63 */
64
65int /* O - 1 on success, 0 on failure */
66cupsMakeServerCredentials(
67 const char *path, /* I - Path to keychain/directory */
68 const char *common_name, /* I - Common name */
69 int num_alt_names, /* I - Number of subject alternate names */
70 const char **alt_names, /* I - Subject Alternate Names */
71 time_t expiration_date) /* I - Expiration date */
72{
eb66bc71 73#if defined(HAVE_SECGENERATESELFSIGNEDCERTIFICATE) && defined(HAVE_SECKEYCHAINOPEN)
41e0907c
MS
74 int status = 0; /* Return status */
75 OSStatus err; /* Error code (if any) */
76 CFStringRef cfcommon_name = NULL;
77 /* CF string for server name */
78 SecIdentityRef ident = NULL; /* Identity */
79 SecKeyRef publicKey = NULL,
80 /* Public key */
81 privateKey = NULL;
82 /* Private key */
83 CFMutableDictionaryRef keyParams = NULL;
84 /* Key generation parameters */
85
86
3af9ac9e
MS
87 (void)num_alt_names;
88 (void)alt_names;
89 (void)expiration_date;
90
41e0907c
MS
91 cfcommon_name = CFStringCreateWithCString(kCFAllocatorDefault, common_name,
92 kCFStringEncodingUTF8);
93 if (!cfcommon_name)
94 goto cleanup;
95
96 /*
97 * Create a public/private key pair...
98 */
99
100 keyParams = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
101 &kCFTypeDictionaryKeyCallBacks,
102 &kCFTypeDictionaryValueCallBacks);
103 if (!keyParams)
104 goto cleanup;
105
106 CFDictionaryAddValue(keyParams, kSecAttrKeyType, kSecAttrKeyTypeRSA);
107 CFDictionaryAddValue(keyParams, kSecAttrKeySizeInBits, CFSTR("2048"));
108 CFDictionaryAddValue(keyParams, kSecAttrLabel,
109 CFSTR("CUPS Self-Signed Certificate"));
110
111 err = SecKeyGeneratePair(keyParams, &publicKey, &privateKey);
112 if (err != noErr)
113 goto cleanup;
114
115 /*
116 * Create a self-signed certificate using the public/private key pair...
117 */
118
119 CFIndex usageInt = kSecKeyUsageAll;
120 CFNumberRef usage = CFNumberCreate(alloc, kCFNumberCFIndexType, &usageInt);
121 CFDictionaryRef certParams = CFDictionaryCreateMutable(kCFAllocatorDefault,
122kSecCSRBasicContraintsPathLen, CFINT(0), kSecSubjectAltName, cfcommon_name, kSecCertificateKeyUsage, usage, NULL, NULL);
123 CFRelease(usage);
124
125 const void *ca_o[] = { kSecOidOrganization, CFSTR("") };
126 const void *ca_cn[] = { kSecOidCommonName, cfcommon_name };
127 CFArrayRef ca_o_dn = CFArrayCreate(kCFAllocatorDefault, ca_o, 2, NULL);
128 CFArrayRef ca_cn_dn = CFArrayCreate(kCFAllocatorDefault, ca_cn, 2, NULL);
129 const void *ca_dn_array[2];
130
131 ca_dn_array[0] = CFArrayCreate(kCFAllocatorDefault, (const void **)&ca_o_dn, 1, NULL);
132 ca_dn_array[1] = CFArrayCreate(kCFAllocatorDefault, (const void **)&ca_cn_dn, 1, NULL);
133
134 CFArrayRef subject = CFArrayCreate(kCFAllocatorDefault, ca_dn_array, 2, NULL);
135 SecCertificateRef cert = SecGenerateSelfSignedCertificate(subject, certParams, publicKey, privateKey);
136 CFRelease(subject);
137 CFRelease(certParams);
138
139 if (!cert)
140 goto cleanup;
141
142 ident = SecIdentityCreate(kCFAllocatorDefault, cert, privateKey);
143
144 if (ident)
145 status = 1;
146
147 /*
148 * Cleanup and return...
149 */
150
151cleanup:
152
153 if (cfcommon_name)
154 CFRelease(cfcommon_name);
155
156 if (keyParams)
157 CFRelease(keyParams);
158
159 if (ident)
160 CFRelease(ident);
161
162 if (cert)
163 CFRelease(cert);
164
165 if (publicKey)
166 CFRelease(publicKey);
167
168 if (privateKey)
169 CFRelease(publicKey);
170
171 return (status);
172
eb66bc71 173#else /* !(HAVE_SECGENERATESELFSIGNEDCERTIFICATE && HAVE_SECKEYCHAINOPEN) */
41e0907c 174 int pid, /* Process ID of command */
7d58a105
MS
175 status, /* Status of command */
176 i; /* Looping var */
41e0907c
MS
177 char command[1024], /* Command */
178 *argv[4], /* Command-line arguments */
7d58a105
MS
179 *envp[1000], /* Environment variables */
180 days[32], /* CERTTOOL_EXPIRATION_DAYS env var */
41e0907c
MS
181 keychain[1024], /* Keychain argument */
182 infofile[1024]; /* Type-in information for cert */
183 cups_file_t *fp; /* Seed/info file */
184
185
186 (void)num_alt_names;
187 (void)alt_names;
41e0907c
MS
188
189 /*
190 * Run the "certtool" command to generate a self-signed certificate...
191 */
192
193 if (!cupsFileFind("certtool", getenv("PATH"), 1, command, sizeof(command)))
194 return (-1);
195
196 /*
197 * Create a file with the certificate information fields...
198 *
199 * Note: This assumes that the default questions are asked by the certtool
200 * command...
201 */
202
203 if ((fp = cupsTempFile2(infofile, sizeof(infofile))) == NULL)
204 return (-1);
205
206 cupsFilePrintf(fp,
207 "CUPS Self-Signed Certificate\n"
208 /* Enter key and certificate label */
209 "r\n" /* Generate RSA key pair */
210 "2048\n" /* Key size in bits */
211 "y\n" /* OK (y = yes) */
212 "b\n" /* Usage (b=signing/encryption) */
213 "s\n" /* Sign with SHA1 */
214 "y\n" /* OK (y = yes) */
215 "%s\n" /* Common name */
216 "\n" /* Country (default) */
217 "\n" /* Organization (default) */
218 "\n" /* Organizational unit (default) */
219 "\n" /* State/Province (default) */
220 "\n" /* Email address */
221 "y\n", /* OK (y = yes) */
222 common_name);
223 cupsFileClose(fp);
224
225 snprintf(keychain, sizeof(keychain), "k=%s", path);
226
227 argv[0] = "certtool";
228 argv[1] = "c";
229 argv[2] = keychain;
230 argv[3] = NULL;
231
7d58a105
MS
232 snprintf(days, sizeof(days), "CERTTOOL_EXPIRATION_DAYS=%d", (int)((expiration_date - time(NULL) + 86399) / 86400));
233 envp[0] = days;
234 for (i = 0; i < (int)(sizeof(envp) / sizeof(envp[0]) - 2) && environ[i]; i ++)
235 envp[i + 1] = environ[i];
236 envp[i] = NULL;
237
41e0907c
MS
238 posix_spawn_file_actions_t actions; /* File actions */
239
240 posix_spawn_file_actions_init(&actions);
241 posix_spawn_file_actions_addclose(&actions, 0);
242 posix_spawn_file_actions_addopen(&actions, 0, infofile, O_RDONLY, 0);
243
7d58a105 244 if (posix_spawn(&pid, command, &actions, NULL, argv, envp))
41e0907c
MS
245 {
246 unlink(infofile);
247 return (-1);
248 }
249
250 posix_spawn_file_actions_destroy(&actions);
251
252 unlink(infofile);
253
254 while (waitpid(pid, &status, 0) < 0)
255 if (errno != EINTR)
256 {
257 status = -1;
258 break;
259 }
260
261 return (!status);
eb66bc71 262#endif /* HAVE_SECGENERATESELFSIGNEDCERTIFICATE && HAVE_SECKEYCHAINOPEN */
3af9ac9e
MS
263}
264
265
266/*
267 * 'cupsSetServerCredentials()' - Set the default server credentials.
268 *
269 * Note: The server credentials are used by all threads in the running process.
270 * This function is threadsafe.
271 *
272 * @since CUPS 2.0@
273 */
274
275int /* O - 1 on success, 0 on failure */
276cupsSetServerCredentials(
277 const char *path, /* I - Path to keychain/directory */
278 const char *common_name, /* I - Default common name for server */
279 int auto_create) /* I - 1 = automatically create self-signed certificates */
280{
a27a134a
MS
281 DEBUG_printf(("cupsSetServerCredentials(path=\"%s\", common_name=\"%s\", auto_create=%d)", path, common_name, auto_create));
282
eb66bc71 283#ifdef HAVE_SECKEYCHAINOPEN
c0459938 284 SecKeychainRef keychain = NULL;/* Temporary keychain */
3af9ac9e 285
c0459938
MS
286
287 if (SecKeychainOpen(path, &keychain) != noErr)
288 {
289 /* TODO: Set cups last error string */
a27a134a 290 DEBUG_puts("1cupsSetServerCredentials: Unable to open keychain, returning 0.");
c0459938
MS
291 return (0);
292 }
293
294 _cupsMutexLock(&tls_mutex);
295
296 /*
297 * Close any keychain that is currently open...
298 */
299
300 if (tls_keychain)
301 CFRelease(tls_keychain);
302
41e0907c
MS
303 if (tls_keypath)
304 _cupsStrFree(tls_keypath);
305
c0459938
MS
306 if (tls_common_name)
307 _cupsStrFree(tls_common_name);
308
309 /*
310 * Save the new keychain...
311 */
312
313 tls_keychain = keychain;
41e0907c 314 tls_keypath = _cupsStrAlloc(path);
c0459938
MS
315 tls_auto_create = auto_create;
316 tls_common_name = _cupsStrAlloc(common_name);
317
318 _cupsMutexUnlock(&tls_mutex);
319
a27a134a 320 DEBUG_puts("1cupsSetServerCredentials: Opened keychain, returning 1.");
c0459938 321 return (1);
eb66bc71
MS
322
323#else
a27a134a 324 DEBUG_puts("1cupsSetServerCredentials: No keychain support compiled in, returning 0.");
eb66bc71
MS
325 return (0);
326#endif /* HAVE_SECKEYCHAINOPEN */
3af9ac9e
MS
327}
328
2c85b752
MS
329
330/*
331 * 'httpCopyCredentials()' - Copy the credentials associated with the peer in
332 * an encrypted connection.
333 *
334 * @since CUPS 1.5/OS X 10.7@
335 */
336
337int /* O - Status of call (0 = success) */
338httpCopyCredentials(
339 http_t *http, /* I - Connection to server */
340 cups_array_t **credentials) /* O - Array of credentials */
341{
342 OSStatus error; /* Error code */
343 SecTrustRef peerTrust; /* Peer trust reference */
344 CFIndex count; /* Number of credentials */
345 SecCertificateRef secCert; /* Certificate reference */
346 CFDataRef data; /* Certificate data */
347 int i; /* Looping var */
348
349
376d7c69
MS
350 DEBUG_printf(("httpCopyCredentials(http=%p, credentials=%p)", http, credentials));
351
2c85b752
MS
352 if (credentials)
353 *credentials = NULL;
354
355 if (!http || !http->tls || !credentials)
356 return (-1);
357
358 if (!(error = SSLCopyPeerTrust(http->tls, &peerTrust)) && peerTrust)
359 {
376d7c69
MS
360 DEBUG_printf(("2httpCopyCredentials: Peer provided %d certificates.", (int)SecTrustGetCertificateCount(peerTrust)));
361
2c85b752
MS
362 if ((*credentials = cupsArrayNew(NULL, NULL)) != NULL)
363 {
364 count = SecTrustGetCertificateCount(peerTrust);
365
366 for (i = 0; i < count; i ++)
367 {
368 secCert = SecTrustGetCertificateAtIndex(peerTrust, i);
376d7c69
MS
369
370#ifdef DEBUG
371 CFStringRef cf_name = SecCertificateCopySubjectSummary(secCert);
372 char name[1024];
373 if (cf_name)
374 CFStringGetCString(cf_name, name, sizeof(name), kCFStringEncodingUTF8);
375 else
376 strlcpy(name, "unknown", sizeof(name));
377
378 DEBUG_printf(("2httpCopyCredentials: Certificate %d name is \"%s\".", i, name));
379#endif /* DEBUG */
380
88f1e9c8 381 if ((data = SecCertificateCopyData(secCert)) != NULL)
2c85b752 382 {
376d7c69
MS
383 DEBUG_printf(("2httpCopyCredentials: Adding %d byte certificate blob.", (int)CFDataGetLength(data)));
384
7e86f2f6 385 httpAddCredential(*credentials, CFDataGetBytePtr(data), (size_t)CFDataGetLength(data));
2c85b752
MS
386 CFRelease(data);
387 }
388 }
389 }
390
391 CFRelease(peerTrust);
392 }
393
394 return (error);
395}
396
397
9653cfdf
MS
398/*
399 * 'http_cdsa_create_credential()' - Create a single credential in the internal format.
400 */
401
402static SecCertificateRef /* O - Certificate */
403http_cdsa_create_credential(
404 http_credential_t *credential) /* I - Credential */
405{
406 if (!credential)
407 return (NULL);
408
409 return (SecCertificateCreateWithBytes(kCFAllocatorDefault, credential->data, (CFIndex)credential->datalen));
410}
411
412
2c85b752
MS
413/*
414 * '_httpCreateCredentials()' - Create credentials in the internal format.
415 */
416
417http_tls_credentials_t /* O - Internal credentials */
418_httpCreateCredentials(
419 cups_array_t *credentials) /* I - Array of credentials */
420{
421 CFMutableArrayRef peerCerts; /* Peer credentials reference */
422 SecCertificateRef secCert; /* Certificate reference */
2c85b752
MS
423 http_credential_t *credential; /* Credential data */
424
425
426 if (!credentials)
427 return (NULL);
428
429 if ((peerCerts = CFArrayCreateMutable(kCFAllocatorDefault,
430 cupsArrayCount(credentials),
431 &kCFTypeArrayCallBacks)) == NULL)
432 return (NULL);
433
434 for (credential = (http_credential_t *)cupsArrayFirst(credentials);
435 credential;
436 credential = (http_credential_t *)cupsArrayNext(credentials))
437 {
9653cfdf 438 if ((secCert = http_cdsa_create_credential(credential)) != NULL)
2c85b752 439 {
9653cfdf
MS
440 CFArrayAppendValue(peerCerts, secCert);
441 CFRelease(secCert);
2c85b752
MS
442 }
443 }
444
445 return (peerCerts);
446}
447
448
3af9ac9e
MS
449/*
450 * 'httpCredentialsAreTrusted()' - Return whether the credentials are trusted.
451 *
452 * @since CUPS 2.0@
453 */
454
455int /* O - 1 if trusted, 0 if not/unknown */
456httpCredentialsAreTrusted(
376d7c69
MS
457 cups_array_t *credentials, /* I - Credentials */
458 const char *common_name) /* I - Common name for trust lookup */
3af9ac9e 459{
9653cfdf
MS
460 SecCertificateRef secCert; /* Certificate reference */
461 int trusted = 1; /* Trusted? */
376d7c69 462 int save = 0; /* Save credentials? */
376d7c69 463 cups_array_t *tcreds = NULL; /* Trusted credentials */
9653cfdf
MS
464 _cups_globals_t *cg = _cupsGlobals();
465 /* Per-thread globals */
3af9ac9e 466
9653cfdf 467
376d7c69
MS
468 if (!common_name)
469 return (0);
470
9653cfdf
MS
471 if ((secCert = http_cdsa_create_credential((http_credential_t *)cupsArrayFirst(credentials))) == NULL)
472 return (0);
473
376d7c69 474 /*
88f1e9c8 475 * Look this common name up in the default keychains...
376d7c69
MS
476 */
477
88f1e9c8 478 httpLoadCredentials(NULL, &tcreds, common_name);
376d7c69
MS
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
9653cfdf
MS
522 if (!cg->expired_certs && !SecCertificateIsValid(secCert, CFAbsoluteTimeGetCurrent()))
523 trusted = 0;
25731360 524 else if (!cg->any_root && cupsArrayCount(credentials) == 1)
9653cfdf
MS
525 trusted = 0;
526
376d7c69 527 if (trusted && save)
88f1e9c8 528 httpSaveCredentials(NULL, credentials, common_name);
376d7c69 529
9653cfdf
MS
530 CFRelease(secCert);
531
532 return (trusted);
3af9ac9e
MS
533}
534
535
536/*
537 * 'httpCredentialsGetExpiration()' - Return the expiration date of the credentials.
538 *
539 * @since CUPS 2.0@
540 */
541
542time_t /* O - Expiration date of credentials */
543httpCredentialsGetExpiration(
544 cups_array_t *credentials) /* I - Credentials */
545{
9653cfdf
MS
546 SecCertificateRef secCert; /* Certificate reference */
547 time_t expiration; /* Expiration date */
3af9ac9e 548
9653cfdf
MS
549
550 if ((secCert = http_cdsa_create_credential((http_credential_t *)cupsArrayFirst(credentials))) == NULL)
551 return (0);
552
376d7c69 553 expiration = (time_t)(SecCertificateNotValidAfter(secCert) + kCFAbsoluteTimeIntervalSince1970);
9653cfdf
MS
554
555 CFRelease(secCert);
556
557 return (expiration);
3af9ac9e
MS
558}
559
560
561/*
562 * 'httpCredentialsIsValidName()' - Return whether the credentials are valid for the given name.
563 *
564 * @since CUPS 2.0@
565 */
566
567int /* O - 1 if valid, 0 otherwise */
568httpCredentialsIsValidName(
569 cups_array_t *credentials, /* I - Credentials */
570 const char *common_name) /* I - Name to check */
571{
9653cfdf 572 SecCertificateRef secCert; /* Certificate reference */
376d7c69
MS
573 CFStringRef cfcert_name = NULL;
574 /* Certificate's common name (CF string) */
575 char cert_name[256]; /* Certificate's common name (C string) */
9653cfdf 576 int valid = 1; /* Valid name? */
3af9ac9e 577
9653cfdf
MS
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
376d7c69 586 if ((cfcert_name = SecCertificateCopySubjectSummary(secCert)) == NULL)
9653cfdf
MS
587 {
588 /*
589 * Can't get common name, cannot be valid...
590 */
591
592 valid = 0;
593 }
376d7c69
MS
594 else if (CFStringGetCString(cfcert_name, cert_name, sizeof(cert_name), kCFStringEncodingUTF8) &&
595 _cups_strcasecmp(common_name, cert_name))
9653cfdf
MS
596 {
597 /*
376d7c69 598 * Not an exact match for the common name, check for wildcard certs...
9653cfdf
MS
599 */
600
376d7c69
MS
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 */
9653cfdf 609
376d7c69
MS
610 /* TODO: Check subject alternate names */
611 valid = 0;
612 }
613 }
9653cfdf 614
376d7c69
MS
615 if (cfcert_name)
616 CFRelease(cfcert_name);
9653cfdf
MS
617
618 CFRelease(secCert);
619
620 return (valid);
3af9ac9e
MS
621}
622
623
72d05bc9
MS
624/*
625 * 'httpCredentialsString()' - Return a string representing the credentials.
626 *
627 * @since CUPS 2.0@
628 */
629
630size_t /* O - Total size of credentials string */
631httpCredentialsString(
632 cups_array_t *credentials, /* I - Credentials */
633 char *buffer, /* I - Buffer or @code NULL@ */
634 size_t bufsize) /* I - Size of buffer */
635{
376d7c69 636 http_credential_t *first; /* First certificate */
9653cfdf 637 SecCertificateRef secCert; /* Certificate reference */
9653cfdf
MS
638
639
376d7c69
MS
640 DEBUG_printf(("httpCredentialsString(credentials=%p, buffer=%p, bufsize=" CUPS_LLFMT ")", credentials, buffer, CUPS_LLCAST bufsize));
641
9653cfdf
MS
642 if (!buffer)
643 return (0);
72d05bc9
MS
644
645 if (buffer && bufsize > 0)
646 *buffer = '\0';
647
376d7c69
MS
648 if ((first = (http_credential_t *)cupsArrayFirst(credentials)) != NULL &&
649 (secCert = http_cdsa_create_credential(first)) != NULL)
9653cfdf 650 {
376d7c69
MS
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)
9653cfdf 658 {
376d7c69
MS
659 CFStringGetCString(cf_name, name, (CFIndex)sizeof(name), kCFStringEncodingUTF8);
660 CFRelease(cf_name);
9653cfdf 661 }
376d7c69
MS
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]);
9653cfdf
MS
672
673 CFRelease(secCert);
674 }
675
376d7c69
MS
676 DEBUG_printf(("1httpCredentialsString: Returning \"%s\".", buffer));
677
9653cfdf 678 return (strlen(buffer));
72d05bc9
MS
679}
680
681
2c85b752
MS
682/*
683 * '_httpFreeCredentials()' - Free internal credentials.
684 */
685
686void
687_httpFreeCredentials(
688 http_tls_credentials_t credentials) /* I - Internal credentials */
689{
690 if (!credentials)
691 return;
692
693 CFRelease(credentials);
694}
695
696
72d05bc9
MS
697/*
698 * 'httpLoadCredentials()' - Load X.509 credentials from a keychain file.
699 *
700 * @since CUPS 2.0@
701 */
702
dafebafd 703int /* O - 0 on success, -1 on error */
72d05bc9
MS
704httpLoadCredentials(
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{
88f1e9c8 709#ifdef HAVE_SECKEYCHAINOPEN
dafebafd 710 OSStatus err; /* Error info */
88f1e9c8 711 char filename[1024]; /* Filename for keychain */
dafebafd
MS
712 SecKeychainRef keychain = NULL;/* Keychain reference */
713 SecIdentitySearchRef search = NULL; /* Search reference */
88f1e9c8
MS
714 SecCertificateRef cert = NULL; /* Certificate */
715 CFDataRef data; /* Certificate data */
dafebafd
MS
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
88f1e9c8
MS
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)
dafebafd
MS
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
88f1e9c8 761 list = CFArrayCreate(kCFAllocatorDefault, (const void **)&keychain, 1, &kCFTypeArrayCallBacks);
dafebafd 762
88f1e9c8 763 CFDictionaryAddValue(query, kSecClass, kSecClassCertificate);
dafebafd
MS
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
88f1e9c8 771 err = SecItemCopyMatching(query, (CFTypeRef *)&cert);
dafebafd
MS
772
773 if (err)
774 goto cleanup;
775
88f1e9c8 776 if (CFGetTypeID(cert) != SecCertificateGetTypeID())
dafebafd
MS
777 goto cleanup;
778
88f1e9c8
MS
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 }
dafebafd
MS
787
788 cleanup :
789
790 if (keychain)
791 CFRelease(keychain);
792 if (search)
793 CFRelease(search);
88f1e9c8
MS
794 if (cert)
795 CFRelease(cert);
dafebafd
MS
796 if (policy)
797 CFRelease(policy);
798 if (query)
799 CFRelease(query);
800
88f1e9c8
MS
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 */
72d05bc9
MS
812}
813
814
72d05bc9
MS
815/*
816 * 'httpSaveCredentials()' - Save X.509 credentials to a keychain file.
817 *
818 * @since CUPS 2.0@
819 */
820
821int /* O - -1 on error, 0 on success */
822httpSaveCredentials(
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{
eb66bc71 827#ifdef HAVE_SECKEYCHAINOPEN
88f1e9c8 828 int ret = -1; /* Return value */
41e0907c 829 OSStatus err; /* Error info */
88f1e9c8
MS
830 char filename[1024]; /* Filename for keychain */
831 SecKeychainRef keychain = NULL;/* Keychain reference */
41e0907c 832 SecIdentitySearchRef search = NULL; /* Search reference */
88f1e9c8 833 SecCertificateRef cert = NULL; /* Certificate */
41e0907c
MS
834 CFStringRef cfcommon_name = NULL;
835 /* Server name */
88f1e9c8 836 CFMutableDictionaryRef attrs = NULL; /* Attributes for add */
41e0907c
MS
837 CFArrayRef list = NULL; /* Keychain list */
838
839
88f1e9c8
MS
840 DEBUG_printf(("httpSaveCredentials(path=\"%s\", credentials=%p, common_name=\"%s\")", path, credentials, common_name));
841 if (!credentials)
41e0907c
MS
842 goto cleanup;
843
88f1e9c8
MS
844 if ((cert = http_cdsa_create_credential((http_credential_t *)cupsArrayFirst(credentials))) == NULL)
845 {
846 DEBUG_puts("1httpSaveCredentials: Unable to create certificate.");
41e0907c 847 goto cleanup;
88f1e9c8 848 }
41e0907c 849
88f1e9c8 850 if (!path)
2c85b752 851 {
88f1e9c8 852 const char *home = getenv("HOME"); /* HOME environment variable */
2c85b752 853
88f1e9c8
MS
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));
2c85b752 858
88f1e9c8
MS
859 path = filename;
860
861 DEBUG_printf(("1httpSaveCredentials: Using default path \"%s\".", path));
2c85b752
MS
862 }
863
88f1e9c8 864 if ((err = SecKeychainOpen(path, &keychain)) != noErr)
2c85b752 865 {
88f1e9c8
MS
866 DEBUG_printf(("1httpSaveCredentials: SecKeychainOpen returned %d.", (int)err));
867 goto cleanup;
2c85b752 868 }
2c85b752 869
88f1e9c8 870 if ((list = CFArrayCreate(kCFAllocatorDefault, (const void **)&keychain, 1, &kCFTypeArrayCallBacks)) == NULL)
2c85b752 871 {
88f1e9c8
MS
872 DEBUG_puts("1httpSaveCredentials: Unable to create list of keychains.");
873 goto cleanup;
2c85b752 874 }
88f1e9c8
MS
875
876 if ((cfcommon_name = CFStringCreateWithCString(kCFAllocatorDefault, common_name, kCFStringEncodingUTF8)) == NULL)
2c85b752 877 {
88f1e9c8
MS
878 DEBUG_puts("1httpSaveCredentials: Unable to create common name string.");
879 goto cleanup;
2c85b752 880 }
2c85b752 881
88f1e9c8
MS
882 if ((attrs = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)) == NULL)
883 {
884 DEBUG_puts("1httpSaveCredentials: Unable to create dictionary.");
885 goto cleanup;
2c85b752
MS
886 }
887
88f1e9c8
MS
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);
2c85b752 893
88f1e9c8
MS
894 /* Note: SecItemAdd consumes "attrs"... */
895 if ((err = SecItemAdd(attrs, NULL)) == noErr)
896 ret = 0;
2c85b752 897
88f1e9c8 898 DEBUG_printf(("1httpSaveCredentials: SecItemAdd returned %d.", (int)err));
2c85b752 899
88f1e9c8 900 cleanup :
2c85b752 901
88f1e9c8
MS
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);
2c85b752 912
88f1e9c8 913 DEBUG_printf(("1httpSaveCredentials: Returning %d.", ret));
2c85b752 914
88f1e9c8 915 return (ret);
2c85b752 916
88f1e9c8
MS
917#else
918 (void)path;
919 (void)credentials;
920 (void)common_name;
2c85b752 921
88f1e9c8
MS
922 return (-1);
923#endif /* HAVE_SECKEYCHAINOPEN */
2c85b752
MS
924}
925
926
927/*
25731360 928 * '_httpTLSInitialize()' - Initialize the TLS stack.
2c85b752
MS
929 */
930
25731360
MS
931void
932_httpTLSInitialize(void)
2c85b752
MS
933{
934 /*
935 * Nothing to do...
936 */
937}
938
939
940/*
25731360 941 * '_httpTLSPending()' - Return the number of pending TLS-encrypted bytes.
2c85b752
MS
942 */
943
25731360
MS
944size_t
945_httpTLSPending(http_t *http) /* I - HTTP connection */
2c85b752
MS
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/*
25731360 958 * '_httpTLSRead()' - Read from a SSL/TLS connection.
2c85b752
MS
959 */
960
25731360
MS
961int /* O - Bytes read */
962_httpTLSRead(http_t *http, /* I - HTTP connection */
2c85b752
MS
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
7e86f2f6 971 error = SSLRead(http->tls, buf, (size_t)len, &processed);
25731360 972 DEBUG_printf(("6_httpTLSRead: error=%d, processed=%d", (int)error,
2c85b752
MS
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
2c85b752 1006/*
25731360 1007 * '_httpTLSStart()' - Set up SSL/TLS support on a connection.
2c85b752
MS
1008 */
1009
25731360
MS
1010int /* O - 0 on success, -1 on failure */
1011_httpTLSStart(http_t *http) /* I - HTTP connection */
2c85b752
MS
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
25731360 1028 DEBUG_printf(("7_httpTLSStart(http=%p)", http));
2c85b752 1029
c913d726 1030#ifdef HAVE_SECKEYCHAINOPEN
41e0907c 1031 if (http->mode == _HTTP_MODE_SERVER && !tls_keychain)
c913d726
MS
1032#else
1033 if (http->mode == _HTTP_MODE_SERVER)
1034#endif /* HAVE_SECKEYCHAINOPEN */
2c85b752 1035 {
25731360 1036 DEBUG_puts("4_httpTLSStart: cupsSetServerCredentials not called.");
41e0907c
MS
1037 http->error = errno = EINVAL;
1038 http->status = HTTP_STATUS_ERROR;
1039 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Server credentials not set."), 1);
2c85b752 1040
41e0907c 1041 return (-1);
2c85b752
MS
1042 }
1043
72d05bc9 1044 if ((http->tls = SSLCreateContext(kCFAllocatorDefault, http->mode == _HTTP_MODE_CLIENT ? kSSLClientSide : kSSLServerSide, kSSLStreamType)) == NULL)
2c85b752 1045 {
25731360 1046 DEBUG_puts("4_httpTLSStart: SSLCreateContext failed.");
2c85b752
MS
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);
25731360 1055 DEBUG_printf(("4_httpTLSStart: SSLSetConnection, error=%d", (int)error));
2c85b752
MS
1056
1057 if (!error)
1058 {
1059 error = SSLSetIOFuncs(http->tls, http_cdsa_read, http_cdsa_write);
25731360 1060 DEBUG_printf(("4_httpTLSStart: SSLSetIOFuncs, error=%d", (int)error));
2c85b752
MS
1061 }
1062
1063 if (!error)
1064 {
1065 error = SSLSetSessionOption(http->tls, kSSLSessionOptionBreakOnServerAuth,
1066 true);
25731360 1067 DEBUG_printf(("4_httpTLSStart: SSLSetSessionOption, error=%d",
2c85b752
MS
1068 (int)error));
1069 }
1070
41e0907c 1071 if (!error && http->mode == _HTTP_MODE_CLIENT)
2c85b752 1072 {
41e0907c
MS
1073 /*
1074 * Client: set client-side credentials, if any...
1075 */
1076
2c85b752
MS
1077 if (cg->client_cert_cb)
1078 {
1079 error = SSLSetSessionOption(http->tls,
1080 kSSLSessionOptionBreakOnCertRequested, true);
25731360 1081 DEBUG_printf(("4_httpTLSStart: kSSLSessionOptionBreakOnCertRequested, "
2c85b752
MS
1082 "error=%d", (int)error));
1083 }
1084 else
1085 {
88f1e9c8
MS
1086 error = http_cdsa_set_credentials(http);
1087 DEBUG_printf(("4_httpTLSStart: http_cdsa_set_credentials, error=%d",
2c85b752
MS
1088 (int)error));
1089 }
1090 }
41e0907c
MS
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 {
25731360 1117 DEBUG_printf(("4_httpTLSStart: Unable to get socket address: %s", strerror(errno)));
41e0907c
MS
1118 hostname[0] = '\0';
1119 }
1120 else if (httpAddrLocalhost(&addr))
1121 hostname[0] = '\0';
1122 else
a27a134a
MS
1123 {
1124 httpAddrLookup(&addr, hostname, sizeof(hostname));
25731360 1125 DEBUG_printf(("4_httpTLSStart: Resolved socket address to \"%s\".", hostname));
a27a134a 1126 }
41e0907c
MS
1127 }
1128
eb66bc71 1129#ifdef HAVE_SECKEYCHAINOPEN
a27a134a
MS
1130 if (isdigit(hostname[0] & 255) || hostname[0] == '[')
1131 hostname[0] = '\0'; /* Don't allow numeric addresses */
1132
41e0907c
MS
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 {
25731360 1140 DEBUG_printf(("4_httpTLSStart: Auto-create credentials for \"%s\".", hostname[0] ? hostname : tls_common_name));
41e0907c
MS
1141
1142 if (!cupsMakeServerCredentials(tls_keypath, hostname[0] ? hostname : tls_common_name, 0, NULL, time(NULL) + 365 * 86400))
1143 {
25731360 1144 DEBUG_puts("4_httpTLSStart: cupsMakeServerCredentials failed.");
41e0907c
MS
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 }
eb66bc71 1154#endif /* HAVE_SECKEYCHAINOPEN */
41e0907c
MS
1155
1156 if (!http->tls_credentials)
1157 {
25731360 1158 DEBUG_puts("4_httpTLSStart: Unable to find server credentials.");
41e0907c
MS
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
25731360 1168 DEBUG_printf(("4_httpTLSStart: SSLSetCertificate, error=%d", (int)error));
41e0907c
MS
1169 }
1170
25731360 1171 DEBUG_printf(("4_httpTLSStart: tls_credentials=%p", http->tls_credentials));
2c85b752
MS
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
41e0907c 1178 if (!error && http->mode == _HTTP_MODE_CLIENT)
2c85b752 1179 {
41e0907c
MS
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
2c85b752
MS
1200 error = SSLSetPeerDomainName(http->tls, hostname, strlen(hostname));
1201
25731360 1202 DEBUG_printf(("4_httpTLSStart: SSLSetPeerDomainName, error=%d", (int)error));
2c85b752
MS
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
25731360 1213 DEBUG_printf(("4_httpTLSStart: SSLHandshake returned %d.", (int)error));
2c85b752
MS
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
25731360 1238 DEBUG_printf(("4_httpTLSStart: Server certificate callback "
2c85b752
MS
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 {
7e86f2f6 1260 credential->datalen = (size_t)CFDataGetLength(data);
2c85b752
MS
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
25731360 1281 DEBUG_printf(("4_httpTLSStart: Client certificate callback "
2c85b752
MS
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/*
25731360 1360 * '_httpTLSStop()' - Shut down SSL/TLS on a connection.
2c85b752
MS
1361 */
1362
25731360
MS
1363void
1364_httpTLSStop(http_t *http) /* I - HTTP connection */
2c85b752
MS
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/*
25731360 1380 * '_httpTLSWrite()' - Write to a SSL/TLS connection.
2c85b752
MS
1381 */
1382
25731360
MS
1383int /* O - Bytes written */
1384_httpTLSWrite(http_t *http, /* I - HTTP connection */
2c85b752
MS
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
25731360 1393 DEBUG_printf(("2_httpTLSWrite(http=%p, buf=%p, len=%d)", http, buf, len));
2c85b752 1394
7e86f2f6 1395 error = SSLWrite(http->tls, buf, (size_t)len, &processed);
2c85b752
MS
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
25731360 1429 DEBUG_printf(("3_httpTLSWrite: Returning %d.", (int)result));
2c85b752
MS
1430
1431 return ((int)result);
1432}
1433
1434
88f1e9c8 1435#ifdef HAVE_SECKEYCHAINOPEN
2c85b752 1436/*
88f1e9c8 1437 * 'http_cdsa_copy_server()' - Find and copy server credentials from the keychain.
2c85b752
MS
1438 */
1439
88f1e9c8
MS
1440static CFArrayRef /* O - Array of certificates or NULL */
1441http_cdsa_copy_server(
1442 const char *common_name) /* I - Server's hostname */
2c85b752 1443{
88f1e9c8
MS
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 */
2c85b752 1454
2c85b752 1455
88f1e9c8 1456 cfcommon_name = CFStringCreateWithCString(kCFAllocatorDefault, common_name, kCFStringEncodingUTF8);
2c85b752 1457
88f1e9c8
MS
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);
2c85b752 1503}
88f1e9c8 1504#endif /* HAVE_SECKEYCHAINOPEN */
2c85b752
MS
1505
1506
1507/*
88f1e9c8 1508 * 'http_cdsa_read()' - Read function for the CDSA library.
2c85b752
MS
1509 */
1510
88f1e9c8
MS
1511static OSStatus /* O - -1 on error, 0 on success */
1512http_cdsa_read(
1513 SSLConnectionRef connection, /* I - SSL/TLS connection */
1514 void *data, /* I - Data buffer */
1515 size_t *dataLength) /* IO - Number of bytes */
2c85b752 1516{
88f1e9c8
MS
1517 OSStatus result; /* Return value */
1518 ssize_t bytes; /* Number of bytes read */
1519 http_t *http; /* HTTP connection */
2c85b752 1520
2c85b752 1521
88f1e9c8 1522 http = (http_t *)connection;
2c85b752 1523
88f1e9c8 1524 if (!http->blocking)
2c85b752
MS
1525 {
1526 /*
88f1e9c8 1527 * Make sure we have data before we read...
2c85b752
MS
1528 */
1529
88f1e9c8
MS
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 }
2c85b752
MS
1538 }
1539
88f1e9c8 1540 do
2c85b752 1541 {
88f1e9c8 1542 bytes = recv(http->fd, data, *dataLength, 0);
2c85b752 1543 }
88f1e9c8 1544 while (bytes == -1 && (errno == EINTR || errno == EAGAIN));
2c85b752 1545
88f1e9c8
MS
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;
2c85b752 1558
88f1e9c8
MS
1559 if (bytes == 0)
1560 result = errSSLClosedGraceful;
1561 else if (errno == EAGAIN)
1562 result = errSSLWouldBlock;
1563 else
1564 result = errSSLClosedAbort;
1565 }
2c85b752 1566
88f1e9c8
MS
1567 return (result);
1568}
2c85b752 1569
2c85b752 1570
88f1e9c8
MS
1571/*
1572 * 'http_cdsa_set_credentials()' - Set the TLS credentials.
1573 */
2c85b752 1574
88f1e9c8
MS
1575static int /* O - Status of connection */
1576http_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 */
2c85b752 1582
2c85b752 1583
88f1e9c8 1584 DEBUG_printf(("7http_tls_set_credentials(%p)", http));
2c85b752 1585
88f1e9c8
MS
1586 /*
1587 * Prefer connection specific credentials...
1588 */
2c85b752 1589
88f1e9c8
MS
1590 if ((credentials = http->tls_credentials) == NULL)
1591 credentials = cg->tls_credentials;
2c85b752 1592
88f1e9c8
MS
1593 if (credentials)
1594 {
1595 error = SSLSetCertificate(http->tls, credentials);
1596 DEBUG_printf(("4http_tls_set_credentials: SSLSetCertificate, error=%d",
1597 (int)error));
2c85b752 1598 }
88f1e9c8
MS
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
1610static OSStatus /* O - -1 on error, 0 on success */
1611http_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 */
2c85b752 1619
2c85b752 1620
88f1e9c8
MS
1621 http = (http_t *)connection;
1622
1623 do
2c85b752 1624 {
88f1e9c8
MS
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;
2c85b752
MS
1637 }
1638 else
88f1e9c8
MS
1639 {
1640 *dataLength = 0;
2c85b752 1641
88f1e9c8
MS
1642 if (errno == EAGAIN)
1643 result = errSSLWouldBlock;
1644 else
1645 result = errSSLClosedAbort;
1646 }
1647
1648 return (result);
2c85b752 1649}
2c85b752
MS
1650
1651
1652/*
1653 * End of "$Id$".
1654 */