#include <Security/Security.h>
#include <Security/SecImportExport.h>
#include <Security/SecCertificateRequest.h>
#include <dotMacTp.h>
#include "identSearch.h"
#include "dotMacTpAttach.h"
#include <unistd.h>
#include <security_cdsa_utils/cuCdsaUtils.h>
#include <security_cdsa_utils/cuFileIo.h>
#include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
#define USER_DEFAULT "dmitch_new"
#define PWD_DEFAULT "password"
#define ARCHIVE_NAME_DEFAULT "dmitch_new"
#define HOST_DEFAULT "certmgmt.mac.com"
typedef enum {
AO_List,
AO_Store,
AO_Fetch,
AO_Remove
} ArchiveOp;
static void usage(char **argv)
{
printf("usage: %s op [options]\n", argv[0]);
printf("Op:\n");
printf(" l -- list archive contents\n");
printf(" s -- store archive\n");
printf(" f -- fetch archive\n");
printf(" r -- remove archive(s)\n");
printf("Options:\n");
printf(" -u username -- Default is %s\n", USER_DEFAULT);
printf(" -Z password -- default is %s\n", PWD_DEFAULT);
printf(" -n archiveName -- default is %s\n", ARCHIVE_NAME_DEFAULT);
printf(" -k keychain -- Source/destination of archive\n");
printf(" -H hostname -- Alternate .mac server host name (default %s)\n",
HOST_DEFAULT);
printf(" -o outFile -- write P12 blob to outFile\n");
printf(" -z p12Phrase -- PKCS12 passphrase (default is GUI prompt)\n");
printf(" -M -- Pause for MallocDebug\n");
printf(" -l -- loop\n");
exit(1);
}
static void printString(
const CSSM_DATA *str)
{
for(unsigned dex=0; dex<str->Length; dex++) {
printf("%c", str->Data[dex]);
}
}
static CSSM_RETURN dotMacPostArchiveRequest(
ArchiveOp op,
CSSM_TP_HANDLE tpHand,
const CSSM_DATA *userName, const CSSM_DATA *password,
const CSSM_DATA *hostName, const CSSM_DATA *archiveName, const CSSM_DATA *timeString, const CSSM_DATA *pfxIn, CSSM_DATA *pfxOut, unsigned *numArchives, DotMacArchive **archives) {
CSSM_RETURN crtn;
CSSM_TP_AUTHORITY_ID tpAuthority;
CSSM_TP_AUTHORITY_ID *tpAuthPtr = NULL;
CSSM_NET_ADDRESS tpNetAddrs;
CSSM_APPLE_DOTMAC_TP_ARCHIVE_REQUEST archReq;
CSSM_TP_REQUEST_SET reqSet;
CSSM_TP_CALLERAUTH_CONTEXT callerAuth;
sint32 estTime;
CSSM_DATA refId = {0, NULL};
CSSM_FIELD policyField;
const CSSM_OID *opOid = NULL;
if((tpHand == 0) || (userName == NULL) || (password == NULL)) {
printf("dotMacPostArchiveRequest: illegal common args\n");
return paramErr;
}
switch(op) {
case AO_List:
if((numArchives == NULL) || (archives == NULL)) {
printf("dotMacPostArchiveRequest: illegal AO_List args\n");
return paramErr;
}
opOid = &CSSMOID_DOTMAC_CERT_REQ_ARCHIVE_LIST;
break;
case AO_Store:
if((archiveName == NULL) || (timeString == NULL) || (pfxIn == NULL)) {
printf("dotMacPostArchiveRequest: illegal AO_Store args\n");
return paramErr;
}
opOid = &CSSMOID_DOTMAC_CERT_REQ_ARCHIVE_STORE;
break;
case AO_Fetch:
if((archiveName == NULL) || (pfxOut == NULL)) {
printf("dotMacPostArchiveRequest: illegal AO_Fetch args\n");
return paramErr;
}
opOid = &CSSMOID_DOTMAC_CERT_REQ_ARCHIVE_FETCH;
break;
case AO_Remove:
if(archiveName == NULL) {
printf("dotMacPostArchiveRequest: illegal AO_Remove args\n");
return paramErr;
}
opOid = &CSSMOID_DOTMAC_CERT_REQ_ARCHIVE_REMOVE;
break;
}
memset(&archReq, 0, sizeof(archReq));
archReq.version = CSSM_DOT_MAC_TP_ARCHIVE_REQ_VERSION;
archReq.userName = *userName;
archReq.password = *password;
if(archiveName) {
archReq.archiveName = *archiveName;
}
if(timeString) {
archReq.timeString = *timeString;
}
if(pfxIn) {
archReq.pfx = *pfxIn;
}
if((hostName != NULL) && (hostName->Data != NULL)) {
tpAuthority.AuthorityCert = NULL;
tpAuthority.AuthorityLocation = &tpNetAddrs;
tpNetAddrs.AddressType = CSSM_ADDR_NAME;
tpNetAddrs.Address = *hostName;
tpAuthPtr = &tpAuthority;
}
reqSet.NumberOfRequests = 1;
reqSet.Requests = &archReq;
policyField.FieldOid = *opOid;
policyField.FieldValue.Data = NULL;
policyField.FieldValue.Length = 0;
memset(&callerAuth, 0, sizeof(callerAuth));
callerAuth.Policy.NumberOfPolicyIds = 1;
callerAuth.Policy.PolicyIds = &policyField;
crtn = CSSM_TP_SubmitCredRequest (tpHand,
tpAuthPtr,
CSSM_TP_AUTHORITY_REQUEST_CERTISSUE, &reqSet, &callerAuth,
&estTime, &refId); if(crtn) {
cssmPerror("CSSM_TP_SubmitCredRequest", crtn);
}
switch(op) {
case AO_List:
*numArchives = archReq.numArchives;
*archives = archReq.archives;
break;
case AO_Store:
break;
case AO_Fetch:
*pfxOut = archReq.pfx;
break;
case AO_Remove:
break;
}
return CSSM_OK;
}
static void cStringToCssmData(
const char *cstr,
CSSM_DATA *cdata)
{
if(cstr) {
cdata->Data = (uint8 *)cstr;
cdata->Length = strlen(cstr);
}
else {
cdata->Data = NULL;
cdata->Length = 0;
}
}
int main(int argc, char **argv)
{
SecKeychainRef kcRef = NULL;
CSSM_RETURN crtn;
CSSM_TP_HANDLE tpHand = 0;
OSStatus ortn;
ArchiveOp op = AO_List;
char *keychainName = NULL;
const char *userName = USER_DEFAULT;
const char *password = PWD_DEFAULT;
const char *archName = ARCHIVE_NAME_DEFAULT;
const char *hostName = HOST_DEFAULT;
char *outFile = NULL;
bool doPause = false;
bool loop = false;
char *p12Phrase = NULL;
if(argc < 2) {
usage(argv);
}
switch(argv[1][0]) {
case 'l':
op = AO_List;
break;
case 's':
op = AO_Store;
break;
case 'f':
op = AO_Fetch;
break;
case 'r':
op = AO_Remove;
break;
default:
usage(argv);
}
extern char *optarg;
extern int optind;
optind = 2;
int arg;
while ((arg = getopt(argc, argv, "u:Z:n:k:H:Nlo:z:")) != -1) {
switch (arg) {
case 'u':
userName = optarg;
break;
case 'Z':
password = optarg;
break;
case 'n':
archName = optarg;
break;
case 'k':
keychainName = optarg;
break;
case 'M':
doPause = true;
break;
case 'H':
hostName = optarg;
break;
case 'l':
loop = true;
break;
case 'o':
outFile = optarg;
break;
case 'z':
p12Phrase = optarg;
break;
case 'h':
usage(argv);
}
}
if(optind != argc) {
usage(argv);
}
if(doPause) {
fpurge(stdin);
printf("Pausing for MallocDebug attach; CR to continue: ");
getchar();
}
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);
exit(1);
}
}
CSSM_DATA userNameData;
CSSM_DATA passwordData;
CSSM_DATA hostNameData;
CSSM_DATA archNameData;
CSSM_DATA timeStringData;
cStringToCssmData(userName, &userNameData);
cStringToCssmData(password, &passwordData);
cStringToCssmData(hostName, &hostNameData);
cStringToCssmData(archName, &archNameData);
char timeStr[20];
time_t nowTime = time(NULL);
printf("...nowTime = %lu\n", nowTime);
nowTime += (60 * 60 * 24 * 25); printf("...expirationTime = %lu\n", nowTime);
sprintf(timeStr, "%lu", nowTime);
timeStringData.Data = (uint8 *)timeStr;
timeStringData.Length = strlen(timeStr);
CFDataRef p12 = NULL;
CSSM_DATA pfxInData = {0, NULL};
CSSM_DATA pfxOutData = {0, NULL};
unsigned numArchives = 0;
DotMacArchive *archives = NULL;
if(op == AO_Store) {
CFStringRef cfPhrase = NULL;
char emailAddr[500];
strcpy(emailAddr, userName);
SecIdentityRef idRef = NULL;
OSStatus ortn = findIdentity(emailAddr, strlen(emailAddr), kcRef, &idRef);
if(ortn) {
printf("***Could not find an identity to store. Aborting.\n");
goto errOut;
}
SecKeyImportExportParameters keyParams;
memset(&keyParams, 0, sizeof(keyParams));
keyParams.version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION;
if(p12Phrase) {
cfPhrase = CFStringCreateWithCString(NULL, p12Phrase,
kCFStringEncodingUTF8);
keyParams.passphrase = cfPhrase;
}
else {
keyParams.flags = kSecKeySecurePassphrase;
}
keyParams.alertTitle = CFSTR(".mac Identity Backup");
keyParams.alertPrompt =
CFSTR("Enter passphrase for encrypting your .mac private key");
ortn = SecKeychainItemExport(idRef, kSecFormatPKCS12, kSecItemPemArmour,
&keyParams, &p12);
if(ortn) {
cssmPerror("SecKeychainItemExport", crtn);
printf("***Error obtaining .mac identity in PKCS12 format. Aborting.\n");
goto errOut;
}
pfxInData.Data = (uint8 *)CFDataGetBytePtr(p12);
pfxInData.Length = CFDataGetLength(p12);
printf("...preparing to store archive of %lu bytes\n", pfxInData.Length);
if(outFile) {
if(writeFile(outFile, pfxInData.Data, pfxInData.Length)) {
printf("***Error writing P12 to %s\n", outFile);
}
else {
printf("...wrote %lu bytes to %s\n", pfxInData.Length, outFile);
}
}
if(cfPhrase) {
CFRelease(cfPhrase);
}
}
tpHand = dotMacTpAttach();
if(tpHand == 0) {
printf("***Error attaching to .mac TP; aborting.\n");
ortn = -1;
goto errOut;
}
crtn = dotMacPostArchiveRequest(op, tpHand, &userNameData, &passwordData,
hostName ? &hostNameData : NULL,
&archNameData,
&timeStringData,
&pfxInData,
&pfxOutData,
&numArchives,
&archives);
if(crtn) {
printf("***Error performing archive request; aborting.\n");
goto errOut;
}
switch(op) {
case AO_List:
{
printf("=== List request complete; numArchives = %u ===\n", numArchives);
for(unsigned dex=0; dex<numArchives; dex++) {
DotMacArchive *dmarch = &archives[dex];
printf("Archive %u:\n", dex);
printf(" name : ");
printString(&dmarch->archiveName);
printf("\n");
printf(" time : ");
printString(&dmarch->timeString);
printf("\n");
APP_FREE(dmarch->archiveName.Data);
APP_FREE(dmarch->timeString.Data);
}
APP_FREE(archives);
break;
}
case AO_Store:
printf("=== archive \'%s\' backup complete ===\n", archName);
break;
case AO_Fetch:
{
bool didSomething = false;
if(pfxOutData.Length == 0) {
printf("***Archive fetch claimed to succeed, but no data seen\n");
ortn = -1;
goto errOut;
}
printf("=== %lu bytes of archive fetched ===\n", pfxOutData.Length);
if(outFile) {
if(writeFile(outFile, pfxOutData.Data, pfxOutData.Length)) {
printf("***Error writing P12 to %s\n", outFile);
}
else {
printf("...wrote %lu bytes to %s\n", pfxOutData.Length, outFile);
didSomething = true;
}
}
if(kcRef) {
CFDataRef p12Data = CFDataCreate(NULL, pfxOutData.Data, pfxOutData.Length);
SecExternalFormat extForm = kSecFormatPKCS12;
SecKeyImportExportParameters keyParams;
memset(&keyParams, 0, sizeof(keyParams));
keyParams.version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION;
CFStringRef cfPhrase = NULL;
if(p12Phrase) {
cfPhrase = CFStringCreateWithCString(NULL, p12Phrase,
kCFStringEncodingUTF8);
keyParams.passphrase = cfPhrase;
}
else {
keyParams.flags = kSecKeySecurePassphrase;
}
keyParams.alertTitle = CFSTR(".mac Identity Restore");
keyParams.alertPrompt =
CFSTR("Enter passphrase for decrypting your .mac private key");
ortn = SecKeychainItemImport(p12Data,
NULL, &extForm,
NULL, 0, &keyParams,
kcRef,
NULL); if(ortn) {
cssmPerror("SecKeychainItemImport", ortn);
printf("***Error importing p12 into keychain %s\n", keychainName);
}
else {
printf("...archive successfully imported into keychain %s\n",
keychainName);
didSomething = true;
}
if(cfPhrase) {
CFRelease(cfPhrase);
}
}
if(!didSomething) {
printf("...note we got an archive from the server but didn't have a "
"place to put it.\n");
}
break;
}
case AO_Remove:
printf("=== Archive %s removed ===\n", archName);
break;
}
if(doPause) {
fpurge(stdin);
printf("Pausing at end of test for MallocDebug attach; CR to continue: ");
getchar();
}
errOut:
if(kcRef) {
CFRelease(kcRef);
}
if(tpHand) {
dotMacTpDetach(tpHand);
}
return ortn;
}