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