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
);