]> git.ipfire.org Git - thirdparty/cups.git/blob - cups/tls-darwin.c
Save work.
[thirdparty/cups.git] / cups / tls-darwin.c
1 /*
2 * "$Id$"
3 *
4 * TLS support code for CUPS on OS X.
5 *
6 * Copyright 2007-2014 by Apple Inc.
7 * Copyright 1997-2007 by Easy Software Products, all rights reserved.
8 *
9 * These coded instructions, statements, and computer programs are the
10 * property of Apple Inc. and are protected by Federal copyright
11 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
12 * which should have been included with this file. If this file is
13 * file is missing or damaged, see the license at "http://www.cups.org/".
14 *
15 * This file is subject to the Apple OS-Developed Software exception.
16 */
17
18 /**** This file is included from http.c ****/
19
20 /*
21 * Include necessary headers...
22 */
23
24 #include <spawn.h>
25
26 extern char **environ;
27
28
29 /*
30 * Local globals...
31 */
32
33 #ifdef HAVE_SECKEYCHAINOPEN
34 static int tls_auto_create = 0;
35 /* Auto-create self-signed certs? */
36 static char *tls_common_name = NULL;
37 /* Default common name */
38 static SecKeychainRef tls_keychain = NULL;
39 /* Server cert keychain */
40 static char *tls_keypath = NULL;
41 /* Server cert keychain path */
42 static _cups_mutex_t tls_mutex = _CUPS_MUTEX_INITIALIZER;
43 /* Mutex for keychain/certs */
44 #endif /* HAVE_SECKEYCHAINOPEN */
45
46
47 /*
48 * Local functions...
49 */
50
51 #ifdef HAVE_SECKEYCHAINOPEN
52 static CFArrayRef http_cdsa_copy_server(const char *common_name);
53 #endif /* HAVE_SECKEYCHAINOPEN */
54 static OSStatus http_cdsa_read(SSLConnectionRef connection, void *data, size_t *dataLength);
55 static OSStatus http_cdsa_write(SSLConnectionRef connection, const void *data, size_t *dataLength);
56
57
58 /*
59 * 'cupsMakeServerCredentials()' - Make a self-signed certificate and private key pair.
60 *
61 * @since CUPS 2.0@
62 */
63
64 int /* O - 1 on success, 0 on failure */
65 cupsMakeServerCredentials(
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 {
72 #if defined(HAVE_SECGENERATESELFSIGNEDCERTIFICATE) && defined(HAVE_SECKEYCHAINOPEN)
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
86 (void)num_alt_names;
87 (void)alt_names;
88 (void)expiration_date;
89
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,
121 kSecCSRBasicContraintsPathLen, 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
150 cleanup:
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
172 #else /* !(HAVE_SECGENERATESELFSIGNEDCERTIFICATE && HAVE_SECKEYCHAINOPEN) */
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);
253 #endif /* HAVE_SECGENERATESELFSIGNEDCERTIFICATE && HAVE_SECKEYCHAINOPEN */
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
266 int /* O - 1 on success, 0 on failure */
267 cupsSetServerCredentials(
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 {
272 DEBUG_printf(("cupsSetServerCredentials(path=\"%s\", common_name=\"%s\", auto_create=%d)", path, common_name, auto_create));
273
274 #ifdef HAVE_SECKEYCHAINOPEN
275 SecKeychainRef keychain = NULL;/* Temporary keychain */
276
277
278 if (SecKeychainOpen(path, &keychain) != noErr)
279 {
280 /* TODO: Set cups last error string */
281 DEBUG_puts("1cupsSetServerCredentials: Unable to open keychain, returning 0.");
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
294 if (tls_keypath)
295 _cupsStrFree(tls_keypath);
296
297 if (tls_common_name)
298 _cupsStrFree(tls_common_name);
299
300 /*
301 * Save the new keychain...
302 */
303
304 tls_keychain = keychain;
305 tls_keypath = _cupsStrAlloc(path);
306 tls_auto_create = auto_create;
307 tls_common_name = _cupsStrAlloc(common_name);
308
309 _cupsMutexUnlock(&tls_mutex);
310
311 DEBUG_puts("1cupsSetServerCredentials: Opened keychain, returning 1.");
312 return (1);
313
314 #else
315 DEBUG_puts("1cupsSetServerCredentials: No keychain support compiled in, returning 0.");
316 return (0);
317 #endif /* HAVE_SECKEYCHAINOPEN */
318 }
319
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
328 int /* O - Status of call (0 = success) */
329 httpCopyCredentials(
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
341 if (credentials)
342 *credentials = NULL;
343
344 if (!http || !http->tls || !credentials)
345 return (-1);
346
347 if (!(error = SSLCopyPeerTrust(http->tls, &peerTrust)) && peerTrust)
348 {
349 if ((*credentials = cupsArrayNew(NULL, NULL)) != NULL)
350 {
351 count = SecTrustGetCertificateCount(peerTrust);
352
353 for (i = 0; i < count; i ++)
354 {
355 secCert = SecTrustGetCertificateAtIndex(peerTrust, i);
356 if ((data = SecCertificateCopyData(secCert)))
357 {
358 httpAddCredential(*credentials, CFDataGetBytePtr(data), (size_t)CFDataGetLength(data));
359 CFRelease(data);
360 }
361 }
362 }
363
364 CFRelease(peerTrust);
365 }
366
367 return (error);
368 }
369
370
371 /*
372 * 'http_cdsa_create_credential()' - Create a single credential in the internal format.
373 */
374
375 static SecCertificateRef /* O - Certificate */
376 http_cdsa_create_credential(
377 http_credential_t *credential) /* I - Credential */
378 {
379 if (!credential)
380 return (NULL);
381
382 return (SecCertificateCreateWithBytes(kCFAllocatorDefault, credential->data, (CFIndex)credential->datalen));
383 }
384
385
386 /*
387 * '_httpCreateCredentials()' - Create credentials in the internal format.
388 */
389
390 http_tls_credentials_t /* O - Internal credentials */
391 _httpCreateCredentials(
392 cups_array_t *credentials) /* I - Array of credentials */
393 {
394 CFMutableArrayRef peerCerts; /* Peer credentials reference */
395 SecCertificateRef secCert; /* Certificate reference */
396 http_credential_t *credential; /* Credential data */
397
398
399 if (!credentials)
400 return (NULL);
401
402 if ((peerCerts = CFArrayCreateMutable(kCFAllocatorDefault,
403 cupsArrayCount(credentials),
404 &kCFTypeArrayCallBacks)) == NULL)
405 return (NULL);
406
407 for (credential = (http_credential_t *)cupsArrayFirst(credentials);
408 credential;
409 credential = (http_credential_t *)cupsArrayNext(credentials))
410 {
411 if ((secCert = http_cdsa_create_credential(credential)) != NULL)
412 {
413 CFArrayAppendValue(peerCerts, secCert);
414 CFRelease(secCert);
415 }
416 }
417
418 return (peerCerts);
419 }
420
421
422 /*
423 * 'httpCredentialsAreTrusted()' - Return whether the credentials are trusted.
424 *
425 * @since CUPS 2.0@
426 */
427
428 int /* O - 1 if trusted, 0 if not/unknown */
429 httpCredentialsAreTrusted(
430 cups_array_t *credentials) /* I - Credentials */
431 {
432 SecCertificateRef secCert; /* Certificate reference */
433 int trusted = 1; /* Trusted? */
434 Boolean isSelfSigned; /* Is this certificate self-signed? */
435 _cups_globals_t *cg = _cupsGlobals();
436 /* Per-thread globals */
437
438
439 if ((secCert = http_cdsa_create_credential((http_credential_t *)cupsArrayFirst(credentials))) == NULL)
440 return (0);
441
442 if (!cg->expired_certs && !SecCertificateIsValid(secCert, CFAbsoluteTimeGetCurrent()))
443 trusted = 0;
444 else if (!cg->any_root && (SecCertificateIsSelfSigned(secCert, &isSelfSigned) != noErr || isSelfSigned))
445 trusted = 0;
446
447 CFRelease(secCert);
448
449 return (trusted);
450 }
451
452
453 /*
454 * 'httpCredentialsGetExpiration()' - Return the expiration date of the credentials.
455 *
456 * @since CUPS 2.0@
457 */
458
459 time_t /* O - Expiration date of credentials */
460 httpCredentialsGetExpiration(
461 cups_array_t *credentials) /* I - Credentials */
462 {
463 SecCertificateRef secCert; /* Certificate reference */
464 time_t expiration; /* Expiration date */
465
466
467 if ((secCert = http_cdsa_create_credential((http_credential_t *)cupsArrayFirst(credentials))) == NULL)
468 return (0);
469
470 expiration = (time_t)(SecCertificateNotValidAfter(secCert) - kCFAbsoluteTimeIntervalSince1970);
471
472 CFRelease(secCert);
473
474 return (expiration);
475 }
476
477
478 /*
479 * 'httpCredentialsIsValidName()' - Return whether the credentials are valid for the given name.
480 *
481 * @since CUPS 2.0@
482 */
483
484 int /* O - 1 if valid, 0 otherwise */
485 httpCredentialsIsValidName(
486 cups_array_t *credentials, /* I - Credentials */
487 const char *common_name) /* I - Name to check */
488 {
489 SecCertificateRef secCert; /* Certificate reference */
490 CFStringRef cfcommon_name; /* CF string for common name */
491 CFStringRef cert_name = NULL;
492 /* Certificate's common name */
493 int valid = 1; /* Valid name? */
494
495
496 if ((secCert = http_cdsa_create_credential((http_credential_t *)cupsArrayFirst(credentials))) == NULL)
497 return (0);
498
499 /*
500 * Compare the common names...
501 */
502
503 cfcommon_name = CFStringCreateWithCString(kCFAllocatorDefault, common_name, kCFStringEncodingUTF8);
504
505 if (SecCertificateCopyCommonName(secCert, &cert_name) != noErr)
506 {
507 /*
508 * Can't get common name, cannot be valid...
509 */
510
511 valid = 0;
512 }
513 else if (CFStringCompare(cfcommon_name, cert_name, kCFCompareCaseInsensitive))
514 {
515 /*
516 * Not the common name, check whether the certificate is saved in the keychain...
517 */
518
519 /* TODO: Pull certificate from the keychain using label */
520 }
521
522 CFRelease(cfcommon_name);
523
524 if (cert_name)
525 CFRelease(cert_name);
526
527 CFRelease(secCert);
528
529 return (valid);
530 }
531
532
533 /*
534 * 'httpCredentialsString()' - Return a string representing the credentials.
535 *
536 * @since CUPS 2.0@
537 */
538
539 size_t /* O - Total size of credentials string */
540 httpCredentialsString(
541 cups_array_t *credentials, /* I - Credentials */
542 char *buffer, /* I - Buffer or @code NULL@ */
543 size_t bufsize) /* I - Size of buffer */
544 {
545 SecCertificateRef secCert; /* Certificate reference */
546 CFStringRef summary; /* CF string for summary */
547
548
549 if (!buffer)
550 return (0);
551
552 if (buffer && bufsize > 0)
553 *buffer = '\0';
554
555 /* TODO: This needs to include a hash of the credentials */
556 if ((secCert = http_cdsa_create_credential((http_credential_t *)cupsArrayFirst(credentials))) != NULL)
557 {
558 if ((summary = SecCertificateCopySubjectSummary(secCert)) != NULL)
559 {
560 CFStringGetCString(summary, buffer, (CFIndex)bufsize, kCFStringEncodingUTF8);
561 CFRelease(summary);
562 }
563
564 CFRelease(secCert);
565 }
566
567 return (strlen(buffer));
568 }
569
570
571 /*
572 * '_httpFreeCredentials()' - Free internal credentials.
573 */
574
575 void
576 _httpFreeCredentials(
577 http_tls_credentials_t credentials) /* I - Internal credentials */
578 {
579 if (!credentials)
580 return;
581
582 CFRelease(credentials);
583 }
584
585
586 /*
587 * 'httpLoadCredentials()' - Load X.509 credentials from a keychain file.
588 *
589 * @since CUPS 2.0@
590 */
591
592 int /* O - 0 on success, -1 on error */
593 httpLoadCredentials(
594 const char *path, /* I - Keychain/PKCS#12 path */
595 cups_array_t **credentials, /* IO - Credentials */
596 const char *common_name) /* I - Common name for credentials */
597 {
598 (void)path;
599 (void)credentials;
600 (void)common_name;
601
602 return (-1);
603
604 #if 0
605 OSStatus err; /* Error info */
606 SecKeychainRef keychain = NULL;/* Keychain reference */
607 SecIdentitySearchRef search = NULL; /* Search reference */
608 SecIdentityRef identity = NULL;/* Identity */
609 CFArrayRef certificates = NULL;
610 /* Certificate array */
611 SecPolicyRef policy = NULL; /* Policy ref */
612 CFStringRef cfcommon_name = NULL;
613 /* Server name */
614 CFMutableDictionaryRef query = NULL; /* Query qualifiers */
615 CFArrayRef list = NULL; /* Keychain list */
616
617
618 if ((err = SecKeychainOpen(path, &keychain)))
619 goto cleanup;
620
621 cfcommon_name = CFStringCreateWithCString(kCFAllocatorDefault, common_name, kCFStringEncodingUTF8);
622
623 policy = SecPolicyCreateSSL(1, cfcommon_name);
624
625 if (cfcommon_name)
626 CFRelease(cfcommon_name);
627
628 if (!policy)
629 goto cleanup;
630
631 if (!(query = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)))
632 goto cleanup;
633
634 list = CFArrayCreate(kCFAllocatorDefault, (const void **)&keychain, 1,
635 &kCFTypeArrayCallBacks);
636
637 CFDictionaryAddValue(query, kSecClass, kSecClassIdentity);
638 CFDictionaryAddValue(query, kSecMatchPolicy, policy);
639 CFDictionaryAddValue(query, kSecReturnRef, kCFBooleanTrue);
640 CFDictionaryAddValue(query, kSecMatchLimit, kSecMatchLimitOne);
641 CFDictionaryAddValue(query, kSecMatchSearchList, list);
642
643 CFRelease(list);
644
645 err = SecItemCopyMatching(query, (CFTypeRef *)&identity);
646
647 if (err)
648 goto cleanup;
649
650 if (CFGetTypeID(identity) != SecIdentityGetTypeID())
651 goto cleanup;
652
653 if ((certificates = CFArrayCreate(NULL, (const void **)&identity, 1, &kCFTypeArrayCallBacks)) == NULL)
654 goto cleanup;
655
656 cleanup :
657
658 if (keychain)
659 CFRelease(keychain);
660 if (search)
661 CFRelease(search);
662 if (identity)
663 CFRelease(identity);
664
665 if (policy)
666 CFRelease(policy);
667 if (query)
668 CFRelease(query);
669
670 return (certificates);
671 #endif /* 0 */
672 }
673
674
675 #if 0
676 /*
677 * 'cupsMakeCredentials()' - Create self-signed credentials for the given
678 * name.
679 *
680 * @since CUPS 2.0@
681 */
682
683 int /* O - 0 on success, -1 on error */
684 cupsMakeCredentials(
685 const char *path, /* I - Keychain/PKCS#12 path */
686 cups_array_t **credentials, /* O - Credentials */
687 const char *common_name) /* I - Common name for X.509 cert */
688 {
689 # ifdef HAVE_SECGENERATESELFSIGNEDCERTIFICATE
690 int status = -1; /* Return status */
691 OSStatus err; /* Error code (if any) */
692 CFStringRef cfcommon_name = NULL;
693 /* CF string for server name */
694 SecIdentityRef ident = NULL; /* Identity */
695 SecKeyRef publicKey = NULL,
696 /* Public key */
697 privateKey = NULL;
698 /* Private key */
699 CFMutableDictionaryRef keyParams = NULL;
700 /* Key generation parameters */
701
702
703 if (credentials)
704 *credentials = NULL;
705
706 cfcommon_name = CFStringCreateWithCString(kCFAllocatorDefault, servername,
707 kCFStringEncodingUTF8);
708 if (!cfcommon_name)
709 goto cleanup;
710
711 /*
712 * Create a public/private key pair...
713 */
714
715 keyParams = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
716 &kCFTypeDictionaryKeyCallBacks,
717 &kCFTypeDictionaryValueCallBacks);
718 if (!keyParams)
719 goto cleanup;
720
721 CFDictionaryAddValue(keyParams, kSecAttrKeyType, kSecAttrKeyTypeRSA);
722 CFDictionaryAddValue(keyParams, kSecAttrKeySizeInBits, CFSTR("2048"));
723 CFDictionaryAddValue(keyParams, kSecAttrLabel,
724 CFSTR("CUPS Self-Signed Certificate"));
725
726 err = SecKeyGeneratePair(keyParams, &publicKey, &privateKey);
727 if (err != noErr)
728 goto cleanup;
729
730 /*
731 * Create a self-signed certificate using the public/private key pair...
732 */
733
734 CFIndex usageInt = kSecKeyUsageAll;
735 CFNumberRef usage = CFNumberCreate(alloc, kCFNumberCFIndexType, &usageInt);
736 CFDictionaryRef certParams = CFDictionaryCreateMutable(kCFAllocatorDefault,
737 kSecCSRBasicContraintsPathLen, CFINT(0), kSecSubjectAltName, cfcommon_name, kSecCertificateKeyUsage, usage, NULL, NULL);
738 CFRelease(usage);
739
740 const void *ca_o[] = { kSecOidOrganization, CFSTR("") };
741 const void *ca_cn[] = { kSecOidCommonName, cfcommon_name };
742 CFArrayRef ca_o_dn = CFArrayCreate(kCFAllocatorDefault, ca_o, 2, NULL);
743 CFArrayRef ca_cn_dn = CFArrayCreate(kCFAllocatorDefault, ca_cn, 2, NULL);
744 const void *ca_dn_array[2];
745
746 ca_dn_array[0] = CFArrayCreate(kCFAllocatorDefault, (const void **)&ca_o_dn, 1, NULL);
747 ca_dn_array[1] = CFArrayCreate(kCFAllocatorDefault, (const void **)&ca_cn_dn, 1, NULL);
748
749 CFArrayRef subject = CFArrayCreate(kCFAllocatorDefault, ca_dn_array, 2, NULL);
750 SecCertificateRef cert = SecGenerateSelfSignedCertificate(subject, certParams, publicKey, privateKey);
751 CFRelease(subject);
752 CFRelease(certParams);
753
754 if (!cert)
755 goto cleanup;
756
757 ident = SecIdentityCreate(kCFAllocatorDefault, cert, privateKey);
758
759 if (ident)
760 status = 0;
761
762 /*
763 * Cleanup and return...
764 */
765
766 cleanup:
767
768 if (cfcommon_name)
769 CFRelease(cfcommon_name);
770
771 if (keyParams)
772 CFRelease(keyParams);
773
774 if (ident)
775 CFRelease(ident);
776
777 if (cert)
778 CFRelease(cert);
779
780 if (publicKey)
781 CFRelease(publicKey);
782
783 if (privateKey)
784 CFRelease(publicKey);
785
786 return (status);
787
788 # else /* !HAVE_SECGENERATESELFSIGNEDCERTIFICATE */
789 int pid, /* Process ID of command */
790 status; /* Status of command */
791 char command[1024], /* Command */
792 *argv[4], /* Command-line arguments */
793 keychain[1024], /* Keychain argument */
794 infofile[1024]; /* Type-in information for cert */
795 cups_file_t *fp; /* Seed/info file */
796
797
798 /*
799 * Run the "certtool" command to generate a self-signed certificate...
800 */
801
802 if (!cupsFileFind("certtool", getenv("PATH"), 1, command, sizeof(command)))
803 return (-1);
804
805 /*
806 * Create a file with the certificate information fields...
807 *
808 * Note: This assumes that the default questions are asked by the certtool
809 * command...
810 */
811
812 if ((fp = cupsTempFile2(infofile, sizeof(infofile))) == NULL)
813 return (-1);
814
815 cupsFilePrintf(fp,
816 "%s\n" /* Enter key and certificate label */
817 "r\n" /* Generate RSA key pair */
818 "2048\n" /* Key size in bits */
819 "y\n" /* OK (y = yes) */
820 "b\n" /* Usage (b=signing/encryption) */
821 "s\n" /* Sign with SHA1 */
822 "y\n" /* OK (y = yes) */
823 "%s\n" /* Common name */
824 "\n" /* Country (default) */
825 "\n" /* Organization (default) */
826 "\n" /* Organizational unit (default) */
827 "\n" /* State/Province (default) */
828 "%s\n" /* Email address */
829 "y\n", /* OK (y = yes) */
830 common_name, common_name, "");
831 cupsFileClose(fp);
832
833 snprintf(keychain, sizeof(keychain), "k=%s", path);
834
835 argv[0] = "certtool";
836 argv[1] = "c";
837 argv[2] = keychain;
838 argv[3] = NULL;
839
840 posix_spawn_file_actions_t actions; /* File actions */
841
842 posix_spawn_file_actions_init(&actions);
843 posix_spawn_file_actions_addclose(&actions, 0);
844 posix_spawn_file_actions_addopen(&actions, 0, infofile, O_RDONLY, 0);
845
846 if (posix_spawn(&pid, command, &actions, NULL, argv, environ))
847 {
848 unlink(infofile);
849 return (-1);
850 }
851
852 posix_spawn_file_actions_destroy(&actions);
853
854 unlink(infofile);
855
856 while (waitpid(pid, &status, 0) < 0)
857 if (errno != EINTR)
858 {
859 status = -1;
860 break;
861 }
862
863 if (status)
864 return (-1);
865
866 # endif /* HAVE_SECGENERATESELFSIGNEDCERTIFICATE */
867
868 return (httpLoadCredentials(path, credentials, common_name));
869 }
870 #endif /* 0 */
871
872
873 /*
874 * 'httpSaveCredentials()' - Save X.509 credentials to a keychain file.
875 *
876 * @since CUPS 2.0@
877 */
878
879 int /* O - -1 on error, 0 on success */
880 httpSaveCredentials(
881 const char *path, /* I - Keychain/PKCS#12 path */
882 cups_array_t *credentials, /* I - Credentials */
883 const char *common_name) /* I - Common name for credentials */
884 {
885 (void)path;
886 (void)credentials;
887 (void)common_name;
888
889 return (-1);
890 }
891
892
893 #ifdef HAVE_SECKEYCHAINOPEN
894 /*
895 * 'http_cdsa_copy_server()' - Find and copy server credentials from the keychain.
896 */
897
898 static CFArrayRef /* O - Array of certificates or NULL */
899 http_cdsa_copy_server(
900 const char *common_name) /* I - Server's hostname */
901 {
902 OSStatus err; /* Error info */
903 SecIdentitySearchRef search = NULL; /* Search reference */
904 SecIdentityRef identity = NULL;/* Identity */
905 CFArrayRef certificates = NULL;
906 /* Certificate array */
907 SecPolicyRef policy = NULL; /* Policy ref */
908 CFStringRef cfcommon_name = NULL;
909 /* Server name */
910 CFMutableDictionaryRef query = NULL; /* Query qualifiers */
911 CFArrayRef list = NULL; /* Keychain list */
912
913
914 cfcommon_name = CFStringCreateWithCString(kCFAllocatorDefault, common_name, kCFStringEncodingUTF8);
915
916 policy = SecPolicyCreateSSL(1, cfcommon_name);
917
918 if (cfcommon_name)
919 CFRelease(cfcommon_name);
920
921 if (!policy)
922 goto cleanup;
923
924 if (!(query = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)))
925 goto cleanup;
926
927 list = CFArrayCreate(kCFAllocatorDefault, (const void **)&tls_keychain, 1, &kCFTypeArrayCallBacks);
928
929 CFDictionaryAddValue(query, kSecClass, kSecClassIdentity);
930 CFDictionaryAddValue(query, kSecMatchPolicy, policy);
931 CFDictionaryAddValue(query, kSecReturnRef, kCFBooleanTrue);
932 CFDictionaryAddValue(query, kSecMatchLimit, kSecMatchLimitOne);
933 CFDictionaryAddValue(query, kSecMatchSearchList, list);
934
935 CFRelease(list);
936
937 err = SecItemCopyMatching(query, (CFTypeRef *)&identity);
938
939 if (err)
940 goto cleanup;
941
942 if (CFGetTypeID(identity) != SecIdentityGetTypeID())
943 goto cleanup;
944
945 if ((certificates = CFArrayCreate(NULL, (const void **)&identity, 1, &kCFTypeArrayCallBacks)) == NULL)
946 goto cleanup;
947
948 cleanup :
949
950 if (search)
951 CFRelease(search);
952 if (identity)
953 CFRelease(identity);
954
955 if (policy)
956 CFRelease(policy);
957 if (query)
958 CFRelease(query);
959
960 return (certificates);
961 }
962 #endif /* HAVE_SECKEYCHAINOPEN */
963
964
965 /*
966 * 'http_cdsa_read()' - Read function for the CDSA library.
967 */
968
969 static OSStatus /* O - -1 on error, 0 on success */
970 http_cdsa_read(
971 SSLConnectionRef connection, /* I - SSL/TLS connection */
972 void *data, /* I - Data buffer */
973 size_t *dataLength) /* IO - Number of bytes */
974 {
975 OSStatus result; /* Return value */
976 ssize_t bytes; /* Number of bytes read */
977 http_t *http; /* HTTP connection */
978
979
980 http = (http_t *)connection;
981
982 if (!http->blocking)
983 {
984 /*
985 * Make sure we have data before we read...
986 */
987
988 while (!_httpWait(http, http->wait_value, 0))
989 {
990 if (http->timeout_cb && (*http->timeout_cb)(http, http->timeout_data))
991 continue;
992
993 http->error = ETIMEDOUT;
994 return (-1);
995 }
996 }
997
998 do
999 {
1000 bytes = recv(http->fd, data, *dataLength, 0);
1001 }
1002 while (bytes == -1 && (errno == EINTR || errno == EAGAIN));
1003
1004 if ((size_t)bytes == *dataLength)
1005 {
1006 result = 0;
1007 }
1008 else if (bytes > 0)
1009 {
1010 *dataLength = (size_t)bytes;
1011 result = errSSLWouldBlock;
1012 }
1013 else
1014 {
1015 *dataLength = 0;
1016
1017 if (bytes == 0)
1018 result = errSSLClosedGraceful;
1019 else if (errno == EAGAIN)
1020 result = errSSLWouldBlock;
1021 else
1022 result = errSSLClosedAbort;
1023 }
1024
1025 return (result);
1026 }
1027
1028
1029 /*
1030 * 'http_cdsa_write()' - Write function for the CDSA library.
1031 */
1032
1033 static OSStatus /* O - -1 on error, 0 on success */
1034 http_cdsa_write(
1035 SSLConnectionRef connection, /* I - SSL/TLS connection */
1036 const void *data, /* I - Data buffer */
1037 size_t *dataLength) /* IO - Number of bytes */
1038 {
1039 OSStatus result; /* Return value */
1040 ssize_t bytes; /* Number of bytes read */
1041 http_t *http; /* HTTP connection */
1042
1043
1044 http = (http_t *)connection;
1045
1046 do
1047 {
1048 bytes = write(http->fd, data, *dataLength);
1049 }
1050 while (bytes == -1 && (errno == EINTR || errno == EAGAIN));
1051
1052 if ((size_t)bytes == *dataLength)
1053 {
1054 result = 0;
1055 }
1056 else if (bytes >= 0)
1057 {
1058 *dataLength = (size_t)bytes;
1059 result = errSSLWouldBlock;
1060 }
1061 else
1062 {
1063 *dataLength = 0;
1064
1065 if (errno == EAGAIN)
1066 result = errSSLWouldBlock;
1067 else
1068 result = errSSLClosedAbort;
1069 }
1070
1071 return (result);
1072 }
1073
1074
1075 /*
1076 * 'http_tls_initialize()' - Initialize the TLS stack.
1077 */
1078
1079 static void
1080 http_tls_initialize(void)
1081 {
1082 /*
1083 * Nothing to do...
1084 */
1085 }
1086
1087
1088 /*
1089 * 'http_tls_pending()' - Return the number of pending TLS-encrypted bytes.
1090 */
1091
1092 static size_t
1093 http_tls_pending(http_t *http) /* I - HTTP connection */
1094 {
1095 size_t bytes; /* Bytes that are available */
1096
1097
1098 if (!SSLGetBufferedReadSize(http->tls, &bytes))
1099 return (bytes);
1100
1101 return (0);
1102 }
1103
1104
1105 /*
1106 * 'http_tls_read()' - Read from a SSL/TLS connection.
1107 */
1108
1109 static int /* O - Bytes read */
1110 http_tls_read(http_t *http, /* I - HTTP connection */
1111 char *buf, /* I - Buffer to store data */
1112 int len) /* I - Length of buffer */
1113 {
1114 int result; /* Return value */
1115 OSStatus error; /* Error info */
1116 size_t processed; /* Number of bytes processed */
1117
1118
1119 error = SSLRead(http->tls, buf, (size_t)len, &processed);
1120 DEBUG_printf(("6http_tls_read: error=%d, processed=%d", (int)error,
1121 (int)processed));
1122 switch (error)
1123 {
1124 case 0 :
1125 result = (int)processed;
1126 break;
1127
1128 case errSSLWouldBlock :
1129 if (processed)
1130 result = (int)processed;
1131 else
1132 {
1133 result = -1;
1134 errno = EINTR;
1135 }
1136 break;
1137
1138 case errSSLClosedGraceful :
1139 default :
1140 if (processed)
1141 result = (int)processed;
1142 else
1143 {
1144 result = -1;
1145 errno = EPIPE;
1146 }
1147 break;
1148 }
1149
1150 return (result);
1151 }
1152
1153
1154 /*
1155 * 'http_tls_set_credentials()' - Set the TLS credentials.
1156 */
1157
1158 static int /* O - Status of connection */
1159 http_tls_set_credentials(http_t *http) /* I - HTTP connection */
1160 {
1161 _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals */
1162 OSStatus error = 0; /* Error code */
1163 http_tls_credentials_t credentials = NULL;
1164 /* TLS credentials */
1165
1166
1167 DEBUG_printf(("7http_tls_set_credentials(%p)", http));
1168
1169 /*
1170 * Prefer connection specific credentials...
1171 */
1172
1173 if ((credentials = http->tls_credentials) == NULL)
1174 credentials = cg->tls_credentials;
1175
1176 if (credentials)
1177 {
1178 error = SSLSetCertificate(http->tls, credentials);
1179 DEBUG_printf(("4http_tls_set_credentials: SSLSetCertificate, error=%d",
1180 (int)error));
1181 }
1182 else
1183 DEBUG_puts("4http_tls_set_credentials: No credentials to set.");
1184
1185 return (error);
1186 }
1187
1188
1189 /*
1190 * 'http_tls_start()' - Set up SSL/TLS support on a connection.
1191 */
1192
1193 static int /* O - 0 on success, -1 on failure */
1194 http_tls_start(http_t *http) /* I - HTTP connection */
1195 {
1196 char hostname[256], /* Hostname */
1197 *hostptr; /* Pointer into hostname */
1198 _cups_globals_t *cg = _cupsGlobals();
1199 /* Pointer to library globals */
1200 OSStatus error; /* Error code */
1201 const char *message = NULL;/* Error message */
1202 cups_array_t *credentials; /* Credentials array */
1203 cups_array_t *names; /* CUPS distinguished names */
1204 CFArrayRef dn_array; /* CF distinguished names array */
1205 CFIndex count; /* Number of credentials */
1206 CFDataRef data; /* Certificate data */
1207 int i; /* Looping var */
1208 http_credential_t *credential; /* Credential data */
1209
1210
1211 DEBUG_printf(("7http_tls_start(http=%p)", http));
1212
1213 #ifdef HAVE_SECKEYCHAINOPEN
1214 if (http->mode == _HTTP_MODE_SERVER && !tls_keychain)
1215 #else
1216 if (http->mode == _HTTP_MODE_SERVER)
1217 #endif /* HAVE_SECKEYCHAINOPEN */
1218 {
1219 DEBUG_puts("4http_tls_start: cupsSetServerCredentials not called.");
1220 http->error = errno = EINVAL;
1221 http->status = HTTP_STATUS_ERROR;
1222 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Server credentials not set."), 1);
1223
1224 return (-1);
1225 }
1226
1227 if ((http->tls = SSLCreateContext(kCFAllocatorDefault, http->mode == _HTTP_MODE_CLIENT ? kSSLClientSide : kSSLServerSide, kSSLStreamType)) == NULL)
1228 {
1229 DEBUG_puts("4http_tls_start: SSLCreateContext failed.");
1230 http->error = errno = ENOMEM;
1231 http->status = HTTP_STATUS_ERROR;
1232 _cupsSetHTTPError(HTTP_STATUS_ERROR);
1233
1234 return (-1);
1235 }
1236
1237 error = SSLSetConnection(http->tls, http);
1238 DEBUG_printf(("4http_tls_start: SSLSetConnection, error=%d", (int)error));
1239
1240 if (!error)
1241 {
1242 error = SSLSetIOFuncs(http->tls, http_cdsa_read, http_cdsa_write);
1243 DEBUG_printf(("4http_tls_start: SSLSetIOFuncs, error=%d", (int)error));
1244 }
1245
1246 if (!error)
1247 {
1248 error = SSLSetSessionOption(http->tls, kSSLSessionOptionBreakOnServerAuth,
1249 true);
1250 DEBUG_printf(("4http_tls_start: SSLSetSessionOption, error=%d",
1251 (int)error));
1252 }
1253
1254 if (!error && http->mode == _HTTP_MODE_CLIENT)
1255 {
1256 /*
1257 * Client: set client-side credentials, if any...
1258 */
1259
1260 if (cg->client_cert_cb)
1261 {
1262 error = SSLSetSessionOption(http->tls,
1263 kSSLSessionOptionBreakOnCertRequested, true);
1264 DEBUG_printf(("4http_tls_start: kSSLSessionOptionBreakOnCertRequested, "
1265 "error=%d", (int)error));
1266 }
1267 else
1268 {
1269 error = http_tls_set_credentials(http);
1270 DEBUG_printf(("4http_tls_start: http_tls_set_credentials, error=%d",
1271 (int)error));
1272 }
1273 }
1274 else if (!error)
1275 {
1276 /*
1277 * Server: find/create a certificate for TLS...
1278 */
1279
1280 if (http->fields[HTTP_FIELD_HOST][0])
1281 {
1282 /*
1283 * Use hostname for TLS upgrade...
1284 */
1285
1286 strlcpy(hostname, http->fields[HTTP_FIELD_HOST], sizeof(hostname));
1287 }
1288 else
1289 {
1290 /*
1291 * Resolve hostname from connection address...
1292 */
1293
1294 http_addr_t addr; /* Connection address */
1295 socklen_t addrlen; /* Length of address */
1296
1297 addrlen = sizeof(addr);
1298 if (getsockname(http->fd, (struct sockaddr *)&addr, &addrlen))
1299 {
1300 DEBUG_printf(("4http_tls_start: Unable to get socket address: %s", strerror(errno)));
1301 hostname[0] = '\0';
1302 }
1303 else if (httpAddrLocalhost(&addr))
1304 hostname[0] = '\0';
1305 else
1306 {
1307 httpAddrLookup(&addr, hostname, sizeof(hostname));
1308 DEBUG_printf(("4http_tls_start: Resolved socket address to \"%s\".", hostname));
1309 }
1310 }
1311
1312 #ifdef HAVE_SECKEYCHAINOPEN
1313 if (isdigit(hostname[0] & 255) || hostname[0] == '[')
1314 hostname[0] = '\0'; /* Don't allow numeric addresses */
1315
1316 if (hostname[0])
1317 http->tls_credentials = http_cdsa_copy_server(hostname);
1318 else if (tls_common_name)
1319 http->tls_credentials = http_cdsa_copy_server(tls_common_name);
1320
1321 if (!http->tls_credentials && tls_auto_create && (hostname[0] || tls_common_name))
1322 {
1323 DEBUG_printf(("4http_tls_start: Auto-create credentials for \"%s\".", hostname[0] ? hostname : tls_common_name));
1324
1325 if (!cupsMakeServerCredentials(tls_keypath, hostname[0] ? hostname : tls_common_name, 0, NULL, time(NULL) + 365 * 86400))
1326 {
1327 DEBUG_puts("4http_tls_start: cupsMakeServerCredentials failed.");
1328 http->error = errno = EINVAL;
1329 http->status = HTTP_STATUS_ERROR;
1330 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to create server credentials."), 1);
1331
1332 return (-1);
1333 }
1334
1335 http->tls_credentials = http_cdsa_copy_server(hostname[0] ? hostname : tls_common_name);
1336 }
1337 #endif /* HAVE_SECKEYCHAINOPEN */
1338
1339 if (!http->tls_credentials)
1340 {
1341 DEBUG_puts("4http_tls_start: Unable to find server credentials.");
1342 http->error = errno = EINVAL;
1343 http->status = HTTP_STATUS_ERROR;
1344 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to find server credentials."), 1);
1345
1346 return (-1);
1347 }
1348
1349 error = SSLSetCertificate(http->tls, http->tls_credentials);
1350
1351 DEBUG_printf(("4http_tls_start: SSLSetCertificate, error=%d", (int)error));
1352 }
1353
1354 DEBUG_printf(("4http_tls_start: tls_credentials=%p", http->tls_credentials));
1355
1356 /*
1357 * Let the server know which hostname/domain we are trying to connect to
1358 * in case it wants to serve up a certificate with a matching common name.
1359 */
1360
1361 if (!error && http->mode == _HTTP_MODE_CLIENT)
1362 {
1363 /*
1364 * Client: get the hostname to use for TLS...
1365 */
1366
1367 if (httpAddrLocalhost(http->hostaddr))
1368 {
1369 strlcpy(hostname, "localhost", sizeof(hostname));
1370 }
1371 else
1372 {
1373 /*
1374 * Otherwise make sure the hostname we have does not end in a trailing dot.
1375 */
1376
1377 strlcpy(hostname, http->hostname, sizeof(hostname));
1378 if ((hostptr = hostname + strlen(hostname) - 1) >= hostname &&
1379 *hostptr == '.')
1380 *hostptr = '\0';
1381 }
1382
1383 error = SSLSetPeerDomainName(http->tls, hostname, strlen(hostname));
1384
1385 DEBUG_printf(("4http_tls_start: SSLSetPeerDomainName, error=%d", (int)error));
1386 }
1387
1388 if (!error)
1389 {
1390 int done = 0; /* Are we done yet? */
1391
1392 while (!error && !done)
1393 {
1394 error = SSLHandshake(http->tls);
1395
1396 DEBUG_printf(("4http_tls_start: SSLHandshake returned %d.", (int)error));
1397
1398 switch (error)
1399 {
1400 case noErr :
1401 done = 1;
1402 break;
1403
1404 case errSSLWouldBlock :
1405 error = noErr; /* Force a retry */
1406 usleep(1000); /* in 1 millisecond */
1407 break;
1408
1409 case errSSLServerAuthCompleted :
1410 error = 0;
1411 if (cg->server_cert_cb)
1412 {
1413 error = httpCopyCredentials(http, &credentials);
1414 if (!error)
1415 {
1416 error = (cg->server_cert_cb)(http, http->tls, credentials,
1417 cg->server_cert_data);
1418 httpFreeCredentials(credentials);
1419 }
1420
1421 DEBUG_printf(("4http_tls_start: Server certificate callback "
1422 "returned %d.", (int)error));
1423 }
1424 break;
1425
1426 case errSSLClientCertRequested :
1427 error = 0;
1428
1429 if (cg->client_cert_cb)
1430 {
1431 names = NULL;
1432 if (!(error = SSLCopyDistinguishedNames(http->tls, &dn_array)) &&
1433 dn_array)
1434 {
1435 if ((names = cupsArrayNew(NULL, NULL)) != NULL)
1436 {
1437 for (i = 0, count = CFArrayGetCount(dn_array); i < count; i++)
1438 {
1439 data = (CFDataRef)CFArrayGetValueAtIndex(dn_array, i);
1440
1441 if ((credential = malloc(sizeof(*credential))) != NULL)
1442 {
1443 credential->datalen = (size_t)CFDataGetLength(data);
1444 if ((credential->data = malloc(credential->datalen)))
1445 {
1446 memcpy((void *)credential->data, CFDataGetBytePtr(data),
1447 credential->datalen);
1448 cupsArrayAdd(names, credential);
1449 }
1450 else
1451 free(credential);
1452 }
1453 }
1454 }
1455
1456 CFRelease(dn_array);
1457 }
1458
1459 if (!error)
1460 {
1461 error = (cg->client_cert_cb)(http, http->tls, names,
1462 cg->client_cert_data);
1463
1464 DEBUG_printf(("4http_tls_start: Client certificate callback "
1465 "returned %d.", (int)error));
1466 }
1467
1468 httpFreeCredentials(names);
1469 }
1470 break;
1471
1472 case errSSLUnknownRootCert :
1473 message = _("Unable to establish a secure connection to host "
1474 "(untrusted certificate).");
1475 break;
1476
1477 case errSSLNoRootCert :
1478 message = _("Unable to establish a secure connection to host "
1479 "(self-signed certificate).");
1480 break;
1481
1482 case errSSLCertExpired :
1483 message = _("Unable to establish a secure connection to host "
1484 "(expired certificate).");
1485 break;
1486
1487 case errSSLCertNotYetValid :
1488 message = _("Unable to establish a secure connection to host "
1489 "(certificate not yet valid).");
1490 break;
1491
1492 case errSSLHostNameMismatch :
1493 message = _("Unable to establish a secure connection to host "
1494 "(host name mismatch).");
1495 break;
1496
1497 case errSSLXCertChainInvalid :
1498 message = _("Unable to establish a secure connection to host "
1499 "(certificate chain invalid).");
1500 break;
1501
1502 case errSSLConnectionRefused :
1503 message = _("Unable to establish a secure connection to host "
1504 "(peer dropped connection before responding).");
1505 break;
1506
1507 default :
1508 break;
1509 }
1510 }
1511 }
1512
1513 if (error)
1514 {
1515 http->error = error;
1516 http->status = HTTP_STATUS_ERROR;
1517 errno = ECONNREFUSED;
1518
1519 CFRelease(http->tls);
1520 http->tls = NULL;
1521
1522 /*
1523 * If an error string wasn't set by the callbacks use a generic one...
1524 */
1525
1526 if (!message)
1527 #ifdef HAVE_CSSMERRORSTRING
1528 message = cssmErrorString(error);
1529 #else
1530 message = _("Unable to establish a secure connection to host.");
1531 #endif /* HAVE_CSSMERRORSTRING */
1532
1533 _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI, message, 1);
1534
1535 return (-1);
1536 }
1537
1538 return (0);
1539 }
1540
1541
1542 /*
1543 * 'http_tls_stop()' - Shut down SSL/TLS on a connection.
1544 */
1545
1546 static void
1547 http_tls_stop(http_t *http) /* I - HTTP connection */
1548 {
1549 while (SSLClose(http->tls) == errSSLWouldBlock)
1550 usleep(1000);
1551
1552 CFRelease(http->tls);
1553
1554 if (http->tls_credentials)
1555 CFRelease(http->tls_credentials);
1556
1557 http->tls = NULL;
1558 http->tls_credentials = NULL;
1559 }
1560
1561
1562 /*
1563 * 'http_tls_write()' - Write to a SSL/TLS connection.
1564 */
1565
1566 static int /* O - Bytes written */
1567 http_tls_write(http_t *http, /* I - HTTP connection */
1568 const char *buf, /* I - Buffer holding data */
1569 int len) /* I - Length of buffer */
1570 {
1571 ssize_t result; /* Return value */
1572 OSStatus error; /* Error info */
1573 size_t processed; /* Number of bytes processed */
1574
1575
1576 DEBUG_printf(("2http_tls_write(http=%p, buf=%p, len=%d)", http, buf, len));
1577
1578 error = SSLWrite(http->tls, buf, (size_t)len, &processed);
1579
1580 switch (error)
1581 {
1582 case 0 :
1583 result = (int)processed;
1584 break;
1585
1586 case errSSLWouldBlock :
1587 if (processed)
1588 {
1589 result = (int)processed;
1590 }
1591 else
1592 {
1593 result = -1;
1594 errno = EINTR;
1595 }
1596 break;
1597
1598 case errSSLClosedGraceful :
1599 default :
1600 if (processed)
1601 {
1602 result = (int)processed;
1603 }
1604 else
1605 {
1606 result = -1;
1607 errno = EPIPE;
1608 }
1609 break;
1610 }
1611
1612 DEBUG_printf(("3http_tls_write: Returning %d.", (int)result));
1613
1614 return ((int)result);
1615 }
1616
1617
1618 #if 0
1619 /*
1620 * 'cupsdEndTLS()' - Shutdown a secure session with the client.
1621 */
1622
1623 int /* O - 1 on success, 0 on error */
1624 cupsdEndTLS(cupsd_client_t *con) /* I - Client connection */
1625 {
1626 while (SSLClose(con->http.tls) == errSSLWouldBlock)
1627 usleep(1000);
1628
1629 CFRelease(con->http.tls);
1630 con->http.tls = NULL;
1631
1632 if (con->http.tls_credentials)
1633 CFRelease(con->http.tls_credentials);
1634
1635 return (1);
1636 }
1637
1638
1639 /*
1640 * 'cupsdStartTLS()' - Start a secure session with the client.
1641 */
1642
1643 int /* O - 1 on success, 0 on error */
1644 cupsdStartTLS(cupsd_client_t *con) /* I - Client connection */
1645 {
1646 OSStatus error = 0; /* Error code */
1647 SecTrustRef peerTrust; /* Peer certificates */
1648
1649
1650 cupsdLogMessage(CUPSD_LOG_DEBUG, "[Client %d] Encrypting connection.",
1651 con->http.fd);
1652
1653 con->http.tls_credentials = copy_cdsa_certificate(con);
1654
1655 if (!con->http.tls_credentials)
1656 {
1657 /*
1658 * No keychain (yet), make a self-signed certificate...
1659 */
1660
1661 if (make_certificate(con))
1662 con->http.tls_credentials = copy_cdsa_certificate(con);
1663 }
1664
1665 if (!con->http.tls_credentials)
1666 {
1667 cupsdLogMessage(CUPSD_LOG_ERROR,
1668 "Could not find signing key in keychain \"%s\"",
1669 ServerCertificate);
1670 error = errSSLBadConfiguration;
1671 }
1672
1673 if (!error)
1674 con->http.tls = SSLCreateContext(kCFAllocatorDefault, kSSLServerSide,
1675 kSSLStreamType);
1676
1677 if (!error)
1678 error = SSLSetIOFuncs(con->http.tls, http_cdsa_read, http_cdsa_write);
1679
1680 if (!error)
1681 error = SSLSetConnection(con->http.tls, HTTP(con));
1682
1683 if (!error)
1684 error = SSLSetCertificate(con->http.tls, con->http.tls_credentials);
1685
1686 if (!error)
1687 {
1688 /*
1689 * Perform SSL/TLS handshake
1690 */
1691
1692 while ((error = SSLHandshake(con->http.tls)) == errSSLWouldBlock)
1693 usleep(1000);
1694 }
1695
1696 if (error)
1697 {
1698 cupsdLogMessage(CUPSD_LOG_ERROR,
1699 "Unable to encrypt connection from %s - %s (%d)",
1700 con->http.hostname, cssmErrorString(error), (int)error);
1701
1702 con->http.error = error;
1703 con->http.status = HTTP_ERROR;
1704
1705 if (con->http.tls)
1706 {
1707 CFRelease(con->http.tls);
1708 con->http.tls = NULL;
1709 }
1710
1711 if (con->http.tls_credentials)
1712 {
1713 CFRelease(con->http.tls_credentials);
1714 con->http.tls_credentials = NULL;
1715 }
1716
1717 return (0);
1718 }
1719
1720 cupsdLogMessage(CUPSD_LOG_DEBUG, "Connection from %s now encrypted.",
1721 con->http.hostname);
1722
1723 if (!SSLCopyPeerTrust(con->http.tls, &peerTrust) && peerTrust)
1724 {
1725 cupsdLogMessage(CUPSD_LOG_DEBUG, "Received %d peer certificates.",
1726 (int)SecTrustGetCertificateCount(peerTrust));
1727 CFRelease(peerTrust);
1728 }
1729 else
1730 cupsdLogMessage(CUPSD_LOG_DEBUG, "Received NO peer certificates.");
1731
1732 return (1);
1733 }
1734 #endif /* 0 */
1735
1736
1737 /*
1738 * End of "$Id$".
1739 */