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