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