]> git.ipfire.org Git - thirdparty/cups.git/blame - scheduler/tls-darwin.c
Merge changes from CUPS 1.6svn-r10510.
[thirdparty/cups.git] / scheduler / tls-darwin.c
CommitLineData
82cc1f9a
MS
1/*
2 * "$Id$"
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
43 SSLDisposeContext(con->http.tls);
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 */
61 CFArrayRef peerCerts; /* Peer certificates */
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)
88 error = SSLNewContext(true, &con->http.tls);
89
90 if (!error)
91 error = SSLSetIOFuncs(con->http.tls, _httpReadCDSA, _httpWriteCDSA);
92
93 if (!error)
94 error = SSLSetConnection(con->http.tls, HTTP(con));
95
96 if (!error)
97 error = SSLSetAllowsExpiredCerts(con->http.tls, true);
98
99 if (!error)
100 error = SSLSetAllowsAnyRoot(con->http.tls, true);
101
102 if (!error)
103 error = SSLSetCertificate(con->http.tls, con->http.tls_credentials);
104
105 if (!error)
106 {
107 /*
108 * Perform SSL/TLS handshake
109 */
110
111 while ((error = SSLHandshake(con->http.tls)) == errSSLWouldBlock)
112 usleep(1000);
113 }
114
115 if (error)
116 {
117 cupsdLogMessage(CUPSD_LOG_ERROR,
118 "Unable to encrypt connection from %s - %s (%d)",
119 con->http.hostname, cssmErrorString(error), (int)error);
120
121 con->http.error = error;
122 con->http.status = HTTP_ERROR;
123
124 if (con->http.tls)
125 {
126 SSLDisposeContext(con->http.tls);
127 con->http.tls = NULL;
128 }
129
130 if (con->http.tls_credentials)
131 {
132 CFRelease(con->http.tls_credentials);
133 con->http.tls_credentials = NULL;
134 }
135
136 return (0);
137 }
138
139 cupsdLogMessage(CUPSD_LOG_DEBUG, "Connection from %s now encrypted.",
140 con->http.hostname);
141
142 if (!SSLCopyPeerCertificates(con->http.tls, &peerCerts) && peerCerts)
143 {
144 cupsdLogMessage(CUPSD_LOG_DEBUG, "Received %d peer certificates!",
145 (int)CFArrayGetCount(peerCerts));
146 CFRelease(peerCerts);
147 }
148 else
149 cupsdLogMessage(CUPSD_LOG_DEBUG, "Received NO peer certificates!");
150
151 return (1);
152}
153
154
155/*
156 * 'copy_cdsa_certificate()' - Copy a SSL/TLS certificate from the System
157 * keychain.
158 */
159
160static CFArrayRef /* O - Array of certificates */
161copy_cdsa_certificate(
162 cupsd_client_t *con) /* I - Client connection */
163{
164 OSStatus err; /* Error info */
165 SecKeychainRef keychain = NULL;/* Keychain reference */
166 SecIdentitySearchRef search = NULL; /* Search reference */
167 SecIdentityRef identity = NULL;/* Identity */
168 CFArrayRef certificates = NULL;
169 /* Certificate array */
170# if HAVE_SECPOLICYCREATESSL
171 SecPolicyRef policy = NULL; /* Policy ref */
172 CFStringRef servername = NULL;
173 /* Server name */
174 CFMutableDictionaryRef query = NULL; /* Query qualifiers */
175 CFArrayRef list = NULL; /* Keychain list */
a29fd7dd 176# if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
82cc1f9a 177 char localname[1024];/* Local hostname */
a29fd7dd 178# endif /* HAVE_DNSSD || HAVE_AVAHI */
82cc1f9a
MS
179# elif defined(HAVE_SECIDENTITYSEARCHCREATEWITHPOLICY)
180 SecPolicyRef policy = NULL; /* Policy ref */
181 SecPolicySearchRef policy_search = NULL;
182 /* Policy search ref */
183 CSSM_DATA options; /* Policy options */
184 CSSM_APPLE_TP_SSL_OPTIONS
185 ssl_options; /* SSL Option for hostname */
186 char localname[1024];/* Local hostname */
187# endif /* HAVE_SECPOLICYCREATESSL */
188
189
190 cupsdLogMessage(CUPSD_LOG_DEBUG,
191 "copy_cdsa_certificate: Looking for certs for \"%s\"...",
192 con->servername);
193
194 if ((err = SecKeychainOpen(ServerCertificate, &keychain)))
195 {
196 cupsdLogMessage(CUPSD_LOG_ERROR, "Cannot open keychain \"%s\" - %s (%d)",
197 ServerCertificate, cssmErrorString(err), (int)err);
198 goto cleanup;
199 }
200
201# if HAVE_SECPOLICYCREATESSL
202 servername = CFStringCreateWithCString(kCFAllocatorDefault, con->servername,
203 kCFStringEncodingUTF8);
204
205 policy = SecPolicyCreateSSL(1, servername);
206
207 if (servername)
208 CFRelease(servername);
209
210 if (!policy)
211 {
212 cupsdLogMessage(CUPSD_LOG_ERROR, "Cannot create ssl policy reference");
213 goto cleanup;
214 }
215
216 if (!(query = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
217 &kCFTypeDictionaryKeyCallBacks,
218 &kCFTypeDictionaryValueCallBacks)))
219 {
220 cupsdLogMessage(CUPSD_LOG_ERROR, "Cannot create query dictionary");
221 goto cleanup;
222 }
223
224 list = CFArrayCreate(kCFAllocatorDefault, (const void **)&keychain, 1,
225 &kCFTypeArrayCallBacks);
226
227 CFDictionaryAddValue(query, kSecClass, kSecClassIdentity);
228 CFDictionaryAddValue(query, kSecMatchPolicy, policy);
229 CFDictionaryAddValue(query, kSecReturnRef, kCFBooleanTrue);
230 CFDictionaryAddValue(query, kSecMatchLimit, kSecMatchLimitOne);
231 CFDictionaryAddValue(query, kSecMatchSearchList, list);
232
233 CFRelease(list);
234
235 err = SecItemCopyMatching(query, (CFTypeRef *)&identity);
236
a29fd7dd 237# if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
82cc1f9a
MS
238 if (err && DNSSDHostName)
239 {
240 /*
241 * Search for the connection server name failed; try the DNS-SD .local
242 * hostname instead...
243 */
244
245 snprintf(localname, sizeof(localname), "%s.local", DNSSDHostName);
246
247 cupsdLogMessage(CUPSD_LOG_DEBUG,
248 "copy_cdsa_certificate: Looking for certs for \"%s\"...",
249 localname);
250
251 servername = CFStringCreateWithCString(kCFAllocatorDefault, localname,
252 kCFStringEncodingUTF8);
253
254 CFRelease(policy);
255
256 policy = SecPolicyCreateSSL(1, servername);
257
258 if (servername)
259 CFRelease(servername);
260
261 if (!policy)
262 {
263 cupsdLogMessage(CUPSD_LOG_ERROR, "Cannot create ssl policy reference");
264 goto cleanup;
265 }
266
267 CFDictionarySetValue(query, kSecMatchPolicy, policy);
268
269 err = SecItemCopyMatching(query, (CFTypeRef *)&identity);
270 }
a29fd7dd 271# endif /* HAVE_DNSSD || HAVE_AVAHI */
82cc1f9a
MS
272
273 if (err)
274 {
275 cupsdLogMessage(CUPSD_LOG_DEBUG,
276 "Cannot find signing key in keychain \"%s\": %s (%d)",
277 ServerCertificate, cssmErrorString(err), (int)err);
278 goto cleanup;
279 }
280
281# elif defined(HAVE_SECIDENTITYSEARCHCREATEWITHPOLICY)
282 /*
283 * Use a policy to search for valid certificates whose common name matches the
284 * servername...
285 */
286
287 if (SecPolicySearchCreate(CSSM_CERT_X_509v3, &CSSMOID_APPLE_TP_SSL,
288 NULL, &policy_search))
289 {
290 cupsdLogMessage(CUPSD_LOG_ERROR, "Cannot create a policy search reference");
291 goto cleanup;
292 }
293
294 if (SecPolicySearchCopyNext(policy_search, &policy))
295 {
296 cupsdLogMessage(CUPSD_LOG_ERROR,
297 "Cannot find a policy to use for searching");
298 goto cleanup;
299 }
300
301 memset(&ssl_options, 0, sizeof(ssl_options));
302 ssl_options.Version = CSSM_APPLE_TP_SSL_OPTS_VERSION;
303 ssl_options.ServerName = con->servername;
304 ssl_options.ServerNameLen = strlen(con->servername);
305
306 options.Data = (uint8 *)&ssl_options;
307 options.Length = sizeof(ssl_options);
308
309 if (SecPolicySetValue(policy, &options))
310 {
311 cupsdLogMessage(CUPSD_LOG_ERROR,
312 "Cannot set policy value to use for searching");
313 goto cleanup;
314 }
315
316 if ((err = SecIdentitySearchCreateWithPolicy(policy, NULL, CSSM_KEYUSE_SIGN,
317 keychain, FALSE, &search)))
318 {
319 cupsdLogMessage(CUPSD_LOG_ERROR,
320 "Cannot create identity search reference: %s (%d)",
321 cssmErrorString(err), (int)err);
322 goto cleanup;
323 }
324
325 err = SecIdentitySearchCopyNext(search, &identity);
326
a29fd7dd 327# if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
82cc1f9a
MS
328 if (err && DNSSDHostName)
329 {
330 /*
331 * Search for the connection server name failed; try the DNS-SD .local
332 * hostname instead...
333 */
334
335 snprintf(localname, sizeof(localname), "%s.local", DNSSDHostName);
336
337 ssl_options.ServerName = localname;
338 ssl_options.ServerNameLen = strlen(localname);
339
340 cupsdLogMessage(CUPSD_LOG_DEBUG,
341 "copy_cdsa_certificate: Looking for certs for \"%s\"...",
342 localname);
343
344 if (SecPolicySetValue(policy, &options))
345 {
346 cupsdLogMessage(CUPSD_LOG_ERROR,
347 "Cannot set policy value to use for searching");
348 goto cleanup;
349 }
350
351 CFRelease(search);
352 search = NULL;
353 if ((err = SecIdentitySearchCreateWithPolicy(policy, NULL, CSSM_KEYUSE_SIGN,
354 keychain, FALSE, &search)))
355 {
356 cupsdLogMessage(CUPSD_LOG_ERROR,
357 "Cannot create identity search reference: %s (%d)",
358 cssmErrorString(err), (int)err);
359 goto cleanup;
360 }
361
362 err = SecIdentitySearchCopyNext(search, &identity);
363
364 }
a29fd7dd 365# endif /* HAVE_DNSSD || HAVE_AVAHI */
82cc1f9a
MS
366
367 if (err)
368 {
369 cupsdLogMessage(CUPSD_LOG_DEBUG,
370 "Cannot find signing key in keychain \"%s\": %s (%d)",
371 ServerCertificate, cssmErrorString(err), (int)err);
372 goto cleanup;
373 }
374
375# else
376 /*
377 * Assume there is exactly one SecIdentity in the keychain...
378 */
379
380 if ((err = SecIdentitySearchCreate(keychain, CSSM_KEYUSE_SIGN, &search)))
381 {
382 cupsdLogMessage(CUPSD_LOG_DEBUG,
383 "Cannot create identity search reference (%d)", (int)err);
384 goto cleanup;
385 }
386
387 if ((err = SecIdentitySearchCopyNext(search, &identity)))
388 {
389 cupsdLogMessage(CUPSD_LOG_DEBUG,
390 "Cannot find signing key in keychain \"%s\": %s (%d)",
391 ServerCertificate, cssmErrorString(err), (int)err);
392 goto cleanup;
393 }
394# endif /* HAVE_SECPOLICYCREATESSL */
395
396 if (CFGetTypeID(identity) != SecIdentityGetTypeID())
397 {
398 cupsdLogMessage(CUPSD_LOG_ERROR, "SecIdentity CFTypeID failure!");
399 goto cleanup;
400 }
401
402 if ((certificates = CFArrayCreate(NULL, (const void **)&identity,
403 1, &kCFTypeArrayCallBacks)) == NULL)
404 {
405 cupsdLogMessage(CUPSD_LOG_ERROR, "Cannot create certificate array");
406 goto cleanup;
407 }
408
409 cleanup :
410
411 if (keychain)
412 CFRelease(keychain);
413 if (search)
414 CFRelease(search);
415 if (identity)
416 CFRelease(identity);
417
418# if HAVE_SECPOLICYCREATESSL
419 if (policy)
420 CFRelease(policy);
421 if (query)
422 CFRelease(query);
423# elif defined(HAVE_SECIDENTITYSEARCHCREATEWITHPOLICY)
424 if (policy)
425 CFRelease(policy);
426 if (policy_search)
427 CFRelease(policy_search);
428# endif /* HAVE_SECPOLICYCREATESSL */
429
430 return (certificates);
431}
432
433
434/*
435 * 'make_certificate()' - Make a self-signed SSL/TLS certificate.
436 */
437
438static int /* O - 1 on success, 0 on failure */
439make_certificate(cupsd_client_t *con) /* I - Client connection */
440{
441 int pid, /* Process ID of command */
442 status; /* Status of command */
443 char command[1024], /* Command */
444 *argv[4], /* Command-line arguments */
445 *envp[MAX_ENV + 1], /* Environment variables */
446 keychain[1024], /* Keychain argument */
447 infofile[1024], /* Type-in information for cert */
a29fd7dd 448# if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
82cc1f9a 449 localname[1024], /* Local hostname */
a29fd7dd 450# endif /* HAVE_DNSSD || HAVE_AVAHI */
82cc1f9a
MS
451 *servername; /* Name of server in cert */
452 cups_file_t *fp; /* Seed/info file */
453 int infofd; /* Info file descriptor */
454
455
a29fd7dd 456# if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
82cc1f9a
MS
457 if (con->servername && isdigit(con->servername[0] & 255) && DNSSDHostName)
458 {
459 snprintf(localname, sizeof(localname), "%s.local", DNSSDHostName);
460 servername = localname;
461 }
462 else
a29fd7dd 463# endif /* HAVE_DNSSD || HAVE_AVAHI */
82cc1f9a
MS
464 servername = con->servername;
465
466 /*
467 * Run the "certtool" command to generate a self-signed certificate...
468 */
469
470 if (!cupsFileFind("certtool", getenv("PATH"), 1, command, sizeof(command)))
471 {
472 cupsdLogMessage(CUPSD_LOG_ERROR,
473 "No SSL certificate and certtool command not found!");
474 return (0);
475 }
476
477 /*
478 * Create a file with the certificate information fields...
479 *
480 * Note: This assumes that the default questions are asked by the certtool
481 * command...
482 */
483
484 if ((fp = cupsTempFile2(infofile, sizeof(infofile))) == NULL)
485 {
486 cupsdLogMessage(CUPSD_LOG_ERROR,
487 "Unable to create certificate information file %s - %s",
488 infofile, strerror(errno));
489 return (0);
490 }
491
492 cupsFilePrintf(fp,
493 "%s\n" /* Enter key and certificate label */
494 "r\n" /* Generate RSA key pair */
495 "2048\n" /* Key size in bits */
496 "y\n" /* OK (y = yes) */
497 "b\n" /* Usage (b=signing/encryption) */
498 "s\n" /* Sign with SHA1 */
499 "y\n" /* OK (y = yes) */
500 "%s\n" /* Common name */
501 "\n" /* Country (default) */
502 "\n" /* Organization (default) */
503 "\n" /* Organizational unit (default) */
504 "\n" /* State/Province (default) */
505 "%s\n" /* Email address */
506 "y\n", /* OK (y = yes) */
507 servername, servername, ServerAdmin);
508 cupsFileClose(fp);
509
510 cupsdLogMessage(CUPSD_LOG_INFO,
511 "Generating SSL server key and certificate...");
512
513 snprintf(keychain, sizeof(keychain), "k=%s", ServerCertificate);
514
515 argv[0] = "certtool";
516 argv[1] = "c";
517 argv[2] = keychain;
518 argv[3] = NULL;
519
520 cupsdLoadEnv(envp, MAX_ENV);
521
522 infofd = open(infofile, O_RDONLY);
523
524 if (!cupsdStartProcess(command, argv, envp, infofd, -1, -1, -1, -1, 1, NULL,
525 NULL, &pid))
526 {
527 close(infofd);
528 unlink(infofile);
529 return (0);
530 }
531
532 close(infofd);
533 unlink(infofile);
534
535 while (waitpid(pid, &status, 0) < 0)
536 if (errno != EINTR)
537 {
538 status = 1;
539 break;
540 }
541
542 cupsdFinishProcess(pid, command, sizeof(command), NULL);
543
544 if (status)
545 {
546 if (WIFEXITED(status))
547 cupsdLogMessage(CUPSD_LOG_ERROR,
548 "Unable to create SSL server key and certificate - "
549 "the certtool command stopped with status %d!",
550 WEXITSTATUS(status));
551 else
552 cupsdLogMessage(CUPSD_LOG_ERROR,
553 "Unable to create SSL server key and certificate - "
554 "the certtool command crashed on signal %d!",
555 WTERMSIG(status));
556 }
557 else
558 {
559 cupsdLogMessage(CUPSD_LOG_INFO,
560 "Created SSL server certificate file \"%s\"...",
561 ServerCertificate);
562 }
563
564 return (!status);
565}
566
567
568/*
569 * End of "$Id$".
570 */