#include "identPicker.h"
#include <sys/param.h>
#include <string.h>
#include <stdio.h>
#include <ctype.h>
static void getString(
char *buf,
unsigned bufSize)
{
unsigned dex;
char c;
char *cp = buf;
for(dex=0; dex<bufSize-1; dex++) {
c = getchar();
if(!isprint(c)) {
break;
}
switch(c) {
case '\n':
case '\r':
goto done;
default:
*cp++ = c;
}
}
done:
*cp = '\0';
}
static char *kcItemPrintableName(
SecKeychainItemRef certRef)
{
char *crtn = NULL;
UInt32 tag = kSecLabelItemAttr;
SecKeychainAttributeInfo attrInfo;
attrInfo.count = 1;
attrInfo.tag = &tag;
attrInfo.format = NULL;
SecKeychainAttributeList *attrList = NULL;
SecKeychainAttribute *attr = NULL;
OSStatus ortn = SecKeychainItemCopyAttributesAndData(
(SecKeychainItemRef)certRef,
&attrInfo,
NULL, &attrList,
NULL, NULL); if(ortn) {
cssmPerror("SecKeychainItemCopyAttributesAndData", ortn);
return strdup("Unnamed KeychainItem");
}
if((attrList == NULL) || (attrList->count != 1)) {
printf("***Unexpected result fetching label attr\n");
crtn = strdup("Unnamed KeychainItem");
goto errOut;
}
attr = attrList->attr;
crtn = (char *)malloc(attr->length + 1);
memmove(crtn, attr->data, attr->length);
crtn[attr->length] = '\0';
errOut:
SecKeychainItemFreeAttributesAndData(attrList, NULL);
return crtn;
}
static char *kcFileName(
SecKeychainRef kcRef)
{
char fullPath[MAXPATHLEN + 1];
OSStatus ortn;
UInt32 pathLen = MAXPATHLEN;
ortn = SecKeychainGetPath(kcRef, &pathLen, fullPath);
if(ortn) {
cssmPerror("SecKeychainGetPath", ortn);
return strdup("orphan keychain");
}
fullPath[pathLen] = '\0';
char *lastSlash = NULL;
char *thisSlash = fullPath;
do {
thisSlash = strchr(thisSlash, '/');
if(thisSlash == NULL) {
break;
}
thisSlash++;
lastSlash = thisSlash;
} while(thisSlash != NULL);
if(lastSlash == NULL) {
return strdup(fullPath);
}
else {
return strdup(lastSlash);
}
}
static bool isCertRefRoot(
SecCertificateRef certRef)
{
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;
bool brtn = false;
OSStatus ortn = SecKeychainItemCopyAttributesAndData(
(SecKeychainItemRef)certRef,
&attrInfo,
NULL, &attrList,
NULL, NULL); if(ortn) {
cssmPerror("SecKeychainItemCopyAttributesAndData", ortn);
return false;
}
if((attrList == NULL) || (attrList->count != 2)) {
printf("***Unexpected result fetching label attr\n");
goto errOut;
}
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);
return brtn;
}
static OSStatus completeCertChain(
SecIdentityRef identity,
SecCertificateRef trustedAnchor, bool includeRoot, CFArrayRef *outArray) {
CFMutableArrayRef certArray;
SecTrustRef secTrust = NULL;
SecPolicyRef policy = NULL;
SecPolicySearchRef policySearch = NULL;
SecTrustResultType secTrustResult;
CSSM_TP_APPLE_EVIDENCE_INFO *dummyEv; CFArrayRef certChain = NULL; CFIndex numResCerts;
certArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
CFArrayAppendValue(certArray, identity);
SecCertificateRef certRef;
OSStatus ortn = SecIdentityCopyCertificate(identity, &certRef);
if(ortn) {
cssmPerror("SecIdentityCopyCertificate", ortn);
return ortn;
}
bool isRoot = isCertRefRoot(certRef);
if(isRoot) {
*outArray = certArray;
CFRelease(certRef);
return noErr;
}
CFMutableArrayRef subjCerts = CFArrayCreateMutable(NULL, 1, &kCFTypeArrayCallBacks);
CFArraySetValueAtIndex(subjCerts, 0, certRef);
CFRelease(certRef);
ortn = SecPolicySearchCreate(CSSM_CERT_X_509v3,
&CSSMOID_APPLE_X509_BASIC,
NULL, &policySearch);
if(ortn) {
cssmPerror("SecPolicySearchCreate", ortn);
goto errOut;
}
ortn = SecPolicySearchCopyNext(policySearch, &policy);
if(ortn) {
cssmPerror("SecPolicySearchCopyNext", ortn);
goto errOut;
}
ortn = SecTrustCreateWithCertificates(subjCerts,
policy, &secTrust);
if(ortn) {
cssmPerror("SecTrustCreateWithCertificates", ortn);
goto errOut;
}
if(trustedAnchor) {
CFMutableArrayRef newAnchors;
CFArrayRef currAnchors;
ortn = SecTrustCopyAnchorCertificates(&currAnchors);
if(ortn) {
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;
}
}
ortn = SecTrustEvaluate(secTrust, &secTrustResult);
if(ortn) {
cssmPerror("SecTrustEvaluate", ortn);
goto errOut;
}
switch(secTrustResult) {
case kSecTrustResultUnspecified:
case kSecTrustResultProceed:
break;
default:
printf("***Warning: could not construct completed cert chain\n");
ortn = noErr;
goto errOut;
}
ortn = SecTrustGetResult(secTrust, &secTrustResult, &certChain, &dummyEv);
if(ortn) {
cssmPerror("SecTrustEvaluate", ortn);
goto errOut;
}
numResCerts = CFArrayGetCount(certChain);
if(numResCerts < 2) {
printf("***sslCompleteCertChain screwup: numResCerts %d\n",
(int)numResCerts);
ortn = noErr;
goto errOut;
}
if(!includeRoot) {
numResCerts--;
}
for(CFIndex dex=1; dex<numResCerts; dex++) {
certRef = (SecCertificateRef)CFArrayGetValueAtIndex(certChain, dex);
CFArrayAppendValue(certArray, certRef);
}
errOut:
if(secTrust) {
CFRelease(secTrust);
}
if(subjCerts) {
CFRelease(subjCerts);
}
if(policy) {
CFRelease(policy);
}
if(policySearch) {
CFRelease(policySearch);
}
*outArray = certArray;
return ortn;
}
static CFIndex pickIdent(
CFArrayRef idArray)
{
CFIndex count = CFArrayGetCount(idArray);
CFIndex dex;
OSStatus ortn;
if(count == 0) {
printf("***sslIdentPicker screwup: no identities found\n");
return -1;
}
for(dex=0; dex<count; dex++) {
SecIdentityRef idRef = (SecIdentityRef)CFArrayGetValueAtIndex(idArray, dex);
SecCertificateRef certRef;
ortn = SecIdentityCopyCertificate(idRef, &certRef);
if(ortn) {
cssmPerror("SecIdentityCopyCertificate", ortn);
return -1;
}
char *certLabel = kcItemPrintableName((SecKeychainItemRef)certRef);
SecKeychainRef kcRef;
char *kcLabel;
ortn = SecKeychainItemCopyKeychain((SecKeychainItemRef)certRef, &kcRef);
if(ortn) {
cssmPerror("SecKeychainItemCopyKeychain", ortn);
kcLabel = "Unnamed keychain";
}
else {
kcLabel = kcFileName(kcRef);
}
printf("[%d] keychain : %s\n", (int)dex, kcLabel);
printf(" cert : %s\n", certLabel);
free(certLabel);
if(ortn == noErr) {
free(kcLabel);
}
CFRelease(certRef);
}
while(1) {
fpurge(stdin);
printf("\nEnter Certificate number or CR to quit : ");
fflush(stdout);
char resp[64];
getString(resp, sizeof(resp));
if(resp[0] == '\0') {
return -1;
}
int ires = atoi(resp);
if((ires >= 0) && (ires < count)) {
return (CFIndex)ires;
}
printf("***Invalid entry. Type a number between 0 and %d\n",
(int)(count-1));
}
return -1;
}
OSStatus simpleIdentPicker(
SecKeychainRef kcRef, SecIdentityRef *ident) {
OSStatus ortn;
CFMutableArrayRef idArray = NULL;
*ident = NULL;
SecIdentitySearchRef srchRef = nil;
ortn = SecIdentitySearchCreate(kcRef,
0, &srchRef);
if(ortn) {
cssmPerror("SecIdentitySearchCreate", (CSSM_RETURN)ortn);
printf("Cannot find signing key in keychain.\n");
return ortn;
}
SecIdentityRef identity = nil;
idArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
do {
ortn = SecIdentitySearchCopyNext(srchRef, &identity);
if(ortn != noErr) {
break;
}
CFArrayAppendValue(idArray, identity);
CFRelease(identity);
} while(ortn == noErr);
switch(ortn) {
case errSecItemNotFound:
if(CFArrayGetCount(idArray) == 0) {
printf("No signing keys found in keychain.\n");
return errSecItemNotFound;
}
else {
break;
}
default:
cssmPerror("SecIdentitySearchCopyNext", (CSSM_RETURN)ortn);
printf("Cannot find signing key in keychain.\n");
return ortn;
}
CFIndex whichId;
if(CFArrayGetCount(idArray) == 1) {
whichId = 0;
}
else {
whichId = pickIdent(idArray);
if(whichId < 0) {
return CSSMERR_CSSM_USER_CANCELED;
}
}
identity = (SecIdentityRef)CFArrayGetValueAtIndex(idArray, whichId);
CFRetain(identity);
CFRelease(idArray);
*ident = identity;
return noErr;
}
OSStatus identPicker(
SecKeychainRef kcRef, SecCertificateRef trustedAnchor, bool includeRoot, CFArrayRef *outArray) {
OSStatus ortn;
SecIdentityRef identity;
ortn = simpleIdentPicker(kcRef, &identity);
if(ortn) {
return ortn;
}
return completeCertChain(identity, trustedAnchor, includeRoot, outArray);
}