#include <Security/Security.h>
#include <Security/SecKeyPriv.h>
#include <security_cdsa_utils/cuCdsaUtils.h>
#include <security_cdsa_utils/cuFileIo.h>
#include <security_cdsa_utils/cuPem.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <dotMacTp.h>
#include <security_cdsa_utils/cuPrintCert.h>
#include "keyPicker.h"
#include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
#define USER_DEFAULT "dmitchtest@mac.com"
#define PWD_DEF "123456"
static void usage(char **argv)
{
printf("usage: %s op [options]\n", argv[0]);
printf("Op:\n");
printf(" g generate identity cert\n");
printf(" G generate email signing cert\n");
printf(" e generate email encrypting cert\n");
printf(" l lookup cert (requires -f)\n");
printf(" L lookup identity cert (via -u)\n");
printf(" M lookup email signing cert (via -u)\n");
printf(" N lookup encrypting cert (via -u)\n");
printf("Options:\n");
printf(" -g Generate keypair\n");
printf(" -p pick key pair from existing\n");
printf(" -u username Default = %s\n", USER_DEFAULT);
printf(" -Z password Specify password immediately\n");
printf(" -z Use default password %s\n", PWD_DEF);
printf(" -k keychain Source/destination of keys and certs\n");
printf(" -c filename Write CSR to filename\n");
printf(" -C filename Use existing CSR (no keygen)\n");
printf(" -f refIdFile RefId file for cert lookup\n");
printf(" -n Do NOT post the CSR to the .mac server\n");
printf(" -H hostname Alternate .mac server host name (default %s)\n",
DOT_MAC_SIGN_HOST_NAME);
printf(" -o outFile Write output cert or refId (if any) to outFile\n");
printf(" -r Renew (default is new)\n");
printf(" -M Pause for MallocDebug\n");
printf(" -q Quiet\n");
printf(" -v Verbose\n");
printf(" -h Usage\n");
exit(1);
}
static CSSM_VERSION vers = {2, 0};
static CSSM_API_MEMORY_FUNCS memFuncs = {
cuAppMalloc,
cuAppFree,
cuAppRealloc,
cuAppCalloc,
NULL
};
static CSSM_TP_HANDLE dotMacStartup()
{
CSSM_TP_HANDLE tpHand;
CSSM_RETURN crtn;
if(cuCssmStartup() == CSSM_FALSE) {
return 0;
}
crtn = CSSM_ModuleLoad(&gGuidAppleDotMacTP,
CSSM_KEY_HIERARCHY_NONE,
NULL, NULL); if(crtn) {
cuPrintError("CSSM_ModuleLoad(DotMacTP)", crtn);
return 0;
}
crtn = CSSM_ModuleAttach (&gGuidAppleDotMacTP,
&vers,
&memFuncs, 0, CSSM_SERVICE_TP, 0, CSSM_KEY_HIERARCHY_NONE,
NULL, 0, NULL, &tpHand);
if(crtn) {
cuPrintError("CSSM_ModuleAttach(DotMacTP)", crtn);
return 0;
}
else {
return tpHand;
}
}
static void snDumpText(
const unsigned char *rcvBuf,
unsigned len)
{
char *cp = (char *)rcvBuf;
unsigned i;
char c;
for(i=0; i<len; i++) {
c = *cp++;
if(c == '\0') {
break;
}
switch(c) {
case '\n':
printf("\\n\n"); break;
case '\r':
printf("\\r\n");
break;
default:
if(isprint(c) && (c != '\n')) {
printf("%c", c);
}
else {
printf("<%02X>", ((unsigned)c) & 0xff);
}
break;
}
}
}
static OSStatus genKeyPair(
SecKeychainRef kcRef, SecKeyRef *pubKey, SecKeyRef *privKey) {
OSStatus ortn;
ortn = SecKeyCreatePair(kcRef,
DOT_MAC_KEY_ALG,
DOT_MAC_KEY_SIZE,
0,
CSSM_KEYUSE_ANY,
CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_PERMANENT | CSSM_KEYATTR_EXTRACTABLE,
CSSM_KEYUSE_ANY,
CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_PERMANENT | CSSM_KEYATTR_EXTRACTABLE |
CSSM_KEYATTR_SENSITIVE,
NULL, pubKey,
privKey);
if(ortn) {
cssmPerror("SecKeyCreatePair", ortn);
}
return ortn;
}
OSStatus doLookupViaRefId(
CSSM_TP_HANDLE tpHand,
unsigned char *refId,
unsigned refIdLen,
char *outFile,
bool verbose)
{
CSSM_DATA refIdData = { refIdLen, refId };
sint32 EstimatedTime;
CSSM_BOOL ConfirmationRequired;
CSSM_TP_RESULT_SET_PTR resultSet = NULL;
CSSM_RETURN crtn;
crtn = CSSM_TP_RetrieveCredResult(tpHand, &refIdData, NULL,
&EstimatedTime, &ConfirmationRequired, &resultSet);
if(crtn) {
cssmPerror("CSSM_TP_RetrieveCredResult", crtn);
return crtn;
}
if(resultSet == NULL) {
printf("***CSSM_TP_RetrieveCredResult OK, but no result set\n");
return -1;
}
if(resultSet->NumberOfResults != 1) {
printf("***CSSM_TP_RetrieveCredResult OK, NumberOfResults (%u)\n",
(unsigned)resultSet->NumberOfResults);
return -1;
}
if(resultSet->Results == NULL) {
printf("***CSSM_TP_RetrieveCredResult OK, but empty result set\n");
return -1;
}
CSSM_DATA_PTR certData = (CSSM_DATA_PTR)resultSet->Results;
printf("...cert retrieval complete\n");
if(outFile) {
if(!writeFile(outFile, certData->Data, certData->Length)) {
printf("...%lu bytes of cert data written to %s\n",
certData->Length, outFile);
}
else {
printf("***Error writing cert to %s\n", outFile);
crtn = ioErr;
}
}
else if(verbose) {
unsigned char *der;
unsigned derLen;
if(pemDecode(certData->Data, certData->Length, &der, &derLen)) {
printf("***Error PEM decoding returned cert\n");
}
else {
printCert(der, derLen, CSSM_FALSE);
free(der);
}
}
return noErr;
}
OSStatus doLookupViaUserName(
CSSM_TP_HANDLE tpHand,
const CSSM_OID *opOid,
const char *userName,
const char *hostName, char *outFile,
bool verbose)
{
CSSM_APPLE_DOTMAC_TP_CERT_REQUEST certReq;
CSSM_TP_AUTHORITY_ID tpAuthority;
CSSM_TP_AUTHORITY_ID *tpAuthPtr = NULL;
CSSM_NET_ADDRESS tpNetAddrs;
CSSM_TP_REQUEST_SET reqSet;
CSSM_FIELD policyField;
CSSM_DATA certData = {0, NULL};
sint32 estTime;
CSSM_TP_CALLERAUTH_CONTEXT callerAuth;
memset(&certReq, 0, sizeof(certReq));
certReq.userName.Data = (uint8 *)userName;
certReq.userName.Length = strlen(userName);
if(hostName != NULL) {
tpAuthority.AuthorityCert = NULL;
tpAuthority.AuthorityLocation = &tpNetAddrs;
tpNetAddrs.AddressType = CSSM_ADDR_NAME;
tpNetAddrs.Address.Data = (uint8 *)hostName;
tpNetAddrs.Address.Length = strlen(hostName);
tpAuthPtr = &tpAuthority;
};
certReq.version = CSSM_DOT_MAC_TP_REQ_VERSION;
reqSet.NumberOfRequests = 1;
reqSet.Requests = &certReq;
policyField.FieldOid = *opOid;
policyField.FieldValue.Data = NULL;
policyField.FieldValue.Length = 0;
memset(&callerAuth, 0, sizeof(callerAuth));
callerAuth.Policy.NumberOfPolicyIds = 1;
callerAuth.Policy.PolicyIds = &policyField;
CSSM_RETURN crtn = CSSM_TP_SubmitCredRequest (tpHand,
tpAuthPtr,
CSSM_TP_AUTHORITY_REQUEST_CERTLOOKUP,
&reqSet, &callerAuth,
&estTime, &certData);
if(crtn) {
cssmPerror("CSSM_TP_SubmitCredRequest(lookup)", crtn);
return crtn;
}
printf("...cert lookup complete\n");
if(outFile) {
if(!writeFile(outFile, certData.Data, certData.Length)) {
printf("...%lu bytes of cert data written to %s\n",
certData.Length, outFile);
}
else {
printf("***Error writing cert to %s\n", outFile);
crtn = ioErr;
}
}
if(verbose) {
printCert(certData.Data, certData.Length, CSSM_FALSE);
}
return crtn;
}
#define FULL_EMAIL_ADDRESS 1
int main(int argc, char **argv)
{
CSSM_RETURN crtn;
CSSM_TP_AUTHORITY_ID tpAuthority;
CSSM_TP_AUTHORITY_ID *tpAuthPtr = NULL;
CSSM_NET_ADDRESS tpNetAddrs;
CSSM_APPLE_DOTMAC_TP_CERT_REQUEST certReq;
CSSM_TP_REQUEST_SET reqSet;
CSSM_CSP_HANDLE cspHand = 0;
CSSM_X509_TYPE_VALUE_PAIR tvp;
char pwdBuf[1000];
CSSM_TP_CALLERAUTH_CONTEXT callerAuth;
sint32 estTime;
CSSM_DATA refId = {0, NULL};
OSStatus ortn;
SecKeyRef pubKeyRef = NULL;
SecKeyRef privKeyRef = NULL;
const CSSM_KEY *privKey = NULL;
const CSSM_KEY *pubKey = NULL;
SecKeychainRef kcRef = NULL;
CSSM_FIELD policyField;
bool genKeys = false;
bool pickKeys = false;
char *keychainName = NULL;
char *csrOutName = NULL;
char *csrInName = NULL;
const char *userName = USER_DEFAULT;
char *password = NULL;
char *hostName = NULL;
bool doNotPost = false;
bool doRenew = false;
const CSSM_OID *opOid = NULL;
char *outFile = NULL;
bool quiet = false;
bool verbose = false;
bool lookupViaRefId = false;
bool lookupViaUserName = false;
char *refIdFile = NULL;
bool doPause = false;
if(argc < 2) {
usage(argv);
}
switch(argv[1][0]) {
case 'L':
lookupViaUserName = true;
case 'g':
opOid = &CSSMOID_DOTMAC_CERT_REQ_IDENTITY;
break;
case 'M':
lookupViaUserName = true;
case 'G':
opOid = &CSSMOID_DOTMAC_CERT_REQ_EMAIL_SIGN;
break;
case 'N':
lookupViaUserName = true;
case 'e':
opOid = &CSSMOID_DOTMAC_CERT_REQ_EMAIL_ENCRYPT;
break;
case 'l':
lookupViaRefId = true;
break;
default:
usage(argv);
}
extern char *optarg;
extern int optind;
optind = 2;
int arg;
while ((arg = getopt(argc, argv, "gpk:c:u:Z:H:nzrC:o:hf:Mqv")) != -1) {
switch (arg) {
case 'g':
genKeys = true;
break;
case 'p':
pickKeys = true;
break;
case 'u':
userName = optarg;
break;
case 'Z':
password = optarg;
break;
case 'z':
password = (char *)PWD_DEF;
break;
case 'k':
keychainName = optarg;
break;
case 'c':
csrOutName = optarg;
break;
case 'C':
csrInName = optarg;
break;
case 'H':
hostName = optarg;
break;
case 'n':
doNotPost = true;
break;
case 'r':
doRenew = true;
break;
case 'o':
outFile = optarg;
break;
case 'f':
refIdFile = optarg;
break;
case 'M':
doPause = true;
break;
case 'v':
verbose = true;
break;
case 'q':
quiet = true;
break;
case 'h':
usage(argv);
}
}
if(doPause) {
fpurge(stdin);
printf("Pausing for MallocDebug attach; CR to continue: ");
getchar();
}
CSSM_TP_HANDLE tpHand = dotMacStartup();
if(tpHand == 0) {
printf("Error attaching to the .mac TP. Check your MDS file.\n");
exit(1);
}
if(lookupViaRefId) {
if(refIdFile == NULL) {
printf("I need a refIdFile to do a lookup.\n");
usage(argv);
}
unsigned char *refId;
unsigned refIdLen;
int irtn = readFile(refIdFile, &refId, &refIdLen);
if(irtn) {
printf("***Error reading refId from %s. Aborting.\n", refIdFile);
exit(1);
}
ortn = doLookupViaRefId(tpHand, refId, refIdLen, outFile, verbose);
free(refId);
goto done;
}
if(lookupViaUserName) {
ortn = doLookupViaUserName(tpHand, opOid, userName, hostName, outFile, verbose);
goto done;
}
if(!pickKeys && !genKeys && (csrInName == NULL)) {
printf("***You must specify either the -p (pick keys) or -g (generate keys)"
" arguments, or provide a CSR (-C).\n");
exit(1);
}
memset(&certReq, 0, sizeof(certReq));
if(keychainName != NULL) {
ortn = SecKeychainOpen(keychainName, &kcRef);
if(ortn) {
cssmPerror("SecKeychainOpen", ortn);
exit(1);
}
SecKeychainStatus kcStat;
ortn = SecKeychainGetStatus(kcRef, &kcStat);
if(ortn) {
cssmPerror("SecKeychainGetStatus", ortn);
goto done;
}
}
if(password == NULL) {
const char *pwdp = getpass("Enter .mac password: ");
if(pwdp == NULL) {
printf("Aboerting.\n");
ortn = paramErr;
goto done;
}
memmove(pwdBuf, pwdp, strlen(pwdp) + 1);
password = pwdBuf;
}
certReq.password.Data = (uint8 *)password;
certReq.password.Length = strlen(password);
certReq.userName.Data = (uint8 *)userName;
certReq.userName.Length = strlen(userName);
if(csrInName) {
unsigned len;
if(readFile(csrInName, &certReq.csr.Data, &len)) {
printf("***Error reading CSR from %s. Aborting.\n", csrInName);
exit(1);
}
certReq.csr.Length = len;
certReq.flags |= CSSM_DOTMAC_TP_EXIST_CSR;
}
else {
if(genKeys) {
ortn = genKeyPair(kcRef, &pubKeyRef, &privKeyRef);
}
else {
ortn = keyPicker(kcRef, &pubKeyRef, &privKeyRef);
}
if(ortn) {
printf("Can't proceed without a keypair. Aborting.\n");
exit(1);
}
ortn = SecKeyGetCSSMKey(pubKeyRef, &pubKey);
if(ortn) {
cssmPerror("SecKeyGetCSSMKey", ortn);
goto done;
}
ortn = SecKeyGetCSSMKey(privKeyRef, &privKey);
if(ortn) {
cssmPerror("SecKeyGetCSSMKey", ortn);
goto done;
}
ortn = SecKeyGetCSPHandle(privKeyRef, &cspHand);
if(ortn) {
cssmPerror("SecKeyGetCSPHandle", ortn);
goto done;
}
tvp.type = CSSMOID_CommonName;
tvp.valueType = BER_TAG_PRINTABLE_STRING;
#if FULL_EMAIL_ADDRESS
{
unsigned nameLen = strlen(userName);
tvp.value.Data = (uint8 *)malloc(nameLen + strlen("@mac.com") + 1);
strcpy((char *)tvp.value.Data, userName);
strcpy((char *)tvp.value.Data + nameLen, "@mac.com");
tvp.value.Length = strlen((char *)tvp.value.Data);
}
#else
tvp.value.Data = (uint8 *)userName;
tvp.value.Length = strlen(userName);
#endif
}
if(hostName != NULL) {
tpAuthority.AuthorityCert = NULL;
tpAuthority.AuthorityLocation = &tpNetAddrs;
tpNetAddrs.AddressType = CSSM_ADDR_NAME;
tpNetAddrs.Address.Data = (uint8 *)hostName;
tpNetAddrs.Address.Length = strlen(hostName);
tpAuthPtr = &tpAuthority;
};
certReq.version = CSSM_DOT_MAC_TP_REQ_VERSION;
if(!csrInName) {
certReq.cspHand = cspHand;
certReq.clHand = cuClStartup();
certReq.numTypeValuePairs = 1;
certReq.typeValuePairs = &tvp;
certReq.publicKey = (CSSM_KEY_PTR)pubKey;
certReq.privateKey = (CSSM_KEY_PTR)privKey;
}
if(doNotPost) {
certReq.flags |= CSSM_DOTMAC_TP_DO_NOT_POST;
}
if(csrOutName != NULL) {
certReq.flags |= CSSM_DOTMAC_TP_RETURN_CSR;
}
if(doRenew) {
certReq.flags |= CSSM_DOTMAC_TP_SIGN_RENEW;
}
reqSet.NumberOfRequests = 1;
reqSet.Requests = &certReq;
policyField.FieldOid = *opOid;
policyField.FieldValue.Data = NULL;
policyField.FieldValue.Length = 0;
memset(&callerAuth, 0, sizeof(callerAuth));
callerAuth.Policy.NumberOfPolicyIds = 1;
callerAuth.Policy.PolicyIds = &policyField;
if(!csrInName) {
ortn = SecKeyGetCredentials(privKeyRef,
CSSM_ACL_AUTHORIZATION_SIGN,
kSecCredentialTypeDefault,
const_cast<const CSSM_ACCESS_CREDENTIALS **>(&callerAuth.CallerCredentials));
if(ortn) {
cssmPerror("SecKeyGetCredentials", crtn);
goto done;
}
}
crtn = CSSM_TP_SubmitCredRequest (tpHand,
tpAuthPtr,
CSSM_TP_AUTHORITY_REQUEST_CERTISSUE, &reqSet, &callerAuth,
&estTime, &refId); switch(crtn) {
case CSSM_OK:
case CSSMERR_APPLE_DOTMAC_REQ_QUEUED:
{
const char *itemType = "Cert";
const char *statStr = "OK";
if(crtn != CSSM_OK) {
itemType = "RefId";
statStr = "Cert";
}
if((refId.Data == NULL) || (refId.Length == 0)) {
printf("CSSM_TP_SubmitCredRequest returned %s but no data\n", statStr);
break;
}
if(crtn == CSSM_OK) {
printf("...cert acquisition complete\n");
}
else {
printf("...Cert request QUEUED\n");
}
if(outFile) {
if(!writeFile(outFile, refId.Data, refId.Length)) {
if(!quiet) {
printf("...%lu bytes of %s written to %s\n",
refId.Length, itemType, outFile);
}
}
else {
printf("***Error writing %s to %s\n", itemType, outFile);
crtn = ioErr;
}
}
else if(verbose) {
if(crtn == CSSM_OK) {
unsigned char *der;
unsigned derLen;
if(pemDecode(refId.Data, refId.Length, &der, &derLen)) {
printf("***Error PEM decoding returned cert\n");
}
else {
printCert(der, derLen, CSSM_FALSE);
free(der);
}
}
else {
printf("RefId data:\n");
snDumpText(refId.Data, refId.Length);
}
}
break;
}
case CSSMERR_APPLE_DOTMAC_REQ_REDIRECT:
if((refId.Data == NULL) || (refId.Length == 0)) {
printf("CSSM_TP_SubmitCredRequest returned REDIRECT but no data\n");
break;
}
printf("...cert acquisition : REDIRECTED to: ");
snDumpText(refId.Data, refId.Length);
printf("\n");
break;
default:
cssmPerror("CSSM_TP_SubmitCredRequest", crtn);
break;
}
if(csrOutName) {
if((certReq.csr.Data == NULL) || (certReq.csr.Length == 0)) {
printf("***Asked for CSR but didn't get one\n");
ortn = paramErr;
goto done;
}
if(writeFile(csrOutName, certReq.csr.Data, certReq.csr.Length)) {
printf("***Error writing CSR to %s.\n", csrOutName);
}
else {
printf("...%lu bytes written as CSR to %s\n", certReq.csr.Length, csrOutName);
}
}
done:
CSSM_ModuleDetach(tpHand);
if(certReq.clHand) {
CSSM_ModuleDetach(certReq.clHand);
}
if(kcRef) {
CFRelease(kcRef);
}
if(csrInName) {
free(certReq.csr.Data);
}
if(privKeyRef) {
CFRelease(privKeyRef);
}
if(pubKeyRef) {
CFRelease(pubKeyRef);
}
if(refId.Data) {
cuAppFree(refId.Data, NULL);
}
if(doPause) {
fpurge(stdin);
printf("Pausing for MallocDebug measurement; CR to continue: ");
getchar();
}
return ortn;
}