X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/72a12576750f52947eb043106ba5c12c0d07decf..b1ab9ed8d0e0f1c3b66d7daa8fd5564444c56195:/libsecurity_ssl/sslViewer/sslAppUtils.cpp diff --git a/libsecurity_ssl/sslViewer/sslAppUtils.cpp b/libsecurity_ssl/sslViewer/sslAppUtils.cpp new file mode 100644 index 00000000..8467f264 --- /dev/null +++ b/libsecurity_ssl/sslViewer/sslAppUtils.cpp @@ -0,0 +1,1646 @@ +/* + * Copyright (c) 2006-2008,2010-2012 Apple Inc. All Rights Reserved. + */ + +#include "sslAppUtils.h" +#include "fileIo.h" +#include +#include +#include +#include +#include +#include +#include +#include + +#define CFReleaseSafe(CF) { CFTypeRef _cf = (CF); if (_cf) CFRelease(_cf); } + +const char *sslGetCipherSuiteString(SSLCipherSuite cs) +{ + static char noSuite[40]; + + switch (cs) { + /* TLS cipher suites, RFC 2246 */ + case SSL_NULL_WITH_NULL_NULL: return "TLS_NULL_WITH_NULL_NULL"; + case SSL_RSA_WITH_NULL_MD5: return "TLS_RSA_WITH_NULL_MD5"; + case SSL_RSA_WITH_NULL_SHA: return "TLS_RSA_WITH_NULL_SHA"; + case SSL_RSA_EXPORT_WITH_RC4_40_MD5: return "TLS_RSA_EXPORT_WITH_RC4_40_MD5"; + case SSL_RSA_WITH_RC4_128_MD5: return "TLS_RSA_WITH_RC4_128_MD5"; + case SSL_RSA_WITH_RC4_128_SHA: return "TLS_RSA_WITH_RC4_128_SHA"; + case SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5: return "TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5"; + case SSL_RSA_WITH_IDEA_CBC_SHA: return "TLS_RSA_WITH_IDEA_CBC_SHA"; + case SSL_RSA_EXPORT_WITH_DES40_CBC_SHA: return "TLS_RSA_EXPORT_WITH_DES40_CBC_SHA"; + case SSL_RSA_WITH_DES_CBC_SHA: return "TLS_RSA_WITH_DES_CBC_SHA"; + case SSL_RSA_WITH_3DES_EDE_CBC_SHA: return "TLS_RSA_WITH_3DES_EDE_CBC_SHA"; + case SSL_DH_DSS_EXPORT_WITH_DES40_CBC_SHA: return "TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA"; + case SSL_DH_DSS_WITH_DES_CBC_SHA: return "TLS_DH_DSS_WITH_DES_CBC_SHA"; + case SSL_DH_DSS_WITH_3DES_EDE_CBC_SHA: return "TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA"; + case SSL_DH_RSA_EXPORT_WITH_DES40_CBC_SHA: return "TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA"; + case SSL_DH_RSA_WITH_DES_CBC_SHA: return "TLS_DH_RSA_WITH_DES_CBC_SHA"; + case SSL_DH_RSA_WITH_3DES_EDE_CBC_SHA: return "TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA"; + case SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA: return "TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA"; + case SSL_DHE_DSS_WITH_DES_CBC_SHA: return "TLS_DHE_DSS_WITH_DES_CBC_SHA"; + case SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA: return "TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA"; + case SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA: return "TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA"; + case SSL_DHE_RSA_WITH_DES_CBC_SHA: return "TLS_DHE_RSA_WITH_DES_CBC_SHA"; + case SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA: return "TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA"; + case SSL_DH_anon_EXPORT_WITH_RC4_40_MD5: return "TLS_DH_anon_EXPORT_WITH_RC4_40_MD5"; + case SSL_DH_anon_WITH_RC4_128_MD5: return "TLS_DH_anon_WITH_RC4_128_MD5"; + case SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA: return "TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA"; + case SSL_DH_anon_WITH_DES_CBC_SHA: return "TLS_DH_anon_WITH_DES_CBC_SHA"; + case SSL_DH_anon_WITH_3DES_EDE_CBC_SHA: return "TLS_DH_anon_WITH_3DES_EDE_CBC_SHA"; + + /* SSLv3 Fortezza cipher suites, from NSS */ + case SSL_FORTEZZA_DMS_WITH_NULL_SHA: return "SSL_FORTEZZA_DMS_WITH_NULL_SHA"; + case SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA:return "SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA"; + + /* TLS addenda using AES-CBC, RFC 3268 */ + case TLS_RSA_WITH_AES_128_CBC_SHA: return "TLS_RSA_WITH_AES_128_CBC_SHA"; + case TLS_DH_DSS_WITH_AES_128_CBC_SHA: return "TLS_DH_DSS_WITH_AES_128_CBC_SHA"; + case TLS_DH_RSA_WITH_AES_128_CBC_SHA: return "TLS_DH_RSA_WITH_AES_128_CBC_SHA"; + case TLS_DHE_DSS_WITH_AES_128_CBC_SHA: return "TLS_DHE_DSS_WITH_AES_128_CBC_SHA"; + case TLS_DHE_RSA_WITH_AES_128_CBC_SHA: return "TLS_DHE_RSA_WITH_AES_128_CBC_SHA"; + case TLS_DH_anon_WITH_AES_128_CBC_SHA: return "TLS_DH_anon_WITH_AES_128_CBC_SHA"; + case TLS_RSA_WITH_AES_256_CBC_SHA: return "TLS_RSA_WITH_AES_256_CBC_SHA"; + case TLS_DH_DSS_WITH_AES_256_CBC_SHA: return "TLS_DH_DSS_WITH_AES_256_CBC_SHA"; + case TLS_DH_RSA_WITH_AES_256_CBC_SHA: return "TLS_DH_RSA_WITH_AES_256_CBC_SHA"; + case TLS_DHE_DSS_WITH_AES_256_CBC_SHA: return "TLS_DHE_DSS_WITH_AES_256_CBC_SHA"; + case TLS_DHE_RSA_WITH_AES_256_CBC_SHA: return "TLS_DHE_RSA_WITH_AES_256_CBC_SHA"; + case TLS_DH_anon_WITH_AES_256_CBC_SHA: return "TLS_DH_anon_WITH_AES_256_CBC_SHA"; + + /* ECDSA addenda, RFC 4492 */ + case TLS_ECDH_ECDSA_WITH_NULL_SHA: return "TLS_ECDH_ECDSA_WITH_NULL_SHA"; + case TLS_ECDH_ECDSA_WITH_RC4_128_SHA: return "TLS_ECDH_ECDSA_WITH_RC4_128_SHA"; + case TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA: return "TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA"; + case TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA: return "TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA"; + case TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA: return "TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA"; + case TLS_ECDHE_ECDSA_WITH_NULL_SHA: return "TLS_ECDHE_ECDSA_WITH_NULL_SHA"; + case TLS_ECDHE_ECDSA_WITH_RC4_128_SHA: return "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA"; + case TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA: return "TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA"; + case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA: return "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA"; + case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA: return "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA"; + case TLS_ECDH_RSA_WITH_NULL_SHA: return "TLS_ECDH_RSA_WITH_NULL_SHA"; + case TLS_ECDH_RSA_WITH_RC4_128_SHA: return "TLS_ECDH_RSA_WITH_RC4_128_SHA"; + case TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA: return "TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA"; + case TLS_ECDH_RSA_WITH_AES_128_CBC_SHA: return "TLS_ECDH_RSA_WITH_AES_128_CBC_SHA"; + case TLS_ECDH_RSA_WITH_AES_256_CBC_SHA: return "TLS_ECDH_RSA_WITH_AES_256_CBC_SHA"; + case TLS_ECDHE_RSA_WITH_NULL_SHA: return "TLS_ECDHE_RSA_WITH_NULL_SHA"; + case TLS_ECDHE_RSA_WITH_RC4_128_SHA: return "TLS_ECDHE_RSA_WITH_RC4_128_SHA"; + case TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA: return "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA"; + case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA: return "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA"; + case TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA: return "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA"; + case TLS_ECDH_anon_WITH_NULL_SHA: return "TLS_ECDH_anon_WITH_NULL_SHA"; + case TLS_ECDH_anon_WITH_RC4_128_SHA: return "TLS_ECDH_anon_WITH_RC4_128_SHA"; + case TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA: return "TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA"; + case TLS_ECDH_anon_WITH_AES_128_CBC_SHA: return "TLS_ECDH_anon_WITH_AES_128_CBC_SHA"; + case TLS_ECDH_anon_WITH_AES_256_CBC_SHA: return "TLS_ECDH_anon_WITH_AES_256_CBC_SHA"; + + /* TLS 1.2 addenda, RFC 5246 */ + case TLS_RSA_WITH_AES_128_CBC_SHA256: return "TLS_RSA_WITH_AES_128_CBC_SHA256"; + case TLS_RSA_WITH_AES_256_CBC_SHA256: return "TLS_RSA_WITH_AES_256_CBC_SHA256"; + case TLS_DH_DSS_WITH_AES_128_CBC_SHA256: return "TLS_DH_DSS_WITH_AES_128_CBC_SHA256"; + case TLS_DH_RSA_WITH_AES_128_CBC_SHA256: return "TLS_DH_RSA_WITH_AES_128_CBC_SHA256"; + case TLS_DHE_DSS_WITH_AES_128_CBC_SHA256: return "TLS_DHE_DSS_WITH_AES_128_CBC_SHA256"; + case TLS_DHE_RSA_WITH_AES_128_CBC_SHA256: return "TLS_DHE_RSA_WITH_AES_128_CBC_SHA256"; + case TLS_DH_DSS_WITH_AES_256_CBC_SHA256: return "TLS_DH_DSS_WITH_AES_256_CBC_SHA256"; + case TLS_DH_RSA_WITH_AES_256_CBC_SHA256: return "TLS_DH_RSA_WITH_AES_256_CBC_SHA256"; + case TLS_DHE_DSS_WITH_AES_256_CBC_SHA256: return "TLS_DHE_DSS_WITH_AES_256_CBC_SHA256"; + case TLS_DHE_RSA_WITH_AES_256_CBC_SHA256: return "TLS_DHE_RSA_WITH_AES_256_CBC_SHA256"; + case TLS_DH_anon_WITH_AES_128_CBC_SHA256: return "TLS_DH_anon_WITH_AES_128_CBC_SHA256"; + case TLS_DH_anon_WITH_AES_256_CBC_SHA256: return "TLS_DH_anon_WITH_AES_256_CBC_SHA256"; + + /* TLS addenda using AES-GCM, RFC 5288 */ + case TLS_RSA_WITH_AES_128_GCM_SHA256: return "TLS_RSA_WITH_AES_128_GCM_SHA256"; + case TLS_RSA_WITH_AES_256_GCM_SHA384: return "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256"; + case TLS_DHE_RSA_WITH_AES_128_GCM_SHA256: return "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256"; + case TLS_DHE_RSA_WITH_AES_256_GCM_SHA384: return "TLS_DHE_RSA_WITH_AES_256_GCM_SHA384"; + case TLS_DH_RSA_WITH_AES_128_GCM_SHA256: return "TLS_DH_RSA_WITH_AES_128_GCM_SHA256"; + case TLS_DH_RSA_WITH_AES_256_GCM_SHA384: return "TLS_DH_RSA_WITH_AES_256_GCM_SHA384"; + case TLS_DHE_DSS_WITH_AES_128_GCM_SHA256: return "TLS_DHE_DSS_WITH_AES_128_GCM_SHA256"; + case TLS_DHE_DSS_WITH_AES_256_GCM_SHA384: return "TLS_DHE_DSS_WITH_AES_256_GCM_SHA384"; + case TLS_DH_DSS_WITH_AES_128_GCM_SHA256: return "TLS_DH_DSS_WITH_AES_128_GCM_SHA256"; + case TLS_DH_DSS_WITH_AES_256_GCM_SHA384: return "TLS_DH_DSS_WITH_AES_256_GCM_SHA384"; + case TLS_DH_anon_WITH_AES_128_GCM_SHA256: return "TLS_DH_anon_WITH_AES_128_GCM_SHA256"; + case TLS_DH_anon_WITH_AES_256_GCM_SHA384: return "TLS_DH_anon_WITH_AES_256_GCM_SHA384"; + + /* ECDSA addenda, RFC 5289 */ + case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256: return "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256"; + case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384: return "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384"; + case TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256: return "TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256"; + case TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384: return "TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384"; + case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256: return "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256"; + case TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384: return "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384"; + case TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256: return "TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256"; + case TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384: return "TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384"; + case TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: return "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256"; + case TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384: return "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384"; + case TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256: return "TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256"; + case TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384: return "TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384"; + case TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: return "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"; + case TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384: return "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384"; + case TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256: return "TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256"; + case TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384: return "TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384"; + + /* + * Tags for SSL 2 cipher kinds which are not specified for SSL 3. + */ + case SSL_RSA_WITH_RC2_CBC_MD5: return "TLS_RSA_WITH_RC2_CBC_MD5"; + case SSL_RSA_WITH_IDEA_CBC_MD5: return "TLS_RSA_WITH_IDEA_CBC_MD5"; + case SSL_RSA_WITH_DES_CBC_MD5: return "TLS_RSA_WITH_DES_CBC_MD5"; + case SSL_RSA_WITH_3DES_EDE_CBC_MD5: return "TLS_RSA_WITH_3DES_EDE_CBC_MD5"; + case SSL_NO_SUCH_CIPHERSUITE: return "SSL_NO_SUCH_CIPHERSUITE"; + + default: + sprintf(noSuite, "Unknown (0x%04X)", (unsigned)cs); + return noSuite; + } +} + +/* + * Given a SSLProtocolVersion - typically from SSLGetProtocolVersion - + * return a string representation. + */ +const char *sslGetProtocolVersionString(SSLProtocol prot) +{ + static char noProt[20]; + + switch(prot) { + case kSSLProtocolUnknown: return "kSSLProtocolUnknown"; + case kSSLProtocol2: return "kSSLProtocol2"; + case kSSLProtocol3: return "kSSLProtocol3"; + case kSSLProtocol3Only: return "kSSLProtocol3Only"; + case kTLSProtocol1: return "kTLSProtocol1"; + case kTLSProtocol1Only: return "kTLSProtocol1Only"; + case kTLSProtocol11: return "kTLSProtocol11"; + case kTLSProtocol12: return "kTLSProtocol12"; + default: + sprintf(noProt, "Unknown (%d)", (unsigned)prot); + return noProt; + } +} + +/* + * Return string representation of SecureTransport-related OSStatus. + */ +const char *sslGetSSLErrString(OSStatus err) +{ + static char noErrStr[20]; + + switch(err) { + case noErr: return "noErr"; + case memFullErr: return "memFullErr"; + case paramErr: return "paramErr"; + case unimpErr: return "unimpErr"; + case ioErr: return "ioErr"; + case badReqErr: return "badReqErr"; + /* SSL errors */ + case errSSLProtocol: return "errSSLProtocol"; + case errSSLNegotiation: return "errSSLNegotiation"; + case errSSLFatalAlert: return "errSSLFatalAlert"; + case errSSLWouldBlock: return "errSSLWouldBlock"; + case errSSLSessionNotFound: return "errSSLSessionNotFound"; + case errSSLClosedGraceful: return "errSSLClosedGraceful"; + case errSSLClosedAbort: return "errSSLClosedAbort"; + case errSSLXCertChainInvalid: return "errSSLXCertChainInvalid"; + case errSSLBadCert: return "errSSLBadCert"; + case errSSLCrypto: return "errSSLCrypto"; + case errSSLInternal: return "errSSLInternal"; + case errSSLModuleAttach: return "errSSLModuleAttach"; + case errSSLUnknownRootCert: return "errSSLUnknownRootCert"; + case errSSLNoRootCert: return "errSSLNoRootCert"; + case errSSLCertExpired: return "errSSLCertExpired"; + case errSSLCertNotYetValid: return "errSSLCertNotYetValid"; + case errSSLClosedNoNotify: return "errSSLClosedNoNotify"; + case errSSLBufferOverflow: return "errSSLBufferOverflow"; + case errSSLBadCipherSuite: return "errSSLBadCipherSuite"; + /* TLS/Panther addenda */ + case errSSLPeerUnexpectedMsg: return "errSSLPeerUnexpectedMsg"; + case errSSLPeerBadRecordMac: return "errSSLPeerBadRecordMac"; + case errSSLPeerDecryptionFail: return "errSSLPeerDecryptionFail"; + case errSSLPeerRecordOverflow: return "errSSLPeerRecordOverflow"; + case errSSLPeerDecompressFail: return "errSSLPeerDecompressFail"; + case errSSLPeerHandshakeFail: return "errSSLPeerHandshakeFail"; + case errSSLPeerBadCert: return "errSSLPeerBadCert"; + case errSSLPeerUnsupportedCert: return "errSSLPeerUnsupportedCert"; + case errSSLPeerCertRevoked: return "errSSLPeerCertRevoked"; + case errSSLPeerCertExpired: return "errSSLPeerCertExpired"; + case errSSLPeerCertUnknown: return "errSSLPeerCertUnknown"; + case errSSLIllegalParam: return "errSSLIllegalParam"; + case errSSLPeerUnknownCA: return "errSSLPeerUnknownCA"; + case errSSLPeerAccessDenied: return "errSSLPeerAccessDenied"; + case errSSLPeerDecodeError: return "errSSLPeerDecodeError"; + case errSSLPeerDecryptError: return "errSSLPeerDecryptError"; + case errSSLPeerExportRestriction: return "errSSLPeerExportRestriction"; + case errSSLPeerProtocolVersion: return "errSSLPeerProtocolVersion"; + case errSSLPeerInsufficientSecurity:return "errSSLPeerInsufficientSecurity"; + case errSSLPeerInternalError: return "errSSLPeerInternalError"; + case errSSLPeerUserCancelled: return "errSSLPeerUserCancelled"; + case errSSLPeerNoRenegotiation: return "errSSLPeerNoRenegotiation"; + case errSSLHostNameMismatch: return "errSSLHostNameMismatch"; + case errSSLConnectionRefused: return "errSSLConnectionRefused"; + case errSSLDecryptionFail: return "errSSLDecryptionFail"; + case errSSLBadRecordMac: return "errSSLBadRecordMac"; + case errSSLRecordOverflow: return "errSSLRecordOverflow"; + case errSSLBadConfiguration: return "errSSLBadConfiguration"; + + /* some from the Sec layer */ + case errSecNotAvailable: return "errSecNotAvailable"; + case errSecDuplicateItem: return "errSecDuplicateItem"; + case errSecItemNotFound: return "errSecItemNotFound"; +#if TARGET_OS_MAC + case errSecReadOnly: return "errSecReadOnly"; + case errSecAuthFailed: return "errSecAuthFailed"; + case errSecNoSuchKeychain: return "errSecNoSuchKeychain"; + case errSecInvalidKeychain: return "errSecInvalidKeychain"; + case errSecNoSuchAttr: return "errSecNoSuchAttr"; + case errSecInvalidItemRef: return "errSecInvalidItemRef"; + case errSecInvalidSearchRef: return "errSecInvalidSearchRef"; + case errSecNoSuchClass: return "errSecNoSuchClass"; + case errSecNoDefaultKeychain: return "errSecNoDefaultKeychain"; + case errSecWrongSecVersion: return "errSecWrongSecVersion"; + case errSecInvalidTrustSettings: return "errSecInvalidTrustSettings"; + case errSecNoTrustSettings: return "errSecNoTrustSettings"; +#endif + default: +#if 0 + if (err < (CSSM_BASE_ERROR + + (CSSM_ERRORCODE_MODULE_EXTENT * 8))) + { + /* assume CSSM error */ + return cssmErrToStr(err); + } + else +#endif + { + sprintf(noErrStr, "Unknown (%d)", (unsigned)err); + return noErrStr; + } + } +} + +void printSslErrStr( + const char *op, + OSStatus err) +{ + printf("*** %s: %s\n", op, sslGetSSLErrString(err)); +} + +const char *sslGetClientCertStateString(SSLClientCertificateState state) +{ + static char noState[20]; + + switch(state) { + case kSSLClientCertNone: return "ClientCertNone"; + case kSSLClientCertRequested: return "CertRequested"; + case kSSLClientCertSent: return "ClientCertSent"; + case kSSLClientCertRejected: return "ClientCertRejected"; + default: + sprintf(noState, "Unknown (%d)", (unsigned)state); + return noState; + } + +} + +const char *sslGetClientAuthTypeString(SSLClientAuthenticationType authType) +{ + static char noType[20]; + + switch(authType) { + case SSLClientAuthNone: return "None"; + case SSLClientAuth_RSASign: return "RSASign"; + case SSLClientAuth_DSSSign: return "DSSSign"; + case SSLClientAuth_RSAFixedDH: return "RSAFixedDH"; + case SSLClientAuth_DSS_FixedDH: return "DSS_FixedDH"; + case SSLClientAuth_ECDSASign: return "ECDSASign"; + case SSLClientAuth_RSAFixedECDH: return "RSAFixedECDH"; + case SSLClientAuth_ECDSAFixedECDH: return "ECDSAFixedECDH"; + default: + sprintf(noType, "Unknown (%d)", (unsigned)authType); + return noType; + } +} + +/* + * Convert a keychain name (which may be NULL) into the CFArrayRef required + * by SSLSetCertificate. This is a bare-bones example of this operation, + * since it requires and assumes that there is exactly one SecIdentity + * in the keychain - i.e., there is exactly one matching cert/private key + * pair. A real world server would probably search a keychain for a SecIdentity + * matching some specific criteria. + */ +CFArrayRef getSslCerts( + const char *kcName, // may be NULL, i.e., use default + bool encryptOnly, + bool completeCertChain, + const char *anchorFile, // optional trusted anchor + SecKeychainRef *pKcRef) // RETURNED +{ +#if 0 + SecKeychainRef kcRef = nil; + OSStatus ortn; + + *pKcRef = nil; + + /* pick a keychain */ + if(kcName) { + ortn = SecKeychainOpen(kcName, &kcRef); + if(ortn) { + printf("SecKeychainOpen returned %d.\n", (int)ortn); + printf("Cannot open keychain at %s. Aborting.\n", kcName); + return NULL; + } + } + else { + /* use default keychain */ + ortn = SecKeychainCopyDefault(&kcRef); + if(ortn) { + printf("SecKeychainCopyDefault returned %d; aborting.\n", (int)ortn); + return nil; + } + } + *pKcRef = kcRef; + return sslKcRefToCertArray(kcRef, encryptOnly, completeCertChain, anchorFile); +#elif TARGET_OS_IOS + SecCertificateRef cert = NULL; + SecIdentityRef identity = NULL; + CFMutableArrayRef certificates = NULL, result = NULL; + CFMutableDictionaryRef certQuery = NULL, keyQuery = NULL, keyResult = NULL; + SecTrustRef trust = NULL; + SecKeyRef key = NULL; + CFTypeRef pkdigest = NULL; + + // Find the first private key in the keychain and return both its + // attributes and a ref to it. + require(keyQuery = CFDictionaryCreateMutable(NULL, 0, NULL, NULL), errOut); + CFDictionaryAddValue(keyQuery, kSecClass, kSecClassKey); + CFDictionaryAddValue(keyQuery, kSecAttrKeyClass, kSecAttrKeyClassPrivate); + CFDictionaryAddValue(keyQuery, kSecReturnRef, kCFBooleanTrue); + CFDictionaryAddValue(keyQuery, kSecReturnAttributes, kCFBooleanTrue); + require_noerr(SecItemCopyMatching(keyQuery, (CFTypeRef *)&keyResult), + errOut); + require(key = (SecKeyRef)CFDictionaryGetValue(keyResult, kSecValueRef), + errOut); + require(pkdigest = CFDictionaryGetValue(keyResult, kSecAttrApplicationLabel), + errOut); + + // Find the first certificate that has the same public key hash as the + // returned private key and return it as a ref. + require(certQuery = CFDictionaryCreateMutable(NULL, 0, NULL, NULL), errOut); + CFDictionaryAddValue(certQuery, kSecClass, kSecClassCertificate); + CFDictionaryAddValue(certQuery, kSecAttrPublicKeyHash, pkdigest); + CFDictionaryAddValue(certQuery, kSecReturnRef, kCFBooleanTrue); + require_noerr(SecItemCopyMatching(certQuery, (CFTypeRef *)&cert), errOut); + + // Create an identity from the key and certificate. + require(identity = SecIdentityCreate(NULL, cert, key), errOut); + + // Build a (partial) certificate chain from cert + require(certificates = CFArrayCreateMutable(NULL, 0, + &kCFTypeArrayCallBacks), errOut); + CFArrayAppendValue(certificates, cert); + require_noerr(SecTrustCreateWithCertificates(certificates, NULL, &trust), + errOut); + SecTrustResultType tresult; + require_noerr(SecTrustEvaluate(trust, &tresult), errOut); + + CFIndex certCount, ix; + // We need at least 1 certificate + require(certCount = SecTrustGetCertificateCount(trust), errOut); + + // Build a result where element 0 is the identity and the other elements + // are the certs in the chain starting at the first intermediate up to the + // anchor, if we found one, or as far as we were able to build the chain + // if not. + require(result = CFArrayCreateMutable(NULL, certCount, &kCFTypeArrayCallBacks), + errOut); + + // We are commited to returning a result now, so do not use require below + // this line without setting result to NULL again. + CFArrayAppendValue(result, identity); + for (ix = 1; ix < certCount; ++ix) { + CFArrayAppendValue(result, SecTrustGetCertificateAtIndex(trust, ix)); + } + +errOut: + CFReleaseSafe(trust); + CFReleaseSafe(certificates); + CFReleaseSafe(identity); + CFReleaseSafe(cert); + CFReleaseSafe(certQuery); + CFReleaseSafe(keyResult); + CFReleaseSafe(keyQuery); + + return result; + +#else /* !TARGET_OS_IOS */ + SecIdentityRef identity = NULL; + CFMutableDictionaryRef query = NULL; + CFArrayRef items = NULL; + require(query = CFDictionaryCreateMutable(NULL, 0, NULL, NULL), errOut); + CFDictionaryAddValue(query, kSecClass, kSecClassIdentity); + CFDictionaryAddValue(query, kSecReturnRef, kCFBooleanTrue); + require_noerr(SecItemCopyMatching(query, (CFTypeRef *)&identity), errOut); + + items = CFArrayCreate(kCFAllocatorDefault, + (const void **)&identity, 1, &kCFTypeArrayCallBacks); + +errOut: + CFReleaseSafe(identity); + CFReleaseSafe(query); + + return items; + +#endif + +} + +#if 0 +/* + * Determine if specified SecCertificateRef is a self-signed cert. + * We do this by comparing the subject and issuerr names; no cryptographic + * verification is performed. + * + * Returns true if the cert appears to be a root. + */ +static bool isCertRefRoot( + SecCertificateRef certRef) +{ + bool brtn = false; +#if 0 + /* just search for the two attrs we want */ + UInt32 tags[2] = {kSecSubjectItemAttr, kSecIssuerItemAttr}; + SecKeychainAttributeInfo attrInfo; + attrInfo.count = 2; + attrInfo.tag = tags; + attrInfo.format = NULL; + SecKeychainAttributeList *attrList = NULL; + SecKeychainAttribute *attr1 = NULL; + SecKeychainAttribute *attr2 = NULL; + + OSStatus ortn = SecKeychainItemCopyAttributesAndData( + (SecKeychainItemRef)certRef, + &attrInfo, + NULL, // itemClass + &attrList, + NULL, // length - don't need the data + NULL); // outData + if(ortn) { + cssmPerror("SecKeychainItemCopyAttributesAndData", ortn); + /* may want to be a bit more robust here, but this should + * never happen */ + return false; + } + /* subsequent errors to errOut: */ + + if((attrList == NULL) || (attrList->count != 2)) { + printf("***Unexpected result fetching label attr\n"); + goto errOut; + } + + /* rootness is just byte-for-byte compare of the two names */ + attr1 = &attrList->attr[0]; + attr2 = &attrList->attr[1]; + if(attr1->length == attr2->length) { + if(memcmp(attr1->data, attr2->data, attr1->length) == 0) { + brtn = true; + } + } +errOut: + SecKeychainItemFreeAttributesAndData(attrList, NULL); +#endif + return brtn; +} +#endif + +#if 0 +/* + * Given a SecIdentityRef, do our best to construct a complete, ordered, and + * verified cert chain, returning the result in a CFArrayRef. The result is + * suitable for use when calling SSLSetCertificate(). + */ +OSStatus sslCompleteCertChain( + SecIdentityRef identity, + SecCertificateRef trustedAnchor, // optional additional trusted anchor + bool includeRoot, // include the root in outArray + CFArrayRef *outArray) // created and RETURNED +{ + CFMutableArrayRef certArray; + SecTrustRef secTrust = NULL; + SecPolicyRef policy = NULL; + SecPolicySearchRef policySearch = NULL; + SecTrustResultType secTrustResult; + CSSM_TP_APPLE_EVIDENCE_INFO *dummyEv; // not used + CFArrayRef certChain = NULL; // constructed chain + CFIndex numResCerts; + + certArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); + CFArrayAppendValue(certArray, identity); + + /* + * Case 1: identity is a root; we're done. Note that this case + * overrides the includeRoot argument. + */ + SecCertificateRef certRef; + OSStatus ortn = SecIdentityCopyCertificate(identity, &certRef); + if(ortn) { + /* should never happen */ + cssmPerror("SecIdentityCopyCertificate", ortn); + return ortn; + } + bool isRoot = isCertRefRoot(certRef); + if(isRoot) { + *outArray = certArray; + CFRelease(certRef); + return noErr; + } + + /* + * Now use SecTrust to get a complete cert chain, using all of the + * user's keychains to look for intermediate certs. + * NOTE this does NOT handle root certs which are not in the system + * root cert DB. (The above case, where the identity is a root cert, does.) + */ + CFMutableArrayRef subjCerts = CFArrayCreateMutable(NULL, 1, &kCFTypeArrayCallBacks); + CFArraySetValueAtIndex(subjCerts, 0, certRef); + + /* the array owns the subject cert ref now */ + CFRelease(certRef); + + /* Get a SecPolicyRef for generic X509 cert chain verification */ + ortn = SecPolicySearchCreate(CSSM_CERT_X_509v3, + &CSSMOID_APPLE_X509_BASIC, + NULL, // value + &policySearch); + if(ortn) { + cssmPerror("SecPolicySearchCreate", ortn); + goto errOut; + } + ortn = SecPolicySearchCopyNext(policySearch, &policy); + if(ortn) { + cssmPerror("SecPolicySearchCopyNext", ortn); + goto errOut; + } + + /* build a SecTrustRef for specified policy and certs */ + ortn = SecTrustCreateWithCertificates(subjCerts, + policy, &secTrust); + if(ortn) { + cssmPerror("SecTrustCreateWithCertificates", ortn); + goto errOut; + } + + if(trustedAnchor) { + /* + * Tell SecTrust to trust this one in addition to the current + * trusted system-wide anchors. + */ + CFMutableArrayRef newAnchors; + CFArrayRef currAnchors; + + ortn = SecTrustCopyAnchorCertificates(&currAnchors); + if(ortn) { + /* should never happen */ + cssmPerror("SecTrustCopyAnchorCertificates", ortn); + goto errOut; + } + newAnchors = CFArrayCreateMutableCopy(NULL, + CFArrayGetCount(currAnchors) + 1, + currAnchors); + CFRelease(currAnchors); + CFArrayAppendValue(newAnchors, trustedAnchor); + ortn = SecTrustSetAnchorCertificates(secTrust, newAnchors); + CFRelease(newAnchors); + if(ortn) { + cssmPerror("SecTrustSetAnchorCertificates", ortn); + goto errOut; + } + } + /* evaluate: GO */ + ortn = SecTrustEvaluate(secTrust, &secTrustResult); + if(ortn) { + cssmPerror("SecTrustEvaluate", ortn); + goto errOut; + } + switch(secTrustResult) { + case kSecTrustResultUnspecified: + /* cert chain valid, no special UserTrust assignments */ + case kSecTrustResultProceed: + /* cert chain valid AND user explicitly trusts this */ + break; + default: + /* + * Cert chain construction failed. + * Just go with the single subject cert we were given. + */ + printf("***Warning: could not construct completed cert chain\n"); + ortn = noErr; + goto errOut; + } + + /* get resulting constructed cert chain */ + ortn = SecTrustGetResult(secTrust, &secTrustResult, &certChain, &dummyEv); + if(ortn) { + cssmPerror("SecTrustEvaluate", ortn); + goto errOut; + } + + /* + * Copy certs from constructed chain to our result array, skipping + * the leaf (which is already there, as a SecIdentityRef) and possibly + * a root. + */ + numResCerts = CFArrayGetCount(certChain); + if(numResCerts < 2) { + /* + * Can't happen: if subject was a root, we'd already have returned. + * If chain doesn't verify to a root, we'd have bailed after + * SecTrustEvaluate(). + */ + printf("***sslCompleteCertChain screwup: numResCerts %d\n", + (int)numResCerts); + ortn = noErr; + goto errOut; + } + if(!includeRoot) { + /* skip the last (root) cert) */ + numResCerts--; + } + for(CFIndex dex=1; dexacceptedProts) { + printf(" Allowed SSL versions : %s\n", params->acceptedProts); + } + else { + printf(" Attempted SSL version : %s\n", + sslGetProtocolVersionString(params->tryVersion)); + } + printf(" Result : %s\n", sslGetSSLErrString(params->ortn)); + printf(" Negotiated SSL version : %s\n", + sslGetProtocolVersionString(params->negVersion)); + printf(" Negotiated CipherSuite : %s\n", + sslGetCipherSuiteString(params->negCipher)); + if(params->certState != kSSLClientCertNone) { + printf(" Client Cert State : %s\n", + sslGetClientCertStateString(params->certState)); + } +} +#endif + +/* print a '.' every few seconds to keep UI alive while connecting */ +static CFAbsoluteTime lastTime = (CFAbsoluteTime)0.0; +#define TIME_INTERVAL 3.0 + +void sslOutputDot() +{ + CFAbsoluteTime thisTime = CFAbsoluteTimeGetCurrent(); + + if(lastTime == 0.0) { + /* avoid printing first time thru */ + lastTime = thisTime; + return; + } + if((thisTime - lastTime) >= TIME_INTERVAL) { + printf("."); fflush(stdout); + lastTime = thisTime; + } +} + +#if 0 +/* main server pthread body */ +static void *sslServerThread(void *arg) +{ + SslAppTestParams *testParams = (SslAppTestParams *)arg; + OSStatus status; + + status = sslAppServe(testParams); + pthread_exit((void*)status); + /* NOT REACHED */ + return (void *)status; +} + +/* + * Run one session, with the server in a separate thread. + * On entry, serverParams->port is the port we attempt to run on; + * the server thread may overwrite that with a different port if it's + * unable to open the port we specify. Whatever is left in + * serverParams->port is what's used for the client side. + */ +#define CLIENT_WAIT_SECONDS 1 +int sslRunSession( + SslAppTestParams*serverParams, + SslAppTestParams *clientParams, + const char *testDesc) +{ + pthread_t serverPthread; + OSStatus clientRtn; + void *serverRtn; + + if(testDesc && !clientParams->quiet) { + printf("===== %s =====\n", testDesc); + } + + if(pthread_mutex_init(&serverParams->pthreadMutex, NULL)) { + printf("***Error initializing mutex; aborting.\n"); + return -1; + } + if(pthread_cond_init(&serverParams->pthreadCond, NULL)) { + printf("***Error initializing pthreadCond; aborting.\n"); + return -1; + } + serverParams->serverReady = false; // server sets true + + int result = pthread_create(&serverPthread, NULL, + sslServerThread, serverParams); + if(result) { + printf("***Error starting up server thread; aborting.\n"); + return result; + } + + /* wait for server to set up a socket we can connect to */ + if(pthread_mutex_lock(&serverParams->pthreadMutex)) { + printf("***Error acquiring server lock; aborting.\n"); + return -1; + } + while(!serverParams->serverReady) { + if(pthread_cond_wait(&serverParams->pthreadCond, &serverParams->pthreadMutex)) { + printf("***Error waiting server thread; aborting.\n"); + return -1; + } + } + pthread_mutex_unlock(&serverParams->pthreadMutex); + pthread_cond_destroy(&serverParams->pthreadCond); + pthread_mutex_destroy(&serverParams->pthreadMutex); + + clientParams->port = serverParams->port; + clientRtn = sslAppClient(clientParams); + /* server doesn't shut down its socket until it sees this */ + serverParams->clientDone = 1; + result = pthread_join(serverPthread, &serverRtn); + if(result) { + printf("***pthread_join returned %d, aborting\n", result); + return result; + } + + if(serverParams->verbose) { + sslShowResult("server", serverParams); + } + if(clientParams->verbose) { + sslShowResult("client", clientParams); + } + + /* verify results */ + int ourRtn = 0; + ourRtn += sslVerifyRtn("server", serverParams->expectRtn, serverParams->ortn); + ourRtn += sslVerifyRtn("client", clientParams->expectRtn, clientParams->ortn); + ourRtn += sslVerifyProtVers("server", serverParams->expectVersion, + serverParams->negVersion); + ourRtn += sslVerifyProtVers("client", clientParams->expectVersion, + clientParams->negVersion); + ourRtn += sslVerifyClientCertState("server", serverParams->expectCertState, + serverParams->certState); + ourRtn += sslVerifyClientCertState("client", clientParams->expectCertState, + clientParams->certState); + if(serverParams->ortn == noErr) { + ourRtn += sslVerifyCipher("server", serverParams->expectCipher, + serverParams->negCipher); + } + if(clientParams->ortn == noErr) { + ourRtn += sslVerifyCipher("client", clientParams->expectCipher, + clientParams->negCipher); + } + return ourRtn; +} + +/* + * Add all of the roots in a given KC to SSL ctx's trusted anchors. + */ +OSStatus sslAddTrustedRoots( + SSLContextRef ctx, + SecKeychainRef keychain, + bool *foundOne) // RETURNED, true if we found + // at least one root cert +{ + OSStatus ortn; + SecCertificateRef secCert; + SecKeychainSearchRef srch; + + *foundOne = false; + ortn = SecKeychainSearchCreateFromAttributes(keychain, + kSecCertificateItemClass, + NULL, // any attrs + &srch); + if(ortn) { + printSslErrStr("SecKeychainSearchCreateFromAttributes", ortn); + return ortn; + } + + /* + * Only use root certs. Not an error if we don't find any. + */ + do { + ortn = SecKeychainSearchCopyNext(srch, + (SecKeychainItemRef *)&secCert); + if(ortn) { + break; + } + + /* see if it's a root */ + if(!isCertRoot(secCert)) { + continue; + } + + /* Tell Secure Transport to trust this one. */ + ortn = addTrustedSecCert(ctx, secCert, false); + if(ortn) { + /* fatal */ + printSslErrStr("addTrustedSecCert", ortn); + return ortn; + } + CFRelease(secCert); + *foundOne = true; + } while(ortn == noErr); + CFRelease(srch); + return noErr; +} + +/* + * Wrapper for sslIdentPicker, with optional trusted anchor specified as a filename. + */ +OSStatus sslIdentityPicker( + SecKeychainRef kcRef, // NULL means use default list + const char *trustedAnchor, // optional additional trusted anchor + bool includeRoot, // true --> root is appended to outArray + // false --> root not included + CFArrayRef *outArray) // created and RETURNED +{ + SecCertificateRef trustedCert = NULL; + OSStatus ortn; + + if(trustedAnchor) { + ortn = sslReadAnchor(trustedAnchor, &trustedCert); + if(ortn) { + printf("***Error reading %s. sslIdentityPicker proceeding with no anchor.\n", + trustedAnchor); + trustedCert = NULL; + } + } + ortn = sslIdentPicker(kcRef, trustedCert, includeRoot, outArray); + if(trustedCert) { + CFRelease(trustedCert); + } + return ortn; +} + +/* + * Given a keychain name, convert it into a full path using the "SSL regression + * test suite algorithm". The Sec layer by default locates root root's keychains + * in different places depending on whether we're actually logged in as root + * or running via e.g. cron, so we force the location of root keychains to + * a hard-coded path. User keychain names we leave alone. + */ +void sslKeychainPath( + const char *kcName, + char *kcPath) // allocd by caller, MAXPATHLEN +{ + if(kcName[0] == '\0') { + kcPath[0] = '\0'; + } + else if(geteuid() == 0) { + /* root */ + sprintf(kcPath, "/Library/Keychains/%s", kcName); + } + else { + /* user, leave alone */ + strcpy(kcPath, kcName); + } +} + +/* Verify presence of required file. Returns nonzero if not found. */ +int sslCheckFile(const char *path) +{ + struct stat sb; + + if(stat(path, &sb)) { + printf("***Can't find file %s.\n", path); + printf(" Try running in the build directory, perhaps after running the\n" + " makeLocalCert script.\n"); + return 1; + } + return 0; +} + +#endif + +/* Stringify a SSL_ECDSA_NamedCurve */ +extern const char *sslCurveString( + SSL_ECDSA_NamedCurve namedCurve) +{ + static char unk[100]; + + switch(namedCurve) { + case SSL_Curve_None: return "Curve_None"; + case SSL_Curve_secp256r1: return "secp256r1"; + case SSL_Curve_secp384r1: return "secp384r1"; + case SSL_Curve_secp521r1: return "secp521r1"; + default: + sprintf(unk, "Unknown <%d>", (int)namedCurve); + return unk; + } +}