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