X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/80e2389990082500d76eb566d4946be3e786c3ef..d8f41ccd20de16f8ebe2ccc84d47bf1cb2b26bbb:/SecurityTests/clxutils/dotMacTool/dotMacTool.cpp diff --git a/SecurityTests/clxutils/dotMacTool/dotMacTool.cpp b/SecurityTests/clxutils/dotMacTool/dotMacTool.cpp new file mode 100644 index 00000000..4596b389 --- /dev/null +++ b/SecurityTests/clxutils/dotMacTool/dotMacTool.cpp @@ -0,0 +1,728 @@ +/* + * dotMacTool.cpp - .mac TP exerciser + */ + +#include +#include +#include +#include +#include +#include +#include +#include +//#include +#include +#include + +#include "keyPicker.h" +#include + +#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, // eventHandler + NULL); // AppNotifyCallbackCtx + if(crtn) { + cuPrintError("CSSM_ModuleLoad(DotMacTP)", crtn); + return 0; + } + crtn = CSSM_ModuleAttach (&gGuidAppleDotMacTP, + &vers, + &memFuncs, // memFuncs + 0, // SubserviceID + CSSM_SERVICE_TP, // SubserviceFlags + 0, // AttachFlags + CSSM_KEY_HIERARCHY_NONE, + NULL, // FunctionTable + 0, // NumFuncTable + NULL, // reserved + &tpHand); + if(crtn) { + cuPrintError("CSSM_ModuleAttach(DotMacTP)", crtn); + return 0; + } + else { + return tpHand; + } +} + +/* print text, safely */ +static void snDumpText( + const unsigned char *rcvBuf, + unsigned len) +{ + char *cp = (char *)rcvBuf; + unsigned i; + char c; + + for(i=0; i", ((unsigned)c) & 0xff); + } + break; + } + + } +} + +static OSStatus genKeyPair( + SecKeychainRef kcRef, // NULL means the default list + SecKeyRef *pubKey, // RETURNED + SecKeyRef *privKey) // RETURNED +{ + OSStatus ortn; + + ortn = SecKeyCreatePair(kcRef, + DOT_MAC_KEY_ALG, + DOT_MAC_KEY_SIZE, + 0, // context handle + /* public key usage and attrs */ + CSSM_KEYUSE_ANY, + CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_PERMANENT | CSSM_KEYATTR_EXTRACTABLE, + /* private key usage and attrs */ + CSSM_KEYUSE_ANY, + CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_PERMANENT | CSSM_KEYATTR_EXTRACTABLE | + CSSM_KEYATTR_SENSITIVE, + NULL, // initial access + pubKey, + privKey); + if(ortn) { + cssmPerror("SecKeyCreatePair", ortn); + } + return ortn; +} + +/* Lookup via ReferenceID, obtained from CSSM_TP_SubmitCredRequest() */ +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; +} + +/* +* Lookup via user name, a greatly simplified form of CSSM_TP_SubmitCredRequest() +*/ +OSStatus doLookupViaUserName( + CSSM_TP_HANDLE tpHand, + const CSSM_OID *opOid, + const char *userName, + const char *hostName, // optional + 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, // const CSSM_TP_REQUEST_SET *RequestInput, + &callerAuth, + &estTime, // sint32 *EstimatedTime, + &certData); // CSSM_DATA_PTR ReferenceIdentifier + + 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) { + /* This one returns the cert in DER format, we might revisit that */ + 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; + + /* user-spec'd variables */ + 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; + /* drop thru */ + case 'g': + opOid = &CSSMOID_DOTMAC_CERT_REQ_IDENTITY; + break; + + case 'M': + lookupViaUserName = true; + /* drop thru */ + case 'G': + opOid = &CSSMOID_DOTMAC_CERT_REQ_EMAIL_SIGN; + break; + + case 'N': + lookupViaUserName = true; + /* drop thru */ + 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)); + + /* all of the subsequest argument are superfluous for lookupViaUserName, except for + * the user name itself, which has a default */ + if(keychainName != NULL) { + /* pick a keychain (optional) */ + ortn = SecKeychainOpen(keychainName, &kcRef); + if(ortn) { + cssmPerror("SecKeychainOpen", ortn); + exit(1); + } + + /* make sure it's there since a successful SecKeychainOpen proves nothing */ + 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 { + /* + * All the stuff the TP needs to actually generate a CSR. + * + * Get a key pair, somehow. + */ + 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; + } + + /* CSSM_X509_TYPE_VALUE_PAIR - one pair for now */ + // tvp.type = CSSMOID_EmailAddress; + 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 + } + /* set up args for CSSM_TP_SubmitCredRequest */ + 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(&callerAuth.CallerCredentials)); + if(ortn) { + cssmPerror("SecKeyGetCredentials", crtn); + goto done; + } + } + + crtn = CSSM_TP_SubmitCredRequest (tpHand, + tpAuthPtr, + CSSM_TP_AUTHORITY_REQUEST_CERTISSUE, // CSSM_TP_AUTHORITY_REQUEST_TYPE + &reqSet, // const CSSM_TP_REQUEST_SET *RequestInput, + &callerAuth, + &estTime, // sint32 *EstimatedTime, + &refId); // CSSM_DATA_PTR ReferenceIdentifier + switch(crtn) { + case CSSM_OK: + case CSSMERR_APPLE_DOTMAC_REQ_QUEUED: + { + /* + * refId should be a cert or RefId + */ + 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: + /* cleanup */ + 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; +}