+/*
+ * dotMacRequest.cpp - simple illustration of using SecCertificateRequestCreate() and
+ * SecCertificateRequestSubmit to post a request for a .mac cert.
+ */
+#include <Security/Security.h>
+#include <Security/SecCertificateRequest.h>
+#include <security_dotmac_tp/dotMacTp.h>
+#include <clAppUtils/keyPicker.h>
+#include <unistd.h>
+#include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
+
+/*
+ * 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; dex<str->Length; 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;
+}
+