]>
Commit | Line | Data |
---|---|---|
2c85b752 MS |
1 | /* |
2 | * "$Id$" | |
3 | * | |
4 | * TLS support code for CUPS on OS X. | |
5 | * | |
6 | * Copyright 2007-2013 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 | * This file is subject to the Apple OS-Developed Software exception. | |
16 | */ | |
17 | ||
dafebafd | 18 | /**** This file is included from http.c ****/ |
2c85b752 MS |
19 | |
20 | /* | |
dafebafd | 21 | * Include necessary headers... |
2c85b752 MS |
22 | */ |
23 | ||
dafebafd MS |
24 | #include <spawn.h> |
25 | ||
26 | extern char **environ; | |
27 | ||
28 | ||
c0459938 MS |
29 | /* |
30 | * Local globals... | |
31 | */ | |
32 | ||
c0459938 MS |
33 | static int tls_auto_create = 0; |
34 | /* Auto-create self-signed certs? */ | |
35 | static char *tls_common_name = NULL; | |
36 | /* Default common name */ | |
41e0907c MS |
37 | static SecKeychainRef tls_keychain = NULL; |
38 | /* Server cert keychain */ | |
39 | static char *tls_keypath = NULL; | |
40 | /* Server cert keychain path */ | |
41 | static _cups_mutex_t tls_mutex = _CUPS_MUTEX_INITIALIZER; | |
42 | /* Mutex for keychain/certs */ | |
c0459938 MS |
43 | |
44 | ||
dafebafd MS |
45 | /* |
46 | * Local functions... | |
47 | */ | |
2c85b752 | 48 | |
41e0907c MS |
49 | static CFArrayRef http_cdsa_copy_server(const char *common_name); |
50 | static OSStatus http_cdsa_read(SSLConnectionRef connection, void *data, size_t *dataLength); | |
51 | static OSStatus http_cdsa_write(SSLConnectionRef connection, const void *data, size_t *dataLength); | |
2c85b752 MS |
52 | |
53 | ||
3af9ac9e MS |
54 | /* |
55 | * 'cupsMakeServerCredentials()' - Make a self-signed certificate and private key pair. | |
56 | * | |
57 | * @since CUPS 2.0@ | |
58 | */ | |
59 | ||
60 | int /* O - 1 on success, 0 on failure */ | |
61 | cupsMakeServerCredentials( | |
62 | const char *path, /* I - Path to keychain/directory */ | |
63 | const char *common_name, /* I - Common name */ | |
64 | int num_alt_names, /* I - Number of subject alternate names */ | |
65 | const char **alt_names, /* I - Subject Alternate Names */ | |
66 | time_t expiration_date) /* I - Expiration date */ | |
67 | { | |
41e0907c MS |
68 | #ifdef HAVE_SECGENERATESELFSIGNEDCERTIFICATE |
69 | int status = 0; /* Return status */ | |
70 | OSStatus err; /* Error code (if any) */ | |
71 | CFStringRef cfcommon_name = NULL; | |
72 | /* CF string for server name */ | |
73 | SecIdentityRef ident = NULL; /* Identity */ | |
74 | SecKeyRef publicKey = NULL, | |
75 | /* Public key */ | |
76 | privateKey = NULL; | |
77 | /* Private key */ | |
78 | CFMutableDictionaryRef keyParams = NULL; | |
79 | /* Key generation parameters */ | |
80 | ||
81 | ||
3af9ac9e MS |
82 | (void)num_alt_names; |
83 | (void)alt_names; | |
84 | (void)expiration_date; | |
85 | ||
41e0907c MS |
86 | cfcommon_name = CFStringCreateWithCString(kCFAllocatorDefault, common_name, |
87 | kCFStringEncodingUTF8); | |
88 | if (!cfcommon_name) | |
89 | goto cleanup; | |
90 | ||
91 | /* | |
92 | * Create a public/private key pair... | |
93 | */ | |
94 | ||
95 | keyParams = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, | |
96 | &kCFTypeDictionaryKeyCallBacks, | |
97 | &kCFTypeDictionaryValueCallBacks); | |
98 | if (!keyParams) | |
99 | goto cleanup; | |
100 | ||
101 | CFDictionaryAddValue(keyParams, kSecAttrKeyType, kSecAttrKeyTypeRSA); | |
102 | CFDictionaryAddValue(keyParams, kSecAttrKeySizeInBits, CFSTR("2048")); | |
103 | CFDictionaryAddValue(keyParams, kSecAttrLabel, | |
104 | CFSTR("CUPS Self-Signed Certificate")); | |
105 | ||
106 | err = SecKeyGeneratePair(keyParams, &publicKey, &privateKey); | |
107 | if (err != noErr) | |
108 | goto cleanup; | |
109 | ||
110 | /* | |
111 | * Create a self-signed certificate using the public/private key pair... | |
112 | */ | |
113 | ||
114 | CFIndex usageInt = kSecKeyUsageAll; | |
115 | CFNumberRef usage = CFNumberCreate(alloc, kCFNumberCFIndexType, &usageInt); | |
116 | CFDictionaryRef certParams = CFDictionaryCreateMutable(kCFAllocatorDefault, | |
117 | kSecCSRBasicContraintsPathLen, CFINT(0), kSecSubjectAltName, cfcommon_name, kSecCertificateKeyUsage, usage, NULL, NULL); | |
118 | CFRelease(usage); | |
119 | ||
120 | const void *ca_o[] = { kSecOidOrganization, CFSTR("") }; | |
121 | const void *ca_cn[] = { kSecOidCommonName, cfcommon_name }; | |
122 | CFArrayRef ca_o_dn = CFArrayCreate(kCFAllocatorDefault, ca_o, 2, NULL); | |
123 | CFArrayRef ca_cn_dn = CFArrayCreate(kCFAllocatorDefault, ca_cn, 2, NULL); | |
124 | const void *ca_dn_array[2]; | |
125 | ||
126 | ca_dn_array[0] = CFArrayCreate(kCFAllocatorDefault, (const void **)&ca_o_dn, 1, NULL); | |
127 | ca_dn_array[1] = CFArrayCreate(kCFAllocatorDefault, (const void **)&ca_cn_dn, 1, NULL); | |
128 | ||
129 | CFArrayRef subject = CFArrayCreate(kCFAllocatorDefault, ca_dn_array, 2, NULL); | |
130 | SecCertificateRef cert = SecGenerateSelfSignedCertificate(subject, certParams, publicKey, privateKey); | |
131 | CFRelease(subject); | |
132 | CFRelease(certParams); | |
133 | ||
134 | if (!cert) | |
135 | goto cleanup; | |
136 | ||
137 | ident = SecIdentityCreate(kCFAllocatorDefault, cert, privateKey); | |
138 | ||
139 | if (ident) | |
140 | status = 1; | |
141 | ||
142 | /* | |
143 | * Cleanup and return... | |
144 | */ | |
145 | ||
146 | cleanup: | |
147 | ||
148 | if (cfcommon_name) | |
149 | CFRelease(cfcommon_name); | |
150 | ||
151 | if (keyParams) | |
152 | CFRelease(keyParams); | |
153 | ||
154 | if (ident) | |
155 | CFRelease(ident); | |
156 | ||
157 | if (cert) | |
158 | CFRelease(cert); | |
159 | ||
160 | if (publicKey) | |
161 | CFRelease(publicKey); | |
162 | ||
163 | if (privateKey) | |
164 | CFRelease(publicKey); | |
165 | ||
166 | return (status); | |
167 | ||
168 | #else /* !HAVE_SECGENERATESELFSIGNEDCERTIFICATE */ | |
169 | int pid, /* Process ID of command */ | |
170 | status; /* Status of command */ | |
171 | char command[1024], /* Command */ | |
172 | *argv[4], /* Command-line arguments */ | |
173 | keychain[1024], /* Keychain argument */ | |
174 | infofile[1024]; /* Type-in information for cert */ | |
175 | cups_file_t *fp; /* Seed/info file */ | |
176 | ||
177 | ||
178 | (void)num_alt_names; | |
179 | (void)alt_names; | |
180 | (void)expiration_date; | |
181 | ||
182 | /* | |
183 | * Run the "certtool" command to generate a self-signed certificate... | |
184 | */ | |
185 | ||
186 | if (!cupsFileFind("certtool", getenv("PATH"), 1, command, sizeof(command))) | |
187 | return (-1); | |
188 | ||
189 | /* | |
190 | * Create a file with the certificate information fields... | |
191 | * | |
192 | * Note: This assumes that the default questions are asked by the certtool | |
193 | * command... | |
194 | */ | |
195 | ||
196 | if ((fp = cupsTempFile2(infofile, sizeof(infofile))) == NULL) | |
197 | return (-1); | |
198 | ||
199 | cupsFilePrintf(fp, | |
200 | "CUPS Self-Signed Certificate\n" | |
201 | /* Enter key and certificate label */ | |
202 | "r\n" /* Generate RSA key pair */ | |
203 | "2048\n" /* Key size in bits */ | |
204 | "y\n" /* OK (y = yes) */ | |
205 | "b\n" /* Usage (b=signing/encryption) */ | |
206 | "s\n" /* Sign with SHA1 */ | |
207 | "y\n" /* OK (y = yes) */ | |
208 | "%s\n" /* Common name */ | |
209 | "\n" /* Country (default) */ | |
210 | "\n" /* Organization (default) */ | |
211 | "\n" /* Organizational unit (default) */ | |
212 | "\n" /* State/Province (default) */ | |
213 | "\n" /* Email address */ | |
214 | "y\n", /* OK (y = yes) */ | |
215 | common_name); | |
216 | cupsFileClose(fp); | |
217 | ||
218 | snprintf(keychain, sizeof(keychain), "k=%s", path); | |
219 | ||
220 | argv[0] = "certtool"; | |
221 | argv[1] = "c"; | |
222 | argv[2] = keychain; | |
223 | argv[3] = NULL; | |
224 | ||
225 | posix_spawn_file_actions_t actions; /* File actions */ | |
226 | ||
227 | posix_spawn_file_actions_init(&actions); | |
228 | posix_spawn_file_actions_addclose(&actions, 0); | |
229 | posix_spawn_file_actions_addopen(&actions, 0, infofile, O_RDONLY, 0); | |
230 | ||
231 | if (posix_spawn(&pid, command, &actions, NULL, argv, environ)) | |
232 | { | |
233 | unlink(infofile); | |
234 | return (-1); | |
235 | } | |
236 | ||
237 | posix_spawn_file_actions_destroy(&actions); | |
238 | ||
239 | unlink(infofile); | |
240 | ||
241 | while (waitpid(pid, &status, 0) < 0) | |
242 | if (errno != EINTR) | |
243 | { | |
244 | status = -1; | |
245 | break; | |
246 | } | |
247 | ||
248 | return (!status); | |
249 | #endif /* HAVE_SECGENERATESELFSIGNEDCERTIFICATE */ | |
3af9ac9e MS |
250 | } |
251 | ||
252 | ||
253 | /* | |
254 | * 'cupsSetServerCredentials()' - Set the default server credentials. | |
255 | * | |
256 | * Note: The server credentials are used by all threads in the running process. | |
257 | * This function is threadsafe. | |
258 | * | |
259 | * @since CUPS 2.0@ | |
260 | */ | |
261 | ||
262 | int /* O - 1 on success, 0 on failure */ | |
263 | cupsSetServerCredentials( | |
264 | const char *path, /* I - Path to keychain/directory */ | |
265 | const char *common_name, /* I - Default common name for server */ | |
266 | int auto_create) /* I - 1 = automatically create self-signed certificates */ | |
267 | { | |
c0459938 | 268 | SecKeychainRef keychain = NULL;/* Temporary keychain */ |
3af9ac9e | 269 | |
c0459938 MS |
270 | |
271 | if (SecKeychainOpen(path, &keychain) != noErr) | |
272 | { | |
273 | /* TODO: Set cups last error string */ | |
274 | return (0); | |
275 | } | |
276 | ||
277 | _cupsMutexLock(&tls_mutex); | |
278 | ||
279 | /* | |
280 | * Close any keychain that is currently open... | |
281 | */ | |
282 | ||
283 | if (tls_keychain) | |
284 | CFRelease(tls_keychain); | |
285 | ||
41e0907c MS |
286 | if (tls_keypath) |
287 | _cupsStrFree(tls_keypath); | |
288 | ||
c0459938 MS |
289 | if (tls_common_name) |
290 | _cupsStrFree(tls_common_name); | |
291 | ||
292 | /* | |
293 | * Save the new keychain... | |
294 | */ | |
295 | ||
296 | tls_keychain = keychain; | |
41e0907c | 297 | tls_keypath = _cupsStrAlloc(path); |
c0459938 MS |
298 | tls_auto_create = auto_create; |
299 | tls_common_name = _cupsStrAlloc(common_name); | |
300 | ||
301 | _cupsMutexUnlock(&tls_mutex); | |
302 | ||
303 | return (1); | |
3af9ac9e MS |
304 | } |
305 | ||
2c85b752 MS |
306 | |
307 | /* | |
308 | * 'httpCopyCredentials()' - Copy the credentials associated with the peer in | |
309 | * an encrypted connection. | |
310 | * | |
311 | * @since CUPS 1.5/OS X 10.7@ | |
312 | */ | |
313 | ||
314 | int /* O - Status of call (0 = success) */ | |
315 | httpCopyCredentials( | |
316 | http_t *http, /* I - Connection to server */ | |
317 | cups_array_t **credentials) /* O - Array of credentials */ | |
318 | { | |
319 | OSStatus error; /* Error code */ | |
320 | SecTrustRef peerTrust; /* Peer trust reference */ | |
321 | CFIndex count; /* Number of credentials */ | |
322 | SecCertificateRef secCert; /* Certificate reference */ | |
323 | CFDataRef data; /* Certificate data */ | |
324 | int i; /* Looping var */ | |
325 | ||
326 | ||
327 | if (credentials) | |
328 | *credentials = NULL; | |
329 | ||
330 | if (!http || !http->tls || !credentials) | |
331 | return (-1); | |
332 | ||
333 | if (!(error = SSLCopyPeerTrust(http->tls, &peerTrust)) && peerTrust) | |
334 | { | |
335 | if ((*credentials = cupsArrayNew(NULL, NULL)) != NULL) | |
336 | { | |
337 | count = SecTrustGetCertificateCount(peerTrust); | |
338 | ||
339 | for (i = 0; i < count; i ++) | |
340 | { | |
341 | secCert = SecTrustGetCertificateAtIndex(peerTrust, i); | |
342 | if ((data = SecCertificateCopyData(secCert))) | |
343 | { | |
344 | httpAddCredential(*credentials, CFDataGetBytePtr(data), | |
345 | CFDataGetLength(data)); | |
346 | CFRelease(data); | |
347 | } | |
348 | } | |
349 | } | |
350 | ||
351 | CFRelease(peerTrust); | |
352 | } | |
353 | ||
354 | return (error); | |
355 | } | |
356 | ||
357 | ||
358 | /* | |
359 | * '_httpCreateCredentials()' - Create credentials in the internal format. | |
360 | */ | |
361 | ||
362 | http_tls_credentials_t /* O - Internal credentials */ | |
363 | _httpCreateCredentials( | |
364 | cups_array_t *credentials) /* I - Array of credentials */ | |
365 | { | |
366 | CFMutableArrayRef peerCerts; /* Peer credentials reference */ | |
367 | SecCertificateRef secCert; /* Certificate reference */ | |
368 | CFDataRef data; /* Credential data reference */ | |
369 | http_credential_t *credential; /* Credential data */ | |
370 | ||
371 | ||
372 | if (!credentials) | |
373 | return (NULL); | |
374 | ||
375 | if ((peerCerts = CFArrayCreateMutable(kCFAllocatorDefault, | |
376 | cupsArrayCount(credentials), | |
377 | &kCFTypeArrayCallBacks)) == NULL) | |
378 | return (NULL); | |
379 | ||
380 | for (credential = (http_credential_t *)cupsArrayFirst(credentials); | |
381 | credential; | |
382 | credential = (http_credential_t *)cupsArrayNext(credentials)) | |
383 | { | |
384 | if ((data = CFDataCreate(kCFAllocatorDefault, credential->data, | |
385 | credential->datalen))) | |
386 | { | |
387 | if ((secCert = SecCertificateCreateWithData(kCFAllocatorDefault, data)) | |
388 | != NULL) | |
389 | { | |
390 | CFArrayAppendValue(peerCerts, secCert); | |
391 | CFRelease(secCert); | |
392 | } | |
393 | ||
394 | CFRelease(data); | |
395 | } | |
396 | } | |
397 | ||
398 | return (peerCerts); | |
399 | } | |
400 | ||
401 | ||
3af9ac9e MS |
402 | /* |
403 | * 'httpCredentialsAreTrusted()' - Return whether the credentials are trusted. | |
404 | * | |
405 | * @since CUPS 2.0@ | |
406 | */ | |
407 | ||
408 | int /* O - 1 if trusted, 0 if not/unknown */ | |
409 | httpCredentialsAreTrusted( | |
410 | cups_array_t *credentials) /* I - Credentials */ | |
411 | { | |
412 | (void)credentials; | |
413 | ||
414 | return (0); | |
415 | } | |
416 | ||
417 | ||
418 | /* | |
419 | * 'httpCredentialsGetExpiration()' - Return the expiration date of the credentials. | |
420 | * | |
421 | * @since CUPS 2.0@ | |
422 | */ | |
423 | ||
424 | time_t /* O - Expiration date of credentials */ | |
425 | httpCredentialsGetExpiration( | |
426 | cups_array_t *credentials) /* I - Credentials */ | |
427 | { | |
428 | (void)credentials; | |
429 | ||
430 | return (0); | |
431 | } | |
432 | ||
433 | ||
434 | /* | |
435 | * 'httpCredentialsIsValidName()' - Return whether the credentials are valid for the given name. | |
436 | * | |
437 | * @since CUPS 2.0@ | |
438 | */ | |
439 | ||
440 | int /* O - 1 if valid, 0 otherwise */ | |
441 | httpCredentialsIsValidName( | |
442 | cups_array_t *credentials, /* I - Credentials */ | |
443 | const char *common_name) /* I - Name to check */ | |
444 | { | |
445 | (void)credentials; | |
446 | (void)common_name; | |
447 | ||
448 | return (0); | |
449 | } | |
450 | ||
451 | ||
72d05bc9 MS |
452 | /* |
453 | * 'httpCredentialsString()' - Return a string representing the credentials. | |
454 | * | |
455 | * @since CUPS 2.0@ | |
456 | */ | |
457 | ||
458 | size_t /* O - Total size of credentials string */ | |
459 | httpCredentialsString( | |
460 | cups_array_t *credentials, /* I - Credentials */ | |
461 | char *buffer, /* I - Buffer or @code NULL@ */ | |
462 | size_t bufsize) /* I - Size of buffer */ | |
463 | { | |
464 | (void)credentials; | |
465 | ||
466 | if (buffer && bufsize > 0) | |
467 | *buffer = '\0'; | |
468 | ||
469 | return (1); | |
470 | } | |
471 | ||
472 | ||
2c85b752 MS |
473 | /* |
474 | * '_httpFreeCredentials()' - Free internal credentials. | |
475 | */ | |
476 | ||
477 | void | |
478 | _httpFreeCredentials( | |
479 | http_tls_credentials_t credentials) /* I - Internal credentials */ | |
480 | { | |
481 | if (!credentials) | |
482 | return; | |
483 | ||
484 | CFRelease(credentials); | |
485 | } | |
486 | ||
487 | ||
72d05bc9 MS |
488 | /* |
489 | * 'httpLoadCredentials()' - Load X.509 credentials from a keychain file. | |
490 | * | |
491 | * @since CUPS 2.0@ | |
492 | */ | |
493 | ||
dafebafd | 494 | int /* O - 0 on success, -1 on error */ |
72d05bc9 MS |
495 | httpLoadCredentials( |
496 | const char *path, /* I - Keychain/PKCS#12 path */ | |
497 | cups_array_t **credentials, /* IO - Credentials */ | |
498 | const char *common_name) /* I - Common name for credentials */ | |
499 | { | |
f4047845 MS |
500 | (void)path; |
501 | (void)credentials; | |
502 | (void)common_name; | |
503 | ||
504 | return (-1); | |
505 | ||
506 | #if 0 | |
dafebafd MS |
507 | OSStatus err; /* Error info */ |
508 | SecKeychainRef keychain = NULL;/* Keychain reference */ | |
509 | SecIdentitySearchRef search = NULL; /* Search reference */ | |
510 | SecIdentityRef identity = NULL;/* Identity */ | |
511 | CFArrayRef certificates = NULL; | |
512 | /* Certificate array */ | |
513 | SecPolicyRef policy = NULL; /* Policy ref */ | |
514 | CFStringRef cfcommon_name = NULL; | |
515 | /* Server name */ | |
516 | CFMutableDictionaryRef query = NULL; /* Query qualifiers */ | |
517 | CFArrayRef list = NULL; /* Keychain list */ | |
518 | ||
519 | ||
520 | if ((err = SecKeychainOpen(path, &keychain))) | |
521 | goto cleanup; | |
522 | ||
523 | cfcommon_name = CFStringCreateWithCString(kCFAllocatorDefault, common_name, kCFStringEncodingUTF8); | |
524 | ||
525 | policy = SecPolicyCreateSSL(1, cfcommon_name); | |
526 | ||
527 | if (cfcommon_name) | |
528 | CFRelease(cfcommon_name); | |
529 | ||
530 | if (!policy) | |
531 | goto cleanup; | |
532 | ||
533 | if (!(query = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks))) | |
534 | goto cleanup; | |
535 | ||
536 | list = CFArrayCreate(kCFAllocatorDefault, (const void **)&keychain, 1, | |
537 | &kCFTypeArrayCallBacks); | |
538 | ||
539 | CFDictionaryAddValue(query, kSecClass, kSecClassIdentity); | |
540 | CFDictionaryAddValue(query, kSecMatchPolicy, policy); | |
541 | CFDictionaryAddValue(query, kSecReturnRef, kCFBooleanTrue); | |
542 | CFDictionaryAddValue(query, kSecMatchLimit, kSecMatchLimitOne); | |
543 | CFDictionaryAddValue(query, kSecMatchSearchList, list); | |
544 | ||
545 | CFRelease(list); | |
546 | ||
547 | err = SecItemCopyMatching(query, (CFTypeRef *)&identity); | |
548 | ||
549 | if (err) | |
550 | goto cleanup; | |
551 | ||
552 | if (CFGetTypeID(identity) != SecIdentityGetTypeID()) | |
553 | goto cleanup; | |
554 | ||
555 | if ((certificates = CFArrayCreate(NULL, (const void **)&identity, 1, &kCFTypeArrayCallBacks)) == NULL) | |
556 | goto cleanup; | |
557 | ||
558 | cleanup : | |
559 | ||
560 | if (keychain) | |
561 | CFRelease(keychain); | |
562 | if (search) | |
563 | CFRelease(search); | |
564 | if (identity) | |
565 | CFRelease(identity); | |
566 | ||
567 | if (policy) | |
568 | CFRelease(policy); | |
569 | if (query) | |
570 | CFRelease(query); | |
571 | ||
572 | return (certificates); | |
f4047845 | 573 | #endif /* 0 */ |
72d05bc9 MS |
574 | } |
575 | ||
576 | ||
f4047845 | 577 | #if 0 |
72d05bc9 | 578 | /* |
3af9ac9e | 579 | * 'cupsMakeCredentials()' - Create self-signed credentials for the given |
72d05bc9 MS |
580 | * name. |
581 | * | |
582 | * @since CUPS 2.0@ | |
583 | */ | |
584 | ||
dafebafd | 585 | int /* O - 0 on success, -1 on error */ |
3af9ac9e | 586 | cupsMakeCredentials( |
dafebafd | 587 | const char *path, /* I - Keychain/PKCS#12 path */ |
72d05bc9 MS |
588 | cups_array_t **credentials, /* O - Credentials */ |
589 | const char *common_name) /* I - Common name for X.509 cert */ | |
590 | { | |
dafebafd MS |
591 | # ifdef HAVE_SECGENERATESELFSIGNEDCERTIFICATE |
592 | int status = -1; /* Return status */ | |
593 | OSStatus err; /* Error code (if any) */ | |
594 | CFStringRef cfcommon_name = NULL; | |
595 | /* CF string for server name */ | |
596 | SecIdentityRef ident = NULL; /* Identity */ | |
597 | SecKeyRef publicKey = NULL, | |
598 | /* Public key */ | |
599 | privateKey = NULL; | |
600 | /* Private key */ | |
601 | CFMutableDictionaryRef keyParams = NULL; | |
602 | /* Key generation parameters */ | |
603 | ||
72d05bc9 MS |
604 | |
605 | if (credentials) | |
606 | *credentials = NULL; | |
607 | ||
dafebafd MS |
608 | cfcommon_name = CFStringCreateWithCString(kCFAllocatorDefault, servername, |
609 | kCFStringEncodingUTF8); | |
610 | if (!cfcommon_name) | |
611 | goto cleanup; | |
612 | ||
613 | /* | |
614 | * Create a public/private key pair... | |
615 | */ | |
616 | ||
617 | keyParams = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, | |
618 | &kCFTypeDictionaryKeyCallBacks, | |
619 | &kCFTypeDictionaryValueCallBacks); | |
620 | if (!keyParams) | |
621 | goto cleanup; | |
622 | ||
623 | CFDictionaryAddValue(keyParams, kSecAttrKeyType, kSecAttrKeyTypeRSA); | |
624 | CFDictionaryAddValue(keyParams, kSecAttrKeySizeInBits, CFSTR("2048")); | |
625 | CFDictionaryAddValue(keyParams, kSecAttrLabel, | |
626 | CFSTR("CUPS Self-Signed Certificate")); | |
627 | ||
628 | err = SecKeyGeneratePair(keyParams, &publicKey, &privateKey); | |
629 | if (err != noErr) | |
630 | goto cleanup; | |
631 | ||
632 | /* | |
633 | * Create a self-signed certificate using the public/private key pair... | |
634 | */ | |
635 | ||
636 | CFIndex usageInt = kSecKeyUsageAll; | |
637 | CFNumberRef usage = CFNumberCreate(alloc, kCFNumberCFIndexType, &usageInt); | |
638 | CFDictionaryRef certParams = CFDictionaryCreateMutable(kCFAllocatorDefault, | |
639 | kSecCSRBasicContraintsPathLen, CFINT(0), kSecSubjectAltName, cfcommon_name, kSecCertificateKeyUsage, usage, NULL, NULL); | |
640 | CFRelease(usage); | |
641 | ||
642 | const void *ca_o[] = { kSecOidOrganization, CFSTR("") }; | |
643 | const void *ca_cn[] = { kSecOidCommonName, cfcommon_name }; | |
644 | CFArrayRef ca_o_dn = CFArrayCreate(kCFAllocatorDefault, ca_o, 2, NULL); | |
645 | CFArrayRef ca_cn_dn = CFArrayCreate(kCFAllocatorDefault, ca_cn, 2, NULL); | |
646 | const void *ca_dn_array[2]; | |
647 | ||
648 | ca_dn_array[0] = CFArrayCreate(kCFAllocatorDefault, (const void **)&ca_o_dn, 1, NULL); | |
649 | ca_dn_array[1] = CFArrayCreate(kCFAllocatorDefault, (const void **)&ca_cn_dn, 1, NULL); | |
650 | ||
651 | CFArrayRef subject = CFArrayCreate(kCFAllocatorDefault, ca_dn_array, 2, NULL); | |
652 | SecCertificateRef cert = SecGenerateSelfSignedCertificate(subject, certParams, publicKey, privateKey); | |
653 | CFRelease(subject); | |
654 | CFRelease(certParams); | |
655 | ||
656 | if (!cert) | |
657 | goto cleanup; | |
658 | ||
659 | ident = SecIdentityCreate(kCFAllocatorDefault, cert, privateKey); | |
660 | ||
661 | if (ident) | |
662 | status = 0; | |
663 | ||
664 | /* | |
665 | * Cleanup and return... | |
666 | */ | |
667 | ||
668 | cleanup: | |
669 | ||
670 | if (cfcommon_name) | |
671 | CFRelease(cfcommon_name); | |
672 | ||
673 | if (keyParams) | |
674 | CFRelease(keyParams); | |
675 | ||
676 | if (ident) | |
677 | CFRelease(ident); | |
678 | ||
679 | if (cert) | |
680 | CFRelease(cert); | |
681 | ||
682 | if (publicKey) | |
683 | CFRelease(publicKey); | |
684 | ||
685 | if (privateKey) | |
686 | CFRelease(publicKey); | |
687 | ||
688 | return (status); | |
689 | ||
690 | # else /* !HAVE_SECGENERATESELFSIGNEDCERTIFICATE */ | |
691 | int pid, /* Process ID of command */ | |
692 | status; /* Status of command */ | |
693 | char command[1024], /* Command */ | |
694 | *argv[4], /* Command-line arguments */ | |
695 | keychain[1024], /* Keychain argument */ | |
696 | infofile[1024]; /* Type-in information for cert */ | |
697 | cups_file_t *fp; /* Seed/info file */ | |
698 | ||
699 | ||
700 | /* | |
701 | * Run the "certtool" command to generate a self-signed certificate... | |
702 | */ | |
703 | ||
704 | if (!cupsFileFind("certtool", getenv("PATH"), 1, command, sizeof(command))) | |
705 | return (-1); | |
706 | ||
707 | /* | |
708 | * Create a file with the certificate information fields... | |
709 | * | |
710 | * Note: This assumes that the default questions are asked by the certtool | |
711 | * command... | |
712 | */ | |
713 | ||
714 | if ((fp = cupsTempFile2(infofile, sizeof(infofile))) == NULL) | |
715 | return (-1); | |
716 | ||
717 | cupsFilePrintf(fp, | |
718 | "%s\n" /* Enter key and certificate label */ | |
719 | "r\n" /* Generate RSA key pair */ | |
720 | "2048\n" /* Key size in bits */ | |
721 | "y\n" /* OK (y = yes) */ | |
722 | "b\n" /* Usage (b=signing/encryption) */ | |
723 | "s\n" /* Sign with SHA1 */ | |
724 | "y\n" /* OK (y = yes) */ | |
725 | "%s\n" /* Common name */ | |
726 | "\n" /* Country (default) */ | |
727 | "\n" /* Organization (default) */ | |
728 | "\n" /* Organizational unit (default) */ | |
729 | "\n" /* State/Province (default) */ | |
730 | "%s\n" /* Email address */ | |
731 | "y\n", /* OK (y = yes) */ | |
732 | common_name, common_name, ""); | |
733 | cupsFileClose(fp); | |
734 | ||
735 | snprintf(keychain, sizeof(keychain), "k=%s", path); | |
736 | ||
737 | argv[0] = "certtool"; | |
738 | argv[1] = "c"; | |
739 | argv[2] = keychain; | |
740 | argv[3] = NULL; | |
741 | ||
742 | posix_spawn_file_actions_t actions; /* File actions */ | |
743 | ||
744 | posix_spawn_file_actions_init(&actions); | |
745 | posix_spawn_file_actions_addclose(&actions, 0); | |
746 | posix_spawn_file_actions_addopen(&actions, 0, infofile, O_RDONLY, 0); | |
747 | ||
748 | if (posix_spawn(&pid, command, &actions, NULL, argv, environ)) | |
749 | { | |
750 | unlink(infofile); | |
751 | return (-1); | |
752 | } | |
753 | ||
754 | posix_spawn_file_actions_destroy(&actions); | |
755 | ||
756 | unlink(infofile); | |
757 | ||
758 | while (waitpid(pid, &status, 0) < 0) | |
759 | if (errno != EINTR) | |
760 | { | |
761 | status = -1; | |
762 | break; | |
763 | } | |
764 | ||
765 | if (status) | |
766 | return (-1); | |
767 | ||
768 | # endif /* HAVE_SECGENERATESELFSIGNEDCERTIFICATE */ | |
769 | ||
770 | return (httpLoadCredentials(path, credentials, common_name)); | |
72d05bc9 | 771 | } |
f4047845 | 772 | #endif /* 0 */ |
72d05bc9 MS |
773 | |
774 | ||
775 | /* | |
776 | * 'httpSaveCredentials()' - Save X.509 credentials to a keychain file. | |
777 | * | |
778 | * @since CUPS 2.0@ | |
779 | */ | |
780 | ||
781 | int /* O - -1 on error, 0 on success */ | |
782 | httpSaveCredentials( | |
783 | const char *path, /* I - Keychain/PKCS#12 path */ | |
784 | cups_array_t *credentials, /* I - Credentials */ | |
785 | const char *common_name) /* I - Common name for credentials */ | |
786 | { | |
787 | (void)path; | |
788 | (void)credentials; | |
789 | (void)common_name; | |
790 | ||
791 | return (-1); | |
792 | } | |
793 | ||
794 | ||
41e0907c MS |
795 | /* |
796 | * 'http_cdsa_copy_server()' - Find and copy server credentials from the keychain. | |
797 | */ | |
798 | ||
799 | static CFArrayRef /* O - Array of certificates or NULL */ | |
800 | http_cdsa_copy_server( | |
801 | const char *common_name) /* I - Server's hostname */ | |
802 | { | |
803 | OSStatus err; /* Error info */ | |
804 | SecIdentitySearchRef search = NULL; /* Search reference */ | |
805 | SecIdentityRef identity = NULL;/* Identity */ | |
806 | CFArrayRef certificates = NULL; | |
807 | /* Certificate array */ | |
808 | SecPolicyRef policy = NULL; /* Policy ref */ | |
809 | CFStringRef cfcommon_name = NULL; | |
810 | /* Server name */ | |
811 | CFMutableDictionaryRef query = NULL; /* Query qualifiers */ | |
812 | CFArrayRef list = NULL; /* Keychain list */ | |
813 | ||
814 | ||
815 | cfcommon_name = CFStringCreateWithCString(kCFAllocatorDefault, common_name, kCFStringEncodingUTF8); | |
816 | ||
817 | policy = SecPolicyCreateSSL(1, cfcommon_name); | |
818 | ||
819 | if (cfcommon_name) | |
820 | CFRelease(cfcommon_name); | |
821 | ||
822 | if (!policy) | |
823 | goto cleanup; | |
824 | ||
825 | if (!(query = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks))) | |
826 | goto cleanup; | |
827 | ||
828 | list = CFArrayCreate(kCFAllocatorDefault, (const void **)&tls_keychain, 1, &kCFTypeArrayCallBacks); | |
829 | ||
830 | CFDictionaryAddValue(query, kSecClass, kSecClassIdentity); | |
831 | CFDictionaryAddValue(query, kSecMatchPolicy, policy); | |
832 | CFDictionaryAddValue(query, kSecReturnRef, kCFBooleanTrue); | |
833 | CFDictionaryAddValue(query, kSecMatchLimit, kSecMatchLimitOne); | |
834 | CFDictionaryAddValue(query, kSecMatchSearchList, list); | |
835 | ||
836 | CFRelease(list); | |
837 | ||
838 | err = SecItemCopyMatching(query, (CFTypeRef *)&identity); | |
839 | ||
840 | if (err) | |
841 | goto cleanup; | |
842 | ||
843 | if (CFGetTypeID(identity) != SecIdentityGetTypeID()) | |
844 | goto cleanup; | |
845 | ||
846 | if ((certificates = CFArrayCreate(NULL, (const void **)&identity, 1, &kCFTypeArrayCallBacks)) == NULL) | |
847 | goto cleanup; | |
848 | ||
849 | cleanup : | |
850 | ||
851 | if (search) | |
852 | CFRelease(search); | |
853 | if (identity) | |
854 | CFRelease(identity); | |
855 | ||
856 | if (policy) | |
857 | CFRelease(policy); | |
858 | if (query) | |
859 | CFRelease(query); | |
860 | ||
861 | return (certificates); | |
862 | } | |
863 | ||
864 | ||
2c85b752 MS |
865 | /* |
866 | * 'http_cdsa_read()' - Read function for the CDSA library. | |
867 | */ | |
868 | ||
869 | static OSStatus /* O - -1 on error, 0 on success */ | |
870 | http_cdsa_read( | |
871 | SSLConnectionRef connection, /* I - SSL/TLS connection */ | |
872 | void *data, /* I - Data buffer */ | |
873 | size_t *dataLength) /* IO - Number of bytes */ | |
874 | { | |
875 | OSStatus result; /* Return value */ | |
876 | ssize_t bytes; /* Number of bytes read */ | |
877 | http_t *http; /* HTTP connection */ | |
878 | ||
879 | ||
880 | http = (http_t *)connection; | |
881 | ||
882 | if (!http->blocking) | |
883 | { | |
884 | /* | |
885 | * Make sure we have data before we read... | |
886 | */ | |
887 | ||
888 | while (!_httpWait(http, http->wait_value, 0)) | |
889 | { | |
890 | if (http->timeout_cb && (*http->timeout_cb)(http, http->timeout_data)) | |
891 | continue; | |
892 | ||
893 | http->error = ETIMEDOUT; | |
894 | return (-1); | |
895 | } | |
896 | } | |
897 | ||
898 | do | |
899 | { | |
900 | bytes = recv(http->fd, data, *dataLength, 0); | |
901 | } | |
902 | while (bytes == -1 && (errno == EINTR || errno == EAGAIN)); | |
903 | ||
904 | if (bytes == *dataLength) | |
905 | { | |
906 | result = 0; | |
907 | } | |
908 | else if (bytes > 0) | |
909 | { | |
910 | *dataLength = bytes; | |
911 | result = errSSLWouldBlock; | |
912 | } | |
913 | else | |
914 | { | |
915 | *dataLength = 0; | |
916 | ||
917 | if (bytes == 0) | |
918 | result = errSSLClosedGraceful; | |
919 | else if (errno == EAGAIN) | |
920 | result = errSSLWouldBlock; | |
921 | else | |
922 | result = errSSLClosedAbort; | |
923 | } | |
924 | ||
925 | return (result); | |
926 | } | |
927 | ||
928 | ||
929 | /* | |
930 | * 'http_cdsa_write()' - Write function for the CDSA library. | |
931 | */ | |
932 | ||
933 | static OSStatus /* O - -1 on error, 0 on success */ | |
934 | http_cdsa_write( | |
935 | SSLConnectionRef connection, /* I - SSL/TLS connection */ | |
936 | const void *data, /* I - Data buffer */ | |
937 | size_t *dataLength) /* IO - Number of bytes */ | |
938 | { | |
939 | OSStatus result; /* Return value */ | |
940 | ssize_t bytes; /* Number of bytes read */ | |
941 | http_t *http; /* HTTP connection */ | |
942 | ||
943 | ||
944 | http = (http_t *)connection; | |
945 | ||
946 | do | |
947 | { | |
948 | bytes = write(http->fd, data, *dataLength); | |
949 | } | |
950 | while (bytes == -1 && (errno == EINTR || errno == EAGAIN)); | |
951 | ||
952 | if (bytes == *dataLength) | |
953 | { | |
954 | result = 0; | |
955 | } | |
956 | else if (bytes >= 0) | |
957 | { | |
958 | *dataLength = bytes; | |
959 | result = errSSLWouldBlock; | |
960 | } | |
961 | else | |
962 | { | |
963 | *dataLength = 0; | |
964 | ||
965 | if (errno == EAGAIN) | |
966 | result = errSSLWouldBlock; | |
967 | else | |
968 | result = errSSLClosedAbort; | |
969 | } | |
970 | ||
971 | return (result); | |
972 | } | |
973 | ||
974 | ||
975 | /* | |
976 | * 'http_tls_initialize()' - Initialize the TLS stack. | |
977 | */ | |
978 | ||
979 | static void | |
980 | http_tls_initialize(void) | |
981 | { | |
982 | /* | |
983 | * Nothing to do... | |
984 | */ | |
985 | } | |
986 | ||
987 | ||
988 | /* | |
989 | * 'http_tls_pending()' - Return the number of pending TLS-encrypted bytes. | |
990 | */ | |
991 | ||
992 | static size_t | |
993 | http_tls_pending(http_t *http) /* I - HTTP connection */ | |
994 | { | |
995 | size_t bytes; /* Bytes that are available */ | |
996 | ||
997 | ||
998 | if (!SSLGetBufferedReadSize(http->tls, &bytes)) | |
999 | return (bytes); | |
1000 | ||
1001 | return (0); | |
1002 | } | |
1003 | ||
1004 | ||
1005 | /* | |
1006 | * 'http_tls_read()' - Read from a SSL/TLS connection. | |
1007 | */ | |
1008 | ||
1009 | static int /* O - Bytes read */ | |
41e0907c | 1010 | http_tls_read(http_t *http, /* I - HTTP connection */ |
2c85b752 MS |
1011 | char *buf, /* I - Buffer to store data */ |
1012 | int len) /* I - Length of buffer */ | |
1013 | { | |
1014 | int result; /* Return value */ | |
1015 | OSStatus error; /* Error info */ | |
1016 | size_t processed; /* Number of bytes processed */ | |
1017 | ||
1018 | ||
1019 | error = SSLRead(http->tls, buf, len, &processed); | |
1020 | DEBUG_printf(("6http_tls_read: error=%d, processed=%d", (int)error, | |
1021 | (int)processed)); | |
1022 | switch (error) | |
1023 | { | |
1024 | case 0 : | |
1025 | result = (int)processed; | |
1026 | break; | |
1027 | ||
1028 | case errSSLWouldBlock : | |
1029 | if (processed) | |
1030 | result = (int)processed; | |
1031 | else | |
1032 | { | |
1033 | result = -1; | |
1034 | errno = EINTR; | |
1035 | } | |
1036 | break; | |
1037 | ||
1038 | case errSSLClosedGraceful : | |
1039 | default : | |
1040 | if (processed) | |
1041 | result = (int)processed; | |
1042 | else | |
1043 | { | |
1044 | result = -1; | |
1045 | errno = EPIPE; | |
1046 | } | |
1047 | break; | |
1048 | } | |
1049 | ||
1050 | return (result); | |
1051 | } | |
1052 | ||
1053 | ||
1054 | /* | |
1055 | * 'http_tls_set_credentials()' - Set the TLS credentials. | |
1056 | */ | |
1057 | ||
1058 | static int /* O - Status of connection */ | |
41e0907c | 1059 | http_tls_set_credentials(http_t *http) /* I - HTTP connection */ |
2c85b752 MS |
1060 | { |
1061 | _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals */ | |
1062 | OSStatus error = 0; /* Error code */ | |
1063 | http_tls_credentials_t credentials = NULL; | |
1064 | /* TLS credentials */ | |
1065 | ||
1066 | ||
1067 | DEBUG_printf(("7http_tls_set_credentials(%p)", http)); | |
1068 | ||
1069 | /* | |
1070 | * Prefer connection specific credentials... | |
1071 | */ | |
1072 | ||
1073 | if ((credentials = http->tls_credentials) == NULL) | |
1074 | credentials = cg->tls_credentials; | |
1075 | ||
1076 | if (credentials) | |
1077 | { | |
1078 | error = SSLSetCertificate(http->tls, credentials); | |
1079 | DEBUG_printf(("4http_tls_set_credentials: SSLSetCertificate, error=%d", | |
1080 | (int)error)); | |
1081 | } | |
1082 | else | |
1083 | DEBUG_puts("4http_tls_set_credentials: No credentials to set."); | |
1084 | ||
1085 | return (error); | |
1086 | } | |
1087 | ||
1088 | ||
1089 | /* | |
1090 | * 'http_tls_start()' - Set up SSL/TLS support on a connection. | |
1091 | */ | |
1092 | ||
1093 | static int /* O - 0 on success, -1 on failure */ | |
41e0907c | 1094 | http_tls_start(http_t *http) /* I - HTTP connection */ |
2c85b752 MS |
1095 | { |
1096 | char hostname[256], /* Hostname */ | |
1097 | *hostptr; /* Pointer into hostname */ | |
1098 | _cups_globals_t *cg = _cupsGlobals(); | |
1099 | /* Pointer to library globals */ | |
1100 | OSStatus error; /* Error code */ | |
1101 | const char *message = NULL;/* Error message */ | |
1102 | cups_array_t *credentials; /* Credentials array */ | |
1103 | cups_array_t *names; /* CUPS distinguished names */ | |
1104 | CFArrayRef dn_array; /* CF distinguished names array */ | |
1105 | CFIndex count; /* Number of credentials */ | |
1106 | CFDataRef data; /* Certificate data */ | |
1107 | int i; /* Looping var */ | |
1108 | http_credential_t *credential; /* Credential data */ | |
1109 | ||
1110 | ||
1111 | DEBUG_printf(("7http_tls_start(http=%p)", http)); | |
1112 | ||
41e0907c | 1113 | if (http->mode == _HTTP_MODE_SERVER && !tls_keychain) |
2c85b752 | 1114 | { |
41e0907c MS |
1115 | DEBUG_puts("4http_tls_start: cupsSetServerCredentials not called."); |
1116 | http->error = errno = EINVAL; | |
1117 | http->status = HTTP_STATUS_ERROR; | |
1118 | _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Server credentials not set."), 1); | |
2c85b752 | 1119 | |
41e0907c | 1120 | return (-1); |
2c85b752 MS |
1121 | } |
1122 | ||
72d05bc9 | 1123 | if ((http->tls = SSLCreateContext(kCFAllocatorDefault, http->mode == _HTTP_MODE_CLIENT ? kSSLClientSide : kSSLServerSide, kSSLStreamType)) == NULL) |
2c85b752 MS |
1124 | { |
1125 | DEBUG_puts("4http_tls_start: SSLCreateContext failed."); | |
1126 | http->error = errno = ENOMEM; | |
1127 | http->status = HTTP_STATUS_ERROR; | |
1128 | _cupsSetHTTPError(HTTP_STATUS_ERROR); | |
1129 | ||
1130 | return (-1); | |
1131 | } | |
1132 | ||
1133 | error = SSLSetConnection(http->tls, http); | |
1134 | DEBUG_printf(("4http_tls_start: SSLSetConnection, error=%d", (int)error)); | |
1135 | ||
1136 | if (!error) | |
1137 | { | |
1138 | error = SSLSetIOFuncs(http->tls, http_cdsa_read, http_cdsa_write); | |
1139 | DEBUG_printf(("4http_tls_start: SSLSetIOFuncs, error=%d", (int)error)); | |
1140 | } | |
1141 | ||
1142 | if (!error) | |
1143 | { | |
1144 | error = SSLSetSessionOption(http->tls, kSSLSessionOptionBreakOnServerAuth, | |
1145 | true); | |
1146 | DEBUG_printf(("4http_tls_start: SSLSetSessionOption, error=%d", | |
1147 | (int)error)); | |
1148 | } | |
1149 | ||
41e0907c | 1150 | if (!error && http->mode == _HTTP_MODE_CLIENT) |
2c85b752 | 1151 | { |
41e0907c MS |
1152 | /* |
1153 | * Client: set client-side credentials, if any... | |
1154 | */ | |
1155 | ||
2c85b752 MS |
1156 | if (cg->client_cert_cb) |
1157 | { | |
1158 | error = SSLSetSessionOption(http->tls, | |
1159 | kSSLSessionOptionBreakOnCertRequested, true); | |
1160 | DEBUG_printf(("4http_tls_start: kSSLSessionOptionBreakOnCertRequested, " | |
1161 | "error=%d", (int)error)); | |
1162 | } | |
1163 | else | |
1164 | { | |
d3d89474 MS |
1165 | error = http_tls_set_credentials(http); |
1166 | DEBUG_printf(("4http_tls_start: http_tls_set_credentials, error=%d", | |
2c85b752 MS |
1167 | (int)error)); |
1168 | } | |
1169 | } | |
41e0907c MS |
1170 | else if (!error) |
1171 | { | |
1172 | /* | |
1173 | * Server: find/create a certificate for TLS... | |
1174 | */ | |
1175 | ||
1176 | if (http->fields[HTTP_FIELD_HOST][0]) | |
1177 | { | |
1178 | /* | |
1179 | * Use hostname for TLS upgrade... | |
1180 | */ | |
1181 | ||
1182 | strlcpy(hostname, http->fields[HTTP_FIELD_HOST], sizeof(hostname)); | |
1183 | } | |
1184 | else | |
1185 | { | |
1186 | /* | |
1187 | * Resolve hostname from connection address... | |
1188 | */ | |
1189 | ||
1190 | http_addr_t addr; /* Connection address */ | |
1191 | socklen_t addrlen; /* Length of address */ | |
1192 | ||
1193 | addrlen = sizeof(addr); | |
1194 | if (getsockname(http->fd, (struct sockaddr *)&addr, &addrlen)) | |
1195 | { | |
1196 | DEBUG_printf(("4http_tls_start: Unable to get socket address: %s", strerror(errno))); | |
1197 | hostname[0] = '\0'; | |
1198 | } | |
1199 | else if (httpAddrLocalhost(&addr)) | |
1200 | hostname[0] = '\0'; | |
1201 | else | |
1202 | httpAddrString(&addr, hostname, sizeof(hostname)); | |
1203 | } | |
1204 | ||
1205 | if (hostname[0]) | |
1206 | http->tls_credentials = http_cdsa_copy_server(hostname); | |
1207 | else if (tls_common_name) | |
1208 | http->tls_credentials = http_cdsa_copy_server(tls_common_name); | |
1209 | ||
1210 | if (!http->tls_credentials && tls_auto_create && (hostname[0] || tls_common_name)) | |
1211 | { | |
1212 | DEBUG_printf(("4http_tls_start: Auto-create credentials for \"%s\".", hostname[0] ? hostname : tls_common_name)); | |
1213 | ||
1214 | if (!cupsMakeServerCredentials(tls_keypath, hostname[0] ? hostname : tls_common_name, 0, NULL, time(NULL) + 365 * 86400)) | |
1215 | { | |
1216 | DEBUG_puts("4http_tls_start: cupsMakeServerCredentials failed."); | |
1217 | http->error = errno = EINVAL; | |
1218 | http->status = HTTP_STATUS_ERROR; | |
1219 | _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to create server credentials."), 1); | |
1220 | ||
1221 | return (-1); | |
1222 | } | |
1223 | ||
1224 | http->tls_credentials = http_cdsa_copy_server(hostname[0] ? hostname : tls_common_name); | |
1225 | } | |
1226 | ||
1227 | if (!http->tls_credentials) | |
1228 | { | |
1229 | DEBUG_puts("4http_tls_start: Unable to find server credentials."); | |
1230 | http->error = errno = EINVAL; | |
1231 | http->status = HTTP_STATUS_ERROR; | |
1232 | _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to find server credentials."), 1); | |
1233 | ||
1234 | return (-1); | |
1235 | } | |
1236 | ||
1237 | error = SSLSetCertificate(http->tls, http->tls_credentials); | |
1238 | ||
1239 | DEBUG_printf(("4http_tls_start: SSLSetCertificate, error=%d", (int)error)); | |
1240 | } | |
1241 | ||
1242 | DEBUG_printf(("4http_tls_start: tls_credentials=%p", http->tls_credentials)); | |
2c85b752 MS |
1243 | |
1244 | /* | |
1245 | * Let the server know which hostname/domain we are trying to connect to | |
1246 | * in case it wants to serve up a certificate with a matching common name. | |
1247 | */ | |
1248 | ||
41e0907c | 1249 | if (!error && http->mode == _HTTP_MODE_CLIENT) |
2c85b752 | 1250 | { |
41e0907c MS |
1251 | /* |
1252 | * Client: get the hostname to use for TLS... | |
1253 | */ | |
1254 | ||
1255 | if (httpAddrLocalhost(http->hostaddr)) | |
1256 | { | |
1257 | strlcpy(hostname, "localhost", sizeof(hostname)); | |
1258 | } | |
1259 | else | |
1260 | { | |
1261 | /* | |
1262 | * Otherwise make sure the hostname we have does not end in a trailing dot. | |
1263 | */ | |
1264 | ||
1265 | strlcpy(hostname, http->hostname, sizeof(hostname)); | |
1266 | if ((hostptr = hostname + strlen(hostname) - 1) >= hostname && | |
1267 | *hostptr == '.') | |
1268 | *hostptr = '\0'; | |
1269 | } | |
1270 | ||
2c85b752 MS |
1271 | error = SSLSetPeerDomainName(http->tls, hostname, strlen(hostname)); |
1272 | ||
41e0907c | 1273 | DEBUG_printf(("4http_tls_start: SSLSetPeerDomainName, error=%d", (int)error)); |
2c85b752 MS |
1274 | } |
1275 | ||
1276 | if (!error) | |
1277 | { | |
1278 | int done = 0; /* Are we done yet? */ | |
1279 | ||
1280 | while (!error && !done) | |
1281 | { | |
1282 | error = SSLHandshake(http->tls); | |
1283 | ||
1284 | DEBUG_printf(("4http_tls_start: SSLHandshake returned %d.", (int)error)); | |
1285 | ||
1286 | switch (error) | |
1287 | { | |
1288 | case noErr : | |
1289 | done = 1; | |
1290 | break; | |
1291 | ||
1292 | case errSSLWouldBlock : | |
1293 | error = noErr; /* Force a retry */ | |
1294 | usleep(1000); /* in 1 millisecond */ | |
1295 | break; | |
1296 | ||
1297 | case errSSLServerAuthCompleted : | |
1298 | error = 0; | |
1299 | if (cg->server_cert_cb) | |
1300 | { | |
1301 | error = httpCopyCredentials(http, &credentials); | |
1302 | if (!error) | |
1303 | { | |
1304 | error = (cg->server_cert_cb)(http, http->tls, credentials, | |
1305 | cg->server_cert_data); | |
1306 | httpFreeCredentials(credentials); | |
1307 | } | |
1308 | ||
1309 | DEBUG_printf(("4http_tls_start: Server certificate callback " | |
1310 | "returned %d.", (int)error)); | |
1311 | } | |
1312 | break; | |
1313 | ||
1314 | case errSSLClientCertRequested : | |
1315 | error = 0; | |
1316 | ||
1317 | if (cg->client_cert_cb) | |
1318 | { | |
1319 | names = NULL; | |
1320 | if (!(error = SSLCopyDistinguishedNames(http->tls, &dn_array)) && | |
1321 | dn_array) | |
1322 | { | |
1323 | if ((names = cupsArrayNew(NULL, NULL)) != NULL) | |
1324 | { | |
1325 | for (i = 0, count = CFArrayGetCount(dn_array); i < count; i++) | |
1326 | { | |
1327 | data = (CFDataRef)CFArrayGetValueAtIndex(dn_array, i); | |
1328 | ||
1329 | if ((credential = malloc(sizeof(*credential))) != NULL) | |
1330 | { | |
1331 | credential->datalen = CFDataGetLength(data); | |
1332 | if ((credential->data = malloc(credential->datalen))) | |
1333 | { | |
1334 | memcpy((void *)credential->data, CFDataGetBytePtr(data), | |
1335 | credential->datalen); | |
1336 | cupsArrayAdd(names, credential); | |
1337 | } | |
1338 | else | |
1339 | free(credential); | |
1340 | } | |
1341 | } | |
1342 | } | |
1343 | ||
1344 | CFRelease(dn_array); | |
1345 | } | |
1346 | ||
1347 | if (!error) | |
1348 | { | |
1349 | error = (cg->client_cert_cb)(http, http->tls, names, | |
1350 | cg->client_cert_data); | |
1351 | ||
1352 | DEBUG_printf(("4http_tls_start: Client certificate callback " | |
1353 | "returned %d.", (int)error)); | |
1354 | } | |
1355 | ||
1356 | httpFreeCredentials(names); | |
1357 | } | |
1358 | break; | |
1359 | ||
1360 | case errSSLUnknownRootCert : | |
1361 | message = _("Unable to establish a secure connection to host " | |
1362 | "(untrusted certificate)."); | |
1363 | break; | |
1364 | ||
1365 | case errSSLNoRootCert : | |
1366 | message = _("Unable to establish a secure connection to host " | |
1367 | "(self-signed certificate)."); | |
1368 | break; | |
1369 | ||
1370 | case errSSLCertExpired : | |
1371 | message = _("Unable to establish a secure connection to host " | |
1372 | "(expired certificate)."); | |
1373 | break; | |
1374 | ||
1375 | case errSSLCertNotYetValid : | |
1376 | message = _("Unable to establish a secure connection to host " | |
1377 | "(certificate not yet valid)."); | |
1378 | break; | |
1379 | ||
1380 | case errSSLHostNameMismatch : | |
1381 | message = _("Unable to establish a secure connection to host " | |
1382 | "(host name mismatch)."); | |
1383 | break; | |
1384 | ||
1385 | case errSSLXCertChainInvalid : | |
1386 | message = _("Unable to establish a secure connection to host " | |
1387 | "(certificate chain invalid)."); | |
1388 | break; | |
1389 | ||
1390 | case errSSLConnectionRefused : | |
1391 | message = _("Unable to establish a secure connection to host " | |
1392 | "(peer dropped connection before responding)."); | |
1393 | break; | |
1394 | ||
1395 | default : | |
1396 | break; | |
1397 | } | |
1398 | } | |
1399 | } | |
1400 | ||
1401 | if (error) | |
1402 | { | |
1403 | http->error = error; | |
1404 | http->status = HTTP_STATUS_ERROR; | |
1405 | errno = ECONNREFUSED; | |
1406 | ||
1407 | CFRelease(http->tls); | |
1408 | http->tls = NULL; | |
1409 | ||
1410 | /* | |
1411 | * If an error string wasn't set by the callbacks use a generic one... | |
1412 | */ | |
1413 | ||
1414 | if (!message) | |
1415 | #ifdef HAVE_CSSMERRORSTRING | |
1416 | message = cssmErrorString(error); | |
1417 | #else | |
1418 | message = _("Unable to establish a secure connection to host."); | |
1419 | #endif /* HAVE_CSSMERRORSTRING */ | |
1420 | ||
1421 | _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI, message, 1); | |
1422 | ||
1423 | return (-1); | |
1424 | } | |
1425 | ||
1426 | return (0); | |
1427 | } | |
1428 | ||
1429 | ||
1430 | /* | |
1431 | * 'http_tls_stop()' - Shut down SSL/TLS on a connection. | |
1432 | */ | |
1433 | ||
1434 | static void | |
41e0907c | 1435 | http_tls_stop(http_t *http) /* I - HTTP connection */ |
2c85b752 MS |
1436 | { |
1437 | while (SSLClose(http->tls) == errSSLWouldBlock) | |
1438 | usleep(1000); | |
1439 | ||
1440 | CFRelease(http->tls); | |
1441 | ||
1442 | if (http->tls_credentials) | |
1443 | CFRelease(http->tls_credentials); | |
1444 | ||
1445 | http->tls = NULL; | |
1446 | http->tls_credentials = NULL; | |
1447 | } | |
1448 | ||
1449 | ||
1450 | /* | |
1451 | * 'http_tls_write()' - Write to a SSL/TLS connection. | |
1452 | */ | |
1453 | ||
1454 | static int /* O - Bytes written */ | |
41e0907c | 1455 | http_tls_write(http_t *http, /* I - HTTP connection */ |
2c85b752 MS |
1456 | const char *buf, /* I - Buffer holding data */ |
1457 | int len) /* I - Length of buffer */ | |
1458 | { | |
1459 | ssize_t result; /* Return value */ | |
1460 | OSStatus error; /* Error info */ | |
1461 | size_t processed; /* Number of bytes processed */ | |
1462 | ||
1463 | ||
1464 | DEBUG_printf(("2http_tls_write(http=%p, buf=%p, len=%d)", http, buf, len)); | |
1465 | ||
1466 | error = SSLWrite(http->tls, buf, len, &processed); | |
1467 | ||
1468 | switch (error) | |
1469 | { | |
1470 | case 0 : | |
1471 | result = (int)processed; | |
1472 | break; | |
1473 | ||
1474 | case errSSLWouldBlock : | |
1475 | if (processed) | |
1476 | { | |
1477 | result = (int)processed; | |
1478 | } | |
1479 | else | |
1480 | { | |
1481 | result = -1; | |
1482 | errno = EINTR; | |
1483 | } | |
1484 | break; | |
1485 | ||
1486 | case errSSLClosedGraceful : | |
1487 | default : | |
1488 | if (processed) | |
1489 | { | |
1490 | result = (int)processed; | |
1491 | } | |
1492 | else | |
1493 | { | |
1494 | result = -1; | |
1495 | errno = EPIPE; | |
1496 | } | |
1497 | break; | |
1498 | } | |
1499 | ||
1500 | DEBUG_printf(("3http_tls_write: Returning %d.", (int)result)); | |
1501 | ||
1502 | return ((int)result); | |
1503 | } | |
1504 | ||
1505 | ||
d3d89474 | 1506 | #if 0 |
2c85b752 MS |
1507 | /* |
1508 | * 'cupsdEndTLS()' - Shutdown a secure session with the client. | |
1509 | */ | |
1510 | ||
1511 | int /* O - 1 on success, 0 on error */ | |
1512 | cupsdEndTLS(cupsd_client_t *con) /* I - Client connection */ | |
1513 | { | |
1514 | while (SSLClose(con->http.tls) == errSSLWouldBlock) | |
1515 | usleep(1000); | |
1516 | ||
1517 | CFRelease(con->http.tls); | |
1518 | con->http.tls = NULL; | |
1519 | ||
1520 | if (con->http.tls_credentials) | |
1521 | CFRelease(con->http.tls_credentials); | |
1522 | ||
1523 | return (1); | |
1524 | } | |
1525 | ||
1526 | ||
1527 | /* | |
1528 | * 'cupsdStartTLS()' - Start a secure session with the client. | |
1529 | */ | |
1530 | ||
1531 | int /* O - 1 on success, 0 on error */ | |
1532 | cupsdStartTLS(cupsd_client_t *con) /* I - Client connection */ | |
1533 | { | |
1534 | OSStatus error = 0; /* Error code */ | |
1535 | SecTrustRef peerTrust; /* Peer certificates */ | |
1536 | ||
1537 | ||
1538 | cupsdLogMessage(CUPSD_LOG_DEBUG, "[Client %d] Encrypting connection.", | |
1539 | con->http.fd); | |
1540 | ||
1541 | con->http.tls_credentials = copy_cdsa_certificate(con); | |
1542 | ||
1543 | if (!con->http.tls_credentials) | |
1544 | { | |
1545 | /* | |
1546 | * No keychain (yet), make a self-signed certificate... | |
1547 | */ | |
1548 | ||
1549 | if (make_certificate(con)) | |
1550 | con->http.tls_credentials = copy_cdsa_certificate(con); | |
1551 | } | |
1552 | ||
1553 | if (!con->http.tls_credentials) | |
1554 | { | |
1555 | cupsdLogMessage(CUPSD_LOG_ERROR, | |
1556 | "Could not find signing key in keychain \"%s\"", | |
1557 | ServerCertificate); | |
1558 | error = errSSLBadConfiguration; | |
1559 | } | |
1560 | ||
1561 | if (!error) | |
1562 | con->http.tls = SSLCreateContext(kCFAllocatorDefault, kSSLServerSide, | |
1563 | kSSLStreamType); | |
1564 | ||
1565 | if (!error) | |
1566 | error = SSLSetIOFuncs(con->http.tls, http_cdsa_read, http_cdsa_write); | |
1567 | ||
1568 | if (!error) | |
1569 | error = SSLSetConnection(con->http.tls, HTTP(con)); | |
1570 | ||
1571 | if (!error) | |
1572 | error = SSLSetCertificate(con->http.tls, con->http.tls_credentials); | |
1573 | ||
1574 | if (!error) | |
1575 | { | |
1576 | /* | |
1577 | * Perform SSL/TLS handshake | |
1578 | */ | |
1579 | ||
1580 | while ((error = SSLHandshake(con->http.tls)) == errSSLWouldBlock) | |
1581 | usleep(1000); | |
1582 | } | |
1583 | ||
1584 | if (error) | |
1585 | { | |
1586 | cupsdLogMessage(CUPSD_LOG_ERROR, | |
1587 | "Unable to encrypt connection from %s - %s (%d)", | |
1588 | con->http.hostname, cssmErrorString(error), (int)error); | |
1589 | ||
1590 | con->http.error = error; | |
1591 | con->http.status = HTTP_ERROR; | |
1592 | ||
1593 | if (con->http.tls) | |
1594 | { | |
1595 | CFRelease(con->http.tls); | |
1596 | con->http.tls = NULL; | |
1597 | } | |
1598 | ||
1599 | if (con->http.tls_credentials) | |
1600 | { | |
1601 | CFRelease(con->http.tls_credentials); | |
1602 | con->http.tls_credentials = NULL; | |
1603 | } | |
1604 | ||
1605 | return (0); | |
1606 | } | |
1607 | ||
1608 | cupsdLogMessage(CUPSD_LOG_DEBUG, "Connection from %s now encrypted.", | |
1609 | con->http.hostname); | |
1610 | ||
1611 | if (!SSLCopyPeerTrust(con->http.tls, &peerTrust) && peerTrust) | |
1612 | { | |
1613 | cupsdLogMessage(CUPSD_LOG_DEBUG, "Received %d peer certificates.", | |
1614 | (int)SecTrustGetCertificateCount(peerTrust)); | |
1615 | CFRelease(peerTrust); | |
1616 | } | |
1617 | else | |
1618 | cupsdLogMessage(CUPSD_LOG_DEBUG, "Received NO peer certificates."); | |
1619 | ||
1620 | return (1); | |
1621 | } | |
d3d89474 | 1622 | #endif /* 0 */ |
2c85b752 MS |
1623 | |
1624 | ||
1625 | /* | |
1626 | * End of "$Id$". | |
1627 | */ |