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