]> git.ipfire.org Git - thirdparty/cups.git/blame - cups/tls-darwin.c
Fix typo.
[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 {
d3d89474
MS
450 error = http_tls_set_credentials(http);
451 DEBUG_printf(("4http_tls_start: http_tls_set_credentials, error=%d",
2c85b752
MS
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
d3d89474 699#if 0
2c85b752
MS
700/*
701 * 'cupsdEndTLS()' - Shutdown a secure session with the client.
702 */
703
704int /* O - 1 on success, 0 on error */
705cupsdEndTLS(cupsd_client_t *con) /* I - Client connection */
706{
707 while (SSLClose(con->http.tls) == errSSLWouldBlock)
708 usleep(1000);
709
710 CFRelease(con->http.tls);
711 con->http.tls = NULL;
712
713 if (con->http.tls_credentials)
714 CFRelease(con->http.tls_credentials);
715
716 return (1);
717}
718
719
720/*
721 * 'cupsdStartTLS()' - Start a secure session with the client.
722 */
723
724int /* O - 1 on success, 0 on error */
725cupsdStartTLS(cupsd_client_t *con) /* I - Client connection */
726{
727 OSStatus error = 0; /* Error code */
728 SecTrustRef peerTrust; /* Peer certificates */
729
730
731 cupsdLogMessage(CUPSD_LOG_DEBUG, "[Client %d] Encrypting connection.",
732 con->http.fd);
733
734 con->http.tls_credentials = copy_cdsa_certificate(con);
735
736 if (!con->http.tls_credentials)
737 {
738 /*
739 * No keychain (yet), make a self-signed certificate...
740 */
741
742 if (make_certificate(con))
743 con->http.tls_credentials = copy_cdsa_certificate(con);
744 }
745
746 if (!con->http.tls_credentials)
747 {
748 cupsdLogMessage(CUPSD_LOG_ERROR,
749 "Could not find signing key in keychain \"%s\"",
750 ServerCertificate);
751 error = errSSLBadConfiguration;
752 }
753
754 if (!error)
755 con->http.tls = SSLCreateContext(kCFAllocatorDefault, kSSLServerSide,
756 kSSLStreamType);
757
758 if (!error)
759 error = SSLSetIOFuncs(con->http.tls, http_cdsa_read, http_cdsa_write);
760
761 if (!error)
762 error = SSLSetConnection(con->http.tls, HTTP(con));
763
764 if (!error)
765 error = SSLSetCertificate(con->http.tls, con->http.tls_credentials);
766
767 if (!error)
768 {
769 /*
770 * Perform SSL/TLS handshake
771 */
772
773 while ((error = SSLHandshake(con->http.tls)) == errSSLWouldBlock)
774 usleep(1000);
775 }
776
777 if (error)
778 {
779 cupsdLogMessage(CUPSD_LOG_ERROR,
780 "Unable to encrypt connection from %s - %s (%d)",
781 con->http.hostname, cssmErrorString(error), (int)error);
782
783 con->http.error = error;
784 con->http.status = HTTP_ERROR;
785
786 if (con->http.tls)
787 {
788 CFRelease(con->http.tls);
789 con->http.tls = NULL;
790 }
791
792 if (con->http.tls_credentials)
793 {
794 CFRelease(con->http.tls_credentials);
795 con->http.tls_credentials = NULL;
796 }
797
798 return (0);
799 }
800
801 cupsdLogMessage(CUPSD_LOG_DEBUG, "Connection from %s now encrypted.",
802 con->http.hostname);
803
804 if (!SSLCopyPeerTrust(con->http.tls, &peerTrust) && peerTrust)
805 {
806 cupsdLogMessage(CUPSD_LOG_DEBUG, "Received %d peer certificates.",
807 (int)SecTrustGetCertificateCount(peerTrust));
808 CFRelease(peerTrust);
809 }
810 else
811 cupsdLogMessage(CUPSD_LOG_DEBUG, "Received NO peer certificates.");
812
813 return (1);
814}
815
816
817/*
818 * 'copy_cdsa_certificate()' - Copy a SSL/TLS certificate from the System
819 * keychain.
820 */
821
822static CFArrayRef /* O - Array of certificates */
823copy_cdsa_certificate(
824 cupsd_client_t *con) /* I - Client connection */
825{
826 OSStatus err; /* Error info */
827 SecKeychainRef keychain = NULL;/* Keychain reference */
828 SecIdentitySearchRef search = NULL; /* Search reference */
829 SecIdentityRef identity = NULL;/* Identity */
830 CFArrayRef certificates = NULL;
831 /* Certificate array */
832 SecPolicyRef policy = NULL; /* Policy ref */
833 CFStringRef servername = NULL;
834 /* Server name */
835 CFMutableDictionaryRef query = NULL; /* Query qualifiers */
836 CFArrayRef list = NULL; /* Keychain list */
837# if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
838 char localname[1024];/* Local hostname */
839# endif /* HAVE_DNSSD || HAVE_AVAHI */
840
841
842 cupsdLogMessage(CUPSD_LOG_DEBUG,
843 "copy_cdsa_certificate: Looking for certs for \"%s\".",
844 con->servername);
845
846 if ((err = SecKeychainOpen(ServerCertificate, &keychain)))
847 {
848 cupsdLogMessage(CUPSD_LOG_ERROR, "Cannot open keychain \"%s\" - %s (%d)",
849 ServerCertificate, cssmErrorString(err), (int)err);
850 goto cleanup;
851 }
852
853 servername = CFStringCreateWithCString(kCFAllocatorDefault, con->servername,
854 kCFStringEncodingUTF8);
855
856 policy = SecPolicyCreateSSL(1, servername);
857
858 if (servername)
859 CFRelease(servername);
860
861 if (!policy)
862 {
863 cupsdLogMessage(CUPSD_LOG_ERROR, "Cannot create ssl policy reference");
864 goto cleanup;
865 }
866
867 if (!(query = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
868 &kCFTypeDictionaryKeyCallBacks,
869 &kCFTypeDictionaryValueCallBacks)))
870 {
871 cupsdLogMessage(CUPSD_LOG_ERROR, "Cannot create query dictionary");
872 goto cleanup;
873 }
874
875 list = CFArrayCreate(kCFAllocatorDefault, (const void **)&keychain, 1,
876 &kCFTypeArrayCallBacks);
877
878 CFDictionaryAddValue(query, kSecClass, kSecClassIdentity);
879 CFDictionaryAddValue(query, kSecMatchPolicy, policy);
880 CFDictionaryAddValue(query, kSecReturnRef, kCFBooleanTrue);
881 CFDictionaryAddValue(query, kSecMatchLimit, kSecMatchLimitOne);
882 CFDictionaryAddValue(query, kSecMatchSearchList, list);
883
884 CFRelease(list);
885
886 err = SecItemCopyMatching(query, (CFTypeRef *)&identity);
887
888# if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
889 if (err && DNSSDHostName)
890 {
891 /*
892 * Search for the connection server name failed; try the DNS-SD .local
893 * hostname instead...
894 */
895
896 snprintf(localname, sizeof(localname), "%s.local", DNSSDHostName);
897
898 cupsdLogMessage(CUPSD_LOG_DEBUG,
899 "copy_cdsa_certificate: Looking for certs for \"%s\".",
900 localname);
901
902 servername = CFStringCreateWithCString(kCFAllocatorDefault, localname,
903 kCFStringEncodingUTF8);
904
905 CFRelease(policy);
906
907 policy = SecPolicyCreateSSL(1, servername);
908
909 if (servername)
910 CFRelease(servername);
911
912 if (!policy)
913 {
914 cupsdLogMessage(CUPSD_LOG_ERROR, "Cannot create ssl policy reference");
915 goto cleanup;
916 }
917
918 CFDictionarySetValue(query, kSecMatchPolicy, policy);
919
920 err = SecItemCopyMatching(query, (CFTypeRef *)&identity);
921 }
922# endif /* HAVE_DNSSD || HAVE_AVAHI */
923
924 if (err)
925 {
926 cupsdLogMessage(CUPSD_LOG_DEBUG,
927 "Cannot find signing key in keychain \"%s\": %s (%d)",
928 ServerCertificate, cssmErrorString(err), (int)err);
929 goto cleanup;
930 }
931
932 if (CFGetTypeID(identity) != SecIdentityGetTypeID())
933 {
934 cupsdLogMessage(CUPSD_LOG_ERROR, "SecIdentity CFTypeID failure.");
935 goto cleanup;
936 }
937
938 if ((certificates = CFArrayCreate(NULL, (const void **)&identity,
939 1, &kCFTypeArrayCallBacks)) == NULL)
940 {
941 cupsdLogMessage(CUPSD_LOG_ERROR, "Cannot create certificate array");
942 goto cleanup;
943 }
944
945 cleanup :
946
947 if (keychain)
948 CFRelease(keychain);
949 if (search)
950 CFRelease(search);
951 if (identity)
952 CFRelease(identity);
953
954 if (policy)
955 CFRelease(policy);
956 if (query)
957 CFRelease(query);
958
959 return (certificates);
960}
961
962
963/*
964 * 'make_certificate()' - Make a self-signed SSL/TLS certificate.
965 */
966
967static int /* O - 1 on success, 0 on failure */
968make_certificate(cupsd_client_t *con) /* I - Client connection */
969{
970# ifdef HAVE_SECGENERATESELFSIGNEDCERTIFICATE
971 int status = 0; /* Return status */
972 OSStatus err; /* Error code (if any) */
973# if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
974 char localname[1024];/* Local hostname */
975# endif /* HAVE_DNSSD || HAVE_AVAHI */
976 const char *servername; /* Name of server in cert */
977 CFStringRef cfservername = NULL;
978 /* CF string for server name */
979 SecIdentityRef ident = NULL; /* Identity */
980 SecKeyRef publicKey = NULL,
981 /* Public key */
982 privateKey = NULL;
983 /* Private key */
984 CFMutableDictionaryRef keyParams = NULL;
985 /* Key generation parameters */
986
987
988 cupsdLogMessage(CUPSD_LOG_INFO,
989 "Generating SSL server key and certificate.");
990
991# if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
992 if (con->servername && isdigit(con->servername[0] & 255) && DNSSDHostName)
993 {
994 snprintf(localname, sizeof(localname), "%s.local", DNSSDHostName);
995 servername = localname;
996 }
997 else
998# endif /* HAVE_DNSSD || HAVE_AVAHI */
999 servername = con->servername;
1000
1001 cfservername = CFStringCreateWithCString(kCFAllocatorDefault, servername,
1002 kCFStringEncodingUTF8);
1003 if (!cfservername)
1004 goto cleanup;
1005
1006 /*
1007 * Create a public/private key pair...
1008 */
1009
1010 keyParams = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
1011 &kCFTypeDictionaryKeyCallBacks,
1012 &kCFTypeDictionaryValueCallBacks);
1013 if (!keyParams)
1014 goto cleanup;
1015
1016 CFDictionaryAddValue(keyParams, kSecAttrKeyType, kSecAttrKeyTypeRSA);
1017 CFDictionaryAddValue(keyParams, kSecAttrKeySizeInBits, CFSTR("2048"));
1018 CFDictionaryAddValue(keyParams, kSecAttrLabel,
1019 CFSTR("CUPS Self-Signed Certificate"));
1020
1021 err = SecKeyGeneratePair(keyParams, &publicKey, &privateKey);
1022 if (err != noErr)
1023 {
1024 cupsdLogMessage(CUPSD_LOG_DEBUG, "SecKeyGeneratePair returned %ld.",
1025 (long)err);
1026 goto cleanup;
1027 }
1028
1029 /*
1030 * Create a self-signed certificate using the public/private key pair...
1031 */
1032
1033 CFIndex usageInt = kSecKeyUsageAll;
1034 CFNumberRef usage = CFNumberCreate(alloc, kCFNumberCFIndexType, &usageInt);
1035 CFDictionaryRef certParams = CFDictionaryCreateMutable(kCFAllocatorDefault,
1036 kSecCSRBasicContraintsPathLen, CFINT(0),
1037 kSecSubjectAltName, cfservername,
1038 kSecCertificateKeyUsage, usage,
1039 NULL, NULL);
1040 CFRelease(usage);
1041
1042 const void *ca_o[] = { kSecOidOrganization, CFSTR("") };
1043 const void *ca_cn[] = { kSecOidCommonName, cfservername };
1044 CFArrayRef ca_o_dn = CFArrayCreate(kCFAllocatorDefault, ca_o, 2, NULL);
1045 CFArrayRef ca_cn_dn = CFArrayCreate(kCFAllocatorDefault, ca_cn, 2, NULL);
1046 const void *ca_dn_array[2];
1047
1048 ca_dn_array[0] = CFArrayCreate(kCFAllocatorDefault, (const void **)&ca_o_dn,
1049 1, NULL);
1050 ca_dn_array[1] = CFArrayCreate(kCFAllocatorDefault, (const void **)&ca_cn_dn,
1051 1, NULL);
1052
1053 CFArrayRef subject = CFArrayCreate(kCFAllocatorDefault, ca_dn_array, 2,
1054 NULL);
1055 SecCertificateRef cert = SecGenerateSelfSignedCertificate(subject, certParams,
1056 publicKey,
1057 privateKey);
1058 CFRelease(subject);
1059 CFRelease(certParams);
1060
1061 if (!cert)
1062 {
1063 cupsdLogMessage(CUPSD_LOG_DEBUG, "SecGenerateSelfSignedCertificate failed.");
1064 goto cleanup;
1065 }
1066
1067 ident = SecIdentityCreate(kCFAllocatorDefault, cert, privateKey);
1068
1069 if (ident)
1070 cupsdLogMessage(CUPSD_LOG_INFO,
1071 "Created SSL server certificate file \"%s\".",
1072 ServerCertificate);
1073
1074 /*
1075 * Cleanup and return...
1076 */
1077
1078cleanup:
1079
1080 if (cfservername)
1081 CFRelease(cfservername);
1082
1083 if (keyParams)
1084 CFRelease(keyParams);
1085
1086 if (ident)
1087 CFRelease(ident);
1088
1089 if (cert)
1090 CFRelease(cert);
1091
1092 if (publicKey)
1093 CFRelease(publicKey);
1094
1095 if (privateKey)
1096 CFRelease(publicKey);
1097
1098 if (!status)
1099 cupsdLogMessage(CUPSD_LOG_ERROR,
1100 "Unable to create SSL server key and certificate.");
1101
1102 return (status);
1103
1104# else /* !HAVE_SECGENERATESELFSIGNEDCERTIFICATE */
1105 int pid, /* Process ID of command */
1106 status; /* Status of command */
1107 char command[1024], /* Command */
1108 *argv[4], /* Command-line arguments */
1109 *envp[MAX_ENV + 1], /* Environment variables */
1110 keychain[1024], /* Keychain argument */
1111 infofile[1024], /* Type-in information for cert */
1112# if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
1113 localname[1024], /* Local hostname */
1114# endif /* HAVE_DNSSD || HAVE_AVAHI */
1115 *servername; /* Name of server in cert */
1116 cups_file_t *fp; /* Seed/info file */
1117 int infofd; /* Info file descriptor */
1118
1119
1120# if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
1121 if (con->servername && isdigit(con->servername[0] & 255) && DNSSDHostName)
1122 {
1123 snprintf(localname, sizeof(localname), "%s.local", DNSSDHostName);
1124 servername = localname;
1125 }
1126 else
1127# endif /* HAVE_DNSSD || HAVE_AVAHI */
1128 servername = con->servername;
1129
1130 /*
1131 * Run the "certtool" command to generate a self-signed certificate...
1132 */
1133
1134 if (!cupsFileFind("certtool", getenv("PATH"), 1, command, sizeof(command)))
1135 {
1136 cupsdLogMessage(CUPSD_LOG_ERROR,
1137 "No SSL certificate and certtool command not found.");
1138 return (0);
1139 }
1140
1141 /*
1142 * Create a file with the certificate information fields...
1143 *
1144 * Note: This assumes that the default questions are asked by the certtool
1145 * command...
1146 */
1147
1148 if ((fp = cupsTempFile2(infofile, sizeof(infofile))) == NULL)
1149 {
1150 cupsdLogMessage(CUPSD_LOG_ERROR,
1151 "Unable to create certificate information file %s - %s",
1152 infofile, strerror(errno));
1153 return (0);
1154 }
1155
1156 cupsFilePrintf(fp,
1157 "%s\n" /* Enter key and certificate label */
1158 "r\n" /* Generate RSA key pair */
1159 "2048\n" /* Key size in bits */
1160 "y\n" /* OK (y = yes) */
1161 "b\n" /* Usage (b=signing/encryption) */
1162 "s\n" /* Sign with SHA1 */
1163 "y\n" /* OK (y = yes) */
1164 "%s\n" /* Common name */
1165 "\n" /* Country (default) */
1166 "\n" /* Organization (default) */
1167 "\n" /* Organizational unit (default) */
1168 "\n" /* State/Province (default) */
1169 "%s\n" /* Email address */
1170 "y\n", /* OK (y = yes) */
1171 servername, servername, ServerAdmin);
1172 cupsFileClose(fp);
1173
1174 cupsdLogMessage(CUPSD_LOG_INFO,
1175 "Generating SSL server key and certificate.");
1176
1177 snprintf(keychain, sizeof(keychain), "k=%s", ServerCertificate);
1178
1179 argv[0] = "certtool";
1180 argv[1] = "c";
1181 argv[2] = keychain;
1182 argv[3] = NULL;
1183
1184 cupsdLoadEnv(envp, MAX_ENV);
1185
1186 infofd = open(infofile, O_RDONLY);
1187
1188 if (!cupsdStartProcess(command, argv, envp, infofd, -1, -1, -1, -1, 1, NULL,
1189 NULL, &pid))
1190 {
1191 close(infofd);
1192 unlink(infofile);
1193 return (0);
1194 }
1195
1196 close(infofd);
1197 unlink(infofile);
1198
1199 while (waitpid(pid, &status, 0) < 0)
1200 if (errno != EINTR)
1201 {
1202 status = 1;
1203 break;
1204 }
1205
1206 cupsdFinishProcess(pid, command, sizeof(command), NULL);
1207
1208 if (status)
1209 {
1210 if (WIFEXITED(status))
1211 cupsdLogMessage(CUPSD_LOG_ERROR,
1212 "Unable to create SSL server key and certificate - "
1213 "the certtool command stopped with status %d.",
1214 WEXITSTATUS(status));
1215 else
1216 cupsdLogMessage(CUPSD_LOG_ERROR,
1217 "Unable to create SSL server key and certificate - "
1218 "the certtool command crashed on signal %d.",
1219 WTERMSIG(status));
1220 }
1221 else
1222 {
1223 cupsdLogMessage(CUPSD_LOG_INFO,
1224 "Created SSL server certificate file \"%s\".",
1225 ServerCertificate);
1226 }
1227
1228 return (!status);
1229# endif /* HAVE_SECGENERATESELFSIGNEDCERTIFICATE */
1230}
d3d89474 1231#endif /* 0 */
2c85b752
MS
1232
1233
1234/*
1235 * End of "$Id$".
1236 */