X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/80e2389990082500d76eb566d4946be3e786c3ef..d8f41ccd20de16f8ebe2ccc84d47bf1cb2b26bbb:/SecurityTests/clxutils/dotMacRequest/dotMacRequest.cpp diff --git a/SecurityTests/clxutils/dotMacRequest/dotMacRequest.cpp b/SecurityTests/clxutils/dotMacRequest/dotMacRequest.cpp new file mode 100644 index 00000000..e610eed4 --- /dev/null +++ b/SecurityTests/clxutils/dotMacRequest/dotMacRequest.cpp @@ -0,0 +1,624 @@ +/* + * dotMacRequest.cpp - simple illustration of using SecCertificateRequestCreate() and + * SecCertificateRequestSubmit to post a request for a .mac cert. + */ +#include +#include +#include +#include +#include +#include + +/* + * Defaults for the test setup du jour + */ +#define USER_DEFAULT "dmitch10" +#define PWD_DEFAULT "password" +#define HOST_DEFAULT "certmgmt.mac.com" + +/* + * Type of cert to request + */ +typedef enum { + CRT_Identity, /* actually, now "iChat encryption", not "identity" */ + CRT_EmailSign, + CRT_EmailEncrypt +} CertRequestType; + +static void usage(char **argv) +{ + printf("usage: %s op [options]\n", argv[0]); + printf("Op:\n"); + printf(" i -- generate iChat encryption cert\n"); + printf(" s -- generate email signing cert\n"); + printf(" e -- generate email encrypting cert\n"); + printf(" I -- search/retrieve request for iChat encryption cert\n"); + printf(" S -- search/retrieve request for signing cert\n"); + printf(" E -- search/retrieve request for encrypting cert\n"); + printf(" p -- pending request poll (via -u)\n"); + printf("Options:\n"); + printf(" -u username -- Default is %s\n", USER_DEFAULT); + printf(" -Z password -- default is %s\n", PWD_DEFAULT); + printf(" -p -- pick key pair from existing (default is generate)\n"); + printf(" -k keychain -- Source/destination of keys\n"); + printf(" -r -- Renew (default is new)\n"); + printf(" -a -- async (default is synchronous)\n"); + printf(" -H hostname -- Alternate .mac server host name (default %s)\n", + HOST_DEFAULT); + printf(" -M -- Pause for MallocDebug\n"); + printf(" -l -- loop\n"); + exit(1); +} + +/* print a string int he form of a CSSM_DATA */ +static void printString( + const CSSM_DATA *str) +{ + for(unsigned dex=0; dexLength; dex++) { + printf("%c", str->Data[dex]); + } +} + +/* basic "generate keypair" routine */ +static OSStatus genKeyPair( + SecKeychainRef kcRef, // optional, 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; +} + +/* max number of oid/value pairs */ +#define MAX_ATTRS 5 + +/* + * search for a pending .mac cert request, get current status. + */ +static OSStatus dotMacGetPendingRequest( + /* required fields */ + const char *userName, // REQUIRED, C string + const char *password, // REQUIRED, C string + CertRequestType requestType, + + /* optional fields */ + const char *hostName, // C string + SecKeychainRef keychain) // destination of created cert (if !async) +{ + SecCertificateRequestAttribute attrs[MAX_ATTRS]; + SecCertificateRequestAttribute *attrp = attrs; + SecCertificateRequestAttributeList attrList; + + attrList.count = 0; + attrList.attr = attrs; + + /* user name */ + attrp->oid = CSSMOID_DOTMAC_CERT_REQ_VALUE_USERNAME; + attrp->value.Data = (uint8 *)userName; + attrp->value.Length = strlen(userName); + attrp++; + attrList.count++; + + /* password */ + attrp->oid = CSSMOID_DOTMAC_CERT_REQ_VALUE_PASSWORD; + attrp->value.Data = (uint8 *)password; + attrp->value.Length = strlen(password); + attrp++; + attrList.count++; + + /* options */ + + if(hostName) { + attrp->oid = CSSMOID_DOTMAC_CERT_REQ_VALUE_HOSTNAME; + attrp->value.Data = (uint8 *)hostName; + attrp->value.Length = strlen(hostName); + attrp++; + attrList.count++; + } + + /* map CertRequestType to a policy OID */ + const CSSM_OID *policy; + switch(requestType) { + case CRT_Identity: + policy = &CSSMOID_DOTMAC_CERT_REQ_IDENTITY; + break; + case CRT_EmailSign: + policy = &CSSMOID_DOTMAC_CERT_REQ_EMAIL_SIGN; + break; + case CRT_EmailEncrypt: + policy = &CSSMOID_DOTMAC_CERT_REQ_EMAIL_ENCRYPT; + break; + default: + printf("GAK! Bad cert type.\n"); + return -1; + } + OSStatus ortn; + SecCertificateRequestRef certReq = NULL; + SecCertificateRef certRef = NULL; + sint32 estTime; + + printf("...calling SecCertificateFindRequest\n"); + ortn = SecCertificateFindRequest(policy, + CSSM_CERT_X_509v3, + CSSM_TP_AUTHORITY_REQUEST_CERTISSUE, + NULL, NULL, // no keys needed + &attrList, + &certReq); + if(ortn) { + cssmPerror("SecCertificateFindRequest", ortn); + return ortn; + } + + printf("...calling SecCertificateRequestGetResult\n"); + ortn = SecCertificateRequestGetResult(certReq, keychain, &estTime, &certRef); + if(ortn) { + cssmPerror("SecCertificateRequestGetResult", ortn); + } + else { + printf("...SecCertificateRequestGetResult succeeded; estTime %d; cert %s\n", + (int)estTime, certRef ? "OBTAINED" : "NOT OBTAINED"); + } + if(certRef) { + CFRelease(certRef); + } + if(certReq) { + CFRelease(certReq); + } + return ortn; +} + +/* + * Do an "is there a pending request for this user?" poll. + * That function - via SecCertificateFindRequest() always returns an error; + * *we* only return an error if the result is something other than the + * expected two results: + * CSSMERR_APPLE_DOTMAC_REQ_IS_PENDING + * CSSMERR_APPLE_DOTMAC_NO_REQ_PENDING + */ +static OSStatus dotMacPostPendingReqPoll( + const char *userName, + const char *password, + const char *hostName) +{ + SecCertificateRequestAttribute attrs[MAX_ATTRS]; + SecCertificateRequestAttribute *attrp = attrs; + SecCertificateRequestAttributeList attrList; + uint8 oneBit = 1; + + attrList.count = 0; + attrList.attr = attrs; + + /* user name, required */ + attrp->oid = CSSMOID_DOTMAC_CERT_REQ_VALUE_USERNAME; + attrp->value.Data = (uint8 *)userName; + attrp->value.Length = strlen(userName); + attrp++; + attrList.count++; + + /* password, required */ + attrp->oid = CSSMOID_DOTMAC_CERT_REQ_VALUE_PASSWORD; + attrp->value.Data = (uint8 *)password; + attrp->value.Length = strlen(password); + attrp++; + attrList.count++; + + /* the "poll the server" indicator */ + attrp->oid = CSSMOID_DOTMAC_CERT_REQ_VALUE_IS_PENDING; + /* true ::= any nonzero data */ + attrp->value.Data = &oneBit; + attrp->value.Length = 1; + attrp++; + attrList.count++; + + /* options */ + + if(hostName) { + attrp->oid = CSSMOID_DOTMAC_CERT_REQ_VALUE_HOSTNAME; + attrp->value.Data = (uint8 *)hostName; + attrp->value.Length = strlen(hostName); + attrp++; + attrList.count++; + } + + /* policy, not technically needed; use this one by convention */ + const CSSM_OID *policy = &CSSMOID_DOTMAC_CERT_REQ_IDENTITY; + + OSStatus ortn; + SecCertificateRequestRef certReq = NULL; + + printf("...calling SecCertificateFindRequest\n"); + ortn = SecCertificateFindRequest(policy, + CSSM_CERT_X_509v3, + CSSM_TP_AUTHORITY_REQUEST_CERTISSUE, + NULL, NULL, // no keys needed + &attrList, + &certReq); + + switch(ortn) { + case CSSMERR_APPLE_DOTMAC_REQ_IS_PENDING: + printf("...result: REQ_IS_PENDING\n"); + ortn = noErr; + break; + case CSSMERR_APPLE_DOTMAC_NO_REQ_PENDING: + printf("...result: NO_REQ_PENDING\n"); + ortn = noErr; + break; + case noErr: + /* should never happen */ + printf("...UNEXPECTED SUCCESS on SecCertificateFindRequest\n"); + ortn = internalComponentErr; + if(certReq != NULL) { + /* Somehow, it got created */ + CFRelease(certReq); + } + break; + default: + cssmPerror("SecCertificateFindRequest", ortn); + break; + } + return ortn; +} + +/* + * Post a .mac cert request, with a small number of options. + */ +static OSStatus dotMacPostCertRequest( + /* required fields */ + const char *userName, // REQUIRED, C string + const char *password, // REQUIRED, C string + SecKeyRef privKey, // REQUIRED + SecKeyRef pubKey, + CertRequestType requestType, + bool renew, // false: new cert + // true : renew existing + bool async, // false: wait for result + // true : just post request and return + /* optional fields */ + const char *hostName, // C string + SecKeychainRef keychain) // destination of created cert (if !async) +{ + + /* the main job here is bundling up the arguments in an array of OID/value pairs */ + SecCertificateRequestAttribute attrs[MAX_ATTRS]; + SecCertificateRequestAttribute *attrp = attrs; + SecCertificateRequestAttributeList attrList; + uint8 oneBit = 1; + + attrList.count = 0; + attrList.attr = attrs; + + /* user name */ + attrp->oid = CSSMOID_DOTMAC_CERT_REQ_VALUE_USERNAME; + attrp->value.Data = (uint8 *)userName; + attrp->value.Length = strlen(userName); + attrp++; + attrList.count++; + + /* password */ + attrp->oid = CSSMOID_DOTMAC_CERT_REQ_VALUE_PASSWORD; + attrp->value.Data = (uint8 *)password; + attrp->value.Length = strlen(password); + attrp++; + attrList.count++; + + /* options */ + + if(hostName) { + attrp->oid = CSSMOID_DOTMAC_CERT_REQ_VALUE_HOSTNAME; + attrp->value.Data = (uint8 *)hostName; + attrp->value.Length = strlen(hostName); + attrp++; + attrList.count++; + } + + if(renew) { + attrp->oid = CSSMOID_DOTMAC_CERT_REQ_VALUE_RENEW; + /* true ::= any nonzero data */ + attrp->value.Data = &oneBit; + attrp->value.Length = 1; + attrp++; + attrList.count++; + } + + if(async) { + attrp->oid = CSSMOID_DOTMAC_CERT_REQ_VALUE_ASYNC; + /* true ::= any nonzero data */ + attrp->value.Data = &oneBit; + attrp->value.Length = 1; + attrp++; + attrList.count++; + } + + /* map CertRequestType to a policy OID */ + const CSSM_OID *policy; + switch(requestType) { + case CRT_Identity: + policy = &CSSMOID_DOTMAC_CERT_REQ_IDENTITY; + break; + case CRT_EmailSign: + policy = &CSSMOID_DOTMAC_CERT_REQ_EMAIL_SIGN; + break; + case CRT_EmailEncrypt: + policy = &CSSMOID_DOTMAC_CERT_REQ_EMAIL_ENCRYPT; + break; + default: + printf("GAK! Bad cert type.\n"); + return -1; + } + OSStatus ortn; + SecCertificateRequestRef certReq = NULL; + + ortn = SecCertificateRequestCreate(policy, + CSSM_CERT_X_509v3, + CSSM_TP_AUTHORITY_REQUEST_CERTISSUE, + privKey, + pubKey, + &attrList, + &certReq); + if(ortn) { + cssmPerror("SecCertificateRequestCreate", ortn); + return ortn; + } + + printf("...submitting request to .mac server\n"); + sint32 estTime = 0; + ortn = SecCertificateRequestSubmit(certReq, &estTime); + switch(ortn) { + case CSSMERR_APPLE_DOTMAC_REQ_REDIRECT: + { + /* + * A special case; the server is redirecting the calling app to + * a URL which we fetch and report like so: + */ + CSSM_DATA url = {0, NULL}; + ortn = SecCertificateRequestGetData(certReq, &url); + if(ortn) { + cssmPerror("SecCertificateRequestGetData", ortn); + printf("***APPLE_DOTMAC_REQ_REDIRECT obtained but no URL availalble.\n"); + } + else { + printf("***APPLE_DOTMAC_REQ_REDIRECT obtained; redirect URL is: "); + printString(&url); + printf("\n"); + } + break; + } + + case CSSM_OK: + printf("...cert request submitted; estimatedTime %d.\n", (int)estTime); + break; + default: + cssmPerror("SecCertificateRequestSubmit", ortn); + break; + } + if(ortn || async) { + /* we're done */ + CFRelease(certReq); + return ortn; + } + + /* + * Running synchronously, and the submit succeeded. Try to get a result. + * In the real world this would be polled, every so often.... + */ + SecCertificateRef certRef = NULL; + printf("...attempting to get result of cert request...\n"); + ortn = SecCertificateRequestGetResult(certReq, keychain, &estTime, &certRef); + if(ortn) { + cssmPerror("SecCertificateRequestGetResult", ortn); + } + else { + printf("...SecCertificateRequestGetResult succeeded; estTime %d; cert %s\n", + (int)estTime, certRef ? "OBTAINED" : "NOT OBTAINED"); + } + if(certRef) { + CFRelease(certRef); + CFRelease(certReq); + } + return ortn; +} + +#define ALWAYS_DO_SUBMIT 0 + + +int main(int argc, char **argv) +{ + SecKeyRef pubKeyRef = NULL; + SecKeyRef privKeyRef = NULL; + SecKeychainRef kcRef = NULL; + OSStatus ortn; + + /* user-spec'd variables */ + bool genKeys = true; /* true: generate; false: pick 'em */ + char *keychainName = NULL; + char *userName = USER_DEFAULT; + char *password = PWD_DEFAULT; + char *hostName = NULL; /* leave as the default! = HOST_DEFAULT; */ + /* + * WARNING: doing a renew operation requires that you delete your *current* + * .mac cert from the destination keychain. The DB attrs of the old and new certs + * are the same! + */ + bool doRenew = false; + CertRequestType reqType = CRT_Identity; + bool doPause = false; + bool async = false; + bool doSearch = false; /* false: post cert request + * true : search for existing request, get + * status for it */ + bool loop = false; + bool doPendingReqPoll = false; + + if(argc < 2) { + usage(argv); + } + switch(argv[1][0]) { + case 'i': + reqType = CRT_Identity; + break; + case 's': + reqType = CRT_EmailSign; + break; + case 'e': + reqType = CRT_EmailEncrypt; + break; + case 'I': + doSearch = true; + reqType = CRT_Identity; + break; + case 'S': + doSearch = true; + reqType = CRT_EmailSign; + break; + case 'E': + doSearch = true; + reqType = CRT_EmailEncrypt; + break; + case 'p': + doPendingReqPoll = true; + break; + default: + usage(argv); + } + + extern char *optarg; + extern int optind; + optind = 2; + int arg; + while ((arg = getopt(argc, argv, "u:Z:pk:rMH:al")) != -1) { + switch (arg) { + case 'u': + userName = optarg; + break; + case 'Z': + password = optarg; + break; + case 'p': + genKeys = false; + break; + case 'k': + keychainName = optarg; + break; + case 'r': + doRenew = true; + break; + case 'M': + doPause = true; + break; + case 'H': + hostName = optarg; + break; + case 'a': + async = true; + break; + case 'l': + loop = true; + break; + case 'h': + default: + usage(argv); + } + } + if(optind != argc) { + usage(argv); + } + + if(doPause) { + fpurge(stdin); + printf("Pausing for MallocDebug attach; CR to continue: "); + getchar(); + } + + 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); + exit(1); + } + } + + if((!doSearch || ALWAYS_DO_SUBMIT) && !doPendingReqPoll) { + /* 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); + } + } + + /* go */ + do { + if(doSearch) { + #if ALWAYS_DO_SUBMIT + /* debug only */ + dotMacPostCertRequest(userName, password, privKeyRef, pubKeyRef, + reqType, doRenew, async, hostName, kcRef); + #endif + + /* end */ + ortn = dotMacGetPendingRequest(userName, password, reqType, hostName, kcRef); + } + else if(doPendingReqPoll) { + ortn = dotMacPostPendingReqPoll(userName, password, hostName); + } + else { + ortn = dotMacPostCertRequest(userName, password, privKeyRef, pubKeyRef, + reqType, doRenew, async, hostName, kcRef); + } + if(doPause) { + fpurge(stdin); + printf("Pausing for MallocDebug attach; CR to continue: "); + getchar(); + } + } while(loop); + if(privKeyRef) { + CFRelease(privKeyRef); + } + if(pubKeyRef) { + CFRelease(pubKeyRef); + } + if(kcRef) { + CFRelease(kcRef); + } + + if(doPause) { + fpurge(stdin); + printf("Pausing at end of test for MallocDebug attach; CR to continue: "); + getchar(); + } + + return ortn; +} +