2 * "$Id: tls-darwin.c 4216 2013-03-11 13:57:36Z msweet $"
4 * TLS support code for the CUPS scheduler on OS X.
6 * Copyright 2007-2012 by Apple Inc.
7 * Copyright 1997-2007 by Easy Software Products, all rights reserved.
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/".
17 * cupsdEndTLS() - Shutdown a secure session with the client.
18 * cupsdStartTLS() - Start a secure session with the client.
19 * copy_cdsa_certificate() - Copy a SSL/TLS certificate from the System
21 * make_certificate() - Make a self-signed SSL/TLS certificate.
29 static CFArrayRef
copy_cdsa_certificate(cupsd_client_t
*con
);
30 static int make_certificate(cupsd_client_t
*con
);
34 * 'cupsdEndTLS()' - Shutdown a secure session with the client.
37 int /* O - 1 on success, 0 on error */
38 cupsdEndTLS(cupsd_client_t
*con
) /* I - Client connection */
40 while (SSLClose(con
->http
.tls
) == errSSLWouldBlock
)
43 CFRelease(con
->http
.tls
);
46 if (con
->http
.tls_credentials
)
47 CFRelease(con
->http
.tls_credentials
);
54 * 'cupsdStartTLS()' - Start a secure session with the client.
57 int /* O - 1 on success, 0 on error */
58 cupsdStartTLS(cupsd_client_t
*con
) /* I - Client connection */
60 OSStatus error
= 0; /* Error code */
61 SecTrustRef peerTrust
; /* Peer certificates */
64 cupsdLogMessage(CUPSD_LOG_DEBUG
, "[Client %d] Encrypting connection.",
67 con
->http
.tls_credentials
= copy_cdsa_certificate(con
);
69 if (!con
->http
.tls_credentials
)
72 * No keychain (yet), make a self-signed certificate...
75 if (make_certificate(con
))
76 con
->http
.tls_credentials
= copy_cdsa_certificate(con
);
79 if (!con
->http
.tls_credentials
)
81 cupsdLogMessage(CUPSD_LOG_ERROR
,
82 "Could not find signing key in keychain \"%s\"",
84 error
= errSSLBadConfiguration
;
88 con
->http
.tls
= SSLCreateContext(kCFAllocatorDefault
, kSSLServerSide
,
92 error
= SSLSetIOFuncs(con
->http
.tls
, _httpReadCDSA
, _httpWriteCDSA
);
95 error
= SSLSetConnection(con
->http
.tls
, HTTP(con
));
98 error
= SSLSetCertificate(con
->http
.tls
, con
->http
.tls_credentials
);
103 * Perform SSL/TLS handshake
106 while ((error
= SSLHandshake(con
->http
.tls
)) == errSSLWouldBlock
)
112 cupsdLogMessage(CUPSD_LOG_ERROR
,
113 "Unable to encrypt connection from %s - %s (%d)",
114 con
->http
.hostname
, cssmErrorString(error
), (int)error
);
116 con
->http
.error
= error
;
117 con
->http
.status
= HTTP_ERROR
;
121 CFRelease(con
->http
.tls
);
122 con
->http
.tls
= NULL
;
125 if (con
->http
.tls_credentials
)
127 CFRelease(con
->http
.tls_credentials
);
128 con
->http
.tls_credentials
= NULL
;
134 cupsdLogMessage(CUPSD_LOG_DEBUG
, "Connection from %s now encrypted.",
137 if (!SSLCopyPeerTrust(con
->http
.tls
, &peerTrust
) && peerTrust
)
139 cupsdLogMessage(CUPSD_LOG_DEBUG
, "Received %d peer certificates!",
140 (int)SecTrustGetCertificateCount(peerTrust
));
141 CFRelease(peerTrust
);
144 cupsdLogMessage(CUPSD_LOG_DEBUG
, "Received NO peer certificates!");
151 * 'copy_cdsa_certificate()' - Copy a SSL/TLS certificate from the System
155 static CFArrayRef
/* O - Array of certificates */
156 copy_cdsa_certificate(
157 cupsd_client_t
*con
) /* I - Client connection */
159 OSStatus err
; /* Error info */
160 SecKeychainRef keychain
= NULL
;/* Keychain reference */
161 SecIdentitySearchRef search
= NULL
; /* Search reference */
162 SecIdentityRef identity
= NULL
;/* Identity */
163 CFArrayRef certificates
= NULL
;
164 /* Certificate array */
165 SecPolicyRef policy
= NULL
; /* Policy ref */
166 CFStringRef servername
= NULL
;
168 CFMutableDictionaryRef query
= NULL
; /* Query qualifiers */
169 CFArrayRef list
= NULL
; /* Keychain list */
170 # if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
171 char localname
[1024];/* Local hostname */
172 # endif /* HAVE_DNSSD || HAVE_AVAHI */
175 cupsdLogMessage(CUPSD_LOG_DEBUG
,
176 "copy_cdsa_certificate: Looking for certs for \"%s\"...",
179 if ((err
= SecKeychainOpen(ServerCertificate
, &keychain
)))
181 cupsdLogMessage(CUPSD_LOG_ERROR
, "Cannot open keychain \"%s\" - %s (%d)",
182 ServerCertificate
, cssmErrorString(err
), (int)err
);
186 servername
= CFStringCreateWithCString(kCFAllocatorDefault
, con
->servername
,
187 kCFStringEncodingUTF8
);
189 policy
= SecPolicyCreateSSL(1, servername
);
192 CFRelease(servername
);
196 cupsdLogMessage(CUPSD_LOG_ERROR
, "Cannot create ssl policy reference");
200 if (!(query
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
201 &kCFTypeDictionaryKeyCallBacks
,
202 &kCFTypeDictionaryValueCallBacks
)))
204 cupsdLogMessage(CUPSD_LOG_ERROR
, "Cannot create query dictionary");
208 list
= CFArrayCreate(kCFAllocatorDefault
, (const void **)&keychain
, 1,
209 &kCFTypeArrayCallBacks
);
211 CFDictionaryAddValue(query
, kSecClass
, kSecClassIdentity
);
212 CFDictionaryAddValue(query
, kSecMatchPolicy
, policy
);
213 CFDictionaryAddValue(query
, kSecReturnRef
, kCFBooleanTrue
);
214 CFDictionaryAddValue(query
, kSecMatchLimit
, kSecMatchLimitOne
);
215 CFDictionaryAddValue(query
, kSecMatchSearchList
, list
);
219 err
= SecItemCopyMatching(query
, (CFTypeRef
*)&identity
);
221 # if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
222 if (err
&& DNSSDHostName
)
225 * Search for the connection server name failed; try the DNS-SD .local
226 * hostname instead...
229 snprintf(localname
, sizeof(localname
), "%s.local", DNSSDHostName
);
231 cupsdLogMessage(CUPSD_LOG_DEBUG
,
232 "copy_cdsa_certificate: Looking for certs for \"%s\"...",
235 servername
= CFStringCreateWithCString(kCFAllocatorDefault
, localname
,
236 kCFStringEncodingUTF8
);
240 policy
= SecPolicyCreateSSL(1, servername
);
243 CFRelease(servername
);
247 cupsdLogMessage(CUPSD_LOG_ERROR
, "Cannot create ssl policy reference");
251 CFDictionarySetValue(query
, kSecMatchPolicy
, policy
);
253 err
= SecItemCopyMatching(query
, (CFTypeRef
*)&identity
);
255 # endif /* HAVE_DNSSD || HAVE_AVAHI */
259 cupsdLogMessage(CUPSD_LOG_DEBUG
,
260 "Cannot find signing key in keychain \"%s\": %s (%d)",
261 ServerCertificate
, cssmErrorString(err
), (int)err
);
265 if (CFGetTypeID(identity
) != SecIdentityGetTypeID())
267 cupsdLogMessage(CUPSD_LOG_ERROR
, "SecIdentity CFTypeID failure!");
271 if ((certificates
= CFArrayCreate(NULL
, (const void **)&identity
,
272 1, &kCFTypeArrayCallBacks
)) == NULL
)
274 cupsdLogMessage(CUPSD_LOG_ERROR
, "Cannot create certificate array");
292 return (certificates
);
297 * 'make_certificate()' - Make a self-signed SSL/TLS certificate.
300 static int /* O - 1 on success, 0 on failure */
301 make_certificate(cupsd_client_t
*con
) /* I - Client connection */
303 int pid
, /* Process ID of command */
304 status
; /* Status of command */
305 char command
[1024], /* Command */
306 *argv
[4], /* Command-line arguments */
307 *envp
[MAX_ENV
+ 1], /* Environment variables */
308 keychain
[1024], /* Keychain argument */
309 infofile
[1024], /* Type-in information for cert */
310 # if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
311 localname
[1024], /* Local hostname */
312 # endif /* HAVE_DNSSD || HAVE_AVAHI */
313 *servername
; /* Name of server in cert */
314 cups_file_t
*fp
; /* Seed/info file */
315 int infofd
; /* Info file descriptor */
318 # if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
319 if (con
->servername
&& isdigit(con
->servername
[0] & 255) && DNSSDHostName
)
321 snprintf(localname
, sizeof(localname
), "%s.local", DNSSDHostName
);
322 servername
= localname
;
325 # endif /* HAVE_DNSSD || HAVE_AVAHI */
326 servername
= con
->servername
;
329 * Run the "certtool" command to generate a self-signed certificate...
332 if (!cupsFileFind("certtool", getenv("PATH"), 1, command
, sizeof(command
)))
334 cupsdLogMessage(CUPSD_LOG_ERROR
,
335 "No SSL certificate and certtool command not found!");
340 * Create a file with the certificate information fields...
342 * Note: This assumes that the default questions are asked by the certtool
346 if ((fp
= cupsTempFile2(infofile
, sizeof(infofile
))) == NULL
)
348 cupsdLogMessage(CUPSD_LOG_ERROR
,
349 "Unable to create certificate information file %s - %s",
350 infofile
, strerror(errno
));
355 "%s\n" /* Enter key and certificate label */
356 "r\n" /* Generate RSA key pair */
357 "2048\n" /* Key size in bits */
358 "y\n" /* OK (y = yes) */
359 "b\n" /* Usage (b=signing/encryption) */
360 "s\n" /* Sign with SHA1 */
361 "y\n" /* OK (y = yes) */
362 "%s\n" /* Common name */
363 "\n" /* Country (default) */
364 "\n" /* Organization (default) */
365 "\n" /* Organizational unit (default) */
366 "\n" /* State/Province (default) */
367 "%s\n" /* Email address */
368 "y\n", /* OK (y = yes) */
369 servername
, servername
, ServerAdmin
);
372 cupsdLogMessage(CUPSD_LOG_INFO
,
373 "Generating SSL server key and certificate...");
375 snprintf(keychain
, sizeof(keychain
), "k=%s", ServerCertificate
);
377 argv
[0] = "certtool";
382 cupsdLoadEnv(envp
, MAX_ENV
);
384 infofd
= open(infofile
, O_RDONLY
);
386 if (!cupsdStartProcess(command
, argv
, envp
, infofd
, -1, -1, -1, -1, 1, NULL
,
397 while (waitpid(pid
, &status
, 0) < 0)
404 cupsdFinishProcess(pid
, command
, sizeof(command
), NULL
);
408 if (WIFEXITED(status
))
409 cupsdLogMessage(CUPSD_LOG_ERROR
,
410 "Unable to create SSL server key and certificate - "
411 "the certtool command stopped with status %d!",
412 WEXITSTATUS(status
));
414 cupsdLogMessage(CUPSD_LOG_ERROR
,
415 "Unable to create SSL server key and certificate - "
416 "the certtool command crashed on signal %d!",
421 cupsdLogMessage(CUPSD_LOG_INFO
,
422 "Created SSL server certificate file \"%s\"...",
431 * End of "$Id: tls-darwin.c 4216 2013-03-11 13:57:36Z msweet $".