2 * Copyright (c) 2002-2004 Apple Computer, 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>
32 #include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
35 #include <Security/oidsattr.h>
36 #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.
69 bool nssCompareCssmData(
70 const CSSM_DATA
*data1
,
71 const CSSM_DATA
*data2
)
73 if((data1
== NULL
) || (data1
->Data
== NULL
) ||
74 (data2
== NULL
) || (data2
->Data
== NULL
) ||
75 (data1
->Length
!= data2
->Length
)) {
78 if(data1
->Length
!= data2
->Length
) {
81 if(memcmp(data1
->Data
, data2
->Data
, data1
->Length
) == 0) {
89 /* any nonzero value means true */
90 static bool attrBoolValue(
91 const SecCertificateRequestAttribute
*attr
)
93 if((attr
->value
.Data
!= NULL
) &&
94 (attr
->value
.Length
!= 0) &&
95 (attr
->value
.Data
[0] != 0)) {
103 static void tokenizeName(
104 const CSSM_DATA
*inName
, /* required */
105 CSSM_DATA
*outName
, /* required */
106 CSSM_DATA
*outDomain
) /* optional */
108 if (!inName
|| !outName
) return;
110 CSSM_SIZE stopIdx
= inName
->Length
;
111 uint8
*p
= inName
->Data
;
114 outDomain
->Length
= idx
;
118 while (idx
< stopIdx
) {
120 outName
->Length
= idx
;
122 outDomain
->Length
= inName
->Length
- (idx
+ 1);
131 using namespace KeychainCore
;
133 CertificateRequest::CertificateRequest(const CSSM_OID
&policy
,
134 CSSM_CERT_TYPE certificateType
,
135 CSSM_TP_AUTHORITY_REQUEST_TYPE requestType
,
136 SecKeyRef privateKeyItemRef
,
137 SecKeyRef publicKeyItemRef
,
138 const SecCertificateRequestAttributeList
*attributeList
,
139 bool isNew
/* = true */)
140 : mAlloc(Allocator::standard()),
141 mTP(gGuidAppleDotMacTP
),
142 mCL(gGuidAppleX509CL
),
143 mPolicy(mAlloc
, policy
.Data
, policy
.Length
),
144 mCertType(certificateType
),
145 mReqType(requestType
),
150 mCertState(isNew
? CRS_New
: CRS_Reconstructed
),
158 mMutex(Mutex::recursive
)
160 StLock
<Mutex
>_(mMutex
);
161 certReqDbg("CertificateRequest construct");
163 /* Validate policy OID. */
164 if(!(nssCompareCssmData(&CSSMOID_DOTMAC_CERT_REQ_IDENTITY
, &policy
) ||
165 nssCompareCssmData(&CSSMOID_DOTMAC_CERT_REQ_EMAIL_SIGN
, &policy
) ||
166 nssCompareCssmData(&CSSMOID_DOTMAC_CERT_REQ_EMAIL_ENCRYPT
, &policy
) ||
167 nssCompareCssmData(&CSSMOID_DOTMAC_CERT_REQ_SHARED_SERVICES
, &policy
))) {
168 certReqDbg("CertificateRequest(): unknown policy oid");
169 MacOSError::throwMe(paramErr
);
171 if(privateKeyItemRef
) {
172 mPrivKey
= privateKeyItemRef
;
175 if(publicKeyItemRef
) {
176 mPubKey
= publicKeyItemRef
;
180 /* parse attr array */
181 if(attributeList
== NULL
) {
185 bool doPendingRequest
= false;
186 for(unsigned dex
=0; dex
<attributeList
->count
; dex
++) {
187 const SecCertificateRequestAttribute
*attr
= &attributeList
->attr
[dex
];
189 if((attr
->oid
.Data
== NULL
) || (attr
->value
.Data
== NULL
)) {
190 MacOSError::throwMe(paramErr
);
192 if(nssCompareCssmData(&CSSMOID_DOTMAC_CERT_REQ_VALUE_USERNAME
, &attr
->oid
)) {
193 CSSM_DATA userName
= { 0, NULL
};
194 CSSM_DATA domainName
= { 0, NULL
};
195 tokenizeName(&attr
->value
, &userName
, &domainName
);
196 if (!domainName
.Length
|| !domainName
.Data
) {
197 domainName
.Length
= strlen(DOT_MAC_DOMAIN
);
198 domainName
.Data
= (uint8
*) DOT_MAC_DOMAIN
;
200 mUserName
.copy(userName
);
201 mDomain
.copy(domainName
);
203 else if(nssCompareCssmData(&CSSMOID_DOTMAC_CERT_REQ_VALUE_PASSWORD
, &attr
->oid
)) {
204 mPassword
.copy(attr
->value
);
206 else if(nssCompareCssmData(&CSSMOID_DOTMAC_CERT_REQ_VALUE_HOSTNAME
, &attr
->oid
)) {
207 mHostName
.copy(attr
->value
);
209 else if(nssCompareCssmData(&CSSMOID_DOTMAC_CERT_REQ_VALUE_RENEW
, &attr
->oid
)) {
211 * any nonzero value means true
212 * FIXME: this is deprecated, Treadstone doesn't allow this. Reject this
215 mDoRenew
= attrBoolValue(attr
);
217 else if(nssCompareCssmData(&CSSMOID_DOTMAC_CERT_REQ_VALUE_ASYNC
, &attr
->oid
)) {
218 /* any nonzero value means true */
219 mIsAsync
= attrBoolValue(attr
);
221 else if(nssCompareCssmData(&CSSMOID_DOTMAC_CERT_REQ_VALUE_IS_PENDING
, &attr
->oid
)) {
222 /* any nonzero value means true */
223 doPendingRequest
= attrBoolValue(attr
);
227 certReqDbg("CertificateRequest(): unknown name/value oid");
228 MacOSError::throwMe(paramErr
);
231 if(mCertState
== CRS_Reconstructed
) {
232 /* see if we have a refId or maybe even a cert in prefs */
234 if(mCertData
.data() != NULL
) {
235 mCertState
= CRS_HaveCert
;
237 else if(mRefId
.data() != NULL
) {
238 mCertState
= CRS_HaveRefId
;
240 else if(doPendingRequest
) {
241 /* ask the server if there's a request pending */
242 postPendingRequest();
243 /* NOT REACHED - that always throws */
246 certReqDbg("CertificateRequest(): nothing in prefs");
247 /* Nothing found in prefs; nothing to go by */
248 MacOSError::throwMe(errSecItemNotFound
);
253 CertificateRequest::~CertificateRequest() throw()
255 StLock
<Mutex
>_(mMutex
);
256 certReqDbg("CertificateRequest destruct");
266 #pragma mark ----- cert request submit -----
268 void CertificateRequest::submit(
269 sint32
*estimatedTime
)
271 StLock
<Mutex
>_(mMutex
);
272 CSSM_DATA
&policy
= mPolicy
.get();
273 if(nssCompareCssmData(&CSSMOID_DOTMAC_CERT_REQ_IDENTITY
, &policy
) ||
274 nssCompareCssmData(&CSSMOID_DOTMAC_CERT_REQ_EMAIL_SIGN
, &policy
) ||
275 nssCompareCssmData(&CSSMOID_DOTMAC_CERT_REQ_EMAIL_ENCRYPT
, &policy
) ||
276 nssCompareCssmData(&CSSMOID_DOTMAC_CERT_REQ_SHARED_SERVICES
, &policy
)) {
277 return submitDotMac(estimatedTime
);
280 /* shouldn't be here, we already validated policy in constructor */
282 certReqDbg("CertificateRequest::submit(): bad policy");
283 MacOSError::throwMe(paramErr
);
287 void CertificateRequest::submitDotMac(
288 sint32
*estimatedTime
)
290 StLock
<Mutex
>_(mMutex
);
292 CSSM_TP_AUTHORITY_ID tpAuthority
;
293 CSSM_TP_AUTHORITY_ID
*tpAuthPtr
= NULL
;
294 CSSM_NET_ADDRESS tpNetAddrs
;
295 CSSM_APPLE_DOTMAC_TP_CERT_REQUEST certReq
;
296 CSSM_TP_REQUEST_SET reqSet
;
297 CSSM_CSP_HANDLE cspHand
= 0;
298 CSSM_X509_TYPE_VALUE_PAIR tvp
;
299 CSSM_TP_CALLERAUTH_CONTEXT callerAuth
;
300 CSSM_FIELD policyField
;
301 CSSM_DATA refId
= {0, NULL
};
302 const CSSM_KEY
*privKey
;
303 const CSSM_KEY
*pubKey
;
306 if(mCertState
!= CRS_New
) {
307 certReqDbg("CertificateRequest: can only submit a new request");
308 MacOSError::throwMe(paramErr
);
310 if((mUserName
.data() == NULL
) || (mPassword
.data() == NULL
)) {
311 certReqDbg("CertificateRequest: user name and password required");
312 MacOSError::throwMe(paramErr
);
315 /* get keys and CSP handle in CSSM terms */
316 if((mPrivKey
== NULL
) || (mPubKey
== NULL
)) {
317 certReqDbg("CertificateRequest: pub and priv keys required");
318 MacOSError::throwMe(paramErr
);
320 ortn
= SecKeyGetCSSMKey(mPrivKey
, &privKey
);
322 MacOSError::throwMe(ortn
);
324 ortn
= SecKeyGetCSSMKey(mPubKey
, &pubKey
);
326 MacOSError::throwMe(ortn
);
328 ortn
= SecKeyGetCSPHandle(mPrivKey
, &cspHand
);
330 MacOSError::throwMe(ortn
);
334 * CSSM_X509_TYPE_VALUE_PAIR_PTR - one pair for now.
335 * Caller passes in user name like "johnsmith"; in the CSR,
336 * we write "johnsmith@mac.com".
338 tvp
.type
= CSSMOID_CommonName
;
339 tvp
.valueType
= BER_TAG_PKIX_UTF8_STRING
;
340 CssmAutoData
fullUserName(mAlloc
);
341 unsigned nameLen
= mUserName
.length();
342 unsigned domainLen
= mDomain
.length();
343 fullUserName
.malloc(nameLen
+ 1 + domainLen
);
344 tvp
.value
= fullUserName
.get();
345 memmove(tvp
.value
.Data
, mUserName
.data(), nameLen
);
346 memmove(tvp
.value
.Data
+ nameLen
, "@", 1);
347 memmove(tvp
.value
.Data
+ nameLen
+ 1, mDomain
.data(), domainLen
);
349 /* Fill in the CSSM_APPLE_DOTMAC_TP_CERT_REQUEST */
350 memset(&certReq
, 0, sizeof(certReq
));
351 certReq
.version
= CSSM_DOT_MAC_TP_REQ_VERSION
;
352 certReq
.cspHand
= cspHand
;
353 certReq
.clHand
= mCL
->handle();
354 certReq
.numTypeValuePairs
= 1;
355 certReq
.typeValuePairs
= &tvp
;
356 certReq
.publicKey
= const_cast<CSSM_KEY_PTR
>(pubKey
);
357 certReq
.privateKey
= const_cast<CSSM_KEY_PTR
>(privKey
);
358 certReq
.userName
= mUserName
.get();
359 certReq
.password
= mPassword
.get();
361 certReq
.flags
|= CSSM_DOTMAC_TP_SIGN_RENEW
;
363 /* we don't deal with CSR here, input or output */
365 /* now the rest of the args for CSSM_TP_SubmitCredRequest() */
366 reqSet
.Requests
= &certReq
;
367 reqSet
.NumberOfRequests
= 1;
368 policyField
.FieldOid
= mPolicy
;
369 policyField
.FieldValue
.Data
= NULL
;
370 policyField
.FieldValue
.Length
= 0;
371 memset(&callerAuth
, 0, sizeof(callerAuth
));
372 callerAuth
.Policy
.NumberOfPolicyIds
= 1;
373 callerAuth
.Policy
.PolicyIds
= &policyField
;
374 ortn
= SecKeyGetCredentials(mPrivKey
,
375 CSSM_ACL_AUTHORIZATION_SIGN
,
376 kSecCredentialTypeDefault
,
377 const_cast<const CSSM_ACCESS_CREDENTIALS
**>(&callerAuth
.CallerCredentials
));
379 certReqDbg("CertificateRequest: SecKeyGetCredentials error");
380 MacOSError::throwMe(ortn
);
383 CssmAutoData
hostName(mAlloc
);
384 tpAuthority
.AuthorityCert
= NULL
;
385 tpAuthority
.AuthorityLocation
= &tpNetAddrs
;
386 tpNetAddrs
.AddressType
= CSSM_ADDR_NAME
;
387 if(mHostName
.data() != NULL
) {
388 tpNetAddrs
.Address
= mHostName
.get();
390 unsigned hostLen
= strlen(DOT_MAC_MGMT_HOST
);
391 hostName
.malloc(hostLen
+ 1 + domainLen
);
392 tpNetAddrs
.Address
= hostName
.get();
393 memmove(tpNetAddrs
.Address
.Data
, DOT_MAC_MGMT_HOST
, hostLen
);
394 memmove(tpNetAddrs
.Address
.Data
+ hostLen
, ".", 1);
395 memmove(tpNetAddrs
.Address
.Data
+ hostLen
+ 1, mDomain
.data(), domainLen
);
397 tpAuthPtr
= &tpAuthority
;
400 crtn
= CSSM_TP_SubmitCredRequest(mTP
->handle(),
402 CSSM_TP_AUTHORITY_REQUEST_CERTISSUE
,
406 &refId
); // CSSM_DATA_PTR ReferenceIdentifier
408 /* handle return, store results */
411 /* refID is a cert, we have to store it in prefs for later retrieval. */
412 certReqDbg("submitDotMac: full success, storing cert");
414 /* store in prefs if not running in async mode */
415 ortn
= storeResults(NULL
, &refId
);
420 /* but keep a local copy too */
421 mCertData
.copy(refId
);
422 mCertState
= CRS_HaveCert
;
424 /* it's ready right now */
429 case CSSMERR_APPLE_DOTMAC_REQ_QUEUED
:
430 /* refID is the blob we use in CSSM_TP_RetrieveCredResult() */
431 certReqDbg("submitDotMac: queued, storing refId");
433 /* return success - this crtn is not visible at API */
436 /* store in prefs if not running in async mode */
437 ortn
= storeResults(&refId
, NULL
);
442 mCertState
= CRS_HaveRefId
;
444 *estimatedTime
= mEstTime
;
448 case CSSMERR_APPLE_DOTMAC_REQ_REDIRECT
:
449 /* refID is a URL, caller obtains via getReturnData() */
450 certReqDbg("submitDotMac: redirect");
452 mCertState
= CRS_HaveOtherData
;
456 /* all others are fatal errors, thrown below */
460 /* mallocd on our behalf by TP */
464 CssmError::throwMe(crtn
);
468 #pragma mark ----- cert request get result -----
470 void CertificateRequest::getResult(
471 sint32
*estimatedTime
, // optional
474 StLock
<Mutex
>_(mMutex
);
475 CSSM_DATA
&policy
= mPolicy
.get();
476 if(nssCompareCssmData(&CSSMOID_DOTMAC_CERT_REQ_IDENTITY
, &policy
) ||
477 nssCompareCssmData(&CSSMOID_DOTMAC_CERT_REQ_EMAIL_SIGN
, &policy
) ||
478 nssCompareCssmData(&CSSMOID_DOTMAC_CERT_REQ_EMAIL_ENCRYPT
, &policy
) ||
479 nssCompareCssmData(&CSSMOID_DOTMAC_CERT_REQ_SHARED_SERVICES
, &policy
)) {
480 return getResultDotMac(estimatedTime
, certData
);
483 /* shouldn't be here, we already validated policy in constructor */
485 certReqDbg("CertificateRequest::getResult(): bad policy");
486 MacOSError::throwMe(paramErr
);
490 void CertificateRequest::getResultDotMac(
491 sint32
*estimatedTime
, // optional
494 StLock
<Mutex
>_(mMutex
);
497 /* trivial case, we already have what caller is looking for */
498 certReqDbg("getResultDotMac: have the cert right now");
499 assert(mCertData
.data() != NULL
);
500 certData
= mCertData
.get();
507 /* ping the server */
508 certReqDbg("getResultDotMac: CRS_HaveRefId; polling server");
509 assert(mRefId
.data() != NULL
);
510 CSSM_BOOL ConfirmationRequired
;
511 CSSM_TP_RESULT_SET_PTR resultSet
= NULL
;
514 crtn
= CSSM_TP_RetrieveCredResult(mTP
->handle(),
516 NULL
, // CallerAuthCredentials
518 &ConfirmationRequired
,
523 case CSSMERR_TP_CERT_NOT_VALID_YET
:
525 * By convention, this means "not ready yet".
526 * The dot mac server does not have a way of telling us the
527 * estimated time on a straight lookup like this (we only get
528 * an estimated completion time on the initial request), so we
531 certReqDbg("getResultDotMac: polled server, not ready yet");
533 *estimatedTime
= (mEstTime
) ? mEstTime
: 1;
535 MacOSError::throwMe(CSSMERR_APPLE_DOTMAC_REQ_IS_PENDING
);
537 certReqDbg("CSSM_TP_RetrieveCredResult error");
538 CssmError::throwMe(crtn
);
540 if(resultSet
== NULL
) {
541 certReqDbg("***CSSM_TP_RetrieveCredResult OK, but no result set");
542 MacOSError::throwMe(internalComponentErr
);
544 if(resultSet
->NumberOfResults
!= 1) {
545 certReqDbg("***CSSM_TP_RetrieveCredResult OK, NumberOfResults (%lu)",
546 (unsigned long)resultSet
->NumberOfResults
);
547 MacOSError::throwMe(internalComponentErr
);
549 if(resultSet
->Results
== NULL
) {
550 certReqDbg("***CSSM_TP_RetrieveCredResult OK, but empty result set");
551 MacOSError::throwMe(internalComponentErr
);
553 certReqDbg("getResultDotMac: polled server, SUCCESS");
554 CSSM_DATA_PTR result
= (CSSM_DATA_PTR
)resultSet
->Results
;
555 if(result
->Data
== NULL
) {
556 certReqDbg("***CSSM_TP_RetrieveCredResult OK, but empty result");
557 MacOSError::throwMe(internalComponentErr
);
559 mCertData
.copy(*result
);
560 certData
= mCertData
.get();
561 mCertState
= CRS_HaveCert
;
567 * Free the stuff allocated on our behalf by TP.
568 * FIXME - are we sure CssmClient is using alloc, free, etc.?
576 /* what do we do with this? */
577 certReqDbg("CertificateRequest::getResultDotMac(): bad state");
578 MacOSError::throwMe(internalComponentErr
);
582 * One more thing: once we pass a cert back to caller, we erase
583 * the record of this transaction from prefs.
585 assert(mCertData
.data() != NULL
);
586 assert(mCertData
.data() == certData
.Data
);
591 * Obtain policy/error specific return data blob. We own the data, it's
594 void CertificateRequest::getReturnData(
597 StLock
<Mutex
>_(mMutex
);
598 rtnData
= mRefId
.get();
601 #pragma mark ----- preferences support -----
603 /* Current user as CFString, for use as key in per-policy dictionary */
604 CFStringRef
CertificateRequest::createUserKey()
606 StLock
<Mutex
>_(mMutex
);
607 return CFStringCreateWithBytes(NULL
, (UInt8
*)mUserName
.data(), mUserName
.length(),
608 kCFStringEncodingUTF8
, false);
611 #define MAX_OID_LEN 2048 // way big... */
613 /* current policy as CFString, for use as key in prefs dictionary */
614 CFStringRef
CertificateRequest::createPolicyKey()
616 StLock
<Mutex
>_(mMutex
);
617 char oidstr
[MAX_OID_LEN
];
618 unsigned char *inp
= (unsigned char *)mPolicy
.data();
620 unsigned len
= mPolicy
.length();
621 for(unsigned dex
=0; dex
<len
; dex
++) {
622 sprintf(outp
, "%02X ", *inp
++);
625 return CFStringCreateWithBytes(NULL
, (UInt8
*)oidstr
, len
* 3,
626 kCFStringEncodingUTF8
, false);
630 * Store cert data or refId in prefs. If both are NULL, delete the
631 * user dictionary entry from the policy dictionary if there, and then
632 * delete the policy dictionary if it's empty.
634 OSStatus
CertificateRequest::storeResults(
635 const CSSM_DATA
*refId
, // optional, for queued requests
636 const CSSM_DATA
*certData
) // optional, for immediate completion
638 StLock
<Mutex
>_(mMutex
);
639 assert(mPolicy
.data() != NULL
);
640 assert(mUserName
.data() != NULL
);
641 assert(mDomain
.data() != NULL
);
643 bool deleteEntry
= ((refId
== NULL
) && (certData
== NULL
));
645 /* get a mutable copy of the existing prefs, or a fresh empty one */
646 MutableDictionary
*prefsDict
= MutableDictionary::CreateMutableDictionary(DOT_MAC_REQ_PREFS
, Dictionary::US_User
);
647 if (prefsDict
== NULL
)
649 prefsDict
= new MutableDictionary();
652 /* get a mutable copy of the dictionary for this policy, or a fresh empty one */
653 CFStringRef policyKey
= createPolicyKey();
654 MutableDictionary
*policyDict
= prefsDict
->copyMutableDictValue(policyKey
);
656 CFStringRef userKey
= createUserKey();
658 /* remove user dictionary from this policy dictionary */
659 policyDict
->removeValue(userKey
);
662 /* get a mutable copy of the dictionary for this user, or a fresh empty one */
663 MutableDictionary
*userDict
= policyDict
->copyMutableDictValue(userKey
);
665 CFStringRef domainKey
= CFStringCreateWithBytes(NULL
, (UInt8
*)mDomain
.data(), mDomain
.length(), kCFStringEncodingUTF8
, false);
666 userDict
->setValue(CFSTR(DOT_MAC_DOMAIN_KEY
), domainKey
);
667 CFRelease(domainKey
);
669 /* write refId and/or cert --> user dictionary */
671 userDict
->setDataValue(CFSTR(DOT_MAC_REF_ID_KEY
), refId
->Data
, refId
->Length
);
674 userDict
->setDataValue(CFSTR(DOT_MAC_CERT_KEY
), certData
->Data
, certData
->Length
);
677 /* new user dictionary --> policy dictionary */
678 policyDict
->setValue(userKey
, userDict
->dict());
683 /* new policy dictionary to prefs dictionary, or nuke it */
684 if(policyDict
->count() == 0) {
685 prefsDict
->removeValue(policyKey
);
688 prefsDict
->setValue(policyKey
, policyDict
->dict());
690 CFRelease(policyKey
);
694 OSStatus ortn
= noErr
;
695 if(!prefsDict
->writePlistToPrefs(DOT_MAC_REQ_PREFS
, Dictionary::US_User
)) {
696 certReqDbg("storeResults: error writing prefs to disk");
704 * Attempt to fetch mCertData or mRefId from preferences.
706 void CertificateRequest::retrieveResults()
708 StLock
<Mutex
>_(mMutex
);
709 assert(mPolicy
.data() != NULL
);
710 assert(mUserName
.data() != NULL
);
712 /* get the .mac cert prefs as a dictionary */
713 Dictionary
*pd
= Dictionary::CreateDictionary(DOT_MAC_REQ_PREFS
, Dictionary::US_User
);
716 certReqDbg("retrieveResults: no prefs found");
720 auto_ptr
<Dictionary
> prefsDict(pd
);
722 /* get dictionary for current policy */
723 CFStringRef policyKey
= createPolicyKey();
724 Dictionary
*policyDict
= prefsDict
->copyDictValue(policyKey
);
725 CFRelease(policyKey
);
726 if(policyDict
!= NULL
) {
727 /* dictionary for user */
728 CFStringRef userKey
= createUserKey();
729 Dictionary
*userDict
= policyDict
->copyDictValue(userKey
);
730 if(userDict
!= NULL
) {
731 /* is there a cert in there? */
732 CFDataRef val
= userDict
->getDataValue(CFSTR(DOT_MAC_CERT_KEY
));
734 mCertData
.copy(CFDataGetBytePtr(val
), CFDataGetLength(val
));
737 /* how about refId? */
738 val
= userDict
->getDataValue(CFSTR(DOT_MAC_REF_ID_KEY
));
740 mRefId
.copy(CFDataGetBytePtr(val
), CFDataGetLength(val
));
750 * Remove all trace of current policy/user. Called when we successfully transferred
751 * the cert back to caller.
753 void CertificateRequest::removeResults()
755 StLock
<Mutex
>_(mMutex
);
756 assert(mPolicy
.data() != NULL
);
757 assert(mUserName
.data() != NULL
);
758 storeResults(NULL
, NULL
);
762 * Have the TP ping the server to see of there's a request pending for the current
763 * user. Always throws: either
764 * CSSMERR_APPLE_DOTMAC_REQ_IS_PENDING -- request pending
765 * CSSMERR_APPLE_DOTMAC_NO_REQ_PENDING -- no request pending
766 * paramErr -- no user, no password
767 * other gross errors, e.g. ioErr for server connection failure
769 * The distinguishing features about this TP request are:
771 * policy OID = CSSMOID_DOTMAC_CERT_REQ_{IDENTITY,EMAIL_SIGN,EMAIL_ENCRYPT,SHARED_SERVICES}
772 * CSSM_TP_AUTHORITY_REQUEST_TYPE = CSSM_TP_AUTHORITY_REQUEST_CERTLOOKUP
773 * CSSM_APPLE_DOTMAC_TP_CERT_REQUEST.flags = CSSM_DOTMAC_TP_IS_REQ_PENDING
774 * must have userName and password
775 * hostname optional as usual
777 void CertificateRequest::postPendingRequest()
779 StLock
<Mutex
>_(mMutex
);
781 CSSM_TP_AUTHORITY_ID tpAuthority
;
782 CSSM_TP_AUTHORITY_ID
*tpAuthPtr
= NULL
;
783 CSSM_NET_ADDRESS tpNetAddrs
;
784 CSSM_APPLE_DOTMAC_TP_CERT_REQUEST certReq
;
785 CSSM_TP_REQUEST_SET reqSet
;
786 CSSM_TP_CALLERAUTH_CONTEXT callerAuth
;
787 CSSM_FIELD policyField
;
788 CSSM_DATA refId
= {0, NULL
};
790 assert(mCertState
== CRS_Reconstructed
);
791 if((mUserName
.data() == NULL
) || (mPassword
.data() == NULL
)) {
792 certReqDbg("postPendingRequest: user name and password required");
793 MacOSError::throwMe(paramErr
);
796 /* Fill in the CSSM_APPLE_DOTMAC_TP_CERT_REQUEST */
797 memset(&certReq
, 0, sizeof(certReq
));
798 certReq
.version
= CSSM_DOT_MAC_TP_REQ_VERSION
;
799 certReq
.userName
= mUserName
.get();
800 certReq
.password
= mPassword
.get();
801 certReq
.flags
= CSSM_DOTMAC_TP_IS_REQ_PENDING
;
803 /* now the rest of the args for CSSM_TP_SubmitCredRequest() */
804 reqSet
.Requests
= &certReq
;
805 reqSet
.NumberOfRequests
= 1;
807 * This OID actually doesn't matter - right? This RPC doesn't know about
808 * which request we seek...
810 policyField
.FieldOid
= mPolicy
;
811 policyField
.FieldValue
.Data
= NULL
;
812 policyField
.FieldValue
.Length
= 0;
813 memset(&callerAuth
, 0, sizeof(callerAuth
));
814 callerAuth
.Policy
.NumberOfPolicyIds
= 1;
815 callerAuth
.Policy
.PolicyIds
= &policyField
;
816 /* no other creds here */
818 if(mHostName
.data() != NULL
) {
819 tpAuthority
.AuthorityCert
= NULL
;
820 tpAuthority
.AuthorityLocation
= &tpNetAddrs
;
821 tpNetAddrs
.AddressType
= CSSM_ADDR_NAME
;
822 tpNetAddrs
.Address
= mHostName
.get();
823 tpAuthPtr
= &tpAuthority
;
827 crtn
= CSSM_TP_SubmitCredRequest(mTP
->handle(),
829 CSSM_TP_AUTHORITY_REQUEST_CERTLOOKUP
,
833 &refId
); // CSSM_DATA_PTR ReferenceIdentifier
836 /* shouldn't be any but just in case.... */
840 case CSSMERR_APPLE_DOTMAC_REQ_IS_PENDING
:
841 certReqDbg("postPendingRequest: REQ_IS_PENDING");
843 case CSSMERR_APPLE_DOTMAC_NO_REQ_PENDING
:
844 certReqDbg("postPendingRequest: NO_REQ_PENDING");
847 /* should never happen */
848 certReqDbg("postPendingRequest: unexpected success!");
849 crtn
= internalComponentErr
;
852 certReqDbg("postPendingRequest: unexpected rtn %lu", (unsigned long)crtn
);
855 CssmError::throwMe(crtn
);