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 #ifdef HAVE_SECKEYCHAINOPEN
273 SecKeychainRef keychain
= NULL
;/* Temporary keychain */
276 if (SecKeychainOpen(path
, &keychain
) != noErr
)
278 /* TODO: Set cups last error string */
282 _cupsMutexLock(&tls_mutex
);
285 * Close any keychain that is currently open...
289 CFRelease(tls_keychain
);
292 _cupsStrFree(tls_keypath
);
295 _cupsStrFree(tls_common_name
);
298 * Save the new keychain...
301 tls_keychain
= keychain
;
302 tls_keypath
= _cupsStrAlloc(path
);
303 tls_auto_create
= auto_create
;
304 tls_common_name
= _cupsStrAlloc(common_name
);
306 _cupsMutexUnlock(&tls_mutex
);
312 #endif /* HAVE_SECKEYCHAINOPEN */
317 * 'httpCopyCredentials()' - Copy the credentials associated with the peer in
318 * an encrypted connection.
320 * @since CUPS 1.5/OS X 10.7@
323 int /* O - Status of call (0 = success) */
325 http_t
*http
, /* I - Connection to server */
326 cups_array_t
**credentials
) /* O - Array of credentials */
328 OSStatus error
; /* Error code */
329 SecTrustRef peerTrust
; /* Peer trust reference */
330 CFIndex count
; /* Number of credentials */
331 SecCertificateRef secCert
; /* Certificate reference */
332 CFDataRef data
; /* Certificate data */
333 int i
; /* Looping var */
339 if (!http
|| !http
->tls
|| !credentials
)
342 if (!(error
= SSLCopyPeerTrust(http
->tls
, &peerTrust
)) && peerTrust
)
344 if ((*credentials
= cupsArrayNew(NULL
, NULL
)) != NULL
)
346 count
= SecTrustGetCertificateCount(peerTrust
);
348 for (i
= 0; i
< count
; i
++)
350 secCert
= SecTrustGetCertificateAtIndex(peerTrust
, i
);
351 if ((data
= SecCertificateCopyData(secCert
)))
353 httpAddCredential(*credentials
, CFDataGetBytePtr(data
), (size_t)CFDataGetLength(data
));
359 CFRelease(peerTrust
);
367 * '_httpCreateCredentials()' - Create credentials in the internal format.
370 http_tls_credentials_t
/* O - Internal credentials */
371 _httpCreateCredentials(
372 cups_array_t
*credentials
) /* I - Array of credentials */
374 CFMutableArrayRef peerCerts
; /* Peer credentials reference */
375 SecCertificateRef secCert
; /* Certificate reference */
376 CFDataRef data
; /* Credential data reference */
377 http_credential_t
*credential
; /* Credential data */
383 if ((peerCerts
= CFArrayCreateMutable(kCFAllocatorDefault
,
384 cupsArrayCount(credentials
),
385 &kCFTypeArrayCallBacks
)) == NULL
)
388 for (credential
= (http_credential_t
*)cupsArrayFirst(credentials
);
390 credential
= (http_credential_t
*)cupsArrayNext(credentials
))
392 if ((data
= CFDataCreate(kCFAllocatorDefault
, credential
->data
, (CFIndex
)credential
->datalen
)))
394 if ((secCert
= SecCertificateCreateWithData(kCFAllocatorDefault
, data
))
397 CFArrayAppendValue(peerCerts
, secCert
);
410 * 'httpCredentialsAreTrusted()' - Return whether the credentials are trusted.
415 int /* O - 1 if trusted, 0 if not/unknown */
416 httpCredentialsAreTrusted(
417 cups_array_t
*credentials
) /* I - Credentials */
426 * 'httpCredentialsGetExpiration()' - Return the expiration date of the credentials.
431 time_t /* O - Expiration date of credentials */
432 httpCredentialsGetExpiration(
433 cups_array_t
*credentials
) /* I - Credentials */
442 * 'httpCredentialsIsValidName()' - Return whether the credentials are valid for the given name.
447 int /* O - 1 if valid, 0 otherwise */
448 httpCredentialsIsValidName(
449 cups_array_t
*credentials
, /* I - Credentials */
450 const char *common_name
) /* I - Name to check */
460 * 'httpCredentialsString()' - Return a string representing the credentials.
465 size_t /* O - Total size of credentials string */
466 httpCredentialsString(
467 cups_array_t
*credentials
, /* I - Credentials */
468 char *buffer
, /* I - Buffer or @code NULL@ */
469 size_t bufsize
) /* I - Size of buffer */
473 if (buffer
&& bufsize
> 0)
481 * '_httpFreeCredentials()' - Free internal credentials.
485 _httpFreeCredentials(
486 http_tls_credentials_t credentials
) /* I - Internal credentials */
491 CFRelease(credentials
);
496 * 'httpLoadCredentials()' - Load X.509 credentials from a keychain file.
501 int /* O - 0 on success, -1 on error */
503 const char *path
, /* I - Keychain/PKCS#12 path */
504 cups_array_t
**credentials
, /* IO - Credentials */
505 const char *common_name
) /* I - Common name for credentials */
514 OSStatus err
; /* Error info */
515 SecKeychainRef keychain
= NULL
;/* Keychain reference */
516 SecIdentitySearchRef search
= NULL
; /* Search reference */
517 SecIdentityRef identity
= NULL
;/* Identity */
518 CFArrayRef certificates
= NULL
;
519 /* Certificate array */
520 SecPolicyRef policy
= NULL
; /* Policy ref */
521 CFStringRef cfcommon_name
= NULL
;
523 CFMutableDictionaryRef query
= NULL
; /* Query qualifiers */
524 CFArrayRef list
= NULL
; /* Keychain list */
527 if ((err
= SecKeychainOpen(path
, &keychain
)))
530 cfcommon_name
= CFStringCreateWithCString(kCFAllocatorDefault
, common_name
, kCFStringEncodingUTF8
);
532 policy
= SecPolicyCreateSSL(1, cfcommon_name
);
535 CFRelease(cfcommon_name
);
540 if (!(query
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
)))
543 list
= CFArrayCreate(kCFAllocatorDefault
, (const void **)&keychain
, 1,
544 &kCFTypeArrayCallBacks
);
546 CFDictionaryAddValue(query
, kSecClass
, kSecClassIdentity
);
547 CFDictionaryAddValue(query
, kSecMatchPolicy
, policy
);
548 CFDictionaryAddValue(query
, kSecReturnRef
, kCFBooleanTrue
);
549 CFDictionaryAddValue(query
, kSecMatchLimit
, kSecMatchLimitOne
);
550 CFDictionaryAddValue(query
, kSecMatchSearchList
, list
);
554 err
= SecItemCopyMatching(query
, (CFTypeRef
*)&identity
);
559 if (CFGetTypeID(identity
) != SecIdentityGetTypeID())
562 if ((certificates
= CFArrayCreate(NULL
, (const void **)&identity
, 1, &kCFTypeArrayCallBacks
)) == NULL
)
579 return (certificates
);
586 * 'cupsMakeCredentials()' - Create self-signed credentials for the given
592 int /* O - 0 on success, -1 on error */
594 const char *path
, /* I - Keychain/PKCS#12 path */
595 cups_array_t
**credentials
, /* O - Credentials */
596 const char *common_name
) /* I - Common name for X.509 cert */
598 # ifdef HAVE_SECGENERATESELFSIGNEDCERTIFICATE
599 int status
= -1; /* Return status */
600 OSStatus err
; /* Error code (if any) */
601 CFStringRef cfcommon_name
= NULL
;
602 /* CF string for server name */
603 SecIdentityRef ident
= NULL
; /* Identity */
604 SecKeyRef publicKey
= NULL
,
608 CFMutableDictionaryRef keyParams
= NULL
;
609 /* Key generation parameters */
615 cfcommon_name
= CFStringCreateWithCString(kCFAllocatorDefault
, servername
,
616 kCFStringEncodingUTF8
);
621 * Create a public/private key pair...
624 keyParams
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
625 &kCFTypeDictionaryKeyCallBacks
,
626 &kCFTypeDictionaryValueCallBacks
);
630 CFDictionaryAddValue(keyParams
, kSecAttrKeyType
, kSecAttrKeyTypeRSA
);
631 CFDictionaryAddValue(keyParams
, kSecAttrKeySizeInBits
, CFSTR("2048"));
632 CFDictionaryAddValue(keyParams
, kSecAttrLabel
,
633 CFSTR("CUPS Self-Signed Certificate"));
635 err
= SecKeyGeneratePair(keyParams
, &publicKey
, &privateKey
);
640 * Create a self-signed certificate using the public/private key pair...
643 CFIndex usageInt
= kSecKeyUsageAll
;
644 CFNumberRef usage
= CFNumberCreate(alloc
, kCFNumberCFIndexType
, &usageInt
);
645 CFDictionaryRef certParams
= CFDictionaryCreateMutable(kCFAllocatorDefault
,
646 kSecCSRBasicContraintsPathLen
, CFINT(0), kSecSubjectAltName
, cfcommon_name
, kSecCertificateKeyUsage
, usage
, NULL
, NULL
);
649 const void *ca_o
[] = { kSecOidOrganization
, CFSTR("") };
650 const void *ca_cn
[] = { kSecOidCommonName
, cfcommon_name
};
651 CFArrayRef ca_o_dn
= CFArrayCreate(kCFAllocatorDefault
, ca_o
, 2, NULL
);
652 CFArrayRef ca_cn_dn
= CFArrayCreate(kCFAllocatorDefault
, ca_cn
, 2, NULL
);
653 const void *ca_dn_array
[2];
655 ca_dn_array
[0] = CFArrayCreate(kCFAllocatorDefault
, (const void **)&ca_o_dn
, 1, NULL
);
656 ca_dn_array
[1] = CFArrayCreate(kCFAllocatorDefault
, (const void **)&ca_cn_dn
, 1, NULL
);
658 CFArrayRef subject
= CFArrayCreate(kCFAllocatorDefault
, ca_dn_array
, 2, NULL
);
659 SecCertificateRef cert
= SecGenerateSelfSignedCertificate(subject
, certParams
, publicKey
, privateKey
);
661 CFRelease(certParams
);
666 ident
= SecIdentityCreate(kCFAllocatorDefault
, cert
, privateKey
);
672 * Cleanup and return...
678 CFRelease(cfcommon_name
);
681 CFRelease(keyParams
);
690 CFRelease(publicKey
);
693 CFRelease(publicKey
);
697 # else /* !HAVE_SECGENERATESELFSIGNEDCERTIFICATE */
698 int pid
, /* Process ID of command */
699 status
; /* Status of command */
700 char command
[1024], /* Command */
701 *argv
[4], /* Command-line arguments */
702 keychain
[1024], /* Keychain argument */
703 infofile
[1024]; /* Type-in information for cert */
704 cups_file_t
*fp
; /* Seed/info file */
708 * Run the "certtool" command to generate a self-signed certificate...
711 if (!cupsFileFind("certtool", getenv("PATH"), 1, command
, sizeof(command
)))
715 * Create a file with the certificate information fields...
717 * Note: This assumes that the default questions are asked by the certtool
721 if ((fp
= cupsTempFile2(infofile
, sizeof(infofile
))) == NULL
)
725 "%s\n" /* Enter key and certificate label */
726 "r\n" /* Generate RSA key pair */
727 "2048\n" /* Key size in bits */
728 "y\n" /* OK (y = yes) */
729 "b\n" /* Usage (b=signing/encryption) */
730 "s\n" /* Sign with SHA1 */
731 "y\n" /* OK (y = yes) */
732 "%s\n" /* Common name */
733 "\n" /* Country (default) */
734 "\n" /* Organization (default) */
735 "\n" /* Organizational unit (default) */
736 "\n" /* State/Province (default) */
737 "%s\n" /* Email address */
738 "y\n", /* OK (y = yes) */
739 common_name
, common_name
, "");
742 snprintf(keychain
, sizeof(keychain
), "k=%s", path
);
744 argv
[0] = "certtool";
749 posix_spawn_file_actions_t actions
; /* File actions */
751 posix_spawn_file_actions_init(&actions
);
752 posix_spawn_file_actions_addclose(&actions
, 0);
753 posix_spawn_file_actions_addopen(&actions
, 0, infofile
, O_RDONLY
, 0);
755 if (posix_spawn(&pid
, command
, &actions
, NULL
, argv
, environ
))
761 posix_spawn_file_actions_destroy(&actions
);
765 while (waitpid(pid
, &status
, 0) < 0)
775 # endif /* HAVE_SECGENERATESELFSIGNEDCERTIFICATE */
777 return (httpLoadCredentials(path
, credentials
, common_name
));
783 * 'httpSaveCredentials()' - Save X.509 credentials to a keychain file.
788 int /* O - -1 on error, 0 on success */
790 const char *path
, /* I - Keychain/PKCS#12 path */
791 cups_array_t
*credentials
, /* I - Credentials */
792 const char *common_name
) /* I - Common name for credentials */
802 #ifdef HAVE_SECKEYCHAINOPEN
804 * 'http_cdsa_copy_server()' - Find and copy server credentials from the keychain.
807 static CFArrayRef
/* O - Array of certificates or NULL */
808 http_cdsa_copy_server(
809 const char *common_name
) /* I - Server's hostname */
811 OSStatus err
; /* Error info */
812 SecIdentitySearchRef search
= NULL
; /* Search reference */
813 SecIdentityRef identity
= NULL
;/* Identity */
814 CFArrayRef certificates
= NULL
;
815 /* Certificate array */
816 SecPolicyRef policy
= NULL
; /* Policy ref */
817 CFStringRef cfcommon_name
= NULL
;
819 CFMutableDictionaryRef query
= NULL
; /* Query qualifiers */
820 CFArrayRef list
= NULL
; /* Keychain list */
823 cfcommon_name
= CFStringCreateWithCString(kCFAllocatorDefault
, common_name
, kCFStringEncodingUTF8
);
825 policy
= SecPolicyCreateSSL(1, cfcommon_name
);
828 CFRelease(cfcommon_name
);
833 if (!(query
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
)))
836 list
= CFArrayCreate(kCFAllocatorDefault
, (const void **)&tls_keychain
, 1, &kCFTypeArrayCallBacks
);
838 CFDictionaryAddValue(query
, kSecClass
, kSecClassIdentity
);
839 CFDictionaryAddValue(query
, kSecMatchPolicy
, policy
);
840 CFDictionaryAddValue(query
, kSecReturnRef
, kCFBooleanTrue
);
841 CFDictionaryAddValue(query
, kSecMatchLimit
, kSecMatchLimitOne
);
842 CFDictionaryAddValue(query
, kSecMatchSearchList
, list
);
846 err
= SecItemCopyMatching(query
, (CFTypeRef
*)&identity
);
851 if (CFGetTypeID(identity
) != SecIdentityGetTypeID())
854 if ((certificates
= CFArrayCreate(NULL
, (const void **)&identity
, 1, &kCFTypeArrayCallBacks
)) == NULL
)
869 return (certificates
);
871 #endif /* HAVE_SECKEYCHAINOPEN */
875 * 'http_cdsa_read()' - Read function for the CDSA library.
878 static OSStatus
/* O - -1 on error, 0 on success */
880 SSLConnectionRef connection
, /* I - SSL/TLS connection */
881 void *data
, /* I - Data buffer */
882 size_t *dataLength
) /* IO - Number of bytes */
884 OSStatus result
; /* Return value */
885 ssize_t bytes
; /* Number of bytes read */
886 http_t
*http
; /* HTTP connection */
889 http
= (http_t
*)connection
;
894 * Make sure we have data before we read...
897 while (!_httpWait(http
, http
->wait_value
, 0))
899 if (http
->timeout_cb
&& (*http
->timeout_cb
)(http
, http
->timeout_data
))
902 http
->error
= ETIMEDOUT
;
909 bytes
= recv(http
->fd
, data
, *dataLength
, 0);
911 while (bytes
== -1 && (errno
== EINTR
|| errno
== EAGAIN
));
913 if ((size_t)bytes
== *dataLength
)
919 *dataLength
= (size_t)bytes
;
920 result
= errSSLWouldBlock
;
927 result
= errSSLClosedGraceful
;
928 else if (errno
== EAGAIN
)
929 result
= errSSLWouldBlock
;
931 result
= errSSLClosedAbort
;
939 * 'http_cdsa_write()' - Write function for the CDSA library.
942 static OSStatus
/* O - -1 on error, 0 on success */
944 SSLConnectionRef connection
, /* I - SSL/TLS connection */
945 const void *data
, /* I - Data buffer */
946 size_t *dataLength
) /* IO - Number of bytes */
948 OSStatus result
; /* Return value */
949 ssize_t bytes
; /* Number of bytes read */
950 http_t
*http
; /* HTTP connection */
953 http
= (http_t
*)connection
;
957 bytes
= write(http
->fd
, data
, *dataLength
);
959 while (bytes
== -1 && (errno
== EINTR
|| errno
== EAGAIN
));
961 if ((size_t)bytes
== *dataLength
)
967 *dataLength
= (size_t)bytes
;
968 result
= errSSLWouldBlock
;
975 result
= errSSLWouldBlock
;
977 result
= errSSLClosedAbort
;
985 * 'http_tls_initialize()' - Initialize the TLS stack.
989 http_tls_initialize(void)
998 * 'http_tls_pending()' - Return the number of pending TLS-encrypted bytes.
1002 http_tls_pending(http_t
*http
) /* I - HTTP connection */
1004 size_t bytes
; /* Bytes that are available */
1007 if (!SSLGetBufferedReadSize(http
->tls
, &bytes
))
1015 * 'http_tls_read()' - Read from a SSL/TLS connection.
1018 static int /* O - Bytes read */
1019 http_tls_read(http_t
*http
, /* I - HTTP connection */
1020 char *buf
, /* I - Buffer to store data */
1021 int len
) /* I - Length of buffer */
1023 int result
; /* Return value */
1024 OSStatus error
; /* Error info */
1025 size_t processed
; /* Number of bytes processed */
1028 error
= SSLRead(http
->tls
, buf
, (size_t)len
, &processed
);
1029 DEBUG_printf(("6http_tls_read: error=%d, processed=%d", (int)error
,
1034 result
= (int)processed
;
1037 case errSSLWouldBlock
:
1039 result
= (int)processed
;
1047 case errSSLClosedGraceful
:
1050 result
= (int)processed
;
1064 * 'http_tls_set_credentials()' - Set the TLS credentials.
1067 static int /* O - Status of connection */
1068 http_tls_set_credentials(http_t
*http
) /* I - HTTP connection */
1070 _cups_globals_t
*cg
= _cupsGlobals(); /* Pointer to library globals */
1071 OSStatus error
= 0; /* Error code */
1072 http_tls_credentials_t credentials
= NULL
;
1073 /* TLS credentials */
1076 DEBUG_printf(("7http_tls_set_credentials(%p)", http
));
1079 * Prefer connection specific credentials...
1082 if ((credentials
= http
->tls_credentials
) == NULL
)
1083 credentials
= cg
->tls_credentials
;
1087 error
= SSLSetCertificate(http
->tls
, credentials
);
1088 DEBUG_printf(("4http_tls_set_credentials: SSLSetCertificate, error=%d",
1092 DEBUG_puts("4http_tls_set_credentials: No credentials to set.");
1099 * 'http_tls_start()' - Set up SSL/TLS support on a connection.
1102 static int /* O - 0 on success, -1 on failure */
1103 http_tls_start(http_t
*http
) /* I - HTTP connection */
1105 char hostname
[256], /* Hostname */
1106 *hostptr
; /* Pointer into hostname */
1107 _cups_globals_t
*cg
= _cupsGlobals();
1108 /* Pointer to library globals */
1109 OSStatus error
; /* Error code */
1110 const char *message
= NULL
;/* Error message */
1111 cups_array_t
*credentials
; /* Credentials array */
1112 cups_array_t
*names
; /* CUPS distinguished names */
1113 CFArrayRef dn_array
; /* CF distinguished names array */
1114 CFIndex count
; /* Number of credentials */
1115 CFDataRef data
; /* Certificate data */
1116 int i
; /* Looping var */
1117 http_credential_t
*credential
; /* Credential data */
1120 DEBUG_printf(("7http_tls_start(http=%p)", http
));
1122 #ifdef HAVE_SECKEYCHAINOPEN
1123 if (http
->mode
== _HTTP_MODE_SERVER
&& !tls_keychain
)
1125 if (http
->mode
== _HTTP_MODE_SERVER
)
1126 #endif /* HAVE_SECKEYCHAINOPEN */
1128 DEBUG_puts("4http_tls_start: cupsSetServerCredentials not called.");
1129 http
->error
= errno
= EINVAL
;
1130 http
->status
= HTTP_STATUS_ERROR
;
1131 _cupsSetError(IPP_STATUS_ERROR_INTERNAL
, _("Server credentials not set."), 1);
1136 if ((http
->tls
= SSLCreateContext(kCFAllocatorDefault
, http
->mode
== _HTTP_MODE_CLIENT
? kSSLClientSide
: kSSLServerSide
, kSSLStreamType
)) == NULL
)
1138 DEBUG_puts("4http_tls_start: SSLCreateContext failed.");
1139 http
->error
= errno
= ENOMEM
;
1140 http
->status
= HTTP_STATUS_ERROR
;
1141 _cupsSetHTTPError(HTTP_STATUS_ERROR
);
1146 error
= SSLSetConnection(http
->tls
, http
);
1147 DEBUG_printf(("4http_tls_start: SSLSetConnection, error=%d", (int)error
));
1151 error
= SSLSetIOFuncs(http
->tls
, http_cdsa_read
, http_cdsa_write
);
1152 DEBUG_printf(("4http_tls_start: SSLSetIOFuncs, error=%d", (int)error
));
1157 error
= SSLSetSessionOption(http
->tls
, kSSLSessionOptionBreakOnServerAuth
,
1159 DEBUG_printf(("4http_tls_start: SSLSetSessionOption, error=%d",
1163 if (!error
&& http
->mode
== _HTTP_MODE_CLIENT
)
1166 * Client: set client-side credentials, if any...
1169 if (cg
->client_cert_cb
)
1171 error
= SSLSetSessionOption(http
->tls
,
1172 kSSLSessionOptionBreakOnCertRequested
, true);
1173 DEBUG_printf(("4http_tls_start: kSSLSessionOptionBreakOnCertRequested, "
1174 "error=%d", (int)error
));
1178 error
= http_tls_set_credentials(http
);
1179 DEBUG_printf(("4http_tls_start: http_tls_set_credentials, error=%d",
1186 * Server: find/create a certificate for TLS...
1189 if (http
->fields
[HTTP_FIELD_HOST
][0])
1192 * Use hostname for TLS upgrade...
1195 strlcpy(hostname
, http
->fields
[HTTP_FIELD_HOST
], sizeof(hostname
));
1200 * Resolve hostname from connection address...
1203 http_addr_t addr
; /* Connection address */
1204 socklen_t addrlen
; /* Length of address */
1206 addrlen
= sizeof(addr
);
1207 if (getsockname(http
->fd
, (struct sockaddr
*)&addr
, &addrlen
))
1209 DEBUG_printf(("4http_tls_start: Unable to get socket address: %s", strerror(errno
)));
1212 else if (httpAddrLocalhost(&addr
))
1215 httpAddrString(&addr
, hostname
, sizeof(hostname
));
1218 #ifdef HAVE_SECKEYCHAINOPEN
1220 http
->tls_credentials
= http_cdsa_copy_server(hostname
);
1221 else if (tls_common_name
)
1222 http
->tls_credentials
= http_cdsa_copy_server(tls_common_name
);
1224 if (!http
->tls_credentials
&& tls_auto_create
&& (hostname
[0] || tls_common_name
))
1226 DEBUG_printf(("4http_tls_start: Auto-create credentials for \"%s\".", hostname
[0] ? hostname
: tls_common_name
));
1228 if (!cupsMakeServerCredentials(tls_keypath
, hostname
[0] ? hostname
: tls_common_name
, 0, NULL
, time(NULL
) + 365 * 86400))
1230 DEBUG_puts("4http_tls_start: cupsMakeServerCredentials failed.");
1231 http
->error
= errno
= EINVAL
;
1232 http
->status
= HTTP_STATUS_ERROR
;
1233 _cupsSetError(IPP_STATUS_ERROR_INTERNAL
, _("Unable to create server credentials."), 1);
1238 http
->tls_credentials
= http_cdsa_copy_server(hostname
[0] ? hostname
: tls_common_name
);
1240 #endif /* HAVE_SECKEYCHAINOPEN */
1242 if (!http
->tls_credentials
)
1244 DEBUG_puts("4http_tls_start: Unable to find server credentials.");
1245 http
->error
= errno
= EINVAL
;
1246 http
->status
= HTTP_STATUS_ERROR
;
1247 _cupsSetError(IPP_STATUS_ERROR_INTERNAL
, _("Unable to find server credentials."), 1);
1252 error
= SSLSetCertificate(http
->tls
, http
->tls_credentials
);
1254 DEBUG_printf(("4http_tls_start: SSLSetCertificate, error=%d", (int)error
));
1257 DEBUG_printf(("4http_tls_start: tls_credentials=%p", http
->tls_credentials
));
1260 * Let the server know which hostname/domain we are trying to connect to
1261 * in case it wants to serve up a certificate with a matching common name.
1264 if (!error
&& http
->mode
== _HTTP_MODE_CLIENT
)
1267 * Client: get the hostname to use for TLS...
1270 if (httpAddrLocalhost(http
->hostaddr
))
1272 strlcpy(hostname
, "localhost", sizeof(hostname
));
1277 * Otherwise make sure the hostname we have does not end in a trailing dot.
1280 strlcpy(hostname
, http
->hostname
, sizeof(hostname
));
1281 if ((hostptr
= hostname
+ strlen(hostname
) - 1) >= hostname
&&
1286 error
= SSLSetPeerDomainName(http
->tls
, hostname
, strlen(hostname
));
1288 DEBUG_printf(("4http_tls_start: SSLSetPeerDomainName, error=%d", (int)error
));
1293 int done
= 0; /* Are we done yet? */
1295 while (!error
&& !done
)
1297 error
= SSLHandshake(http
->tls
);
1299 DEBUG_printf(("4http_tls_start: SSLHandshake returned %d.", (int)error
));
1307 case errSSLWouldBlock
:
1308 error
= noErr
; /* Force a retry */
1309 usleep(1000); /* in 1 millisecond */
1312 case errSSLServerAuthCompleted
:
1314 if (cg
->server_cert_cb
)
1316 error
= httpCopyCredentials(http
, &credentials
);
1319 error
= (cg
->server_cert_cb
)(http
, http
->tls
, credentials
,
1320 cg
->server_cert_data
);
1321 httpFreeCredentials(credentials
);
1324 DEBUG_printf(("4http_tls_start: Server certificate callback "
1325 "returned %d.", (int)error
));
1329 case errSSLClientCertRequested
:
1332 if (cg
->client_cert_cb
)
1335 if (!(error
= SSLCopyDistinguishedNames(http
->tls
, &dn_array
)) &&
1338 if ((names
= cupsArrayNew(NULL
, NULL
)) != NULL
)
1340 for (i
= 0, count
= CFArrayGetCount(dn_array
); i
< count
; i
++)
1342 data
= (CFDataRef
)CFArrayGetValueAtIndex(dn_array
, i
);
1344 if ((credential
= malloc(sizeof(*credential
))) != NULL
)
1346 credential
->datalen
= (size_t)CFDataGetLength(data
);
1347 if ((credential
->data
= malloc(credential
->datalen
)))
1349 memcpy((void *)credential
->data
, CFDataGetBytePtr(data
),
1350 credential
->datalen
);
1351 cupsArrayAdd(names
, credential
);
1359 CFRelease(dn_array
);
1364 error
= (cg
->client_cert_cb
)(http
, http
->tls
, names
,
1365 cg
->client_cert_data
);
1367 DEBUG_printf(("4http_tls_start: Client certificate callback "
1368 "returned %d.", (int)error
));
1371 httpFreeCredentials(names
);
1375 case errSSLUnknownRootCert
:
1376 message
= _("Unable to establish a secure connection to host "
1377 "(untrusted certificate).");
1380 case errSSLNoRootCert
:
1381 message
= _("Unable to establish a secure connection to host "
1382 "(self-signed certificate).");
1385 case errSSLCertExpired
:
1386 message
= _("Unable to establish a secure connection to host "
1387 "(expired certificate).");
1390 case errSSLCertNotYetValid
:
1391 message
= _("Unable to establish a secure connection to host "
1392 "(certificate not yet valid).");
1395 case errSSLHostNameMismatch
:
1396 message
= _("Unable to establish a secure connection to host "
1397 "(host name mismatch).");
1400 case errSSLXCertChainInvalid
:
1401 message
= _("Unable to establish a secure connection to host "
1402 "(certificate chain invalid).");
1405 case errSSLConnectionRefused
:
1406 message
= _("Unable to establish a secure connection to host "
1407 "(peer dropped connection before responding).");
1418 http
->error
= error
;
1419 http
->status
= HTTP_STATUS_ERROR
;
1420 errno
= ECONNREFUSED
;
1422 CFRelease(http
->tls
);
1426 * If an error string wasn't set by the callbacks use a generic one...
1430 #ifdef HAVE_CSSMERRORSTRING
1431 message
= cssmErrorString(error
);
1433 message
= _("Unable to establish a secure connection to host.");
1434 #endif /* HAVE_CSSMERRORSTRING */
1436 _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI
, message
, 1);
1446 * 'http_tls_stop()' - Shut down SSL/TLS on a connection.
1450 http_tls_stop(http_t
*http
) /* I - HTTP connection */
1452 while (SSLClose(http
->tls
) == errSSLWouldBlock
)
1455 CFRelease(http
->tls
);
1457 if (http
->tls_credentials
)
1458 CFRelease(http
->tls_credentials
);
1461 http
->tls_credentials
= NULL
;
1466 * 'http_tls_write()' - Write to a SSL/TLS connection.
1469 static int /* O - Bytes written */
1470 http_tls_write(http_t
*http
, /* I - HTTP connection */
1471 const char *buf
, /* I - Buffer holding data */
1472 int len
) /* I - Length of buffer */
1474 ssize_t result
; /* Return value */
1475 OSStatus error
; /* Error info */
1476 size_t processed
; /* Number of bytes processed */
1479 DEBUG_printf(("2http_tls_write(http=%p, buf=%p, len=%d)", http
, buf
, len
));
1481 error
= SSLWrite(http
->tls
, buf
, (size_t)len
, &processed
);
1486 result
= (int)processed
;
1489 case errSSLWouldBlock
:
1492 result
= (int)processed
;
1501 case errSSLClosedGraceful
:
1505 result
= (int)processed
;
1515 DEBUG_printf(("3http_tls_write: Returning %d.", (int)result
));
1517 return ((int)result
);
1523 * 'cupsdEndTLS()' - Shutdown a secure session with the client.
1526 int /* O - 1 on success, 0 on error */
1527 cupsdEndTLS(cupsd_client_t
*con
) /* I - Client connection */
1529 while (SSLClose(con
->http
.tls
) == errSSLWouldBlock
)
1532 CFRelease(con
->http
.tls
);
1533 con
->http
.tls
= NULL
;
1535 if (con
->http
.tls_credentials
)
1536 CFRelease(con
->http
.tls_credentials
);
1543 * 'cupsdStartTLS()' - Start a secure session with the client.
1546 int /* O - 1 on success, 0 on error */
1547 cupsdStartTLS(cupsd_client_t
*con
) /* I - Client connection */
1549 OSStatus error
= 0; /* Error code */
1550 SecTrustRef peerTrust
; /* Peer certificates */
1553 cupsdLogMessage(CUPSD_LOG_DEBUG
, "[Client %d] Encrypting connection.",
1556 con
->http
.tls_credentials
= copy_cdsa_certificate(con
);
1558 if (!con
->http
.tls_credentials
)
1561 * No keychain (yet), make a self-signed certificate...
1564 if (make_certificate(con
))
1565 con
->http
.tls_credentials
= copy_cdsa_certificate(con
);
1568 if (!con
->http
.tls_credentials
)
1570 cupsdLogMessage(CUPSD_LOG_ERROR
,
1571 "Could not find signing key in keychain \"%s\"",
1573 error
= errSSLBadConfiguration
;
1577 con
->http
.tls
= SSLCreateContext(kCFAllocatorDefault
, kSSLServerSide
,
1581 error
= SSLSetIOFuncs(con
->http
.tls
, http_cdsa_read
, http_cdsa_write
);
1584 error
= SSLSetConnection(con
->http
.tls
, HTTP(con
));
1587 error
= SSLSetCertificate(con
->http
.tls
, con
->http
.tls_credentials
);
1592 * Perform SSL/TLS handshake
1595 while ((error
= SSLHandshake(con
->http
.tls
)) == errSSLWouldBlock
)
1601 cupsdLogMessage(CUPSD_LOG_ERROR
,
1602 "Unable to encrypt connection from %s - %s (%d)",
1603 con
->http
.hostname
, cssmErrorString(error
), (int)error
);
1605 con
->http
.error
= error
;
1606 con
->http
.status
= HTTP_ERROR
;
1610 CFRelease(con
->http
.tls
);
1611 con
->http
.tls
= NULL
;
1614 if (con
->http
.tls_credentials
)
1616 CFRelease(con
->http
.tls_credentials
);
1617 con
->http
.tls_credentials
= NULL
;
1623 cupsdLogMessage(CUPSD_LOG_DEBUG
, "Connection from %s now encrypted.",
1624 con
->http
.hostname
);
1626 if (!SSLCopyPeerTrust(con
->http
.tls
, &peerTrust
) && peerTrust
)
1628 cupsdLogMessage(CUPSD_LOG_DEBUG
, "Received %d peer certificates.",
1629 (int)SecTrustGetCertificateCount(peerTrust
));
1630 CFRelease(peerTrust
);
1633 cupsdLogMessage(CUPSD_LOG_DEBUG
, "Received NO peer certificates.");