]> git.ipfire.org Git - thirdparty/cups.git/blame - scheduler/tls-darwin.c
Import CUPS v1.7.1
[thirdparty/cups.git] / scheduler / tls-darwin.c
CommitLineData
82cc1f9a 1/*
61515785 2 * "$Id: tls-darwin.c 4216 2013-03-11 13:57:36Z msweet $"
82cc1f9a
MS
3 *
4 * TLS support code for the CUPS scheduler on OS X.
5 *
6 * Copyright 2007-2012 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 * Contents:
16 *
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
20 * keychain.
21 * make_certificate() - Make a self-signed SSL/TLS certificate.
22 */
23
24
25/*
26 * Local functions...
27 */
28
29static CFArrayRef copy_cdsa_certificate(cupsd_client_t *con);
30static int make_certificate(cupsd_client_t *con);
31
32
33/*
34 * 'cupsdEndTLS()' - Shutdown a secure session with the client.
35 */
36
37int /* O - 1 on success, 0 on error */
38cupsdEndTLS(cupsd_client_t *con) /* I - Client connection */
39{
40 while (SSLClose(con->http.tls) == errSSLWouldBlock)
41 usleep(1000);
42
cb7f98ee 43 CFRelease(con->http.tls);
82cc1f9a
MS
44 con->http.tls = NULL;
45
46 if (con->http.tls_credentials)
47 CFRelease(con->http.tls_credentials);
48
49 return (1);
50}
51
52
53/*
54 * 'cupsdStartTLS()' - Start a secure session with the client.
55 */
56
57int /* O - 1 on success, 0 on error */
58cupsdStartTLS(cupsd_client_t *con) /* I - Client connection */
59{
60 OSStatus error = 0; /* Error code */
cb7f98ee 61 SecTrustRef peerTrust; /* Peer certificates */
82cc1f9a
MS
62
63
64 cupsdLogMessage(CUPSD_LOG_DEBUG, "[Client %d] Encrypting connection.",
65 con->http.fd);
66
67 con->http.tls_credentials = copy_cdsa_certificate(con);
68
69 if (!con->http.tls_credentials)
70 {
71 /*
72 * No keychain (yet), make a self-signed certificate...
73 */
74
75 if (make_certificate(con))
76 con->http.tls_credentials = copy_cdsa_certificate(con);
77 }
78
79 if (!con->http.tls_credentials)
80 {
81 cupsdLogMessage(CUPSD_LOG_ERROR,
82 "Could not find signing key in keychain \"%s\"",
83 ServerCertificate);
84 error = errSSLBadConfiguration;
85 }
86
87 if (!error)
cb7f98ee
MS
88 con->http.tls = SSLCreateContext(kCFAllocatorDefault, kSSLServerSide,
89 kSSLStreamType);
82cc1f9a
MS
90
91 if (!error)
92 error = SSLSetIOFuncs(con->http.tls, _httpReadCDSA, _httpWriteCDSA);
93
94 if (!error)
95 error = SSLSetConnection(con->http.tls, HTTP(con));
96
82cc1f9a
MS
97 if (!error)
98 error = SSLSetCertificate(con->http.tls, con->http.tls_credentials);
99
100 if (!error)
101 {
102 /*
103 * Perform SSL/TLS handshake
104 */
105
106 while ((error = SSLHandshake(con->http.tls)) == errSSLWouldBlock)
107 usleep(1000);
108 }
109
110 if (error)
111 {
112 cupsdLogMessage(CUPSD_LOG_ERROR,
113 "Unable to encrypt connection from %s - %s (%d)",
114 con->http.hostname, cssmErrorString(error), (int)error);
115
116 con->http.error = error;
117 con->http.status = HTTP_ERROR;
118
119 if (con->http.tls)
120 {
cb7f98ee 121 CFRelease(con->http.tls);
82cc1f9a
MS
122 con->http.tls = NULL;
123 }
124
125 if (con->http.tls_credentials)
126 {
127 CFRelease(con->http.tls_credentials);
128 con->http.tls_credentials = NULL;
129 }
130
131 return (0);
132 }
133
134 cupsdLogMessage(CUPSD_LOG_DEBUG, "Connection from %s now encrypted.",
135 con->http.hostname);
136
cb7f98ee 137 if (!SSLCopyPeerTrust(con->http.tls, &peerTrust) && peerTrust)
82cc1f9a
MS
138 {
139 cupsdLogMessage(CUPSD_LOG_DEBUG, "Received %d peer certificates!",
cb7f98ee
MS
140 (int)SecTrustGetCertificateCount(peerTrust));
141 CFRelease(peerTrust);
82cc1f9a
MS
142 }
143 else
144 cupsdLogMessage(CUPSD_LOG_DEBUG, "Received NO peer certificates!");
145
146 return (1);
147}
148
149
150/*
151 * 'copy_cdsa_certificate()' - Copy a SSL/TLS certificate from the System
152 * keychain.
153 */
154
155static CFArrayRef /* O - Array of certificates */
156copy_cdsa_certificate(
157 cupsd_client_t *con) /* I - Client connection */
158{
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 */
82cc1f9a
MS
165 SecPolicyRef policy = NULL; /* Policy ref */
166 CFStringRef servername = NULL;
167 /* Server name */
168 CFMutableDictionaryRef query = NULL; /* Query qualifiers */
169 CFArrayRef list = NULL; /* Keychain list */
a29fd7dd 170# if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
82cc1f9a 171 char localname[1024];/* Local hostname */
a29fd7dd 172# endif /* HAVE_DNSSD || HAVE_AVAHI */
82cc1f9a
MS
173
174
175 cupsdLogMessage(CUPSD_LOG_DEBUG,
176 "copy_cdsa_certificate: Looking for certs for \"%s\"...",
177 con->servername);
178
179 if ((err = SecKeychainOpen(ServerCertificate, &keychain)))
180 {
181 cupsdLogMessage(CUPSD_LOG_ERROR, "Cannot open keychain \"%s\" - %s (%d)",
182 ServerCertificate, cssmErrorString(err), (int)err);
183 goto cleanup;
184 }
185
82cc1f9a
MS
186 servername = CFStringCreateWithCString(kCFAllocatorDefault, con->servername,
187 kCFStringEncodingUTF8);
188
189 policy = SecPolicyCreateSSL(1, servername);
190
191 if (servername)
192 CFRelease(servername);
193
194 if (!policy)
195 {
196 cupsdLogMessage(CUPSD_LOG_ERROR, "Cannot create ssl policy reference");
197 goto cleanup;
198 }
199
200 if (!(query = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
201 &kCFTypeDictionaryKeyCallBacks,
202 &kCFTypeDictionaryValueCallBacks)))
203 {
204 cupsdLogMessage(CUPSD_LOG_ERROR, "Cannot create query dictionary");
205 goto cleanup;
206 }
207
208 list = CFArrayCreate(kCFAllocatorDefault, (const void **)&keychain, 1,
209 &kCFTypeArrayCallBacks);
210
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);
216
217 CFRelease(list);
218
219 err = SecItemCopyMatching(query, (CFTypeRef *)&identity);
220
a29fd7dd 221# if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
82cc1f9a
MS
222 if (err && DNSSDHostName)
223 {
224 /*
225 * Search for the connection server name failed; try the DNS-SD .local
226 * hostname instead...
227 */
228
229 snprintf(localname, sizeof(localname), "%s.local", DNSSDHostName);
230
231 cupsdLogMessage(CUPSD_LOG_DEBUG,
232 "copy_cdsa_certificate: Looking for certs for \"%s\"...",
233 localname);
234
235 servername = CFStringCreateWithCString(kCFAllocatorDefault, localname,
236 kCFStringEncodingUTF8);
237
238 CFRelease(policy);
239
240 policy = SecPolicyCreateSSL(1, servername);
241
242 if (servername)
243 CFRelease(servername);
244
245 if (!policy)
246 {
247 cupsdLogMessage(CUPSD_LOG_ERROR, "Cannot create ssl policy reference");
248 goto cleanup;
249 }
250
251 CFDictionarySetValue(query, kSecMatchPolicy, policy);
252
253 err = SecItemCopyMatching(query, (CFTypeRef *)&identity);
254 }
a29fd7dd 255# endif /* HAVE_DNSSD || HAVE_AVAHI */
82cc1f9a
MS
256
257 if (err)
258 {
259 cupsdLogMessage(CUPSD_LOG_DEBUG,
260 "Cannot find signing key in keychain \"%s\": %s (%d)",
261 ServerCertificate, cssmErrorString(err), (int)err);
262 goto cleanup;
263 }
264
82cc1f9a
MS
265 if (CFGetTypeID(identity) != SecIdentityGetTypeID())
266 {
267 cupsdLogMessage(CUPSD_LOG_ERROR, "SecIdentity CFTypeID failure!");
268 goto cleanup;
269 }
270
271 if ((certificates = CFArrayCreate(NULL, (const void **)&identity,
272 1, &kCFTypeArrayCallBacks)) == NULL)
273 {
274 cupsdLogMessage(CUPSD_LOG_ERROR, "Cannot create certificate array");
275 goto cleanup;
276 }
277
278 cleanup :
279
280 if (keychain)
281 CFRelease(keychain);
282 if (search)
283 CFRelease(search);
284 if (identity)
285 CFRelease(identity);
286
82cc1f9a
MS
287 if (policy)
288 CFRelease(policy);
289 if (query)
290 CFRelease(query);
82cc1f9a
MS
291
292 return (certificates);
293}
294
295
296/*
297 * 'make_certificate()' - Make a self-signed SSL/TLS certificate.
298 */
299
300static int /* O - 1 on success, 0 on failure */
301make_certificate(cupsd_client_t *con) /* I - Client connection */
302{
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 */
a29fd7dd 310# if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
82cc1f9a 311 localname[1024], /* Local hostname */
a29fd7dd 312# endif /* HAVE_DNSSD || HAVE_AVAHI */
82cc1f9a
MS
313 *servername; /* Name of server in cert */
314 cups_file_t *fp; /* Seed/info file */
315 int infofd; /* Info file descriptor */
316
317
a29fd7dd 318# if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
82cc1f9a
MS
319 if (con->servername && isdigit(con->servername[0] & 255) && DNSSDHostName)
320 {
321 snprintf(localname, sizeof(localname), "%s.local", DNSSDHostName);
322 servername = localname;
323 }
324 else
a29fd7dd 325# endif /* HAVE_DNSSD || HAVE_AVAHI */
82cc1f9a
MS
326 servername = con->servername;
327
328 /*
329 * Run the "certtool" command to generate a self-signed certificate...
330 */
331
332 if (!cupsFileFind("certtool", getenv("PATH"), 1, command, sizeof(command)))
333 {
334 cupsdLogMessage(CUPSD_LOG_ERROR,
335 "No SSL certificate and certtool command not found!");
336 return (0);
337 }
338
339 /*
340 * Create a file with the certificate information fields...
341 *
342 * Note: This assumes that the default questions are asked by the certtool
343 * command...
344 */
345
346 if ((fp = cupsTempFile2(infofile, sizeof(infofile))) == NULL)
347 {
348 cupsdLogMessage(CUPSD_LOG_ERROR,
349 "Unable to create certificate information file %s - %s",
350 infofile, strerror(errno));
351 return (0);
352 }
353
354 cupsFilePrintf(fp,
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);
370 cupsFileClose(fp);
371
372 cupsdLogMessage(CUPSD_LOG_INFO,
373 "Generating SSL server key and certificate...");
374
375 snprintf(keychain, sizeof(keychain), "k=%s", ServerCertificate);
376
377 argv[0] = "certtool";
378 argv[1] = "c";
379 argv[2] = keychain;
380 argv[3] = NULL;
381
382 cupsdLoadEnv(envp, MAX_ENV);
383
384 infofd = open(infofile, O_RDONLY);
385
386 if (!cupsdStartProcess(command, argv, envp, infofd, -1, -1, -1, -1, 1, NULL,
387 NULL, &pid))
388 {
389 close(infofd);
390 unlink(infofile);
391 return (0);
392 }
393
394 close(infofd);
395 unlink(infofile);
396
397 while (waitpid(pid, &status, 0) < 0)
398 if (errno != EINTR)
399 {
400 status = 1;
401 break;
402 }
403
404 cupsdFinishProcess(pid, command, sizeof(command), NULL);
405
406 if (status)
407 {
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));
413 else
414 cupsdLogMessage(CUPSD_LOG_ERROR,
415 "Unable to create SSL server key and certificate - "
416 "the certtool command crashed on signal %d!",
417 WTERMSIG(status));
418 }
419 else
420 {
421 cupsdLogMessage(CUPSD_LOG_INFO,
422 "Created SSL server certificate file \"%s\"...",
423 ServerCertificate);
424 }
425
426 return (!status);
427}
428
429
430/*
61515785 431 * End of "$Id: tls-darwin.c 4216 2013-03-11 13:57:36Z msweet $".
82cc1f9a 432 */