2 * Copyright (c) 2002-2004,2011-2014 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
25 // CertificateRequest.cpp
27 #include <security_keychain/CertificateRequest.h>
28 #include <Security/oidsalg.h>
29 #include <Security/SecKey.h>
30 #include <Security/SecKeyPriv.h>
31 #include <Security/cssmapi.h>
34 #include <Security/oidsattr.h>
35 #include <security_utilities/simpleprefs.h>
38 /* one top-level prefs file for all of .mac cert requests */
39 #define DOT_MAC_REQ_PREFS "com.apple.security.certreq"
42 * Within that dictionary is a set of per-policy dictionaries; the key in the
43 * top-level prefs for these dictionaries is the raw policy OID data encoded
46 * Within one per-policy dictionary exists a number of per-user dictionaries,
47 * with the username key as a string. Note that this user name, the one passed to the
48 * .mac server, does NOT have to have any relation to the current Unix user name; one
49 * Unix user can have multiple .mac accounts.
52 * Within the per-policy, per user dictionary are these two values, both stored
53 * as raw data (CFData) blobs.
55 #define DOT_MAC_REF_ID_KEY "refId"
56 #define DOT_MAC_CERT_KEY "certificate"
58 /* Domain for .mac cert requests */
59 #define DOT_MAC_DOMAIN_KEY "domain"
60 #define DOT_MAC_DOMAIN "mac.com"
62 /* Hosts for .mac cert requests */
63 #define DOT_MAC_MGMT_HOST "certmgmt"
64 #define DOT_MAC_INFO_HOST "certinfo"
67 * Compare two CSSM_DATAs (or two CSSM_OIDs), return true if identical.
70 bool nssCompareCssmData(
71 const CSSM_DATA
*data1
,
72 const CSSM_DATA
*data2
)
74 if((data1
== NULL
) || (data1
->Data
== NULL
) ||
75 (data2
== NULL
) || (data2
->Data
== NULL
) ||
76 (data1
->Length
!= data2
->Length
)) {
79 if(data1
->Length
!= data2
->Length
) {
82 if(memcmp(data1
->Data
, data2
->Data
, data1
->Length
) == 0) {
90 /* any nonzero value means true */
91 static bool attrBoolValue(
92 const SecCertificateRequestAttribute
*attr
)
94 if((attr
->value
.Data
!= NULL
) &&
95 (attr
->value
.Length
!= 0) &&
96 (attr
->value
.Data
[0] != 0)) {
104 static void tokenizeName(
105 const CSSM_DATA
*inName
, /* required */
106 CSSM_DATA
*outName
, /* required */
107 CSSM_DATA
*outDomain
) /* optional */
109 if (!inName
|| !outName
) return;
111 CSSM_SIZE stopIdx
= inName
->Length
;
112 uint8
*p
= inName
->Data
;
115 outDomain
->Length
= idx
;
119 while (idx
< stopIdx
) {
121 outName
->Length
= idx
;
123 outDomain
->Length
= inName
->Length
- (idx
+ 1);
132 using namespace KeychainCore
;
134 CertificateRequest::CertificateRequest(const CSSM_OID
&policy
,
135 CSSM_CERT_TYPE certificateType
,
136 CSSM_TP_AUTHORITY_REQUEST_TYPE requestType
,
137 SecKeyRef privateKeyItemRef
,
138 SecKeyRef publicKeyItemRef
,
139 const SecCertificateRequestAttributeList
*attributeList
,
140 bool isNew
/* = true */)
141 : mAlloc(Allocator::standard()),
142 mTP(gGuidAppleDotMacTP
),
143 mCL(gGuidAppleX509CL
),
144 mPolicy(mAlloc
, policy
.Data
, policy
.Length
),
145 mCertType(certificateType
),
146 mReqType(requestType
),
151 mCertState(isNew
? CRS_New
: CRS_Reconstructed
),
159 mMutex(Mutex::recursive
)
161 StLock
<Mutex
>_(mMutex
);
162 certReqDbg("CertificateRequest construct");
164 /* Validate policy OID. */
165 if(!(nssCompareCssmData(&CSSMOID_DOTMAC_CERT_REQ_IDENTITY
, &policy
) ||
166 nssCompareCssmData(&CSSMOID_DOTMAC_CERT_REQ_EMAIL_SIGN
, &policy
) ||
167 nssCompareCssmData(&CSSMOID_DOTMAC_CERT_REQ_EMAIL_ENCRYPT
, &policy
) ||
168 nssCompareCssmData(&CSSMOID_DOTMAC_CERT_REQ_SHARED_SERVICES
, &policy
))) {
169 certReqDbg("CertificateRequest(): unknown policy oid");
170 MacOSError::throwMe(errSecParam
);
172 if(privateKeyItemRef
) {
173 mPrivKey
= privateKeyItemRef
;
176 if(publicKeyItemRef
) {
177 mPubKey
= publicKeyItemRef
;
181 /* parse attr array */
182 if(attributeList
== NULL
) {
186 bool doPendingRequest
= false;
187 for(unsigned dex
=0; dex
<attributeList
->count
; dex
++) {
188 const SecCertificateRequestAttribute
*attr
= &attributeList
->attr
[dex
];
190 if((attr
->oid
.Data
== NULL
) || (attr
->value
.Data
== NULL
)) {
191 MacOSError::throwMe(errSecParam
);
193 if(nssCompareCssmData(&CSSMOID_DOTMAC_CERT_REQ_VALUE_USERNAME
, &attr
->oid
)) {
194 CSSM_DATA userName
= { 0, NULL
};
195 CSSM_DATA domainName
= { 0, NULL
};
196 tokenizeName(&attr
->value
, &userName
, &domainName
);
197 if (!domainName
.Length
|| !domainName
.Data
) {
198 domainName
.Length
= strlen(DOT_MAC_DOMAIN
);
199 domainName
.Data
= (uint8
*) DOT_MAC_DOMAIN
;
201 mUserName
.copy(userName
);
202 mDomain
.copy(domainName
);
204 else if(nssCompareCssmData(&CSSMOID_DOTMAC_CERT_REQ_VALUE_PASSWORD
, &attr
->oid
)) {
205 mPassword
.copy(attr
->value
);
207 else if(nssCompareCssmData(&CSSMOID_DOTMAC_CERT_REQ_VALUE_HOSTNAME
, &attr
->oid
)) {
208 mHostName
.copy(attr
->value
);
210 else if(nssCompareCssmData(&CSSMOID_DOTMAC_CERT_REQ_VALUE_RENEW
, &attr
->oid
)) {
212 * any nonzero value means true
213 * FIXME: this is deprecated, Treadstone doesn't allow this. Reject this
216 mDoRenew
= attrBoolValue(attr
);
218 else if(nssCompareCssmData(&CSSMOID_DOTMAC_CERT_REQ_VALUE_ASYNC
, &attr
->oid
)) {
219 /* any nonzero value means true */
220 mIsAsync
= attrBoolValue(attr
);
222 else if(nssCompareCssmData(&CSSMOID_DOTMAC_CERT_REQ_VALUE_IS_PENDING
, &attr
->oid
)) {
223 /* any nonzero value means true */
224 doPendingRequest
= attrBoolValue(attr
);
228 certReqDbg("CertificateRequest(): unknown name/value oid");
229 MacOSError::throwMe(errSecParam
);
232 if(mCertState
== CRS_Reconstructed
) {
233 /* see if we have a refId or maybe even a cert in prefs */
235 if(mCertData
.data() != NULL
) {
236 mCertState
= CRS_HaveCert
;
238 else if(mRefId
.data() != NULL
) {
239 mCertState
= CRS_HaveRefId
;
241 else if(doPendingRequest
) {
242 /* ask the server if there's a request pending */
243 postPendingRequest();
244 /* NOT REACHED - that always throws */
247 certReqDbg("CertificateRequest(): nothing in prefs");
248 /* Nothing found in prefs; nothing to go by */
249 MacOSError::throwMe(errSecItemNotFound
);
254 CertificateRequest::~CertificateRequest() throw()
256 StLock
<Mutex
>_(mMutex
);
257 certReqDbg("CertificateRequest destruct");
267 #pragma mark ----- cert request submit -----
269 void CertificateRequest::submit(
270 sint32
*estimatedTime
)
272 StLock
<Mutex
>_(mMutex
);
273 CSSM_DATA
&policy
= mPolicy
.get();
274 if(nssCompareCssmData(&CSSMOID_DOTMAC_CERT_REQ_IDENTITY
, &policy
) ||
275 nssCompareCssmData(&CSSMOID_DOTMAC_CERT_REQ_EMAIL_SIGN
, &policy
) ||
276 nssCompareCssmData(&CSSMOID_DOTMAC_CERT_REQ_EMAIL_ENCRYPT
, &policy
) ||
277 nssCompareCssmData(&CSSMOID_DOTMAC_CERT_REQ_SHARED_SERVICES
, &policy
)) {
278 return submitDotMac(estimatedTime
);
281 /* shouldn't be here, we already validated policy in constructor */
283 certReqDbg("CertificateRequest::submit(): bad policy");
284 MacOSError::throwMe(errSecParam
);
288 void CertificateRequest::submitDotMac(
289 sint32
*estimatedTime
)
291 StLock
<Mutex
>_(mMutex
);
293 CSSM_TP_AUTHORITY_ID tpAuthority
;
294 CSSM_TP_AUTHORITY_ID
*tpAuthPtr
= NULL
;
295 CSSM_NET_ADDRESS tpNetAddrs
;
296 CSSM_APPLE_DOTMAC_TP_CERT_REQUEST certReq
;
297 CSSM_TP_REQUEST_SET reqSet
;
298 CSSM_CSP_HANDLE cspHand
= 0;
299 CSSM_X509_TYPE_VALUE_PAIR tvp
;
300 CSSM_TP_CALLERAUTH_CONTEXT callerAuth
;
301 CSSM_FIELD policyField
;
302 CSSM_DATA refId
= {0, NULL
};
303 const CSSM_KEY
*privKey
;
304 const CSSM_KEY
*pubKey
;
307 if(mCertState
!= CRS_New
) {
308 certReqDbg("CertificateRequest: can only submit a new request");
309 MacOSError::throwMe(errSecParam
);
311 if((mUserName
.data() == NULL
) || (mPassword
.data() == NULL
)) {
312 certReqDbg("CertificateRequest: user name and password required");
313 MacOSError::throwMe(errSecParam
);
316 /* get keys and CSP handle in CSSM terms */
317 if((mPrivKey
== NULL
) || (mPubKey
== NULL
)) {
318 certReqDbg("CertificateRequest: pub and priv keys required");
319 MacOSError::throwMe(errSecParam
);
321 ortn
= SecKeyGetCSSMKey(mPrivKey
, &privKey
);
323 MacOSError::throwMe(ortn
);
325 ortn
= SecKeyGetCSSMKey(mPubKey
, &pubKey
);
327 MacOSError::throwMe(ortn
);
329 ortn
= SecKeyGetCSPHandle(mPrivKey
, &cspHand
);
331 MacOSError::throwMe(ortn
);
335 * CSSM_X509_TYPE_VALUE_PAIR_PTR - one pair for now.
336 * Caller passes in user name like "johnsmith"; in the CSR,
337 * we write "johnsmith@mac.com".
339 tvp
.type
= CSSMOID_CommonName
;
340 tvp
.valueType
= BER_TAG_PKIX_UTF8_STRING
;
341 CssmAutoData
fullUserName(mAlloc
);
342 size_t nameLen
= mUserName
.length();
343 size_t domainLen
= mDomain
.length();
344 fullUserName
.malloc(nameLen
+ 1 + domainLen
);
345 tvp
.value
= fullUserName
.get();
346 memmove(tvp
.value
.Data
, mUserName
.data(), nameLen
);
347 memmove(tvp
.value
.Data
+ nameLen
, "@", 1);
348 memmove(tvp
.value
.Data
+ nameLen
+ 1, mDomain
.data(), domainLen
);
350 /* Fill in the CSSM_APPLE_DOTMAC_TP_CERT_REQUEST */
351 memset(&certReq
, 0, sizeof(certReq
));
352 certReq
.version
= CSSM_DOT_MAC_TP_REQ_VERSION
;
353 certReq
.cspHand
= cspHand
;
354 certReq
.clHand
= mCL
->handle();
355 certReq
.numTypeValuePairs
= 1;
356 certReq
.typeValuePairs
= &tvp
;
357 certReq
.publicKey
= const_cast<CSSM_KEY_PTR
>(pubKey
);
358 certReq
.privateKey
= const_cast<CSSM_KEY_PTR
>(privKey
);
359 certReq
.userName
= mUserName
.get();
360 certReq
.password
= mPassword
.get();
362 certReq
.flags
|= CSSM_DOTMAC_TP_SIGN_RENEW
;
364 /* we don't deal with CSR here, input or output */
366 /* now the rest of the args for CSSM_TP_SubmitCredRequest() */
367 reqSet
.Requests
= &certReq
;
368 reqSet
.NumberOfRequests
= 1;
369 policyField
.FieldOid
= mPolicy
;
370 policyField
.FieldValue
.Data
= NULL
;
371 policyField
.FieldValue
.Length
= 0;
372 memset(&callerAuth
, 0, sizeof(callerAuth
));
373 callerAuth
.Policy
.NumberOfPolicyIds
= 1;
374 callerAuth
.Policy
.PolicyIds
= &policyField
;
375 ortn
= SecKeyGetCredentials(mPrivKey
,
376 CSSM_ACL_AUTHORIZATION_SIGN
,
377 kSecCredentialTypeDefault
,
378 const_cast<const CSSM_ACCESS_CREDENTIALS
**>(&callerAuth
.CallerCredentials
));
380 certReqDbg("CertificateRequest: SecKeyGetCredentials error");
381 MacOSError::throwMe(ortn
);
384 CssmAutoData
hostName(mAlloc
);
385 tpAuthority
.AuthorityCert
= NULL
;
386 tpAuthority
.AuthorityLocation
= &tpNetAddrs
;
387 tpNetAddrs
.AddressType
= CSSM_ADDR_NAME
;
388 if(mHostName
.data() != NULL
) {
389 tpNetAddrs
.Address
= mHostName
.get();
391 unsigned hostLen
= strlen(DOT_MAC_MGMT_HOST
);
392 hostName
.malloc(hostLen
+ 1 + domainLen
);
393 tpNetAddrs
.Address
= hostName
.get();
394 memmove(tpNetAddrs
.Address
.Data
, DOT_MAC_MGMT_HOST
, hostLen
);
395 memmove(tpNetAddrs
.Address
.Data
+ hostLen
, ".", 1);
396 memmove(tpNetAddrs
.Address
.Data
+ hostLen
+ 1, mDomain
.data(), domainLen
);
398 tpAuthPtr
= &tpAuthority
;
401 crtn
= CSSM_TP_SubmitCredRequest(mTP
->handle(),
403 CSSM_TP_AUTHORITY_REQUEST_CERTISSUE
,
407 &refId
); // CSSM_DATA_PTR ReferenceIdentifier
409 /* handle return, store results */
412 /* refID is a cert, we have to store it in prefs for later retrieval. */
413 certReqDbg("submitDotMac: full success, storing cert");
415 /* store in prefs if not running in async mode */
416 ortn
= storeResults(NULL
, &refId
);
421 /* but keep a local copy too */
422 mCertData
.copy(refId
);
423 mCertState
= CRS_HaveCert
;
425 /* it's ready right now */
430 case CSSMERR_APPLE_DOTMAC_REQ_QUEUED
:
431 /* refID is the blob we use in CSSM_TP_RetrieveCredResult() */
432 certReqDbg("submitDotMac: queued, storing refId");
434 /* return success - this crtn is not visible at API */
437 /* store in prefs if not running in async mode */
438 ortn
= storeResults(&refId
, NULL
);
443 mCertState
= CRS_HaveRefId
;
445 *estimatedTime
= mEstTime
;
449 case CSSMERR_APPLE_DOTMAC_REQ_REDIRECT
:
450 /* refID is a URL, caller obtains via getReturnData() */
451 certReqDbg("submitDotMac: redirect");
453 mCertState
= CRS_HaveOtherData
;
457 /* all others are fatal errors, thrown below */
461 /* mallocd on our behalf by TP */
465 CssmError::throwMe(crtn
);
469 #pragma mark ----- cert request get result -----
471 void CertificateRequest::getResult(
472 sint32
*estimatedTime
, // optional
475 StLock
<Mutex
>_(mMutex
);
476 CSSM_DATA
&policy
= mPolicy
.get();
477 if(nssCompareCssmData(&CSSMOID_DOTMAC_CERT_REQ_IDENTITY
, &policy
) ||
478 nssCompareCssmData(&CSSMOID_DOTMAC_CERT_REQ_EMAIL_SIGN
, &policy
) ||
479 nssCompareCssmData(&CSSMOID_DOTMAC_CERT_REQ_EMAIL_ENCRYPT
, &policy
) ||
480 nssCompareCssmData(&CSSMOID_DOTMAC_CERT_REQ_SHARED_SERVICES
, &policy
)) {
481 return getResultDotMac(estimatedTime
, certData
);
484 /* shouldn't be here, we already validated policy in constructor */
486 certReqDbg("CertificateRequest::getResult(): bad policy");
487 MacOSError::throwMe(errSecParam
);
491 void CertificateRequest::getResultDotMac(
492 sint32
*estimatedTime
, // optional
495 StLock
<Mutex
>_(mMutex
);
498 /* trivial case, we already have what caller is looking for */
499 certReqDbg("getResultDotMac: have the cert right now");
500 assert(mCertData
.data() != NULL
);
501 certData
= mCertData
.get();
508 /* ping the server */
509 certReqDbg("getResultDotMac: CRS_HaveRefId; polling server");
510 assert(mRefId
.data() != NULL
);
511 CSSM_BOOL ConfirmationRequired
;
512 CSSM_TP_RESULT_SET_PTR resultSet
= NULL
;
515 crtn
= CSSM_TP_RetrieveCredResult(mTP
->handle(),
517 NULL
, // CallerAuthCredentials
519 &ConfirmationRequired
,
524 case CSSMERR_TP_CERT_NOT_VALID_YET
:
526 * By convention, this means "not ready yet".
527 * The dot mac server does not have a way of telling us the
528 * estimated time on a straight lookup like this (we only get
529 * an estimated completion time on the initial request), so we
532 certReqDbg("getResultDotMac: polled server, not ready yet");
534 *estimatedTime
= (mEstTime
) ? mEstTime
: 1;
536 MacOSError::throwMe(CSSMERR_APPLE_DOTMAC_REQ_IS_PENDING
);
538 certReqDbg("CSSM_TP_RetrieveCredResult error");
539 CssmError::throwMe(crtn
);
541 if(resultSet
== NULL
) {
542 certReqDbg("***CSSM_TP_RetrieveCredResult OK, but no result set");
543 MacOSError::throwMe(errSecInternalComponent
);
545 if(resultSet
->NumberOfResults
!= 1) {
546 certReqDbg("***CSSM_TP_RetrieveCredResult OK, NumberOfResults (%lu)",
547 (unsigned long)resultSet
->NumberOfResults
);
548 MacOSError::throwMe(errSecInternalComponent
);
550 if(resultSet
->Results
== NULL
) {
551 certReqDbg("***CSSM_TP_RetrieveCredResult OK, but empty result set");
552 MacOSError::throwMe(errSecInternalComponent
);
554 certReqDbg("getResultDotMac: polled server, SUCCESS");
555 CSSM_DATA_PTR result
= (CSSM_DATA_PTR
)resultSet
->Results
;
556 if(result
->Data
== NULL
) {
557 certReqDbg("***CSSM_TP_RetrieveCredResult OK, but empty result");
558 MacOSError::throwMe(errSecInternalComponent
);
560 mCertData
.copy(*result
);
561 certData
= mCertData
.get();
562 mCertState
= CRS_HaveCert
;
568 * Free the stuff allocated on our behalf by TP.
569 * FIXME - are we sure CssmClient is using alloc, free, etc.?
577 /* what do we do with this? */
578 certReqDbg("CertificateRequest::getResultDotMac(): bad state");
579 MacOSError::throwMe(errSecInternalComponent
);
583 * One more thing: once we pass a cert back to caller, we erase
584 * the record of this transaction from prefs.
586 assert(mCertData
.data() != NULL
);
587 assert(mCertData
.data() == certData
.Data
);
592 * Obtain policy/error specific return data blob. We own the data, it's
595 void CertificateRequest::getReturnData(
598 StLock
<Mutex
>_(mMutex
);
599 rtnData
= mRefId
.get();
602 #pragma mark ----- preferences support -----
604 /* Current user as CFString, for use as key in per-policy dictionary */
605 CFStringRef
CertificateRequest::createUserKey()
607 StLock
<Mutex
>_(mMutex
);
608 return CFStringCreateWithBytes(NULL
, (UInt8
*)mUserName
.data(), mUserName
.length(),
609 kCFStringEncodingUTF8
, false);
612 #define MAX_OID_LEN 2048 // way big... */
614 /* current policy as CFString, for use as key in prefs dictionary */
615 CFStringRef
CertificateRequest::createPolicyKey()
617 StLock
<Mutex
>_(mMutex
);
618 char oidstr
[MAX_OID_LEN
];
619 unsigned char *inp
= (unsigned char *)mPolicy
.data();
621 CFIndex len
= mPolicy
.length();
622 for(CFIndex dex
=0; dex
<len
; dex
++) {
623 sprintf(outp
, "%02X ", *inp
++);
626 return CFStringCreateWithBytes(NULL
, (UInt8
*)oidstr
, len
* 3,
627 kCFStringEncodingUTF8
, false);
631 * Store cert data or refId in prefs. If both are NULL, delete the
632 * user dictionary entry from the policy dictionary if there, and then
633 * delete the policy dictionary if it's empty.
635 OSStatus
CertificateRequest::storeResults(
636 const CSSM_DATA
*refId
, // optional, for queued requests
637 const CSSM_DATA
*certData
) // optional, for immediate completion
639 StLock
<Mutex
>_(mMutex
);
640 assert(mPolicy
.data() != NULL
);
641 assert(mUserName
.data() != NULL
);
642 assert(mDomain
.data() != NULL
);
644 bool deleteEntry
= ((refId
== NULL
) && (certData
== NULL
));
646 /* get a mutable copy of the existing prefs, or a fresh empty one */
647 MutableDictionary
*prefsDict
= MutableDictionary::CreateMutableDictionary(DOT_MAC_REQ_PREFS
, Dictionary::US_User
);
648 if (prefsDict
== NULL
)
650 prefsDict
= new MutableDictionary();
653 /* get a mutable copy of the dictionary for this policy, or a fresh empty one */
654 CFStringRef policyKey
= createPolicyKey();
655 MutableDictionary
*policyDict
= prefsDict
->copyMutableDictValue(policyKey
);
657 CFStringRef userKey
= createUserKey();
659 /* remove user dictionary from this policy dictionary */
660 policyDict
->removeValue(userKey
);
663 /* get a mutable copy of the dictionary for this user, or a fresh empty one */
664 MutableDictionary
*userDict
= policyDict
->copyMutableDictValue(userKey
);
666 CFStringRef domainKey
= CFStringCreateWithBytes(NULL
, (UInt8
*)mDomain
.data(), mDomain
.length(), kCFStringEncodingUTF8
, false);
667 userDict
->setValue(CFSTR(DOT_MAC_DOMAIN_KEY
), domainKey
);
668 CFRelease(domainKey
);
670 /* write refId and/or cert --> user dictionary */
672 userDict
->setDataValue(CFSTR(DOT_MAC_REF_ID_KEY
), refId
->Data
, refId
->Length
);
675 userDict
->setDataValue(CFSTR(DOT_MAC_CERT_KEY
), certData
->Data
, certData
->Length
);
678 /* new user dictionary --> policy dictionary */
679 policyDict
->setValue(userKey
, userDict
->dict());
684 /* new policy dictionary to prefs dictionary, or nuke it */
685 if(policyDict
->count() == 0) {
686 prefsDict
->removeValue(policyKey
);
689 prefsDict
->setValue(policyKey
, policyDict
->dict());
691 CFRelease(policyKey
);
695 OSStatus ortn
= errSecSuccess
;
696 if(!prefsDict
->writePlistToPrefs(DOT_MAC_REQ_PREFS
, Dictionary::US_User
)) {
697 certReqDbg("storeResults: error writing prefs to disk");
705 * Attempt to fetch mCertData or mRefId from preferences.
707 void CertificateRequest::retrieveResults()
709 StLock
<Mutex
>_(mMutex
);
710 assert(mPolicy
.data() != NULL
);
711 assert(mUserName
.data() != NULL
);
713 /* get the .mac cert prefs as a dictionary */
714 Dictionary
*pd
= Dictionary::CreateDictionary(DOT_MAC_REQ_PREFS
, Dictionary::US_User
);
717 certReqDbg("retrieveResults: no prefs found");
721 auto_ptr
<Dictionary
> prefsDict(pd
);
723 /* get dictionary for current policy */
724 CFStringRef policyKey
= createPolicyKey();
725 Dictionary
*policyDict
= prefsDict
->copyDictValue(policyKey
);
726 CFRelease(policyKey
);
727 if(policyDict
!= NULL
) {
728 /* dictionary for user */
729 CFStringRef userKey
= createUserKey();
730 Dictionary
*userDict
= policyDict
->copyDictValue(userKey
);
731 if(userDict
!= NULL
) {
732 /* is there a cert in there? */
733 CFDataRef val
= userDict
->getDataValue(CFSTR(DOT_MAC_CERT_KEY
));
735 mCertData
.copy(CFDataGetBytePtr(val
), CFDataGetLength(val
));
738 /* how about refId? */
739 val
= userDict
->getDataValue(CFSTR(DOT_MAC_REF_ID_KEY
));
741 mRefId
.copy(CFDataGetBytePtr(val
), CFDataGetLength(val
));
751 * Remove all trace of current policy/user. Called when we successfully transferred
752 * the cert back to caller.
754 void CertificateRequest::removeResults()
756 StLock
<Mutex
>_(mMutex
);
757 assert(mPolicy
.data() != NULL
);
758 assert(mUserName
.data() != NULL
);
759 storeResults(NULL
, NULL
);
763 * Have the TP ping the server to see of there's a request pending for the current
764 * user. Always throws: either
765 * CSSMERR_APPLE_DOTMAC_REQ_IS_PENDING -- request pending
766 * CSSMERR_APPLE_DOTMAC_NO_REQ_PENDING -- no request pending
767 * errSecParam -- no user, no password
768 * other gross errors, e.g. errSecIO for server connection failure
770 * The distinguishing features about this TP request are:
772 * policy OID = CSSMOID_DOTMAC_CERT_REQ_{IDENTITY,EMAIL_SIGN,EMAIL_ENCRYPT,SHARED_SERVICES}
773 * CSSM_TP_AUTHORITY_REQUEST_TYPE = CSSM_TP_AUTHORITY_REQUEST_CERTLOOKUP
774 * CSSM_APPLE_DOTMAC_TP_CERT_REQUEST.flags = CSSM_DOTMAC_TP_IS_REQ_PENDING
775 * must have userName and password
776 * hostname optional as usual
778 void CertificateRequest::postPendingRequest()
780 StLock
<Mutex
>_(mMutex
);
782 CSSM_TP_AUTHORITY_ID tpAuthority
;
783 CSSM_TP_AUTHORITY_ID
*tpAuthPtr
= NULL
;
784 CSSM_NET_ADDRESS tpNetAddrs
;
785 CSSM_APPLE_DOTMAC_TP_CERT_REQUEST certReq
;
786 CSSM_TP_REQUEST_SET reqSet
;
787 CSSM_TP_CALLERAUTH_CONTEXT callerAuth
;
788 CSSM_FIELD policyField
;
789 CSSM_DATA refId
= {0, NULL
};
791 assert(mCertState
== CRS_Reconstructed
);
792 if((mUserName
.data() == NULL
) || (mPassword
.data() == NULL
)) {
793 certReqDbg("postPendingRequest: user name and password required");
794 MacOSError::throwMe(errSecParam
);
797 /* Fill in the CSSM_APPLE_DOTMAC_TP_CERT_REQUEST */
798 memset(&certReq
, 0, sizeof(certReq
));
799 certReq
.version
= CSSM_DOT_MAC_TP_REQ_VERSION
;
800 certReq
.userName
= mUserName
.get();
801 certReq
.password
= mPassword
.get();
802 certReq
.flags
= CSSM_DOTMAC_TP_IS_REQ_PENDING
;
804 /* now the rest of the args for CSSM_TP_SubmitCredRequest() */
805 reqSet
.Requests
= &certReq
;
806 reqSet
.NumberOfRequests
= 1;
808 * This OID actually doesn't matter - right? This RPC doesn't know about
809 * which request we seek...
811 policyField
.FieldOid
= mPolicy
;
812 policyField
.FieldValue
.Data
= NULL
;
813 policyField
.FieldValue
.Length
= 0;
814 memset(&callerAuth
, 0, sizeof(callerAuth
));
815 callerAuth
.Policy
.NumberOfPolicyIds
= 1;
816 callerAuth
.Policy
.PolicyIds
= &policyField
;
817 /* no other creds here */
819 if(mHostName
.data() != NULL
) {
820 tpAuthority
.AuthorityCert
= NULL
;
821 tpAuthority
.AuthorityLocation
= &tpNetAddrs
;
822 tpNetAddrs
.AddressType
= CSSM_ADDR_NAME
;
823 tpNetAddrs
.Address
= mHostName
.get();
824 tpAuthPtr
= &tpAuthority
;
828 crtn
= CSSM_TP_SubmitCredRequest(mTP
->handle(),
830 CSSM_TP_AUTHORITY_REQUEST_CERTLOOKUP
,
834 &refId
); // CSSM_DATA_PTR ReferenceIdentifier
837 /* shouldn't be any but just in case.... */
841 case CSSMERR_APPLE_DOTMAC_REQ_IS_PENDING
:
842 certReqDbg("postPendingRequest: REQ_IS_PENDING");
844 case CSSMERR_APPLE_DOTMAC_NO_REQ_PENDING
:
845 certReqDbg("postPendingRequest: NO_REQ_PENDING");
848 /* should never happen */
849 certReqDbg("postPendingRequest: unexpected success!");
850 crtn
= errSecInternalComponent
;
853 certReqDbg("postPendingRequest: unexpected rtn %lu", (unsigned long)crtn
);
856 CssmError::throwMe(crtn
);