]> git.ipfire.org Git - thirdparty/cups.git/blame - cups/tls-darwin.c
Drop support for ServerCertificate and ServerKey directives; now we just support
[thirdparty/cups.git] / cups / tls-darwin.c
CommitLineData
2c85b752
MS
1/*
2 * "$Id$"
3 *
4 * TLS support code for CUPS on OS X.
5 *
6 * Copyright 2007-2013 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
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
29/*
30 * Local functions...
31 */
2c85b752
MS
32
33static OSStatus http_cdsa_read(SSLConnectionRef connection, void *data,
34 size_t *dataLength);
35static OSStatus http_cdsa_write(SSLConnectionRef connection, const void *data,
36 size_t *dataLength);
37
38
3af9ac9e
MS
39/*
40 * 'cupsMakeServerCredentials()' - Make a self-signed certificate and private key pair.
41 *
42 * @since CUPS 2.0@
43 */
44
45int /* O - 1 on success, 0 on failure */
46cupsMakeServerCredentials(
47 const char *path, /* I - Path to keychain/directory */
48 const char *common_name, /* I - Common name */
49 int num_alt_names, /* I - Number of subject alternate names */
50 const char **alt_names, /* I - Subject Alternate Names */
51 time_t expiration_date) /* I - Expiration date */
52{
53 (void)path;
54 (void)common_name;
55 (void)num_alt_names;
56 (void)alt_names;
57 (void)expiration_date;
58
59 return (0);
60}
61
62
63/*
64 * 'cupsSetServerCredentials()' - Set the default server credentials.
65 *
66 * Note: The server credentials are used by all threads in the running process.
67 * This function is threadsafe.
68 *
69 * @since CUPS 2.0@
70 */
71
72int /* O - 1 on success, 0 on failure */
73cupsSetServerCredentials(
74 const char *path, /* I - Path to keychain/directory */
75 const char *common_name, /* I - Default common name for server */
76 int auto_create) /* I - 1 = automatically create self-signed certificates */
77{
78 (void)path;
79 (void)common_name;
80 (void)auto_create;
81
82 return (0);
83}
84
2c85b752
MS
85
86/*
87 * 'httpCopyCredentials()' - Copy the credentials associated with the peer in
88 * an encrypted connection.
89 *
90 * @since CUPS 1.5/OS X 10.7@
91 */
92
93int /* O - Status of call (0 = success) */
94httpCopyCredentials(
95 http_t *http, /* I - Connection to server */
96 cups_array_t **credentials) /* O - Array of credentials */
97{
98 OSStatus error; /* Error code */
99 SecTrustRef peerTrust; /* Peer trust reference */
100 CFIndex count; /* Number of credentials */
101 SecCertificateRef secCert; /* Certificate reference */
102 CFDataRef data; /* Certificate data */
103 int i; /* Looping var */
104
105
106 if (credentials)
107 *credentials = NULL;
108
109 if (!http || !http->tls || !credentials)
110 return (-1);
111
112 if (!(error = SSLCopyPeerTrust(http->tls, &peerTrust)) && peerTrust)
113 {
114 if ((*credentials = cupsArrayNew(NULL, NULL)) != NULL)
115 {
116 count = SecTrustGetCertificateCount(peerTrust);
117
118 for (i = 0; i < count; i ++)
119 {
120 secCert = SecTrustGetCertificateAtIndex(peerTrust, i);
121 if ((data = SecCertificateCopyData(secCert)))
122 {
123 httpAddCredential(*credentials, CFDataGetBytePtr(data),
124 CFDataGetLength(data));
125 CFRelease(data);
126 }
127 }
128 }
129
130 CFRelease(peerTrust);
131 }
132
133 return (error);
134}
135
136
137/*
138 * '_httpCreateCredentials()' - Create credentials in the internal format.
139 */
140
141http_tls_credentials_t /* O - Internal credentials */
142_httpCreateCredentials(
143 cups_array_t *credentials) /* I - Array of credentials */
144{
145 CFMutableArrayRef peerCerts; /* Peer credentials reference */
146 SecCertificateRef secCert; /* Certificate reference */
147 CFDataRef data; /* Credential data reference */
148 http_credential_t *credential; /* Credential data */
149
150
151 if (!credentials)
152 return (NULL);
153
154 if ((peerCerts = CFArrayCreateMutable(kCFAllocatorDefault,
155 cupsArrayCount(credentials),
156 &kCFTypeArrayCallBacks)) == NULL)
157 return (NULL);
158
159 for (credential = (http_credential_t *)cupsArrayFirst(credentials);
160 credential;
161 credential = (http_credential_t *)cupsArrayNext(credentials))
162 {
163 if ((data = CFDataCreate(kCFAllocatorDefault, credential->data,
164 credential->datalen)))
165 {
166 if ((secCert = SecCertificateCreateWithData(kCFAllocatorDefault, data))
167 != NULL)
168 {
169 CFArrayAppendValue(peerCerts, secCert);
170 CFRelease(secCert);
171 }
172
173 CFRelease(data);
174 }
175 }
176
177 return (peerCerts);
178}
179
180
3af9ac9e
MS
181/*
182 * 'httpCredentialsAreTrusted()' - Return whether the credentials are trusted.
183 *
184 * @since CUPS 2.0@
185 */
186
187int /* O - 1 if trusted, 0 if not/unknown */
188httpCredentialsAreTrusted(
189 cups_array_t *credentials) /* I - Credentials */
190{
191 (void)credentials;
192
193 return (0);
194}
195
196
197/*
198 * 'httpCredentialsGetExpiration()' - Return the expiration date of the credentials.
199 *
200 * @since CUPS 2.0@
201 */
202
203time_t /* O - Expiration date of credentials */
204httpCredentialsGetExpiration(
205 cups_array_t *credentials) /* I - Credentials */
206{
207 (void)credentials;
208
209 return (0);
210}
211
212
213/*
214 * 'httpCredentialsIsValidName()' - Return whether the credentials are valid for the given name.
215 *
216 * @since CUPS 2.0@
217 */
218
219int /* O - 1 if valid, 0 otherwise */
220httpCredentialsIsValidName(
221 cups_array_t *credentials, /* I - Credentials */
222 const char *common_name) /* I - Name to check */
223{
224 (void)credentials;
225 (void)common_name;
226
227 return (0);
228}
229
230
72d05bc9
MS
231/*
232 * 'httpCredentialsString()' - Return a string representing the credentials.
233 *
234 * @since CUPS 2.0@
235 */
236
237size_t /* O - Total size of credentials string */
238httpCredentialsString(
239 cups_array_t *credentials, /* I - Credentials */
240 char *buffer, /* I - Buffer or @code NULL@ */
241 size_t bufsize) /* I - Size of buffer */
242{
243 (void)credentials;
244
245 if (buffer && bufsize > 0)
246 *buffer = '\0';
247
248 return (1);
249}
250
251
2c85b752
MS
252/*
253 * '_httpFreeCredentials()' - Free internal credentials.
254 */
255
256void
257_httpFreeCredentials(
258 http_tls_credentials_t credentials) /* I - Internal credentials */
259{
260 if (!credentials)
261 return;
262
263 CFRelease(credentials);
264}
265
266
72d05bc9
MS
267/*
268 * 'httpLoadCredentials()' - Load X.509 credentials from a keychain file.
269 *
270 * @since CUPS 2.0@
271 */
272
dafebafd 273int /* O - 0 on success, -1 on error */
72d05bc9
MS
274httpLoadCredentials(
275 const char *path, /* I - Keychain/PKCS#12 path */
276 cups_array_t **credentials, /* IO - Credentials */
277 const char *common_name) /* I - Common name for credentials */
278{
f4047845
MS
279 (void)path;
280 (void)credentials;
281 (void)common_name;
282
283 return (-1);
284
285#if 0
dafebafd
MS
286 OSStatus err; /* Error info */
287 SecKeychainRef keychain = NULL;/* Keychain reference */
288 SecIdentitySearchRef search = NULL; /* Search reference */
289 SecIdentityRef identity = NULL;/* Identity */
290 CFArrayRef certificates = NULL;
291 /* Certificate array */
292 SecPolicyRef policy = NULL; /* Policy ref */
293 CFStringRef cfcommon_name = NULL;
294 /* Server name */
295 CFMutableDictionaryRef query = NULL; /* Query qualifiers */
296 CFArrayRef list = NULL; /* Keychain list */
297
298
299 if ((err = SecKeychainOpen(path, &keychain)))
300 goto cleanup;
301
302 cfcommon_name = CFStringCreateWithCString(kCFAllocatorDefault, common_name, kCFStringEncodingUTF8);
303
304 policy = SecPolicyCreateSSL(1, cfcommon_name);
305
306 if (cfcommon_name)
307 CFRelease(cfcommon_name);
308
309 if (!policy)
310 goto cleanup;
311
312 if (!(query = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)))
313 goto cleanup;
314
315 list = CFArrayCreate(kCFAllocatorDefault, (const void **)&keychain, 1,
316 &kCFTypeArrayCallBacks);
317
318 CFDictionaryAddValue(query, kSecClass, kSecClassIdentity);
319 CFDictionaryAddValue(query, kSecMatchPolicy, policy);
320 CFDictionaryAddValue(query, kSecReturnRef, kCFBooleanTrue);
321 CFDictionaryAddValue(query, kSecMatchLimit, kSecMatchLimitOne);
322 CFDictionaryAddValue(query, kSecMatchSearchList, list);
323
324 CFRelease(list);
325
326 err = SecItemCopyMatching(query, (CFTypeRef *)&identity);
327
328 if (err)
329 goto cleanup;
330
331 if (CFGetTypeID(identity) != SecIdentityGetTypeID())
332 goto cleanup;
333
334 if ((certificates = CFArrayCreate(NULL, (const void **)&identity, 1, &kCFTypeArrayCallBacks)) == NULL)
335 goto cleanup;
336
337 cleanup :
338
339 if (keychain)
340 CFRelease(keychain);
341 if (search)
342 CFRelease(search);
343 if (identity)
344 CFRelease(identity);
345
346 if (policy)
347 CFRelease(policy);
348 if (query)
349 CFRelease(query);
350
351 return (certificates);
f4047845 352#endif /* 0 */
72d05bc9
MS
353}
354
355
f4047845 356#if 0
72d05bc9 357/*
3af9ac9e 358 * 'cupsMakeCredentials()' - Create self-signed credentials for the given
72d05bc9
MS
359 * name.
360 *
361 * @since CUPS 2.0@
362 */
363
dafebafd 364int /* O - 0 on success, -1 on error */
3af9ac9e 365cupsMakeCredentials(
dafebafd 366 const char *path, /* I - Keychain/PKCS#12 path */
72d05bc9
MS
367 cups_array_t **credentials, /* O - Credentials */
368 const char *common_name) /* I - Common name for X.509 cert */
369{
dafebafd
MS
370# ifdef HAVE_SECGENERATESELFSIGNEDCERTIFICATE
371 int status = -1; /* Return status */
372 OSStatus err; /* Error code (if any) */
373 CFStringRef cfcommon_name = NULL;
374 /* CF string for server name */
375 SecIdentityRef ident = NULL; /* Identity */
376 SecKeyRef publicKey = NULL,
377 /* Public key */
378 privateKey = NULL;
379 /* Private key */
380 CFMutableDictionaryRef keyParams = NULL;
381 /* Key generation parameters */
382
72d05bc9
MS
383
384 if (credentials)
385 *credentials = NULL;
386
dafebafd
MS
387 cfcommon_name = CFStringCreateWithCString(kCFAllocatorDefault, servername,
388 kCFStringEncodingUTF8);
389 if (!cfcommon_name)
390 goto cleanup;
391
392 /*
393 * Create a public/private key pair...
394 */
395
396 keyParams = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
397 &kCFTypeDictionaryKeyCallBacks,
398 &kCFTypeDictionaryValueCallBacks);
399 if (!keyParams)
400 goto cleanup;
401
402 CFDictionaryAddValue(keyParams, kSecAttrKeyType, kSecAttrKeyTypeRSA);
403 CFDictionaryAddValue(keyParams, kSecAttrKeySizeInBits, CFSTR("2048"));
404 CFDictionaryAddValue(keyParams, kSecAttrLabel,
405 CFSTR("CUPS Self-Signed Certificate"));
406
407 err = SecKeyGeneratePair(keyParams, &publicKey, &privateKey);
408 if (err != noErr)
409 goto cleanup;
410
411 /*
412 * Create a self-signed certificate using the public/private key pair...
413 */
414
415 CFIndex usageInt = kSecKeyUsageAll;
416 CFNumberRef usage = CFNumberCreate(alloc, kCFNumberCFIndexType, &usageInt);
417 CFDictionaryRef certParams = CFDictionaryCreateMutable(kCFAllocatorDefault,
418kSecCSRBasicContraintsPathLen, CFINT(0), kSecSubjectAltName, cfcommon_name, kSecCertificateKeyUsage, usage, NULL, NULL);
419 CFRelease(usage);
420
421 const void *ca_o[] = { kSecOidOrganization, CFSTR("") };
422 const void *ca_cn[] = { kSecOidCommonName, cfcommon_name };
423 CFArrayRef ca_o_dn = CFArrayCreate(kCFAllocatorDefault, ca_o, 2, NULL);
424 CFArrayRef ca_cn_dn = CFArrayCreate(kCFAllocatorDefault, ca_cn, 2, NULL);
425 const void *ca_dn_array[2];
426
427 ca_dn_array[0] = CFArrayCreate(kCFAllocatorDefault, (const void **)&ca_o_dn, 1, NULL);
428 ca_dn_array[1] = CFArrayCreate(kCFAllocatorDefault, (const void **)&ca_cn_dn, 1, NULL);
429
430 CFArrayRef subject = CFArrayCreate(kCFAllocatorDefault, ca_dn_array, 2, NULL);
431 SecCertificateRef cert = SecGenerateSelfSignedCertificate(subject, certParams, publicKey, privateKey);
432 CFRelease(subject);
433 CFRelease(certParams);
434
435 if (!cert)
436 goto cleanup;
437
438 ident = SecIdentityCreate(kCFAllocatorDefault, cert, privateKey);
439
440 if (ident)
441 status = 0;
442
443 /*
444 * Cleanup and return...
445 */
446
447cleanup:
448
449 if (cfcommon_name)
450 CFRelease(cfcommon_name);
451
452 if (keyParams)
453 CFRelease(keyParams);
454
455 if (ident)
456 CFRelease(ident);
457
458 if (cert)
459 CFRelease(cert);
460
461 if (publicKey)
462 CFRelease(publicKey);
463
464 if (privateKey)
465 CFRelease(publicKey);
466
467 return (status);
468
469# else /* !HAVE_SECGENERATESELFSIGNEDCERTIFICATE */
470 int pid, /* Process ID of command */
471 status; /* Status of command */
472 char command[1024], /* Command */
473 *argv[4], /* Command-line arguments */
474 keychain[1024], /* Keychain argument */
475 infofile[1024]; /* Type-in information for cert */
476 cups_file_t *fp; /* Seed/info file */
477
478
479 /*
480 * Run the "certtool" command to generate a self-signed certificate...
481 */
482
483 if (!cupsFileFind("certtool", getenv("PATH"), 1, command, sizeof(command)))
484 return (-1);
485
486 /*
487 * Create a file with the certificate information fields...
488 *
489 * Note: This assumes that the default questions are asked by the certtool
490 * command...
491 */
492
493 if ((fp = cupsTempFile2(infofile, sizeof(infofile))) == NULL)
494 return (-1);
495
496 cupsFilePrintf(fp,
497 "%s\n" /* Enter key and certificate label */
498 "r\n" /* Generate RSA key pair */
499 "2048\n" /* Key size in bits */
500 "y\n" /* OK (y = yes) */
501 "b\n" /* Usage (b=signing/encryption) */
502 "s\n" /* Sign with SHA1 */
503 "y\n" /* OK (y = yes) */
504 "%s\n" /* Common name */
505 "\n" /* Country (default) */
506 "\n" /* Organization (default) */
507 "\n" /* Organizational unit (default) */
508 "\n" /* State/Province (default) */
509 "%s\n" /* Email address */
510 "y\n", /* OK (y = yes) */
511 common_name, common_name, "");
512 cupsFileClose(fp);
513
514 snprintf(keychain, sizeof(keychain), "k=%s", path);
515
516 argv[0] = "certtool";
517 argv[1] = "c";
518 argv[2] = keychain;
519 argv[3] = NULL;
520
521 posix_spawn_file_actions_t actions; /* File actions */
522
523 posix_spawn_file_actions_init(&actions);
524 posix_spawn_file_actions_addclose(&actions, 0);
525 posix_spawn_file_actions_addopen(&actions, 0, infofile, O_RDONLY, 0);
526
527 if (posix_spawn(&pid, command, &actions, NULL, argv, environ))
528 {
529 unlink(infofile);
530 return (-1);
531 }
532
533 posix_spawn_file_actions_destroy(&actions);
534
535 unlink(infofile);
536
537 while (waitpid(pid, &status, 0) < 0)
538 if (errno != EINTR)
539 {
540 status = -1;
541 break;
542 }
543
544 if (status)
545 return (-1);
546
547# endif /* HAVE_SECGENERATESELFSIGNEDCERTIFICATE */
548
549 return (httpLoadCredentials(path, credentials, common_name));
72d05bc9 550}
f4047845 551#endif /* 0 */
72d05bc9
MS
552
553
554/*
555 * 'httpSaveCredentials()' - Save X.509 credentials to a keychain file.
556 *
557 * @since CUPS 2.0@
558 */
559
560int /* O - -1 on error, 0 on success */
561httpSaveCredentials(
562 const char *path, /* I - Keychain/PKCS#12 path */
563 cups_array_t *credentials, /* I - Credentials */
564 const char *common_name) /* I - Common name for credentials */
565{
566 (void)path;
567 (void)credentials;
568 (void)common_name;
569
570 return (-1);
571}
572
573
2c85b752
MS
574/*
575 * 'http_cdsa_read()' - Read function for the CDSA library.
576 */
577
578static OSStatus /* O - -1 on error, 0 on success */
579http_cdsa_read(
580 SSLConnectionRef connection, /* I - SSL/TLS connection */
581 void *data, /* I - Data buffer */
582 size_t *dataLength) /* IO - Number of bytes */
583{
584 OSStatus result; /* Return value */
585 ssize_t bytes; /* Number of bytes read */
586 http_t *http; /* HTTP connection */
587
588
589 http = (http_t *)connection;
590
591 if (!http->blocking)
592 {
593 /*
594 * Make sure we have data before we read...
595 */
596
597 while (!_httpWait(http, http->wait_value, 0))
598 {
599 if (http->timeout_cb && (*http->timeout_cb)(http, http->timeout_data))
600 continue;
601
602 http->error = ETIMEDOUT;
603 return (-1);
604 }
605 }
606
607 do
608 {
609 bytes = recv(http->fd, data, *dataLength, 0);
610 }
611 while (bytes == -1 && (errno == EINTR || errno == EAGAIN));
612
613 if (bytes == *dataLength)
614 {
615 result = 0;
616 }
617 else if (bytes > 0)
618 {
619 *dataLength = bytes;
620 result = errSSLWouldBlock;
621 }
622 else
623 {
624 *dataLength = 0;
625
626 if (bytes == 0)
627 result = errSSLClosedGraceful;
628 else if (errno == EAGAIN)
629 result = errSSLWouldBlock;
630 else
631 result = errSSLClosedAbort;
632 }
633
634 return (result);
635}
636
637
638/*
639 * 'http_cdsa_write()' - Write function for the CDSA library.
640 */
641
642static OSStatus /* O - -1 on error, 0 on success */
643http_cdsa_write(
644 SSLConnectionRef connection, /* I - SSL/TLS connection */
645 const void *data, /* I - Data buffer */
646 size_t *dataLength) /* IO - Number of bytes */
647{
648 OSStatus result; /* Return value */
649 ssize_t bytes; /* Number of bytes read */
650 http_t *http; /* HTTP connection */
651
652
653 http = (http_t *)connection;
654
655 do
656 {
657 bytes = write(http->fd, data, *dataLength);
658 }
659 while (bytes == -1 && (errno == EINTR || errno == EAGAIN));
660
661 if (bytes == *dataLength)
662 {
663 result = 0;
664 }
665 else if (bytes >= 0)
666 {
667 *dataLength = bytes;
668 result = errSSLWouldBlock;
669 }
670 else
671 {
672 *dataLength = 0;
673
674 if (errno == EAGAIN)
675 result = errSSLWouldBlock;
676 else
677 result = errSSLClosedAbort;
678 }
679
680 return (result);
681}
682
683
684/*
685 * 'http_tls_initialize()' - Initialize the TLS stack.
686 */
687
688static void
689http_tls_initialize(void)
690{
691 /*
692 * Nothing to do...
693 */
694}
695
696
697/*
698 * 'http_tls_pending()' - Return the number of pending TLS-encrypted bytes.
699 */
700
701static size_t
702http_tls_pending(http_t *http) /* I - HTTP connection */
703{
704 size_t bytes; /* Bytes that are available */
705
706
707 if (!SSLGetBufferedReadSize(http->tls, &bytes))
708 return (bytes);
709
710 return (0);
711}
712
713
714/*
715 * 'http_tls_read()' - Read from a SSL/TLS connection.
716 */
717
718static int /* O - Bytes read */
719http_tls_read(http_t *http, /* I - Connection to server */
720 char *buf, /* I - Buffer to store data */
721 int len) /* I - Length of buffer */
722{
723 int result; /* Return value */
724 OSStatus error; /* Error info */
725 size_t processed; /* Number of bytes processed */
726
727
728 error = SSLRead(http->tls, buf, len, &processed);
729 DEBUG_printf(("6http_tls_read: error=%d, processed=%d", (int)error,
730 (int)processed));
731 switch (error)
732 {
733 case 0 :
734 result = (int)processed;
735 break;
736
737 case errSSLWouldBlock :
738 if (processed)
739 result = (int)processed;
740 else
741 {
742 result = -1;
743 errno = EINTR;
744 }
745 break;
746
747 case errSSLClosedGraceful :
748 default :
749 if (processed)
750 result = (int)processed;
751 else
752 {
753 result = -1;
754 errno = EPIPE;
755 }
756 break;
757 }
758
759 return (result);
760}
761
762
763/*
764 * 'http_tls_set_credentials()' - Set the TLS credentials.
765 */
766
767static int /* O - Status of connection */
768http_tls_set_credentials(http_t *http) /* I - Connection to server */
769{
770 _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals */
771 OSStatus error = 0; /* Error code */
772 http_tls_credentials_t credentials = NULL;
773 /* TLS credentials */
774
775
776 DEBUG_printf(("7http_tls_set_credentials(%p)", http));
777
778 /*
779 * Prefer connection specific credentials...
780 */
781
782 if ((credentials = http->tls_credentials) == NULL)
783 credentials = cg->tls_credentials;
784
785 if (credentials)
786 {
787 error = SSLSetCertificate(http->tls, credentials);
788 DEBUG_printf(("4http_tls_set_credentials: SSLSetCertificate, error=%d",
789 (int)error));
790 }
791 else
792 DEBUG_puts("4http_tls_set_credentials: No credentials to set.");
793
794 return (error);
795}
796
797
798/*
799 * 'http_tls_start()' - Set up SSL/TLS support on a connection.
800 */
801
802static int /* O - 0 on success, -1 on failure */
803http_tls_start(http_t *http) /* I - Connection to server */
804{
805 char hostname[256], /* Hostname */
806 *hostptr; /* Pointer into hostname */
807 _cups_globals_t *cg = _cupsGlobals();
808 /* Pointer to library globals */
809 OSStatus error; /* Error code */
810 const char *message = NULL;/* Error message */
811 cups_array_t *credentials; /* Credentials array */
812 cups_array_t *names; /* CUPS distinguished names */
813 CFArrayRef dn_array; /* CF distinguished names array */
814 CFIndex count; /* Number of credentials */
815 CFDataRef data; /* Certificate data */
816 int i; /* Looping var */
817 http_credential_t *credential; /* Credential data */
818
819
820 DEBUG_printf(("7http_tls_start(http=%p)", http));
821
822 /*
823 * Get the hostname to use for SSL...
824 */
825
826 if (httpAddrLocalhost(http->hostaddr))
827 {
828 strlcpy(hostname, "localhost", sizeof(hostname));
829 }
830 else
831 {
832 /*
833 * Otherwise make sure the hostname we have does not end in a trailing dot.
834 */
835
836 strlcpy(hostname, http->hostname, sizeof(hostname));
837 if ((hostptr = hostname + strlen(hostname) - 1) >= hostname &&
838 *hostptr == '.')
839 *hostptr = '\0';
840 }
841
72d05bc9 842 if ((http->tls = SSLCreateContext(kCFAllocatorDefault, http->mode == _HTTP_MODE_CLIENT ? kSSLClientSide : kSSLServerSide, kSSLStreamType)) == NULL)
2c85b752
MS
843 {
844 DEBUG_puts("4http_tls_start: SSLCreateContext failed.");
845 http->error = errno = ENOMEM;
846 http->status = HTTP_STATUS_ERROR;
847 _cupsSetHTTPError(HTTP_STATUS_ERROR);
848
849 return (-1);
850 }
851
852 error = SSLSetConnection(http->tls, http);
853 DEBUG_printf(("4http_tls_start: SSLSetConnection, error=%d", (int)error));
854
855 if (!error)
856 {
857 error = SSLSetIOFuncs(http->tls, http_cdsa_read, http_cdsa_write);
858 DEBUG_printf(("4http_tls_start: SSLSetIOFuncs, error=%d", (int)error));
859 }
860
861 if (!error)
862 {
863 error = SSLSetSessionOption(http->tls, kSSLSessionOptionBreakOnServerAuth,
864 true);
865 DEBUG_printf(("4http_tls_start: SSLSetSessionOption, error=%d",
866 (int)error));
867 }
868
869 if (!error)
870 {
871 if (cg->client_cert_cb)
872 {
873 error = SSLSetSessionOption(http->tls,
874 kSSLSessionOptionBreakOnCertRequested, true);
875 DEBUG_printf(("4http_tls_start: kSSLSessionOptionBreakOnCertRequested, "
876 "error=%d", (int)error));
877 }
878 else
879 {
d3d89474
MS
880 error = http_tls_set_credentials(http);
881 DEBUG_printf(("4http_tls_start: http_tls_set_credentials, error=%d",
2c85b752
MS
882 (int)error));
883 }
884 }
885
886 /*
887 * Let the server know which hostname/domain we are trying to connect to
888 * in case it wants to serve up a certificate with a matching common name.
889 */
890
891 if (!error)
892 {
893 error = SSLSetPeerDomainName(http->tls, hostname, strlen(hostname));
894
895 DEBUG_printf(("4http_tls_start: SSLSetPeerDomainName, error=%d",
896 (int)error));
897 }
898
899 if (!error)
900 {
901 int done = 0; /* Are we done yet? */
902
903 while (!error && !done)
904 {
905 error = SSLHandshake(http->tls);
906
907 DEBUG_printf(("4http_tls_start: SSLHandshake returned %d.", (int)error));
908
909 switch (error)
910 {
911 case noErr :
912 done = 1;
913 break;
914
915 case errSSLWouldBlock :
916 error = noErr; /* Force a retry */
917 usleep(1000); /* in 1 millisecond */
918 break;
919
920 case errSSLServerAuthCompleted :
921 error = 0;
922 if (cg->server_cert_cb)
923 {
924 error = httpCopyCredentials(http, &credentials);
925 if (!error)
926 {
927 error = (cg->server_cert_cb)(http, http->tls, credentials,
928 cg->server_cert_data);
929 httpFreeCredentials(credentials);
930 }
931
932 DEBUG_printf(("4http_tls_start: Server certificate callback "
933 "returned %d.", (int)error));
934 }
935 break;
936
937 case errSSLClientCertRequested :
938 error = 0;
939
940 if (cg->client_cert_cb)
941 {
942 names = NULL;
943 if (!(error = SSLCopyDistinguishedNames(http->tls, &dn_array)) &&
944 dn_array)
945 {
946 if ((names = cupsArrayNew(NULL, NULL)) != NULL)
947 {
948 for (i = 0, count = CFArrayGetCount(dn_array); i < count; i++)
949 {
950 data = (CFDataRef)CFArrayGetValueAtIndex(dn_array, i);
951
952 if ((credential = malloc(sizeof(*credential))) != NULL)
953 {
954 credential->datalen = CFDataGetLength(data);
955 if ((credential->data = malloc(credential->datalen)))
956 {
957 memcpy((void *)credential->data, CFDataGetBytePtr(data),
958 credential->datalen);
959 cupsArrayAdd(names, credential);
960 }
961 else
962 free(credential);
963 }
964 }
965 }
966
967 CFRelease(dn_array);
968 }
969
970 if (!error)
971 {
972 error = (cg->client_cert_cb)(http, http->tls, names,
973 cg->client_cert_data);
974
975 DEBUG_printf(("4http_tls_start: Client certificate callback "
976 "returned %d.", (int)error));
977 }
978
979 httpFreeCredentials(names);
980 }
981 break;
982
983 case errSSLUnknownRootCert :
984 message = _("Unable to establish a secure connection to host "
985 "(untrusted certificate).");
986 break;
987
988 case errSSLNoRootCert :
989 message = _("Unable to establish a secure connection to host "
990 "(self-signed certificate).");
991 break;
992
993 case errSSLCertExpired :
994 message = _("Unable to establish a secure connection to host "
995 "(expired certificate).");
996 break;
997
998 case errSSLCertNotYetValid :
999 message = _("Unable to establish a secure connection to host "
1000 "(certificate not yet valid).");
1001 break;
1002
1003 case errSSLHostNameMismatch :
1004 message = _("Unable to establish a secure connection to host "
1005 "(host name mismatch).");
1006 break;
1007
1008 case errSSLXCertChainInvalid :
1009 message = _("Unable to establish a secure connection to host "
1010 "(certificate chain invalid).");
1011 break;
1012
1013 case errSSLConnectionRefused :
1014 message = _("Unable to establish a secure connection to host "
1015 "(peer dropped connection before responding).");
1016 break;
1017
1018 default :
1019 break;
1020 }
1021 }
1022 }
1023
1024 if (error)
1025 {
1026 http->error = error;
1027 http->status = HTTP_STATUS_ERROR;
1028 errno = ECONNREFUSED;
1029
1030 CFRelease(http->tls);
1031 http->tls = NULL;
1032
1033 /*
1034 * If an error string wasn't set by the callbacks use a generic one...
1035 */
1036
1037 if (!message)
1038#ifdef HAVE_CSSMERRORSTRING
1039 message = cssmErrorString(error);
1040#else
1041 message = _("Unable to establish a secure connection to host.");
1042#endif /* HAVE_CSSMERRORSTRING */
1043
1044 _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI, message, 1);
1045
1046 return (-1);
1047 }
1048
1049 return (0);
1050}
1051
1052
1053/*
1054 * 'http_tls_stop()' - Shut down SSL/TLS on a connection.
1055 */
1056
1057static void
1058http_tls_stop(http_t *http) /* I - Connection to server */
1059{
1060 while (SSLClose(http->tls) == errSSLWouldBlock)
1061 usleep(1000);
1062
1063 CFRelease(http->tls);
1064
1065 if (http->tls_credentials)
1066 CFRelease(http->tls_credentials);
1067
1068 http->tls = NULL;
1069 http->tls_credentials = NULL;
1070}
1071
1072
1073/*
1074 * 'http_tls_write()' - Write to a SSL/TLS connection.
1075 */
1076
1077static int /* O - Bytes written */
1078http_tls_write(http_t *http, /* I - Connection to server */
1079 const char *buf, /* I - Buffer holding data */
1080 int len) /* I - Length of buffer */
1081{
1082 ssize_t result; /* Return value */
1083 OSStatus error; /* Error info */
1084 size_t processed; /* Number of bytes processed */
1085
1086
1087 DEBUG_printf(("2http_tls_write(http=%p, buf=%p, len=%d)", http, buf, len));
1088
1089 error = SSLWrite(http->tls, buf, len, &processed);
1090
1091 switch (error)
1092 {
1093 case 0 :
1094 result = (int)processed;
1095 break;
1096
1097 case errSSLWouldBlock :
1098 if (processed)
1099 {
1100 result = (int)processed;
1101 }
1102 else
1103 {
1104 result = -1;
1105 errno = EINTR;
1106 }
1107 break;
1108
1109 case errSSLClosedGraceful :
1110 default :
1111 if (processed)
1112 {
1113 result = (int)processed;
1114 }
1115 else
1116 {
1117 result = -1;
1118 errno = EPIPE;
1119 }
1120 break;
1121 }
1122
1123 DEBUG_printf(("3http_tls_write: Returning %d.", (int)result));
1124
1125 return ((int)result);
1126}
1127
1128
d3d89474 1129#if 0
2c85b752
MS
1130/*
1131 * 'cupsdEndTLS()' - Shutdown a secure session with the client.
1132 */
1133
1134int /* O - 1 on success, 0 on error */
1135cupsdEndTLS(cupsd_client_t *con) /* I - Client connection */
1136{
1137 while (SSLClose(con->http.tls) == errSSLWouldBlock)
1138 usleep(1000);
1139
1140 CFRelease(con->http.tls);
1141 con->http.tls = NULL;
1142
1143 if (con->http.tls_credentials)
1144 CFRelease(con->http.tls_credentials);
1145
1146 return (1);
1147}
1148
1149
1150/*
1151 * 'cupsdStartTLS()' - Start a secure session with the client.
1152 */
1153
1154int /* O - 1 on success, 0 on error */
1155cupsdStartTLS(cupsd_client_t *con) /* I - Client connection */
1156{
1157 OSStatus error = 0; /* Error code */
1158 SecTrustRef peerTrust; /* Peer certificates */
1159
1160
1161 cupsdLogMessage(CUPSD_LOG_DEBUG, "[Client %d] Encrypting connection.",
1162 con->http.fd);
1163
1164 con->http.tls_credentials = copy_cdsa_certificate(con);
1165
1166 if (!con->http.tls_credentials)
1167 {
1168 /*
1169 * No keychain (yet), make a self-signed certificate...
1170 */
1171
1172 if (make_certificate(con))
1173 con->http.tls_credentials = copy_cdsa_certificate(con);
1174 }
1175
1176 if (!con->http.tls_credentials)
1177 {
1178 cupsdLogMessage(CUPSD_LOG_ERROR,
1179 "Could not find signing key in keychain \"%s\"",
1180 ServerCertificate);
1181 error = errSSLBadConfiguration;
1182 }
1183
1184 if (!error)
1185 con->http.tls = SSLCreateContext(kCFAllocatorDefault, kSSLServerSide,
1186 kSSLStreamType);
1187
1188 if (!error)
1189 error = SSLSetIOFuncs(con->http.tls, http_cdsa_read, http_cdsa_write);
1190
1191 if (!error)
1192 error = SSLSetConnection(con->http.tls, HTTP(con));
1193
1194 if (!error)
1195 error = SSLSetCertificate(con->http.tls, con->http.tls_credentials);
1196
1197 if (!error)
1198 {
1199 /*
1200 * Perform SSL/TLS handshake
1201 */
1202
1203 while ((error = SSLHandshake(con->http.tls)) == errSSLWouldBlock)
1204 usleep(1000);
1205 }
1206
1207 if (error)
1208 {
1209 cupsdLogMessage(CUPSD_LOG_ERROR,
1210 "Unable to encrypt connection from %s - %s (%d)",
1211 con->http.hostname, cssmErrorString(error), (int)error);
1212
1213 con->http.error = error;
1214 con->http.status = HTTP_ERROR;
1215
1216 if (con->http.tls)
1217 {
1218 CFRelease(con->http.tls);
1219 con->http.tls = NULL;
1220 }
1221
1222 if (con->http.tls_credentials)
1223 {
1224 CFRelease(con->http.tls_credentials);
1225 con->http.tls_credentials = NULL;
1226 }
1227
1228 return (0);
1229 }
1230
1231 cupsdLogMessage(CUPSD_LOG_DEBUG, "Connection from %s now encrypted.",
1232 con->http.hostname);
1233
1234 if (!SSLCopyPeerTrust(con->http.tls, &peerTrust) && peerTrust)
1235 {
1236 cupsdLogMessage(CUPSD_LOG_DEBUG, "Received %d peer certificates.",
1237 (int)SecTrustGetCertificateCount(peerTrust));
1238 CFRelease(peerTrust);
1239 }
1240 else
1241 cupsdLogMessage(CUPSD_LOG_DEBUG, "Received NO peer certificates.");
1242
1243 return (1);
1244}
d3d89474 1245#endif /* 0 */
2c85b752
MS
1246
1247
1248/*
1249 * End of "$Id$".
1250 */