]> git.ipfire.org Git - thirdparty/cups.git/blame - cups/tls-darwin.c
Add yet another key to the launchd plists...
[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
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 {
7e86f2f6 358 httpAddCredential(*credentials, CFDataGetBytePtr(data), (size_t)CFDataGetLength(data));
2c85b752
MS
359 CFRelease(data);
360 }
361 }
362 }
363
364 CFRelease(peerTrust);
365 }
366
367 return (error);
368}
369
370
9653cfdf
MS
371/*
372 * 'http_cdsa_create_credential()' - Create a single credential in the internal format.
373 */
374
375static SecCertificateRef /* O - Certificate */
376http_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
2c85b752
MS
386/*
387 * '_httpCreateCredentials()' - Create credentials in the internal format.
388 */
389
390http_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 */
2c85b752
MS
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 {
9653cfdf 411 if ((secCert = http_cdsa_create_credential(credential)) != NULL)
2c85b752 412 {
9653cfdf
MS
413 CFArrayAppendValue(peerCerts, secCert);
414 CFRelease(secCert);
2c85b752
MS
415 }
416 }
417
418 return (peerCerts);
419}
420
421
3af9ac9e
MS
422/*
423 * 'httpCredentialsAreTrusted()' - Return whether the credentials are trusted.
424 *
425 * @since CUPS 2.0@
426 */
427
428int /* O - 1 if trusted, 0 if not/unknown */
429httpCredentialsAreTrusted(
430 cups_array_t *credentials) /* I - Credentials */
431{
9653cfdf
MS
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 */
3af9ac9e 437
9653cfdf
MS
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);
3af9ac9e
MS
450}
451
452
453/*
454 * 'httpCredentialsGetExpiration()' - Return the expiration date of the credentials.
455 *
456 * @since CUPS 2.0@
457 */
458
459time_t /* O - Expiration date of credentials */
460httpCredentialsGetExpiration(
461 cups_array_t *credentials) /* I - Credentials */
462{
9653cfdf
MS
463 SecCertificateRef secCert; /* Certificate reference */
464 time_t expiration; /* Expiration date */
3af9ac9e 465
9653cfdf
MS
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);
3af9ac9e
MS
475}
476
477
478/*
479 * 'httpCredentialsIsValidName()' - Return whether the credentials are valid for the given name.
480 *
481 * @since CUPS 2.0@
482 */
483
484int /* O - 1 if valid, 0 otherwise */
485httpCredentialsIsValidName(
486 cups_array_t *credentials, /* I - Credentials */
487 const char *common_name) /* I - Name to check */
488{
9653cfdf
MS
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? */
3af9ac9e 494
9653cfdf
MS
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);
3af9ac9e
MS
530}
531
532
72d05bc9
MS
533/*
534 * 'httpCredentialsString()' - Return a string representing the credentials.
535 *
536 * @since CUPS 2.0@
537 */
538
539size_t /* O - Total size of credentials string */
540httpCredentialsString(
541 cups_array_t *credentials, /* I - Credentials */
542 char *buffer, /* I - Buffer or @code NULL@ */
543 size_t bufsize) /* I - Size of buffer */
544{
9653cfdf
MS
545 SecCertificateRef secCert; /* Certificate reference */
546 CFStringRef summary; /* CF string for summary */
547
548
549 if (!buffer)
550 return (0);
72d05bc9
MS
551
552 if (buffer && bufsize > 0)
553 *buffer = '\0';
554
9653cfdf
MS
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));
72d05bc9
MS
568}
569
570
2c85b752
MS
571/*
572 * '_httpFreeCredentials()' - Free internal credentials.
573 */
574
575void
576_httpFreeCredentials(
577 http_tls_credentials_t credentials) /* I - Internal credentials */
578{
579 if (!credentials)
580 return;
581
582 CFRelease(credentials);
583}
584
585
72d05bc9
MS
586/*
587 * 'httpLoadCredentials()' - Load X.509 credentials from a keychain file.
588 *
589 * @since CUPS 2.0@
590 */
591
dafebafd 592int /* O - 0 on success, -1 on error */
72d05bc9
MS
593httpLoadCredentials(
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{
f4047845
MS
598 (void)path;
599 (void)credentials;
600 (void)common_name;
601
602 return (-1);
603
604#if 0
dafebafd
MS
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);
f4047845 671#endif /* 0 */
72d05bc9
MS
672}
673
674
f4047845 675#if 0
72d05bc9 676/*
3af9ac9e 677 * 'cupsMakeCredentials()' - Create self-signed credentials for the given
72d05bc9
MS
678 * name.
679 *
680 * @since CUPS 2.0@
681 */
682
dafebafd 683int /* O - 0 on success, -1 on error */
3af9ac9e 684cupsMakeCredentials(
dafebafd 685 const char *path, /* I - Keychain/PKCS#12 path */
72d05bc9
MS
686 cups_array_t **credentials, /* O - Credentials */
687 const char *common_name) /* I - Common name for X.509 cert */
688{
dafebafd
MS
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
72d05bc9
MS
702
703 if (credentials)
704 *credentials = NULL;
705
dafebafd
MS
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,
737kSecCSRBasicContraintsPathLen, 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
766cleanup:
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));
72d05bc9 869}
f4047845 870#endif /* 0 */
72d05bc9
MS
871
872
873/*
874 * 'httpSaveCredentials()' - Save X.509 credentials to a keychain file.
875 *
876 * @since CUPS 2.0@
877 */
878
879int /* O - -1 on error, 0 on success */
880httpSaveCredentials(
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
eb66bc71 893#ifdef HAVE_SECKEYCHAINOPEN
41e0907c
MS
894/*
895 * 'http_cdsa_copy_server()' - Find and copy server credentials from the keychain.
896 */
897
898static CFArrayRef /* O - Array of certificates or NULL */
899http_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}
eb66bc71 962#endif /* HAVE_SECKEYCHAINOPEN */
41e0907c
MS
963
964
2c85b752
MS
965/*
966 * 'http_cdsa_read()' - Read function for the CDSA library.
967 */
968
969static OSStatus /* O - -1 on error, 0 on success */
970http_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
7e86f2f6 1004 if ((size_t)bytes == *dataLength)
2c85b752
MS
1005 {
1006 result = 0;
1007 }
1008 else if (bytes > 0)
1009 {
7e86f2f6 1010 *dataLength = (size_t)bytes;
2c85b752
MS
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
1033static OSStatus /* O - -1 on error, 0 on success */
1034http_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
7e86f2f6 1052 if ((size_t)bytes == *dataLength)
2c85b752
MS
1053 {
1054 result = 0;
1055 }
1056 else if (bytes >= 0)
1057 {
7e86f2f6 1058 *dataLength = (size_t)bytes;
2c85b752
MS
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
1079static void
1080http_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
1092static size_t
1093http_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
1109static int /* O - Bytes read */
41e0907c 1110http_tls_read(http_t *http, /* I - HTTP connection */
2c85b752
MS
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
7e86f2f6 1119 error = SSLRead(http->tls, buf, (size_t)len, &processed);
2c85b752
MS
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
1158static int /* O - Status of connection */
41e0907c 1159http_tls_set_credentials(http_t *http) /* I - HTTP connection */
2c85b752
MS
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
1193static int /* O - 0 on success, -1 on failure */
41e0907c 1194http_tls_start(http_t *http) /* I - HTTP connection */
2c85b752
MS
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
c913d726 1213#ifdef HAVE_SECKEYCHAINOPEN
41e0907c 1214 if (http->mode == _HTTP_MODE_SERVER && !tls_keychain)
c913d726
MS
1215#else
1216 if (http->mode == _HTTP_MODE_SERVER)
1217#endif /* HAVE_SECKEYCHAINOPEN */
2c85b752 1218 {
41e0907c
MS
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);
2c85b752 1223
41e0907c 1224 return (-1);
2c85b752
MS
1225 }
1226
72d05bc9 1227 if ((http->tls = SSLCreateContext(kCFAllocatorDefault, http->mode == _HTTP_MODE_CLIENT ? kSSLClientSide : kSSLServerSide, kSSLStreamType)) == NULL)
2c85b752
MS
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
41e0907c 1254 if (!error && http->mode == _HTTP_MODE_CLIENT)
2c85b752 1255 {
41e0907c
MS
1256 /*
1257 * Client: set client-side credentials, if any...
1258 */
1259
2c85b752
MS
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 {
d3d89474
MS
1269 error = http_tls_set_credentials(http);
1270 DEBUG_printf(("4http_tls_start: http_tls_set_credentials, error=%d",
2c85b752
MS
1271 (int)error));
1272 }
1273 }
41e0907c
MS
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
a27a134a
MS
1306 {
1307 httpAddrLookup(&addr, hostname, sizeof(hostname));
1308 DEBUG_printf(("4http_tls_start: Resolved socket address to \"%s\".", hostname));
1309 }
41e0907c
MS
1310 }
1311
eb66bc71 1312#ifdef HAVE_SECKEYCHAINOPEN
a27a134a
MS
1313 if (isdigit(hostname[0] & 255) || hostname[0] == '[')
1314 hostname[0] = '\0'; /* Don't allow numeric addresses */
1315
41e0907c
MS
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 }
eb66bc71 1337#endif /* HAVE_SECKEYCHAINOPEN */
41e0907c
MS
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));
2c85b752
MS
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
41e0907c 1361 if (!error && http->mode == _HTTP_MODE_CLIENT)
2c85b752 1362 {
41e0907c
MS
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
2c85b752
MS
1383 error = SSLSetPeerDomainName(http->tls, hostname, strlen(hostname));
1384
41e0907c 1385 DEBUG_printf(("4http_tls_start: SSLSetPeerDomainName, error=%d", (int)error));
2c85b752
MS
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 {
7e86f2f6 1443 credential->datalen = (size_t)CFDataGetLength(data);
2c85b752
MS
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
1546static void
41e0907c 1547http_tls_stop(http_t *http) /* I - HTTP connection */
2c85b752
MS
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
1566static int /* O - Bytes written */
41e0907c 1567http_tls_write(http_t *http, /* I - HTTP connection */
2c85b752
MS
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
7e86f2f6 1578 error = SSLWrite(http->tls, buf, (size_t)len, &processed);
2c85b752
MS
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
d3d89474 1618#if 0
2c85b752
MS
1619/*
1620 * 'cupsdEndTLS()' - Shutdown a secure session with the client.
1621 */
1622
1623int /* O - 1 on success, 0 on error */
1624cupsdEndTLS(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
1643int /* O - 1 on success, 0 on error */
1644cupsdStartTLS(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}
d3d89474 1734#endif /* 0 */
2c85b752
MS
1735
1736
1737/*
1738 * End of "$Id$".
1739 */