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