4 * TLS support code for CUPS on OS X.
6 * Copyright 2007-2014 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/".
15 * This file is subject to the Apple OS-Developed Software exception.
18 /**** This file is included from http.c ****/
21 * Include necessary headers...
26 extern char **environ
;
33 #ifdef HAVE_SECKEYCHAINOPEN
34 static int tls_auto_create
= 0;
35 /* Auto-create self-signed certs? */
36 static char *tls_common_name
= NULL
;
37 /* Default common name */
38 static SecKeychainRef tls_keychain
= NULL
;
39 /* Server cert keychain */
40 static char *tls_keypath
= NULL
;
41 /* Server cert keychain path */
42 static _cups_mutex_t tls_mutex
= _CUPS_MUTEX_INITIALIZER
;
43 /* Mutex for keychain/certs */
44 #endif /* HAVE_SECKEYCHAINOPEN */
51 #ifdef HAVE_SECKEYCHAINOPEN
52 static CFArrayRef
http_cdsa_copy_server(const char *common_name
);
53 #endif /* HAVE_SECKEYCHAINOPEN */
54 static OSStatus
http_cdsa_read(SSLConnectionRef connection
, void *data
, size_t *dataLength
);
55 static OSStatus
http_cdsa_write(SSLConnectionRef connection
, const void *data
, size_t *dataLength
);
59 * 'cupsMakeServerCredentials()' - Make a self-signed certificate and private key pair.
64 int /* O - 1 on success, 0 on failure */
65 cupsMakeServerCredentials(
66 const char *path
, /* I - Path to keychain/directory */
67 const char *common_name
, /* I - Common name */
68 int num_alt_names
, /* I - Number of subject alternate names */
69 const char **alt_names
, /* I - Subject Alternate Names */
70 time_t expiration_date
) /* I - Expiration date */
72 #if defined(HAVE_SECGENERATESELFSIGNEDCERTIFICATE) && defined(HAVE_SECKEYCHAINOPEN)
73 int status
= 0; /* Return status */
74 OSStatus err
; /* Error code (if any) */
75 CFStringRef cfcommon_name
= NULL
;
76 /* CF string for server name */
77 SecIdentityRef ident
= NULL
; /* Identity */
78 SecKeyRef publicKey
= NULL
,
82 CFMutableDictionaryRef keyParams
= NULL
;
83 /* Key generation parameters */
88 (void)expiration_date
;
90 cfcommon_name
= CFStringCreateWithCString(kCFAllocatorDefault
, common_name
,
91 kCFStringEncodingUTF8
);
96 * Create a public/private key pair...
99 keyParams
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
100 &kCFTypeDictionaryKeyCallBacks
,
101 &kCFTypeDictionaryValueCallBacks
);
105 CFDictionaryAddValue(keyParams
, kSecAttrKeyType
, kSecAttrKeyTypeRSA
);
106 CFDictionaryAddValue(keyParams
, kSecAttrKeySizeInBits
, CFSTR("2048"));
107 CFDictionaryAddValue(keyParams
, kSecAttrLabel
,
108 CFSTR("CUPS Self-Signed Certificate"));
110 err
= SecKeyGeneratePair(keyParams
, &publicKey
, &privateKey
);
115 * Create a self-signed certificate using the public/private key pair...
118 CFIndex usageInt
= kSecKeyUsageAll
;
119 CFNumberRef usage
= CFNumberCreate(alloc
, kCFNumberCFIndexType
, &usageInt
);
120 CFDictionaryRef certParams
= CFDictionaryCreateMutable(kCFAllocatorDefault
,
121 kSecCSRBasicContraintsPathLen
, CFINT(0), kSecSubjectAltName
, cfcommon_name
, kSecCertificateKeyUsage
, usage
, NULL
, NULL
);
124 const void *ca_o
[] = { kSecOidOrganization
, CFSTR("") };
125 const void *ca_cn
[] = { kSecOidCommonName
, cfcommon_name
};
126 CFArrayRef ca_o_dn
= CFArrayCreate(kCFAllocatorDefault
, ca_o
, 2, NULL
);
127 CFArrayRef ca_cn_dn
= CFArrayCreate(kCFAllocatorDefault
, ca_cn
, 2, NULL
);
128 const void *ca_dn_array
[2];
130 ca_dn_array
[0] = CFArrayCreate(kCFAllocatorDefault
, (const void **)&ca_o_dn
, 1, NULL
);
131 ca_dn_array
[1] = CFArrayCreate(kCFAllocatorDefault
, (const void **)&ca_cn_dn
, 1, NULL
);
133 CFArrayRef subject
= CFArrayCreate(kCFAllocatorDefault
, ca_dn_array
, 2, NULL
);
134 SecCertificateRef cert
= SecGenerateSelfSignedCertificate(subject
, certParams
, publicKey
, privateKey
);
136 CFRelease(certParams
);
141 ident
= SecIdentityCreate(kCFAllocatorDefault
, cert
, privateKey
);
147 * Cleanup and return...
153 CFRelease(cfcommon_name
);
156 CFRelease(keyParams
);
165 CFRelease(publicKey
);
168 CFRelease(publicKey
);
172 #else /* !(HAVE_SECGENERATESELFSIGNEDCERTIFICATE && HAVE_SECKEYCHAINOPEN) */
173 int pid
, /* Process ID of command */
174 status
; /* Status of command */
175 char command
[1024], /* Command */
176 *argv
[4], /* Command-line arguments */
177 keychain
[1024], /* Keychain argument */
178 infofile
[1024]; /* Type-in information for cert */
179 cups_file_t
*fp
; /* Seed/info file */
184 (void)expiration_date
;
187 * Run the "certtool" command to generate a self-signed certificate...
190 if (!cupsFileFind("certtool", getenv("PATH"), 1, command
, sizeof(command
)))
194 * Create a file with the certificate information fields...
196 * Note: This assumes that the default questions are asked by the certtool
200 if ((fp
= cupsTempFile2(infofile
, sizeof(infofile
))) == NULL
)
204 "CUPS Self-Signed Certificate\n"
205 /* Enter key and certificate label */
206 "r\n" /* Generate RSA key pair */
207 "2048\n" /* Key size in bits */
208 "y\n" /* OK (y = yes) */
209 "b\n" /* Usage (b=signing/encryption) */
210 "s\n" /* Sign with SHA1 */
211 "y\n" /* OK (y = yes) */
212 "%s\n" /* Common name */
213 "\n" /* Country (default) */
214 "\n" /* Organization (default) */
215 "\n" /* Organizational unit (default) */
216 "\n" /* State/Province (default) */
217 "\n" /* Email address */
218 "y\n", /* OK (y = yes) */
222 snprintf(keychain
, sizeof(keychain
), "k=%s", path
);
224 argv
[0] = "certtool";
229 posix_spawn_file_actions_t actions
; /* File actions */
231 posix_spawn_file_actions_init(&actions
);
232 posix_spawn_file_actions_addclose(&actions
, 0);
233 posix_spawn_file_actions_addopen(&actions
, 0, infofile
, O_RDONLY
, 0);
235 if (posix_spawn(&pid
, command
, &actions
, NULL
, argv
, environ
))
241 posix_spawn_file_actions_destroy(&actions
);
245 while (waitpid(pid
, &status
, 0) < 0)
253 #endif /* HAVE_SECGENERATESELFSIGNEDCERTIFICATE && HAVE_SECKEYCHAINOPEN */
258 * 'cupsSetServerCredentials()' - Set the default server credentials.
260 * Note: The server credentials are used by all threads in the running process.
261 * This function is threadsafe.
266 int /* O - 1 on success, 0 on failure */
267 cupsSetServerCredentials(
268 const char *path
, /* I - Path to keychain/directory */
269 const char *common_name
, /* I - Default common name for server */
270 int auto_create
) /* I - 1 = automatically create self-signed certificates */
272 DEBUG_printf(("cupsSetServerCredentials(path=\"%s\", common_name=\"%s\", auto_create=%d)", path
, common_name
, auto_create
));
274 #ifdef HAVE_SECKEYCHAINOPEN
275 SecKeychainRef keychain
= NULL
;/* Temporary keychain */
278 if (SecKeychainOpen(path
, &keychain
) != noErr
)
280 /* TODO: Set cups last error string */
281 DEBUG_puts("1cupsSetServerCredentials: Unable to open keychain, returning 0.");
285 _cupsMutexLock(&tls_mutex
);
288 * Close any keychain that is currently open...
292 CFRelease(tls_keychain
);
295 _cupsStrFree(tls_keypath
);
298 _cupsStrFree(tls_common_name
);
301 * Save the new keychain...
304 tls_keychain
= keychain
;
305 tls_keypath
= _cupsStrAlloc(path
);
306 tls_auto_create
= auto_create
;
307 tls_common_name
= _cupsStrAlloc(common_name
);
309 _cupsMutexUnlock(&tls_mutex
);
311 DEBUG_puts("1cupsSetServerCredentials: Opened keychain, returning 1.");
315 DEBUG_puts("1cupsSetServerCredentials: No keychain support compiled in, returning 0.");
317 #endif /* HAVE_SECKEYCHAINOPEN */
322 * 'httpCopyCredentials()' - Copy the credentials associated with the peer in
323 * an encrypted connection.
325 * @since CUPS 1.5/OS X 10.7@
328 int /* O - Status of call (0 = success) */
330 http_t
*http
, /* I - Connection to server */
331 cups_array_t
**credentials
) /* O - Array of credentials */
333 OSStatus error
; /* Error code */
334 SecTrustRef peerTrust
; /* Peer trust reference */
335 CFIndex count
; /* Number of credentials */
336 SecCertificateRef secCert
; /* Certificate reference */
337 CFDataRef data
; /* Certificate data */
338 int i
; /* Looping var */
344 if (!http
|| !http
->tls
|| !credentials
)
347 if (!(error
= SSLCopyPeerTrust(http
->tls
, &peerTrust
)) && peerTrust
)
349 if ((*credentials
= cupsArrayNew(NULL
, NULL
)) != NULL
)
351 count
= SecTrustGetCertificateCount(peerTrust
);
353 for (i
= 0; i
< count
; i
++)
355 secCert
= SecTrustGetCertificateAtIndex(peerTrust
, i
);
356 if ((data
= SecCertificateCopyData(secCert
)))
358 httpAddCredential(*credentials
, CFDataGetBytePtr(data
), (size_t)CFDataGetLength(data
));
364 CFRelease(peerTrust
);
372 * 'http_cdsa_create_credential()' - Create a single credential in the internal format.
375 static SecCertificateRef
/* O - Certificate */
376 http_cdsa_create_credential(
377 http_credential_t
*credential
) /* I - Credential */
382 return (SecCertificateCreateWithBytes(kCFAllocatorDefault
, credential
->data
, (CFIndex
)credential
->datalen
));
387 * '_httpCreateCredentials()' - Create credentials in the internal format.
390 http_tls_credentials_t
/* O - Internal credentials */
391 _httpCreateCredentials(
392 cups_array_t
*credentials
) /* I - Array of credentials */
394 CFMutableArrayRef peerCerts
; /* Peer credentials reference */
395 SecCertificateRef secCert
; /* Certificate reference */
396 http_credential_t
*credential
; /* Credential data */
402 if ((peerCerts
= CFArrayCreateMutable(kCFAllocatorDefault
,
403 cupsArrayCount(credentials
),
404 &kCFTypeArrayCallBacks
)) == NULL
)
407 for (credential
= (http_credential_t
*)cupsArrayFirst(credentials
);
409 credential
= (http_credential_t
*)cupsArrayNext(credentials
))
411 if ((secCert
= http_cdsa_create_credential(credential
)) != NULL
)
413 CFArrayAppendValue(peerCerts
, secCert
);
423 * 'httpCredentialsAreTrusted()' - Return whether the credentials are trusted.
428 int /* O - 1 if trusted, 0 if not/unknown */
429 httpCredentialsAreTrusted(
430 cups_array_t
*credentials
) /* I - Credentials */
432 SecCertificateRef secCert
; /* Certificate reference */
433 int trusted
= 1; /* Trusted? */
434 Boolean isSelfSigned
; /* Is this certificate self-signed? */
435 _cups_globals_t
*cg
= _cupsGlobals();
436 /* Per-thread globals */
439 if ((secCert
= http_cdsa_create_credential((http_credential_t
*)cupsArrayFirst(credentials
))) == NULL
)
442 if (!cg
->expired_certs
&& !SecCertificateIsValid(secCert
, CFAbsoluteTimeGetCurrent()))
444 else if (!cg
->any_root
&& (SecCertificateIsSelfSigned(secCert
, &isSelfSigned
) != noErr
|| isSelfSigned
))
454 * 'httpCredentialsGetExpiration()' - Return the expiration date of the credentials.
459 time_t /* O - Expiration date of credentials */
460 httpCredentialsGetExpiration(
461 cups_array_t
*credentials
) /* I - Credentials */
463 SecCertificateRef secCert
; /* Certificate reference */
464 time_t expiration
; /* Expiration date */
467 if ((secCert
= http_cdsa_create_credential((http_credential_t
*)cupsArrayFirst(credentials
))) == NULL
)
470 expiration
= (time_t)(SecCertificateNotValidAfter(secCert
) - kCFAbsoluteTimeIntervalSince1970
);
479 * 'httpCredentialsIsValidName()' - Return whether the credentials are valid for the given name.
484 int /* O - 1 if valid, 0 otherwise */
485 httpCredentialsIsValidName(
486 cups_array_t
*credentials
, /* I - Credentials */
487 const char *common_name
) /* I - Name to check */
489 SecCertificateRef secCert
; /* Certificate reference */
490 CFStringRef cfcommon_name
; /* CF string for common name */
491 CFStringRef cert_name
= NULL
;
492 /* Certificate's common name */
493 int valid
= 1; /* Valid name? */
496 if ((secCert
= http_cdsa_create_credential((http_credential_t
*)cupsArrayFirst(credentials
))) == NULL
)
500 * Compare the common names...
503 cfcommon_name
= CFStringCreateWithCString(kCFAllocatorDefault
, common_name
, kCFStringEncodingUTF8
);
505 if (SecCertificateCopyCommonName(secCert
, &cert_name
) != noErr
)
508 * Can't get common name, cannot be valid...
513 else if (CFStringCompare(cfcommon_name
, cert_name
, kCFCompareCaseInsensitive
))
516 * Not the common name, check whether the certificate is saved in the keychain...
519 /* TODO: Pull certificate from the keychain using label */
522 CFRelease(cfcommon_name
);
525 CFRelease(cert_name
);
534 * 'httpCredentialsString()' - Return a string representing the credentials.
539 size_t /* O - Total size of credentials string */
540 httpCredentialsString(
541 cups_array_t
*credentials
, /* I - Credentials */
542 char *buffer
, /* I - Buffer or @code NULL@ */
543 size_t bufsize
) /* I - Size of buffer */
545 SecCertificateRef secCert
; /* Certificate reference */
546 CFStringRef summary
; /* CF string for summary */
552 if (buffer
&& bufsize
> 0)
555 /* TODO: This needs to include a hash of the credentials */
556 if ((secCert
= http_cdsa_create_credential((http_credential_t
*)cupsArrayFirst(credentials
))) != NULL
)
558 if ((summary
= SecCertificateCopySubjectSummary(secCert
)) != NULL
)
560 CFStringGetCString(summary
, buffer
, (CFIndex
)bufsize
, kCFStringEncodingUTF8
);
567 return (strlen(buffer
));
572 * '_httpFreeCredentials()' - Free internal credentials.
576 _httpFreeCredentials(
577 http_tls_credentials_t credentials
) /* I - Internal credentials */
582 CFRelease(credentials
);
587 * 'httpLoadCredentials()' - Load X.509 credentials from a keychain file.
592 int /* O - 0 on success, -1 on error */
594 const char *path
, /* I - Keychain/PKCS#12 path */
595 cups_array_t
**credentials
, /* IO - Credentials */
596 const char *common_name
) /* I - Common name for credentials */
605 OSStatus err
; /* Error info */
606 SecKeychainRef keychain
= NULL
;/* Keychain reference */
607 SecIdentitySearchRef search
= NULL
; /* Search reference */
608 SecIdentityRef identity
= NULL
;/* Identity */
609 CFArrayRef certificates
= NULL
;
610 /* Certificate array */
611 SecPolicyRef policy
= NULL
; /* Policy ref */
612 CFStringRef cfcommon_name
= NULL
;
614 CFMutableDictionaryRef query
= NULL
; /* Query qualifiers */
615 CFArrayRef list
= NULL
; /* Keychain list */
618 if ((err
= SecKeychainOpen(path
, &keychain
)))
621 cfcommon_name
= CFStringCreateWithCString(kCFAllocatorDefault
, common_name
, kCFStringEncodingUTF8
);
623 policy
= SecPolicyCreateSSL(1, cfcommon_name
);
626 CFRelease(cfcommon_name
);
631 if (!(query
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
)))
634 list
= CFArrayCreate(kCFAllocatorDefault
, (const void **)&keychain
, 1,
635 &kCFTypeArrayCallBacks
);
637 CFDictionaryAddValue(query
, kSecClass
, kSecClassIdentity
);
638 CFDictionaryAddValue(query
, kSecMatchPolicy
, policy
);
639 CFDictionaryAddValue(query
, kSecReturnRef
, kCFBooleanTrue
);
640 CFDictionaryAddValue(query
, kSecMatchLimit
, kSecMatchLimitOne
);
641 CFDictionaryAddValue(query
, kSecMatchSearchList
, list
);
645 err
= SecItemCopyMatching(query
, (CFTypeRef
*)&identity
);
650 if (CFGetTypeID(identity
) != SecIdentityGetTypeID())
653 if ((certificates
= CFArrayCreate(NULL
, (const void **)&identity
, 1, &kCFTypeArrayCallBacks
)) == NULL
)
670 return (certificates
);
677 * 'cupsMakeCredentials()' - Create self-signed credentials for the given
683 int /* O - 0 on success, -1 on error */
685 const char *path
, /* I - Keychain/PKCS#12 path */
686 cups_array_t
**credentials
, /* O - Credentials */
687 const char *common_name
) /* I - Common name for X.509 cert */
689 # ifdef HAVE_SECGENERATESELFSIGNEDCERTIFICATE
690 int status
= -1; /* Return status */
691 OSStatus err
; /* Error code (if any) */
692 CFStringRef cfcommon_name
= NULL
;
693 /* CF string for server name */
694 SecIdentityRef ident
= NULL
; /* Identity */
695 SecKeyRef publicKey
= NULL
,
699 CFMutableDictionaryRef keyParams
= NULL
;
700 /* Key generation parameters */
706 cfcommon_name
= CFStringCreateWithCString(kCFAllocatorDefault
, servername
,
707 kCFStringEncodingUTF8
);
712 * Create a public/private key pair...
715 keyParams
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
716 &kCFTypeDictionaryKeyCallBacks
,
717 &kCFTypeDictionaryValueCallBacks
);
721 CFDictionaryAddValue(keyParams
, kSecAttrKeyType
, kSecAttrKeyTypeRSA
);
722 CFDictionaryAddValue(keyParams
, kSecAttrKeySizeInBits
, CFSTR("2048"));
723 CFDictionaryAddValue(keyParams
, kSecAttrLabel
,
724 CFSTR("CUPS Self-Signed Certificate"));
726 err
= SecKeyGeneratePair(keyParams
, &publicKey
, &privateKey
);
731 * Create a self-signed certificate using the public/private key pair...
734 CFIndex usageInt
= kSecKeyUsageAll
;
735 CFNumberRef usage
= CFNumberCreate(alloc
, kCFNumberCFIndexType
, &usageInt
);
736 CFDictionaryRef certParams
= CFDictionaryCreateMutable(kCFAllocatorDefault
,
737 kSecCSRBasicContraintsPathLen
, CFINT(0), kSecSubjectAltName
, cfcommon_name
, kSecCertificateKeyUsage
, usage
, NULL
, NULL
);
740 const void *ca_o
[] = { kSecOidOrganization
, CFSTR("") };
741 const void *ca_cn
[] = { kSecOidCommonName
, cfcommon_name
};
742 CFArrayRef ca_o_dn
= CFArrayCreate(kCFAllocatorDefault
, ca_o
, 2, NULL
);
743 CFArrayRef ca_cn_dn
= CFArrayCreate(kCFAllocatorDefault
, ca_cn
, 2, NULL
);
744 const void *ca_dn_array
[2];
746 ca_dn_array
[0] = CFArrayCreate(kCFAllocatorDefault
, (const void **)&ca_o_dn
, 1, NULL
);
747 ca_dn_array
[1] = CFArrayCreate(kCFAllocatorDefault
, (const void **)&ca_cn_dn
, 1, NULL
);
749 CFArrayRef subject
= CFArrayCreate(kCFAllocatorDefault
, ca_dn_array
, 2, NULL
);
750 SecCertificateRef cert
= SecGenerateSelfSignedCertificate(subject
, certParams
, publicKey
, privateKey
);
752 CFRelease(certParams
);
757 ident
= SecIdentityCreate(kCFAllocatorDefault
, cert
, privateKey
);
763 * Cleanup and return...
769 CFRelease(cfcommon_name
);
772 CFRelease(keyParams
);
781 CFRelease(publicKey
);
784 CFRelease(publicKey
);
788 # else /* !HAVE_SECGENERATESELFSIGNEDCERTIFICATE */
789 int pid
, /* Process ID of command */
790 status
; /* Status of command */
791 char command
[1024], /* Command */
792 *argv
[4], /* Command-line arguments */
793 keychain
[1024], /* Keychain argument */
794 infofile
[1024]; /* Type-in information for cert */
795 cups_file_t
*fp
; /* Seed/info file */
799 * Run the "certtool" command to generate a self-signed certificate...
802 if (!cupsFileFind("certtool", getenv("PATH"), 1, command
, sizeof(command
)))
806 * Create a file with the certificate information fields...
808 * Note: This assumes that the default questions are asked by the certtool
812 if ((fp
= cupsTempFile2(infofile
, sizeof(infofile
))) == NULL
)
816 "%s\n" /* Enter key and certificate label */
817 "r\n" /* Generate RSA key pair */
818 "2048\n" /* Key size in bits */
819 "y\n" /* OK (y = yes) */
820 "b\n" /* Usage (b=signing/encryption) */
821 "s\n" /* Sign with SHA1 */
822 "y\n" /* OK (y = yes) */
823 "%s\n" /* Common name */
824 "\n" /* Country (default) */
825 "\n" /* Organization (default) */
826 "\n" /* Organizational unit (default) */
827 "\n" /* State/Province (default) */
828 "%s\n" /* Email address */
829 "y\n", /* OK (y = yes) */
830 common_name
, common_name
, "");
833 snprintf(keychain
, sizeof(keychain
), "k=%s", path
);
835 argv
[0] = "certtool";
840 posix_spawn_file_actions_t actions
; /* File actions */
842 posix_spawn_file_actions_init(&actions
);
843 posix_spawn_file_actions_addclose(&actions
, 0);
844 posix_spawn_file_actions_addopen(&actions
, 0, infofile
, O_RDONLY
, 0);
846 if (posix_spawn(&pid
, command
, &actions
, NULL
, argv
, environ
))
852 posix_spawn_file_actions_destroy(&actions
);
856 while (waitpid(pid
, &status
, 0) < 0)
866 # endif /* HAVE_SECGENERATESELFSIGNEDCERTIFICATE */
868 return (httpLoadCredentials(path
, credentials
, common_name
));
874 * 'httpSaveCredentials()' - Save X.509 credentials to a keychain file.
879 int /* O - -1 on error, 0 on success */
881 const char *path
, /* I - Keychain/PKCS#12 path */
882 cups_array_t
*credentials
, /* I - Credentials */
883 const char *common_name
) /* I - Common name for credentials */
893 #ifdef HAVE_SECKEYCHAINOPEN
895 * 'http_cdsa_copy_server()' - Find and copy server credentials from the keychain.
898 static CFArrayRef
/* O - Array of certificates or NULL */
899 http_cdsa_copy_server(
900 const char *common_name
) /* I - Server's hostname */
902 OSStatus err
; /* Error info */
903 SecIdentitySearchRef search
= NULL
; /* Search reference */
904 SecIdentityRef identity
= NULL
;/* Identity */
905 CFArrayRef certificates
= NULL
;
906 /* Certificate array */
907 SecPolicyRef policy
= NULL
; /* Policy ref */
908 CFStringRef cfcommon_name
= NULL
;
910 CFMutableDictionaryRef query
= NULL
; /* Query qualifiers */
911 CFArrayRef list
= NULL
; /* Keychain list */
914 cfcommon_name
= CFStringCreateWithCString(kCFAllocatorDefault
, common_name
, kCFStringEncodingUTF8
);
916 policy
= SecPolicyCreateSSL(1, cfcommon_name
);
919 CFRelease(cfcommon_name
);
924 if (!(query
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
)))
927 list
= CFArrayCreate(kCFAllocatorDefault
, (const void **)&tls_keychain
, 1, &kCFTypeArrayCallBacks
);
929 CFDictionaryAddValue(query
, kSecClass
, kSecClassIdentity
);
930 CFDictionaryAddValue(query
, kSecMatchPolicy
, policy
);
931 CFDictionaryAddValue(query
, kSecReturnRef
, kCFBooleanTrue
);
932 CFDictionaryAddValue(query
, kSecMatchLimit
, kSecMatchLimitOne
);
933 CFDictionaryAddValue(query
, kSecMatchSearchList
, list
);
937 err
= SecItemCopyMatching(query
, (CFTypeRef
*)&identity
);
942 if (CFGetTypeID(identity
) != SecIdentityGetTypeID())
945 if ((certificates
= CFArrayCreate(NULL
, (const void **)&identity
, 1, &kCFTypeArrayCallBacks
)) == NULL
)
960 return (certificates
);
962 #endif /* HAVE_SECKEYCHAINOPEN */
966 * 'http_cdsa_read()' - Read function for the CDSA library.
969 static OSStatus
/* O - -1 on error, 0 on success */
971 SSLConnectionRef connection
, /* I - SSL/TLS connection */
972 void *data
, /* I - Data buffer */
973 size_t *dataLength
) /* IO - Number of bytes */
975 OSStatus result
; /* Return value */
976 ssize_t bytes
; /* Number of bytes read */
977 http_t
*http
; /* HTTP connection */
980 http
= (http_t
*)connection
;
985 * Make sure we have data before we read...
988 while (!_httpWait(http
, http
->wait_value
, 0))
990 if (http
->timeout_cb
&& (*http
->timeout_cb
)(http
, http
->timeout_data
))
993 http
->error
= ETIMEDOUT
;
1000 bytes
= recv(http
->fd
, data
, *dataLength
, 0);
1002 while (bytes
== -1 && (errno
== EINTR
|| errno
== EAGAIN
));
1004 if ((size_t)bytes
== *dataLength
)
1010 *dataLength
= (size_t)bytes
;
1011 result
= errSSLWouldBlock
;
1018 result
= errSSLClosedGraceful
;
1019 else if (errno
== EAGAIN
)
1020 result
= errSSLWouldBlock
;
1022 result
= errSSLClosedAbort
;
1030 * 'http_cdsa_write()' - Write function for the CDSA library.
1033 static OSStatus
/* O - -1 on error, 0 on success */
1035 SSLConnectionRef connection
, /* I - SSL/TLS connection */
1036 const void *data
, /* I - Data buffer */
1037 size_t *dataLength
) /* IO - Number of bytes */
1039 OSStatus result
; /* Return value */
1040 ssize_t bytes
; /* Number of bytes read */
1041 http_t
*http
; /* HTTP connection */
1044 http
= (http_t
*)connection
;
1048 bytes
= write(http
->fd
, data
, *dataLength
);
1050 while (bytes
== -1 && (errno
== EINTR
|| errno
== EAGAIN
));
1052 if ((size_t)bytes
== *dataLength
)
1056 else if (bytes
>= 0)
1058 *dataLength
= (size_t)bytes
;
1059 result
= errSSLWouldBlock
;
1065 if (errno
== EAGAIN
)
1066 result
= errSSLWouldBlock
;
1068 result
= errSSLClosedAbort
;
1076 * 'http_tls_initialize()' - Initialize the TLS stack.
1080 http_tls_initialize(void)
1089 * 'http_tls_pending()' - Return the number of pending TLS-encrypted bytes.
1093 http_tls_pending(http_t
*http
) /* I - HTTP connection */
1095 size_t bytes
; /* Bytes that are available */
1098 if (!SSLGetBufferedReadSize(http
->tls
, &bytes
))
1106 * 'http_tls_read()' - Read from a SSL/TLS connection.
1109 static int /* O - Bytes read */
1110 http_tls_read(http_t
*http
, /* I - HTTP connection */
1111 char *buf
, /* I - Buffer to store data */
1112 int len
) /* I - Length of buffer */
1114 int result
; /* Return value */
1115 OSStatus error
; /* Error info */
1116 size_t processed
; /* Number of bytes processed */
1119 error
= SSLRead(http
->tls
, buf
, (size_t)len
, &processed
);
1120 DEBUG_printf(("6http_tls_read: error=%d, processed=%d", (int)error
,
1125 result
= (int)processed
;
1128 case errSSLWouldBlock
:
1130 result
= (int)processed
;
1138 case errSSLClosedGraceful
:
1141 result
= (int)processed
;
1155 * 'http_tls_set_credentials()' - Set the TLS credentials.
1158 static int /* O - Status of connection */
1159 http_tls_set_credentials(http_t
*http
) /* I - HTTP connection */
1161 _cups_globals_t
*cg
= _cupsGlobals(); /* Pointer to library globals */
1162 OSStatus error
= 0; /* Error code */
1163 http_tls_credentials_t credentials
= NULL
;
1164 /* TLS credentials */
1167 DEBUG_printf(("7http_tls_set_credentials(%p)", http
));
1170 * Prefer connection specific credentials...
1173 if ((credentials
= http
->tls_credentials
) == NULL
)
1174 credentials
= cg
->tls_credentials
;
1178 error
= SSLSetCertificate(http
->tls
, credentials
);
1179 DEBUG_printf(("4http_tls_set_credentials: SSLSetCertificate, error=%d",
1183 DEBUG_puts("4http_tls_set_credentials: No credentials to set.");
1190 * 'http_tls_start()' - Set up SSL/TLS support on a connection.
1193 static int /* O - 0 on success, -1 on failure */
1194 http_tls_start(http_t
*http
) /* I - HTTP connection */
1196 char hostname
[256], /* Hostname */
1197 *hostptr
; /* Pointer into hostname */
1198 _cups_globals_t
*cg
= _cupsGlobals();
1199 /* Pointer to library globals */
1200 OSStatus error
; /* Error code */
1201 const char *message
= NULL
;/* Error message */
1202 cups_array_t
*credentials
; /* Credentials array */
1203 cups_array_t
*names
; /* CUPS distinguished names */
1204 CFArrayRef dn_array
; /* CF distinguished names array */
1205 CFIndex count
; /* Number of credentials */
1206 CFDataRef data
; /* Certificate data */
1207 int i
; /* Looping var */
1208 http_credential_t
*credential
; /* Credential data */
1211 DEBUG_printf(("7http_tls_start(http=%p)", http
));
1213 #ifdef HAVE_SECKEYCHAINOPEN
1214 if (http
->mode
== _HTTP_MODE_SERVER
&& !tls_keychain
)
1216 if (http
->mode
== _HTTP_MODE_SERVER
)
1217 #endif /* HAVE_SECKEYCHAINOPEN */
1219 DEBUG_puts("4http_tls_start: cupsSetServerCredentials not called.");
1220 http
->error
= errno
= EINVAL
;
1221 http
->status
= HTTP_STATUS_ERROR
;
1222 _cupsSetError(IPP_STATUS_ERROR_INTERNAL
, _("Server credentials not set."), 1);
1227 if ((http
->tls
= SSLCreateContext(kCFAllocatorDefault
, http
->mode
== _HTTP_MODE_CLIENT
? kSSLClientSide
: kSSLServerSide
, kSSLStreamType
)) == NULL
)
1229 DEBUG_puts("4http_tls_start: SSLCreateContext failed.");
1230 http
->error
= errno
= ENOMEM
;
1231 http
->status
= HTTP_STATUS_ERROR
;
1232 _cupsSetHTTPError(HTTP_STATUS_ERROR
);
1237 error
= SSLSetConnection(http
->tls
, http
);
1238 DEBUG_printf(("4http_tls_start: SSLSetConnection, error=%d", (int)error
));
1242 error
= SSLSetIOFuncs(http
->tls
, http_cdsa_read
, http_cdsa_write
);
1243 DEBUG_printf(("4http_tls_start: SSLSetIOFuncs, error=%d", (int)error
));
1248 error
= SSLSetSessionOption(http
->tls
, kSSLSessionOptionBreakOnServerAuth
,
1250 DEBUG_printf(("4http_tls_start: SSLSetSessionOption, error=%d",
1254 if (!error
&& http
->mode
== _HTTP_MODE_CLIENT
)
1257 * Client: set client-side credentials, if any...
1260 if (cg
->client_cert_cb
)
1262 error
= SSLSetSessionOption(http
->tls
,
1263 kSSLSessionOptionBreakOnCertRequested
, true);
1264 DEBUG_printf(("4http_tls_start: kSSLSessionOptionBreakOnCertRequested, "
1265 "error=%d", (int)error
));
1269 error
= http_tls_set_credentials(http
);
1270 DEBUG_printf(("4http_tls_start: http_tls_set_credentials, error=%d",
1277 * Server: find/create a certificate for TLS...
1280 if (http
->fields
[HTTP_FIELD_HOST
][0])
1283 * Use hostname for TLS upgrade...
1286 strlcpy(hostname
, http
->fields
[HTTP_FIELD_HOST
], sizeof(hostname
));
1291 * Resolve hostname from connection address...
1294 http_addr_t addr
; /* Connection address */
1295 socklen_t addrlen
; /* Length of address */
1297 addrlen
= sizeof(addr
);
1298 if (getsockname(http
->fd
, (struct sockaddr
*)&addr
, &addrlen
))
1300 DEBUG_printf(("4http_tls_start: Unable to get socket address: %s", strerror(errno
)));
1303 else if (httpAddrLocalhost(&addr
))
1307 httpAddrLookup(&addr
, hostname
, sizeof(hostname
));
1308 DEBUG_printf(("4http_tls_start: Resolved socket address to \"%s\".", hostname
));
1312 #ifdef HAVE_SECKEYCHAINOPEN
1313 if (isdigit(hostname
[0] & 255) || hostname
[0] == '[')
1314 hostname
[0] = '\0'; /* Don't allow numeric addresses */
1317 http
->tls_credentials
= http_cdsa_copy_server(hostname
);
1318 else if (tls_common_name
)
1319 http
->tls_credentials
= http_cdsa_copy_server(tls_common_name
);
1321 if (!http
->tls_credentials
&& tls_auto_create
&& (hostname
[0] || tls_common_name
))
1323 DEBUG_printf(("4http_tls_start: Auto-create credentials for \"%s\".", hostname
[0] ? hostname
: tls_common_name
));
1325 if (!cupsMakeServerCredentials(tls_keypath
, hostname
[0] ? hostname
: tls_common_name
, 0, NULL
, time(NULL
) + 365 * 86400))
1327 DEBUG_puts("4http_tls_start: cupsMakeServerCredentials failed.");
1328 http
->error
= errno
= EINVAL
;
1329 http
->status
= HTTP_STATUS_ERROR
;
1330 _cupsSetError(IPP_STATUS_ERROR_INTERNAL
, _("Unable to create server credentials."), 1);
1335 http
->tls_credentials
= http_cdsa_copy_server(hostname
[0] ? hostname
: tls_common_name
);
1337 #endif /* HAVE_SECKEYCHAINOPEN */
1339 if (!http
->tls_credentials
)
1341 DEBUG_puts("4http_tls_start: Unable to find server credentials.");
1342 http
->error
= errno
= EINVAL
;
1343 http
->status
= HTTP_STATUS_ERROR
;
1344 _cupsSetError(IPP_STATUS_ERROR_INTERNAL
, _("Unable to find server credentials."), 1);
1349 error
= SSLSetCertificate(http
->tls
, http
->tls_credentials
);
1351 DEBUG_printf(("4http_tls_start: SSLSetCertificate, error=%d", (int)error
));
1354 DEBUG_printf(("4http_tls_start: tls_credentials=%p", http
->tls_credentials
));
1357 * Let the server know which hostname/domain we are trying to connect to
1358 * in case it wants to serve up a certificate with a matching common name.
1361 if (!error
&& http
->mode
== _HTTP_MODE_CLIENT
)
1364 * Client: get the hostname to use for TLS...
1367 if (httpAddrLocalhost(http
->hostaddr
))
1369 strlcpy(hostname
, "localhost", sizeof(hostname
));
1374 * Otherwise make sure the hostname we have does not end in a trailing dot.
1377 strlcpy(hostname
, http
->hostname
, sizeof(hostname
));
1378 if ((hostptr
= hostname
+ strlen(hostname
) - 1) >= hostname
&&
1383 error
= SSLSetPeerDomainName(http
->tls
, hostname
, strlen(hostname
));
1385 DEBUG_printf(("4http_tls_start: SSLSetPeerDomainName, error=%d", (int)error
));
1390 int done
= 0; /* Are we done yet? */
1392 while (!error
&& !done
)
1394 error
= SSLHandshake(http
->tls
);
1396 DEBUG_printf(("4http_tls_start: SSLHandshake returned %d.", (int)error
));
1404 case errSSLWouldBlock
:
1405 error
= noErr
; /* Force a retry */
1406 usleep(1000); /* in 1 millisecond */
1409 case errSSLServerAuthCompleted
:
1411 if (cg
->server_cert_cb
)
1413 error
= httpCopyCredentials(http
, &credentials
);
1416 error
= (cg
->server_cert_cb
)(http
, http
->tls
, credentials
,
1417 cg
->server_cert_data
);
1418 httpFreeCredentials(credentials
);
1421 DEBUG_printf(("4http_tls_start: Server certificate callback "
1422 "returned %d.", (int)error
));
1426 case errSSLClientCertRequested
:
1429 if (cg
->client_cert_cb
)
1432 if (!(error
= SSLCopyDistinguishedNames(http
->tls
, &dn_array
)) &&
1435 if ((names
= cupsArrayNew(NULL
, NULL
)) != NULL
)
1437 for (i
= 0, count
= CFArrayGetCount(dn_array
); i
< count
; i
++)
1439 data
= (CFDataRef
)CFArrayGetValueAtIndex(dn_array
, i
);
1441 if ((credential
= malloc(sizeof(*credential
))) != NULL
)
1443 credential
->datalen
= (size_t)CFDataGetLength(data
);
1444 if ((credential
->data
= malloc(credential
->datalen
)))
1446 memcpy((void *)credential
->data
, CFDataGetBytePtr(data
),
1447 credential
->datalen
);
1448 cupsArrayAdd(names
, credential
);
1456 CFRelease(dn_array
);
1461 error
= (cg
->client_cert_cb
)(http
, http
->tls
, names
,
1462 cg
->client_cert_data
);
1464 DEBUG_printf(("4http_tls_start: Client certificate callback "
1465 "returned %d.", (int)error
));
1468 httpFreeCredentials(names
);
1472 case errSSLUnknownRootCert
:
1473 message
= _("Unable to establish a secure connection to host "
1474 "(untrusted certificate).");
1477 case errSSLNoRootCert
:
1478 message
= _("Unable to establish a secure connection to host "
1479 "(self-signed certificate).");
1482 case errSSLCertExpired
:
1483 message
= _("Unable to establish a secure connection to host "
1484 "(expired certificate).");
1487 case errSSLCertNotYetValid
:
1488 message
= _("Unable to establish a secure connection to host "
1489 "(certificate not yet valid).");
1492 case errSSLHostNameMismatch
:
1493 message
= _("Unable to establish a secure connection to host "
1494 "(host name mismatch).");
1497 case errSSLXCertChainInvalid
:
1498 message
= _("Unable to establish a secure connection to host "
1499 "(certificate chain invalid).");
1502 case errSSLConnectionRefused
:
1503 message
= _("Unable to establish a secure connection to host "
1504 "(peer dropped connection before responding).");
1515 http
->error
= error
;
1516 http
->status
= HTTP_STATUS_ERROR
;
1517 errno
= ECONNREFUSED
;
1519 CFRelease(http
->tls
);
1523 * If an error string wasn't set by the callbacks use a generic one...
1527 #ifdef HAVE_CSSMERRORSTRING
1528 message
= cssmErrorString(error
);
1530 message
= _("Unable to establish a secure connection to host.");
1531 #endif /* HAVE_CSSMERRORSTRING */
1533 _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI
, message
, 1);
1543 * 'http_tls_stop()' - Shut down SSL/TLS on a connection.
1547 http_tls_stop(http_t
*http
) /* I - HTTP connection */
1549 while (SSLClose(http
->tls
) == errSSLWouldBlock
)
1552 CFRelease(http
->tls
);
1554 if (http
->tls_credentials
)
1555 CFRelease(http
->tls_credentials
);
1558 http
->tls_credentials
= NULL
;
1563 * 'http_tls_write()' - Write to a SSL/TLS connection.
1566 static int /* O - Bytes written */
1567 http_tls_write(http_t
*http
, /* I - HTTP connection */
1568 const char *buf
, /* I - Buffer holding data */
1569 int len
) /* I - Length of buffer */
1571 ssize_t result
; /* Return value */
1572 OSStatus error
; /* Error info */
1573 size_t processed
; /* Number of bytes processed */
1576 DEBUG_printf(("2http_tls_write(http=%p, buf=%p, len=%d)", http
, buf
, len
));
1578 error
= SSLWrite(http
->tls
, buf
, (size_t)len
, &processed
);
1583 result
= (int)processed
;
1586 case errSSLWouldBlock
:
1589 result
= (int)processed
;
1598 case errSSLClosedGraceful
:
1602 result
= (int)processed
;
1612 DEBUG_printf(("3http_tls_write: Returning %d.", (int)result
));
1614 return ((int)result
);
1620 * 'cupsdEndTLS()' - Shutdown a secure session with the client.
1623 int /* O - 1 on success, 0 on error */
1624 cupsdEndTLS(cupsd_client_t
*con
) /* I - Client connection */
1626 while (SSLClose(con
->http
.tls
) == errSSLWouldBlock
)
1629 CFRelease(con
->http
.tls
);
1630 con
->http
.tls
= NULL
;
1632 if (con
->http
.tls_credentials
)
1633 CFRelease(con
->http
.tls_credentials
);
1640 * 'cupsdStartTLS()' - Start a secure session with the client.
1643 int /* O - 1 on success, 0 on error */
1644 cupsdStartTLS(cupsd_client_t
*con
) /* I - Client connection */
1646 OSStatus error
= 0; /* Error code */
1647 SecTrustRef peerTrust
; /* Peer certificates */
1650 cupsdLogMessage(CUPSD_LOG_DEBUG
, "[Client %d] Encrypting connection.",
1653 con
->http
.tls_credentials
= copy_cdsa_certificate(con
);
1655 if (!con
->http
.tls_credentials
)
1658 * No keychain (yet), make a self-signed certificate...
1661 if (make_certificate(con
))
1662 con
->http
.tls_credentials
= copy_cdsa_certificate(con
);
1665 if (!con
->http
.tls_credentials
)
1667 cupsdLogMessage(CUPSD_LOG_ERROR
,
1668 "Could not find signing key in keychain \"%s\"",
1670 error
= errSSLBadConfiguration
;
1674 con
->http
.tls
= SSLCreateContext(kCFAllocatorDefault
, kSSLServerSide
,
1678 error
= SSLSetIOFuncs(con
->http
.tls
, http_cdsa_read
, http_cdsa_write
);
1681 error
= SSLSetConnection(con
->http
.tls
, HTTP(con
));
1684 error
= SSLSetCertificate(con
->http
.tls
, con
->http
.tls_credentials
);
1689 * Perform SSL/TLS handshake
1692 while ((error
= SSLHandshake(con
->http
.tls
)) == errSSLWouldBlock
)
1698 cupsdLogMessage(CUPSD_LOG_ERROR
,
1699 "Unable to encrypt connection from %s - %s (%d)",
1700 con
->http
.hostname
, cssmErrorString(error
), (int)error
);
1702 con
->http
.error
= error
;
1703 con
->http
.status
= HTTP_ERROR
;
1707 CFRelease(con
->http
.tls
);
1708 con
->http
.tls
= NULL
;
1711 if (con
->http
.tls_credentials
)
1713 CFRelease(con
->http
.tls_credentials
);
1714 con
->http
.tls_credentials
= NULL
;
1720 cupsdLogMessage(CUPSD_LOG_DEBUG
, "Connection from %s now encrypted.",
1721 con
->http
.hostname
);
1723 if (!SSLCopyPeerTrust(con
->http
.tls
, &peerTrust
) && peerTrust
)
1725 cupsdLogMessage(CUPSD_LOG_DEBUG
, "Received %d peer certificates.",
1726 (int)SecTrustGetCertificateCount(peerTrust
));
1727 CFRelease(peerTrust
);
1730 cupsdLogMessage(CUPSD_LOG_DEBUG
, "Received NO peer certificates.");