#include <Security/SecBase.h>
#include <Security/SecCertificate.h>
#include <Security/SecPolicy.h>
#include <Security/SecIdentity.h>
#include <Security/SecIdentityPriv.h>
#include <Security/SecIdentitySearch.h>
#include <Security/SecKeychain.h>
#include <Security/SecKeychainItem.h>
#include <Security/SecKeychainItemPriv.h>
#include <Security/SecKey.h>
#include <Security/SecKeyPriv.h>
#include <Security/SecTrust.h>
#include <Security/oidsalg.h>
#include <Security/cssmapi.h>
#include <Security/SecPolicySearch.h>
#include <CoreFoundation/CoreFoundation.h>
#include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
#include "plog.h"
#include "debug.h"
#include "misc.h"
#include "crypto_cssm.h"
static OSStatus FindPolicy(const CSSM_OID *policyOID, SecPolicyRef *policyRef);
static OSStatus EvaluateCert(SecCertificateRef cert, CFTypeRef policyRef);
static OSStatus CopySystemKeychain(SecKeychainRef *keychainRef);
static const char *GetSecurityErrorString(OSStatus err);
int crypto_cssm_check_x509cert(vchar_t *cert)
{
OSStatus status;
SecCertificateRef certRef = 0;
CSSM_DATA certData;
CSSM_OID ourPolicyOID = CSSMOID_APPLE_TP_IP_SEC;
SecPolicyRef policyRef = 0;
certData.Length = cert->l;
certData.Data = cert->v;
status = SecCertificateCreateFromData(&certData, CSSM_CERT_X_509v3, CSSM_CERT_ENCODING_DER,
&certRef);
if (status != noErr)
goto end;
status = FindPolicy(&ourPolicyOID, &policyRef);
if (status != noErr)
goto end;
status = EvaluateCert(certRef, policyRef);
end:
if (certRef)
CFRelease(certRef);
if (policyRef)
CFRelease(policyRef);
if (status != noErr && status != -1) {
plog(LLV_ERROR, LOCATION, NULL,
"error %d %s.\n", status, GetSecurityErrorString(status));
status = -1;
}
return status;
}
vchar_t* crypto_cssm_getsign(CFDataRef persistentCertRef, vchar_t* hash)
{
OSStatus status;
SecCertificateRef certificateRef = NULL;
SecIdentityRef identityRef = NULL;
SecIdentitySearchRef idSearchRef = NULL;
SecKeychainRef keychainRef = NULL;
SecKeyRef privateKeyRef = NULL;
const CSSM_KEY *cssmKey = NULL;
CSSM_CSP_HANDLE cspHandle = nil;
CSSM_CC_HANDLE cssmContextHandle = nil;
const CSSM_ACCESS_CREDENTIALS *credentials = NULL;
uint32 bytesEncrypted = 0;
CSSM_DATA clearData;
CSSM_DATA cipherData;
CSSM_DATA remData;
CSSM_CONTEXT_ATTRIBUTE newAttr;
vchar_t *sig = NULL;
remData.Length = 0;
remData.Data = 0;
if (persistentCertRef) {
status = SecKeychainItemCopyFromPersistentReference(persistentCertRef, (SecKeychainItemRef*)&certificateRef);
if (status != noErr)
goto end;
status = SecKeychainItemCopyKeychain((SecKeychainItemRef)certificateRef, &keychainRef);
if (status != noErr)
goto end;
status = SecIdentityCreateWithCertificate(keychainRef, certificateRef, &identityRef);
if (status != noErr)
goto end;
} else {
status = CopySystemKeychain(&keychainRef);
if (status != noErr)
goto end;
status = SecIdentitySearchCreate(keychainRef, CSSM_KEYUSE_SIGN, &idSearchRef);
if (status != noErr)
goto end;
status = SecIdentitySearchCopyNext(idSearchRef, &identityRef);
if (status != noErr)
goto end;
status = SecIdentityCopyCertificate(identityRef, &certificateRef);
if (status != noErr)
goto end;
}
status = SecIdentityCopyPrivateKey(identityRef, &privateKeyRef);
if (status != noErr)
goto end;
status = SecKeyGetCSSMKey(privateKeyRef, &cssmKey);
if (status != noErr)
goto end;
status = SecKeychainGetCSPHandle(keychainRef, &cspHandle);
if (status != noErr)
goto end;
status = SecKeyGetCredentials(privateKeyRef, CSSM_ACL_AUTHORIZATION_ENCRYPT,
kSecCredentialTypeNoUI, &credentials);
if (status != noErr)
goto end;
status = CSSM_CSP_CreateAsymmetricContext(cspHandle, CSSM_ALGID_RSA, credentials, cssmKey,
CSSM_PADDING_PKCS1, &cssmContextHandle);
if (status != noErr)
goto end;
newAttr.AttributeType = CSSM_ATTRIBUTE_MODE;
newAttr.AttributeLength = sizeof(uint32);
newAttr.Attribute.Data = (CSSM_DATA_PTR)CSSM_ALGMODE_PRIVATE_KEY;
status = CSSM_UpdateContextAttributes(cssmContextHandle, 1, &newAttr);
if(status != noErr)
goto end;
clearData.Length = hash->l;
clearData.Data = hash->v;
cipherData.Length = 0;
cipherData.Data = NULL;
status = CSSM_EncryptData(cssmContextHandle, &clearData, 1, &cipherData, 1, &bytesEncrypted,
&remData);
if (status != noErr)
goto end;
if (remData.Length != 0) { status = -1;
plog(LLV_ERROR, LOCATION, NULL,
"unencrypted data remaining after encrypting hash.\n");
goto end;
}
sig = vmalloc(cipherData.Length);
if (sig == NULL)
goto end;
sig->v = cipherData.Data;
end:
if (certificateRef)
CFRelease(certificateRef);
if (keychainRef)
CFRelease(keychainRef);
if (identityRef)
CFRelease(identityRef);
if (privateKeyRef)
CFRelease(privateKeyRef);
if (idSearchRef)
CFRelease(idSearchRef);
if (cssmContextHandle)
CSSM_DeleteContext(cssmContextHandle);
if (status != noErr) {
if (sig) {
vfree(sig);
sig = NULL;
}
}
if (status != noErr && status != -1) {
plog(LLV_ERROR, LOCATION, NULL,
"error %d %s.\n", status, GetSecurityErrorString(status));
status = -1;
}
return sig;
}
vchar_t* crypto_cssm_get_x509cert(CFDataRef persistentCertRef)
{
OSStatus status;
CSSM_DATA cssmData;
vchar_t *cert = NULL;
SecIdentityRef identityRef = NULL;
SecIdentitySearchRef idSearchRef = NULL;
SecKeychainRef keychainRef = NULL;
SecCertificateRef certificateRef = NULL;
if (persistentCertRef) {
status = SecKeychainItemCopyFromPersistentReference(persistentCertRef, (SecKeychainItemRef*)&certificateRef);
if (status != noErr)
goto end;
} else {
status = CopySystemKeychain(&keychainRef);
if (status != noErr)
goto end;
status = SecIdentitySearchCreate(keychainRef, CSSM_KEYUSE_SIGN, &idSearchRef);
if (status != noErr)
goto end;
status = SecIdentitySearchCopyNext(idSearchRef, &identityRef);
if (status != noErr)
goto end;
status = SecIdentityCopyCertificate(identityRef, &certificateRef);
if (status != noErr)
goto end;
}
cssmData.Length = 0;
cssmData.Data = NULL;
status = SecCertificateGetData(certificateRef, &cssmData);
if (status != noErr)
goto end;
if (cssmData.Length == 0)
goto end;
cert = vmalloc(cssmData.Length);
if (cert == NULL)
goto end;
memcpy(cert->v, cssmData.Data, cssmData.Length);
end:
if (certificateRef)
CFRelease(certificateRef);
if (identityRef)
CFRelease(identityRef);
if (idSearchRef)
CFRelease(idSearchRef);
if (keychainRef)
CFRelease(keychainRef);
if (status != noErr && status != -1) {
plog(LLV_ERROR, LOCATION, NULL,
"error %d %s.\n", status, GetSecurityErrorString(status));
status = -1;
}
return cert;
}
static OSStatus FindPolicy(const CSSM_OID *policyOID, SecPolicyRef *policyRef)
{
OSStatus status;
SecPolicySearchRef searchRef = nil;
status = SecPolicySearchCreate(CSSM_CERT_X_509v3, policyOID, NULL, &searchRef);
if (status != noErr)
goto end;
status = SecPolicySearchCopyNext(searchRef, policyRef);
end:
if (searchRef)
CFRelease(searchRef);
if (status != noErr) {
plog(LLV_ERROR, LOCATION, NULL,
"error %d %s.\n", status, GetSecurityErrorString(status));
status = -1;
}
return status;
}
static OSStatus EvaluateCert(SecCertificateRef cert, CFTypeRef policyRef)
{
OSStatus status;
SecTrustRef trustRef = 0;
SecTrustResultType evalResult;
SecCertificateRef evalCertArray[1] = { cert };
CFArrayRef cfCertRef = CFArrayCreate((CFAllocatorRef) NULL, (void*)evalCertArray, 1,
&kCFTypeArrayCallBacks);
if (!cfCertRef) {
plog(LLV_ERROR, LOCATION, NULL,
"unable to create CFArray.\n");
return -1;
}
status = SecTrustCreateWithCertificates(cfCertRef, policyRef, &trustRef);
if (status != noErr)
goto end;
status = SecTrustEvaluate(trustRef, &evalResult);
if (status != noErr)
goto end;
if (evalResult != kSecTrustResultProceed && evalResult != kSecTrustResultUnspecified) {
plog(LLV_ERROR, LOCATION, NULL,
"error evaluating certificate.\n");
status = -1;
}
end:
if (cfCertRef)
CFRelease(cfCertRef);
if (trustRef)
CFRelease(trustRef);
if (status != noErr && status != -1) {
plog(LLV_ERROR, LOCATION, NULL,
"error %d %s.\n", status, GetSecurityErrorString(status));
status = -1;
}
return status;
}
static OSStatus CopySystemKeychain(SecKeychainRef *keychainRef)
{
OSStatus status;
status = SecKeychainSetPreferenceDomain(kSecPreferencesDomainSystem);
if (status != noErr)
goto end;
status = SecKeychainCopyDomainDefault(kSecPreferencesDomainSystem, keychainRef);
end:
if (status != noErr) {
plog(LLV_ERROR, LOCATION, NULL,
"error %d %s.\n", status, GetSecurityErrorString(status));
status = -1;
}
return status;
}
const char *
GetSecurityErrorString(OSStatus err)
{
switch(err) {
case noErr:
return "noErr";
case memFullErr:
return "memFullErr";
case paramErr:
return "paramErr";
case unimpErr:
return "unimpErr";
case errSecNotAvailable:
return "errSecNotAvailable";
case errSecReadOnly:
return "errSecReadOnly";
case errSecAuthFailed:
return "errSecAuthFailed";
case errSecNoSuchKeychain:
return "errSecNoSuchKeychain";
case errSecInvalidKeychain:
return "errSecInvalidKeychain";
case errSecDuplicateKeychain:
return "errSecDuplicateKeychain";
case errSecDuplicateCallback:
return "errSecDuplicateCallback";
case errSecInvalidCallback:
return "errSecInvalidCallback";
case errSecDuplicateItem:
return "errSecDuplicateItem";
case errSecItemNotFound:
return "errSecItemNotFound";
case errSecBufferTooSmall:
return "errSecBufferTooSmall";
case errSecDataTooLarge:
return "errSecDataTooLarge";
case errSecNoSuchAttr:
return "errSecNoSuchAttr";
case errSecInvalidItemRef:
return "errSecInvalidItemRef";
case errSecInvalidSearchRef:
return "errSecInvalidSearchRef";
case errSecNoSuchClass:
return "errSecNoSuchClass";
case errSecNoDefaultKeychain:
return "errSecNoDefaultKeychain";
case errSecInteractionNotAllowed:
return "errSecInteractionNotAllowed";
case errSecReadOnlyAttr:
return "errSecReadOnlyAttr";
case errSecWrongSecVersion:
return "errSecWrongSecVersion";
case errSecKeySizeNotAllowed:
return "errSecKeySizeNotAllowed";
case errSecNoStorageModule:
return "errSecNoStorageModule";
case errSecNoCertificateModule:
return "errSecNoCertificateModule";
case errSecNoPolicyModule:
return "errSecNoPolicyModule";
case errSecInteractionRequired:
return "errSecInteractionRequired";
case errSecDataNotAvailable:
return "errSecDataNotAvailable";
case errSecDataNotModifiable:
return "errSecDataNotModifiable";
case errSecCreateChainFailed:
return "errSecCreateChainFailed";
case errSecACLNotSimple:
return "errSecACLNotSimple";
case errSecPolicyNotFound:
return "errSecPolicyNotFound";
case errSecInvalidTrustSetting:
return "errSecInvalidTrustSetting";
case errSecNoAccessForItem:
return "errSecNoAccessForItem";
case errSecInvalidOwnerEdit:
return "errSecInvalidOwnerEdit";
default:
return "<unknown>";
}
}