X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/80e2389990082500d76eb566d4946be3e786c3ef..d8f41ccd20de16f8ebe2ccc84d47bf1cb2b26bbb:/Security/libsecurity_keychain/lib/CertificateRequest.cpp diff --git a/Security/libsecurity_keychain/lib/CertificateRequest.cpp b/Security/libsecurity_keychain/lib/CertificateRequest.cpp new file mode 100644 index 00000000..76c1808c --- /dev/null +++ b/Security/libsecurity_keychain/lib/CertificateRequest.cpp @@ -0,0 +1,858 @@ +/* + * Copyright (c) 2002-2004,2011-2014 Apple Inc. All Rights Reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +// +// CertificateRequest.cpp +// +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* one top-level prefs file for all of .mac cert requests */ +#define DOT_MAC_REQ_PREFS "com.apple.security.certreq" + +/* + * Within that dictionary is a set of per-policy dictionaries; the key in the + * top-level prefs for these dictionaries is the raw policy OID data encoded + * as an ASCII string. + * + * Within one per-policy dictionary exists a number of per-user dictionaries, + * with the username key as a string. Note that this user name, the one passed to the + * .mac server, does NOT have to have any relation to the current Unix user name; one + * Unix user can have multiple .mac accounts. + * + * + * Within the per-policy, per user dictionary are these two values, both stored + * as raw data (CFData) blobs. + */ +#define DOT_MAC_REF_ID_KEY "refId" +#define DOT_MAC_CERT_KEY "certificate" + +/* Domain for .mac cert requests */ +#define DOT_MAC_DOMAIN_KEY "domain" +#define DOT_MAC_DOMAIN "mac.com" + +/* Hosts for .mac cert requests */ +#define DOT_MAC_MGMT_HOST "certmgmt" +#define DOT_MAC_INFO_HOST "certinfo" + +/* + * Compare two CSSM_DATAs (or two CSSM_OIDs), return true if identical. + */ +static +bool nssCompareCssmData( + const CSSM_DATA *data1, + const CSSM_DATA *data2) +{ + if((data1 == NULL) || (data1->Data == NULL) || + (data2 == NULL) || (data2->Data == NULL) || + (data1->Length != data2->Length)) { + return false; + } + if(data1->Length != data2->Length) { + return false; + } + if(memcmp(data1->Data, data2->Data, data1->Length) == 0) { + return true; + } + else { + return false; + } +} + +/* any nonzero value means true */ +static bool attrBoolValue( + const SecCertificateRequestAttribute *attr) +{ + if((attr->value.Data != NULL) && + (attr->value.Length != 0) && + (attr->value.Data[0] != 0)) { + return true; + } + else { + return false; + } +} + +static void tokenizeName( + const CSSM_DATA *inName, /* required */ + CSSM_DATA *outName, /* required */ + CSSM_DATA *outDomain) /* optional */ +{ + if (!inName || !outName) return; + CSSM_SIZE idx = 0; + CSSM_SIZE stopIdx = inName->Length; + uint8 *p = inName->Data; + *outName = *inName; + if (outDomain) { + outDomain->Length = idx; + outDomain->Data = p; + } + if (!p) return; + while (idx < stopIdx) { + if (*p++ == '@') { + outName->Length = idx; + if (outDomain) { + outDomain->Length = inName->Length - (idx + 1); + outDomain->Data = p; + } + break; + } + idx++; + } +} + +using namespace KeychainCore; + +CertificateRequest::CertificateRequest(const CSSM_OID &policy, + CSSM_CERT_TYPE certificateType, + CSSM_TP_AUTHORITY_REQUEST_TYPE requestType, + SecKeyRef privateKeyItemRef, + SecKeyRef publicKeyItemRef, + const SecCertificateRequestAttributeList *attributeList, + bool isNew /* = true */) + : mAlloc(Allocator::standard()), + mTP(gGuidAppleDotMacTP), + mCL(gGuidAppleX509CL), + mPolicy(mAlloc, policy.Data, policy.Length), + mCertType(certificateType), + mReqType(requestType), + mPrivKey(NULL), + mPubKey(NULL), + mEstTime(0), + mRefId(mAlloc), + mCertState(isNew ? CRS_New : CRS_Reconstructed), + mCertData(mAlloc), + mUserName(mAlloc), + mPassword(mAlloc), + mHostName(mAlloc), + mDomain(mAlloc), + mDoRenew(false), + mIsAsync(false), + mMutex(Mutex::recursive) +{ + StLock_(mMutex); + certReqDbg("CertificateRequest construct"); + + /* Validate policy OID. */ + if(!(nssCompareCssmData(&CSSMOID_DOTMAC_CERT_REQ_IDENTITY, &policy) || + nssCompareCssmData(&CSSMOID_DOTMAC_CERT_REQ_EMAIL_SIGN, &policy) || + nssCompareCssmData(&CSSMOID_DOTMAC_CERT_REQ_EMAIL_ENCRYPT, &policy) || + nssCompareCssmData(&CSSMOID_DOTMAC_CERT_REQ_SHARED_SERVICES, &policy))) { + certReqDbg("CertificateRequest(): unknown policy oid"); + MacOSError::throwMe(errSecParam); + } + if(privateKeyItemRef) { + mPrivKey = privateKeyItemRef; + CFRetain(mPrivKey); + } + if(publicKeyItemRef) { + mPubKey = publicKeyItemRef; + CFRetain(mPubKey); + } + + /* parse attr array */ + if(attributeList == NULL) { + return; + } + + bool doPendingRequest = false; + for(unsigned dex=0; dexcount; dex++) { + const SecCertificateRequestAttribute *attr = &attributeList->attr[dex]; + + if((attr->oid.Data == NULL) || (attr->value.Data == NULL)) { + MacOSError::throwMe(errSecParam); + } + if(nssCompareCssmData(&CSSMOID_DOTMAC_CERT_REQ_VALUE_USERNAME, &attr->oid)) { + CSSM_DATA userName = { 0, NULL }; + CSSM_DATA domainName = { 0, NULL }; + tokenizeName(&attr->value, &userName, &domainName); + if (!domainName.Length || !domainName.Data) { + domainName.Length = strlen(DOT_MAC_DOMAIN); + domainName.Data = (uint8*) DOT_MAC_DOMAIN; + } + mUserName.copy(userName); + mDomain.copy(domainName); + } + else if(nssCompareCssmData(&CSSMOID_DOTMAC_CERT_REQ_VALUE_PASSWORD, &attr->oid)) { + mPassword.copy(attr->value); + } + else if(nssCompareCssmData(&CSSMOID_DOTMAC_CERT_REQ_VALUE_HOSTNAME, &attr->oid)) { + mHostName.copy(attr->value); + } + else if(nssCompareCssmData(&CSSMOID_DOTMAC_CERT_REQ_VALUE_RENEW, &attr->oid)) { + /* + * any nonzero value means true + * FIXME: this is deprecated, Treadstone doesn't allow this. Reject this + * request? Ignore? + */ + mDoRenew = attrBoolValue(attr); + } + else if(nssCompareCssmData(&CSSMOID_DOTMAC_CERT_REQ_VALUE_ASYNC, &attr->oid)) { + /* any nonzero value means true */ + mIsAsync = attrBoolValue(attr); + } + else if(nssCompareCssmData(&CSSMOID_DOTMAC_CERT_REQ_VALUE_IS_PENDING, &attr->oid)) { + /* any nonzero value means true */ + doPendingRequest = attrBoolValue(attr); + } + + else { + certReqDbg("CertificateRequest(): unknown name/value oid"); + MacOSError::throwMe(errSecParam); + } + } + if(mCertState == CRS_Reconstructed) { + /* see if we have a refId or maybe even a cert in prefs */ + retrieveResults(); + if(mCertData.data() != NULL) { + mCertState = CRS_HaveCert; + } + else if(mRefId.data() != NULL) { + mCertState = CRS_HaveRefId; + } + else if(doPendingRequest) { + /* ask the server if there's a request pending */ + postPendingRequest(); + /* NOT REACHED - that always throws */ + } + else { + certReqDbg("CertificateRequest(): nothing in prefs"); + /* Nothing found in prefs; nothing to go by */ + MacOSError::throwMe(errSecItemNotFound); + } + } +} + +CertificateRequest::~CertificateRequest() throw() +{ + StLock_(mMutex); + certReqDbg("CertificateRequest destruct"); + + if(mPrivKey) { + CFRelease(mPrivKey); + } + if(mPubKey) { + CFRelease(mPubKey); + } +} + +#pragma mark ----- cert request submit ----- + +void CertificateRequest::submit( + sint32 *estimatedTime) +{ + StLock_(mMutex); + CSSM_DATA &policy = mPolicy.get(); + if(nssCompareCssmData(&CSSMOID_DOTMAC_CERT_REQ_IDENTITY, &policy) || + nssCompareCssmData(&CSSMOID_DOTMAC_CERT_REQ_EMAIL_SIGN, &policy) || + nssCompareCssmData(&CSSMOID_DOTMAC_CERT_REQ_EMAIL_ENCRYPT, &policy) || + nssCompareCssmData(&CSSMOID_DOTMAC_CERT_REQ_SHARED_SERVICES, &policy)) { + return submitDotMac(estimatedTime); + } + else { + /* shouldn't be here, we already validated policy in constructor */ + assert(0); + certReqDbg("CertificateRequest::submit(): bad policy"); + MacOSError::throwMe(errSecParam); + } +} + +void CertificateRequest::submitDotMac( + sint32 *estimatedTime) +{ + StLock_(mMutex); + 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; + CSSM_TP_CALLERAUTH_CONTEXT callerAuth; + CSSM_FIELD policyField; + CSSM_DATA refId = {0, NULL}; + const CSSM_KEY *privKey; + const CSSM_KEY *pubKey; + OSStatus ortn; + + if(mCertState != CRS_New) { + certReqDbg("CertificateRequest: can only submit a new request"); + MacOSError::throwMe(errSecParam); + } + if((mUserName.data() == NULL) || (mPassword.data() == NULL)) { + certReqDbg("CertificateRequest: user name and password required"); + MacOSError::throwMe(errSecParam); + } + + /* get keys and CSP handle in CSSM terms */ + if((mPrivKey == NULL) || (mPubKey == NULL)) { + certReqDbg("CertificateRequest: pub and priv keys required"); + MacOSError::throwMe(errSecParam); + } + ortn = SecKeyGetCSSMKey(mPrivKey, &privKey); + if(ortn) { + MacOSError::throwMe(ortn); + } + ortn = SecKeyGetCSSMKey(mPubKey, &pubKey); + if(ortn) { + MacOSError::throwMe(ortn); + } + ortn = SecKeyGetCSPHandle(mPrivKey, &cspHand); + if(ortn) { + MacOSError::throwMe(ortn); + } + + /* + * CSSM_X509_TYPE_VALUE_PAIR_PTR - one pair for now. + * Caller passes in user name like "johnsmith"; in the CSR, + * we write "johnsmith@mac.com". + */ + tvp.type = CSSMOID_CommonName; + tvp.valueType = BER_TAG_PKIX_UTF8_STRING; + CssmAutoData fullUserName(mAlloc); + size_t nameLen = mUserName.length(); + size_t domainLen = mDomain.length(); + fullUserName.malloc(nameLen + 1 + domainLen); + tvp.value = fullUserName.get(); + memmove(tvp.value.Data, mUserName.data(), nameLen); + memmove(tvp.value.Data + nameLen, "@", 1); + memmove(tvp.value.Data + nameLen + 1, mDomain.data(), domainLen); + + /* Fill in the CSSM_APPLE_DOTMAC_TP_CERT_REQUEST */ + memset(&certReq, 0, sizeof(certReq)); + certReq.version = CSSM_DOT_MAC_TP_REQ_VERSION; + certReq.cspHand = cspHand; + certReq.clHand = mCL->handle(); + certReq.numTypeValuePairs = 1; + certReq.typeValuePairs = &tvp; + certReq.publicKey = const_cast(pubKey); + certReq.privateKey = const_cast(privKey); + certReq.userName = mUserName.get(); + certReq.password = mPassword.get(); + if(mDoRenew) { + certReq.flags |= CSSM_DOTMAC_TP_SIGN_RENEW; + } + /* we don't deal with CSR here, input or output */ + + /* now the rest of the args for CSSM_TP_SubmitCredRequest() */ + reqSet.Requests = &certReq; + reqSet.NumberOfRequests = 1; + policyField.FieldOid = mPolicy; + policyField.FieldValue.Data = NULL; + policyField.FieldValue.Length = 0; + memset(&callerAuth, 0, sizeof(callerAuth)); + callerAuth.Policy.NumberOfPolicyIds = 1; + callerAuth.Policy.PolicyIds = &policyField; + ortn = SecKeyGetCredentials(mPrivKey, + CSSM_ACL_AUTHORIZATION_SIGN, + kSecCredentialTypeDefault, + const_cast(&callerAuth.CallerCredentials)); + if(ortn) { + certReqDbg("CertificateRequest: SecKeyGetCredentials error"); + MacOSError::throwMe(ortn); + } + + CssmAutoData hostName(mAlloc); + tpAuthority.AuthorityCert = NULL; + tpAuthority.AuthorityLocation = &tpNetAddrs; + tpNetAddrs.AddressType = CSSM_ADDR_NAME; + if(mHostName.data() != NULL) { + tpNetAddrs.Address = mHostName.get(); + } else { + unsigned hostLen = strlen(DOT_MAC_MGMT_HOST); + hostName.malloc(hostLen + 1 + domainLen); + tpNetAddrs.Address = hostName.get(); + memmove(tpNetAddrs.Address.Data, DOT_MAC_MGMT_HOST, hostLen); + memmove(tpNetAddrs.Address.Data + hostLen, ".", 1); + memmove(tpNetAddrs.Address.Data + hostLen + 1, mDomain.data(), domainLen); + } + tpAuthPtr = &tpAuthority; + + /* go */ + crtn = CSSM_TP_SubmitCredRequest(mTP->handle(), + tpAuthPtr, + CSSM_TP_AUTHORITY_REQUEST_CERTISSUE, + &reqSet, + &callerAuth, + &mEstTime, + &refId); // CSSM_DATA_PTR ReferenceIdentifier + + /* handle return, store results */ + switch(crtn) { + case CSSM_OK: + /* refID is a cert, we have to store it in prefs for later retrieval. */ + certReqDbg("submitDotMac: full success, storing cert"); + if(!mIsAsync) { + /* store in prefs if not running in async mode */ + ortn = storeResults(NULL, &refId); + if(ortn) { + crtn = ortn; + } + } + /* but keep a local copy too */ + mCertData.copy(refId); + mCertState = CRS_HaveCert; + if(estimatedTime) { + /* it's ready right now */ + *estimatedTime = 0; + } + break; + + case CSSMERR_APPLE_DOTMAC_REQ_QUEUED: + /* refID is the blob we use in CSSM_TP_RetrieveCredResult() */ + certReqDbg("submitDotMac: queued, storing refId"); + mRefId.copy(refId); + /* return success - this crtn is not visible at API */ + crtn = CSSM_OK; + if(!mIsAsync) { + /* store in prefs if not running in async mode */ + ortn = storeResults(&refId, NULL); + if(ortn) { + crtn = ortn; + } + } + mCertState = CRS_HaveRefId; + if(estimatedTime) { + *estimatedTime = mEstTime; + } + break; + + case CSSMERR_APPLE_DOTMAC_REQ_REDIRECT: + /* refID is a URL, caller obtains via getReturnData() */ + certReqDbg("submitDotMac: redirect"); + mRefId.copy(refId); + mCertState = CRS_HaveOtherData; + break; + + default: + /* all others are fatal errors, thrown below */ + break; + } + if(refId.Data) { + /* mallocd on our behalf by TP */ + free(refId.Data); + } + if(crtn) { + CssmError::throwMe(crtn); + } +} + +#pragma mark ----- cert request get result ----- + +void CertificateRequest::getResult( + sint32 *estimatedTime, // optional + CssmData &certData) +{ + StLock_(mMutex); + CSSM_DATA &policy = mPolicy.get(); + if(nssCompareCssmData(&CSSMOID_DOTMAC_CERT_REQ_IDENTITY, &policy) || + nssCompareCssmData(&CSSMOID_DOTMAC_CERT_REQ_EMAIL_SIGN, &policy) || + nssCompareCssmData(&CSSMOID_DOTMAC_CERT_REQ_EMAIL_ENCRYPT, &policy) || + nssCompareCssmData(&CSSMOID_DOTMAC_CERT_REQ_SHARED_SERVICES, &policy)) { + return getResultDotMac(estimatedTime, certData); + } + else { + /* shouldn't be here, we already validated policy in constructor */ + assert(0); + certReqDbg("CertificateRequest::getResult(): bad policy"); + MacOSError::throwMe(errSecParam); + } +} + +void CertificateRequest::getResultDotMac( + sint32 *estimatedTime, // optional + CssmData &certData) +{ + StLock_(mMutex); + switch(mCertState) { + case CRS_HaveCert: + /* trivial case, we already have what caller is looking for */ + certReqDbg("getResultDotMac: have the cert right now"); + assert(mCertData.data() != NULL); + certData = mCertData.get(); + if(estimatedTime) { + *estimatedTime = 0; + } + break; + case CRS_HaveRefId: + { + /* ping the server */ + certReqDbg("getResultDotMac: CRS_HaveRefId; polling server"); + assert(mRefId.data() != NULL); + CSSM_BOOL ConfirmationRequired; + CSSM_TP_RESULT_SET_PTR resultSet = NULL; + CSSM_RETURN crtn; + + crtn = CSSM_TP_RetrieveCredResult(mTP->handle(), + &mRefId.get(), + NULL, // CallerAuthCredentials + &mEstTime, + &ConfirmationRequired, + &resultSet); + switch(crtn) { + case CSSM_OK: + break; + case CSSMERR_TP_CERT_NOT_VALID_YET: + /* + * By convention, this means "not ready yet". + * The dot mac server does not have a way of telling us the + * estimated time on a straight lookup like this (we only get + * an estimated completion time on the initial request), so we + * fake it. + */ + certReqDbg("getResultDotMac: polled server, not ready yet"); + if(estimatedTime) { + *estimatedTime = (mEstTime) ? mEstTime : 1; + } + MacOSError::throwMe(CSSMERR_APPLE_DOTMAC_REQ_IS_PENDING); + default: + certReqDbg("CSSM_TP_RetrieveCredResult error"); + CssmError::throwMe(crtn); + } + if(resultSet == NULL) { + certReqDbg("***CSSM_TP_RetrieveCredResult OK, but no result set"); + MacOSError::throwMe(errSecInternalComponent); + } + if(resultSet->NumberOfResults != 1) { + certReqDbg("***CSSM_TP_RetrieveCredResult OK, NumberOfResults (%lu)", + (unsigned long)resultSet->NumberOfResults); + MacOSError::throwMe(errSecInternalComponent); + } + if(resultSet->Results == NULL) { + certReqDbg("***CSSM_TP_RetrieveCredResult OK, but empty result set"); + MacOSError::throwMe(errSecInternalComponent); + } + certReqDbg("getResultDotMac: polled server, SUCCESS"); + CSSM_DATA_PTR result = (CSSM_DATA_PTR)resultSet->Results; + if(result->Data == NULL) { + certReqDbg("***CSSM_TP_RetrieveCredResult OK, but empty result"); + MacOSError::throwMe(errSecInternalComponent); + } + mCertData.copy(*result); + certData = mCertData.get(); + mCertState = CRS_HaveCert; + if(estimatedTime) { + *estimatedTime = 0; + } + + /* + * Free the stuff allocated on our behalf by TP. + * FIXME - are we sure CssmClient is using alloc, free, etc.? + */ + free(result->Data); + free(result); + free(resultSet); + break; + } + default: + /* what do we do with this? */ + certReqDbg("CertificateRequest::getResultDotMac(): bad state"); + MacOSError::throwMe(errSecInternalComponent); + } + + /* + * One more thing: once we pass a cert back to caller, we erase + * the record of this transaction from prefs. + */ + assert(mCertData.data() != NULL); + assert(mCertData.data() == certData.Data); + removeResults(); +} + +/* + * Obtain policy/error specific return data blob. We own the data, it's + * not copied. + */ +void CertificateRequest::getReturnData( + CssmData &rtnData) +{ + StLock_(mMutex); + rtnData = mRefId.get(); +} + +#pragma mark ----- preferences support ----- + +/* Current user as CFString, for use as key in per-policy dictionary */ +CFStringRef CertificateRequest::createUserKey() +{ + StLock_(mMutex); + return CFStringCreateWithBytes(NULL, (UInt8 *)mUserName.data(), mUserName.length(), + kCFStringEncodingUTF8, false); +} + +#define MAX_OID_LEN 2048 // way big... */ + +/* current policy as CFString, for use as key in prefs dictionary */ +CFStringRef CertificateRequest::createPolicyKey() +{ + StLock_(mMutex); + char oidstr[MAX_OID_LEN]; + unsigned char *inp = (unsigned char *)mPolicy.data(); + char *outp = oidstr; + CFIndex len = mPolicy.length(); + for(CFIndex dex=0; dex_(mMutex); + assert(mPolicy.data() != NULL); + assert(mUserName.data() != NULL); + assert(mDomain.data() != NULL); + + bool deleteEntry = ((refId == NULL) && (certData == NULL)); + + /* get a mutable copy of the existing prefs, or a fresh empty one */ + MutableDictionary *prefsDict = MutableDictionary::CreateMutableDictionary(DOT_MAC_REQ_PREFS, Dictionary::US_User); + if (prefsDict == NULL) + { + prefsDict = new MutableDictionary(); + } + + /* get a mutable copy of the dictionary for this policy, or a fresh empty one */ + CFStringRef policyKey = createPolicyKey(); + MutableDictionary *policyDict = prefsDict->copyMutableDictValue(policyKey); + + CFStringRef userKey = createUserKey(); + if(deleteEntry) { + /* remove user dictionary from this policy dictionary */ + policyDict->removeValue(userKey); + } + else { + /* get a mutable copy of the dictionary for this user, or a fresh empty one */ + MutableDictionary *userDict = policyDict->copyMutableDictValue(userKey); + + CFStringRef domainKey = CFStringCreateWithBytes(NULL, (UInt8 *)mDomain.data(), mDomain.length(), kCFStringEncodingUTF8, false); + userDict->setValue(CFSTR(DOT_MAC_DOMAIN_KEY), domainKey); + CFRelease(domainKey); + + /* write refId and/or cert --> user dictionary */ + if(refId) { + userDict->setDataValue(CFSTR(DOT_MAC_REF_ID_KEY), refId->Data, refId->Length); + } + if(certData) { + userDict->setDataValue(CFSTR(DOT_MAC_CERT_KEY), certData->Data, certData->Length); + } + + /* new user dictionary --> policy dictionary */ + policyDict->setValue(userKey, userDict->dict()); + delete userDict; + } + CFRelease(userKey); + + /* new policy dictionary to prefs dictionary, or nuke it */ + if(policyDict->count() == 0) { + prefsDict->removeValue(policyKey); + } + else { + prefsDict->setValue(policyKey, policyDict->dict()); + } + CFRelease(policyKey); + delete policyDict; + + /* prefs --> disk */ + OSStatus ortn = errSecSuccess; + if(!prefsDict->writePlistToPrefs(DOT_MAC_REQ_PREFS, Dictionary::US_User)) { + certReqDbg("storeResults: error writing prefs to disk"); + ortn = errSecIO; + } + delete prefsDict; + return ortn; +} + +/* + * Attempt to fetch mCertData or mRefId from preferences. + */ +void CertificateRequest::retrieveResults() +{ + StLock_(mMutex); + assert(mPolicy.data() != NULL); + assert(mUserName.data() != NULL); + + /* get the .mac cert prefs as a dictionary */ + Dictionary *pd = Dictionary::CreateDictionary(DOT_MAC_REQ_PREFS, Dictionary::US_User); + if (pd == NULL) + { + certReqDbg("retrieveResults: no prefs found"); + return; + } + + auto_ptr prefsDict(pd); + + /* get dictionary for current policy */ + CFStringRef policyKey = createPolicyKey(); + Dictionary *policyDict = prefsDict->copyDictValue(policyKey); + CFRelease(policyKey); + if(policyDict != NULL) { + /* dictionary for user */ + CFStringRef userKey = createUserKey(); + Dictionary *userDict = policyDict->copyDictValue(userKey); + if(userDict != NULL) { + /* is there a cert in there? */ + CFDataRef val = userDict->getDataValue(CFSTR(DOT_MAC_CERT_KEY)); + if(val) { + mCertData.copy(CFDataGetBytePtr(val), CFDataGetLength(val)); + } + + /* how about refId? */ + val = userDict->getDataValue(CFSTR(DOT_MAC_REF_ID_KEY)); + if(val) { + mRefId.copy(CFDataGetBytePtr(val), CFDataGetLength(val)); + } + delete userDict; + } + CFRelease(userKey); + delete policyDict; + } +} + +/* + * Remove all trace of current policy/user. Called when we successfully transferred + * the cert back to caller. + */ +void CertificateRequest::removeResults() +{ + StLock_(mMutex); + assert(mPolicy.data() != NULL); + assert(mUserName.data() != NULL); + storeResults(NULL, NULL); +} + +/* + * Have the TP ping the server to see of there's a request pending for the current + * user. Always throws: either + * CSSMERR_APPLE_DOTMAC_REQ_IS_PENDING -- request pending + * CSSMERR_APPLE_DOTMAC_NO_REQ_PENDING -- no request pending + * errSecParam -- no user, no password + * other gross errors, e.g. errSecIO for server connection failure + * + * The distinguishing features about this TP request are: + * + * policy OID = CSSMOID_DOTMAC_CERT_REQ_{IDENTITY,EMAIL_SIGN,EMAIL_ENCRYPT,SHARED_SERVICES} + * CSSM_TP_AUTHORITY_REQUEST_TYPE = CSSM_TP_AUTHORITY_REQUEST_CERTLOOKUP + * CSSM_APPLE_DOTMAC_TP_CERT_REQUEST.flags = CSSM_DOTMAC_TP_IS_REQ_PENDING + * must have userName and password + * hostname optional as usual + */ +void CertificateRequest::postPendingRequest() +{ + StLock_(mMutex); + 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_TP_CALLERAUTH_CONTEXT callerAuth; + CSSM_FIELD policyField; + CSSM_DATA refId = {0, NULL}; + + assert(mCertState == CRS_Reconstructed); + if((mUserName.data() == NULL) || (mPassword.data() == NULL)) { + certReqDbg("postPendingRequest: user name and password required"); + MacOSError::throwMe(errSecParam); + } + + /* Fill in the CSSM_APPLE_DOTMAC_TP_CERT_REQUEST */ + memset(&certReq, 0, sizeof(certReq)); + certReq.version = CSSM_DOT_MAC_TP_REQ_VERSION; + certReq.userName = mUserName.get(); + certReq.password = mPassword.get(); + certReq.flags = CSSM_DOTMAC_TP_IS_REQ_PENDING; + + /* now the rest of the args for CSSM_TP_SubmitCredRequest() */ + reqSet.Requests = &certReq; + reqSet.NumberOfRequests = 1; + /* + * This OID actually doesn't matter - right? This RPC doesn't know about + * which request we seek... + */ + policyField.FieldOid = mPolicy; + policyField.FieldValue.Data = NULL; + policyField.FieldValue.Length = 0; + memset(&callerAuth, 0, sizeof(callerAuth)); + callerAuth.Policy.NumberOfPolicyIds = 1; + callerAuth.Policy.PolicyIds = &policyField; + /* no other creds here */ + + if(mHostName.data() != NULL) { + tpAuthority.AuthorityCert = NULL; + tpAuthority.AuthorityLocation = &tpNetAddrs; + tpNetAddrs.AddressType = CSSM_ADDR_NAME; + tpNetAddrs.Address = mHostName.get(); + tpAuthPtr = &tpAuthority; + } + + /* go */ + crtn = CSSM_TP_SubmitCredRequest(mTP->handle(), + tpAuthPtr, + CSSM_TP_AUTHORITY_REQUEST_CERTLOOKUP, + &reqSet, + &callerAuth, + &mEstTime, + &refId); // CSSM_DATA_PTR ReferenceIdentifier + + if(refId.Data) { + /* shouldn't be any but just in case.... */ + free(refId.Data); + } + switch(crtn) { + case CSSMERR_APPLE_DOTMAC_REQ_IS_PENDING: + certReqDbg("postPendingRequest: REQ_IS_PENDING"); + break; + case CSSMERR_APPLE_DOTMAC_NO_REQ_PENDING: + certReqDbg("postPendingRequest: NO_REQ_PENDING"); + break; + case CSSM_OK: + /* should never happen */ + certReqDbg("postPendingRequest: unexpected success!"); + crtn = errSecInternalComponent; + break; + default: + certReqDbg("postPendingRequest: unexpected rtn %lu", (unsigned long)crtn); + break; + } + CssmError::throwMe(crtn); +} +