4 * TLS support code for the CUPS scheduler on OS X.
6 * Copyright 2007-2013 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
);
44 con
->http
->tls
= NULL
;
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.",
135 con
->http
->hostname
);
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 # ifdef HAVE_SECGENERATESELFSIGNEDCERTIFICATE
304 int status
= 0; /* Return status */
305 OSStatus err
; /* Error code (if any) */
306 # if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
307 char localname
[1024];/* Local hostname */
308 # endif /* HAVE_DNSSD || HAVE_AVAHI */
309 const char *servername
; /* Name of server in cert */
310 CFStringRef cfservername
= NULL
;
311 /* CF string for server name */
312 SecIdentityRef ident
= NULL
; /* Identity */
313 SecKeyRef publicKey
= NULL
,
317 CFMutableDictionaryRef keyParams
= NULL
;
318 /* Key generation parameters */
321 cupsdLogMessage(CUPSD_LOG_INFO
,
322 "Generating SSL server key and certificate.");
324 # if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
325 if (con
->servername
&& isdigit(con
->servername
[0] & 255) && DNSSDHostName
)
327 snprintf(localname
, sizeof(localname
), "%s.local", DNSSDHostName
);
328 servername
= localname
;
331 # endif /* HAVE_DNSSD || HAVE_AVAHI */
332 servername
= con
->servername
;
334 cfservername
= CFStringCreateWithCString(kCFAllocatorDefault
, servername
,
335 kCFStringEncodingUTF8
);
340 * Create a public/private key pair...
343 keyParams
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
344 &kCFTypeDictionaryKeyCallBacks
,
345 &kCFTypeDictionaryValueCallBacks
);
349 CFDictionaryAddValue(keyParams
, kSecAttrKeyType
, kSecAttrKeyTypeRSA
);
350 CFDictionaryAddValue(keyParams
, kSecAttrKeySizeInBits
, CFSTR("2048"));
351 CFDictionaryAddValue(keyParams
, kSecAttrLabel
,
352 CFSTR("CUPS Self-Signed Certificate"));
354 err
= SecKeyGeneratePair(keyParams
, &publicKey
, &privateKey
);
357 cupsdLogMessage(CUPSD_LOG_DEBUG
, "SecKeyGeneratePair returned %ld.",
363 * Create a self-signed certificate using the public/private key pair...
366 CFIndex usageInt
= kSecKeyUsageAll
;
367 CFNumberRef usage
= CFNumberCreate(alloc
, kCFNumberCFIndexType
, &usageInt
);
368 CFDictionaryRef certParams
= CFDictionaryCreateMutable(kCFAllocatorDefault
,
369 kSecCSRBasicContraintsPathLen
, CFINT(0),
370 kSecSubjectAltName
, cfservername
,
371 kSecCertificateKeyUsage
, usage
,
375 const void *ca_o
[] = { kSecOidOrganization
, CFSTR("") };
376 const void *ca_cn
[] = { kSecOidCommonName
, cfservername
};
377 CFArrayRef ca_o_dn
= CFArrayCreate(kCFAllocatorDefault
, ca_o
, 2, NULL
);
378 CFArrayRef ca_cn_dn
= CFArrayCreate(kCFAllocatorDefault
, ca_cn
, 2, NULL
);
379 const void *ca_dn_array
[2];
381 ca_dn_array
[0] = CFArrayCreate(kCFAllocatorDefault
, (const void **)&ca_o_dn
,
383 ca_dn_array
[1] = CFArrayCreate(kCFAllocatorDefault
, (const void **)&ca_cn_dn
,
386 CFArrayRef subject
= CFArrayCreate(kCFAllocatorDefault
, ca_dn_array
, 2,
388 SecCertificateRef cert
= SecGenerateSelfSignedCertificate(subject
, certParams
,
392 CFRelease(certParams
);
396 cupsdLogMessage(CUPSD_LOG_DEBUG
, "SecGenerateSelfSignedCertificate failed.");
400 ident
= SecIdentityCreate(kCFAllocatorDefault
, cert
, privateKey
);
403 cupsdLogMessage(CUPSD_LOG_INFO
,
404 "Created SSL server certificate file \"%s\".",
408 * Cleanup and return...
414 CFRelease(cfservername
);
417 CFRelease(keyParams
);
426 CFRelease(publicKey
);
429 CFRelease(publicKey
);
432 cupsdLogMessage(CUPSD_LOG_ERROR
,
433 "Unable to create SSL server key and certificate.");
437 # else /* !HAVE_SECGENERATESELFSIGNEDCERTIFICATE */
438 int pid
, /* Process ID of command */
439 status
; /* Status of command */
440 char command
[1024], /* Command */
441 *argv
[4], /* Command-line arguments */
442 *envp
[MAX_ENV
+ 1], /* Environment variables */
443 keychain
[1024], /* Keychain argument */
444 infofile
[1024], /* Type-in information for cert */
445 # if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
446 localname
[1024], /* Local hostname */
447 # endif /* HAVE_DNSSD || HAVE_AVAHI */
448 *servername
; /* Name of server in cert */
449 cups_file_t
*fp
; /* Seed/info file */
450 int infofd
; /* Info file descriptor */
453 # if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
454 if (con
->servername
&& isdigit(con
->servername
[0] & 255) && DNSSDHostName
)
456 snprintf(localname
, sizeof(localname
), "%s.local", DNSSDHostName
);
457 servername
= localname
;
460 # endif /* HAVE_DNSSD || HAVE_AVAHI */
461 servername
= con
->servername
;
464 * Run the "certtool" command to generate a self-signed certificate...
467 if (!cupsFileFind("certtool", getenv("PATH"), 1, command
, sizeof(command
)))
469 cupsdLogMessage(CUPSD_LOG_ERROR
,
470 "No SSL certificate and certtool command not found.");
475 * Create a file with the certificate information fields...
477 * Note: This assumes that the default questions are asked by the certtool
481 if ((fp
= cupsTempFile2(infofile
, sizeof(infofile
))) == NULL
)
483 cupsdLogMessage(CUPSD_LOG_ERROR
,
484 "Unable to create certificate information file %s - %s",
485 infofile
, strerror(errno
));
490 "%s\n" /* Enter key and certificate label */
491 "r\n" /* Generate RSA key pair */
492 "2048\n" /* Key size in bits */
493 "y\n" /* OK (y = yes) */
494 "b\n" /* Usage (b=signing/encryption) */
495 "s\n" /* Sign with SHA1 */
496 "y\n" /* OK (y = yes) */
497 "%s\n" /* Common name */
498 "\n" /* Country (default) */
499 "\n" /* Organization (default) */
500 "\n" /* Organizational unit (default) */
501 "\n" /* State/Province (default) */
502 "%s\n" /* Email address */
503 "y\n", /* OK (y = yes) */
504 servername
, servername
, ServerAdmin
);
507 cupsdLogMessage(CUPSD_LOG_INFO
,
508 "Generating SSL server key and certificate.");
510 snprintf(keychain
, sizeof(keychain
), "k=%s", ServerCertificate
);
512 argv
[0] = "certtool";
517 cupsdLoadEnv(envp
, MAX_ENV
);
519 infofd
= open(infofile
, O_RDONLY
);
521 if (!cupsdStartProcess(command
, argv
, envp
, infofd
, -1, -1, -1, -1, 1, NULL
,
532 while (waitpid(pid
, &status
, 0) < 0)
539 cupsdFinishProcess(pid
, command
, sizeof(command
), NULL
);
543 if (WIFEXITED(status
))
544 cupsdLogMessage(CUPSD_LOG_ERROR
,
545 "Unable to create SSL server key and certificate - "
546 "the certtool command stopped with status %d.",
547 WEXITSTATUS(status
));
549 cupsdLogMessage(CUPSD_LOG_ERROR
,
550 "Unable to create SSL server key and certificate - "
551 "the certtool command crashed on signal %d.",
556 cupsdLogMessage(CUPSD_LOG_INFO
,
557 "Created SSL server certificate file \"%s\".",
562 # endif /* HAVE_SECGENERATESELFSIGNEDCERTIFICATE */