]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_keychain/lib/CertificateRequest.cpp
Security-57337.50.23.tar.gz
[apple/security.git] / OSX / libsecurity_keychain / lib / CertificateRequest.cpp
1 /*
2 * Copyright (c) 2002-2004,2011-2014 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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
11 * file.
12 *
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.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 //
25 // CertificateRequest.cpp
26 //
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 <string.h>
33 #include <dotMacTp.h>
34 #include <Security/oidsattr.h>
35 #include <security_utilities/simpleprefs.h>
36 #include <SecBase.h>
37
38 /* one top-level prefs file for all of .mac cert requests */
39 #define DOT_MAC_REQ_PREFS "com.apple.security.certreq"
40
41 /*
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
44 * as an ASCII string.
45 *
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.
50 *
51 *
52 * Within the per-policy, per user dictionary are these two values, both stored
53 * as raw data (CFData) blobs.
54 */
55 #define DOT_MAC_REF_ID_KEY "refId"
56 #define DOT_MAC_CERT_KEY "certificate"
57
58 /* Domain for .mac cert requests */
59 #define DOT_MAC_DOMAIN_KEY "domain"
60 #define DOT_MAC_DOMAIN "mac.com"
61
62 /* Hosts for .mac cert requests */
63 #define DOT_MAC_MGMT_HOST "certmgmt"
64 #define DOT_MAC_INFO_HOST "certinfo"
65
66 /*
67 * Compare two CSSM_DATAs (or two CSSM_OIDs), return true if identical.
68 */
69 static
70 bool nssCompareCssmData(
71 const CSSM_DATA *data1,
72 const CSSM_DATA *data2)
73 {
74 if((data1 == NULL) || (data1->Data == NULL) ||
75 (data2 == NULL) || (data2->Data == NULL) ||
76 (data1->Length != data2->Length)) {
77 return false;
78 }
79 if(data1->Length != data2->Length) {
80 return false;
81 }
82 if(memcmp(data1->Data, data2->Data, data1->Length) == 0) {
83 return true;
84 }
85 else {
86 return false;
87 }
88 }
89
90 /* any nonzero value means true */
91 static bool attrBoolValue(
92 const SecCertificateRequestAttribute *attr)
93 {
94 if((attr->value.Data != NULL) &&
95 (attr->value.Length != 0) &&
96 (attr->value.Data[0] != 0)) {
97 return true;
98 }
99 else {
100 return false;
101 }
102 }
103
104 static void tokenizeName(
105 const CSSM_DATA *inName, /* required */
106 CSSM_DATA *outName, /* required */
107 CSSM_DATA *outDomain) /* optional */
108 {
109 if (!inName || !outName) return;
110 CSSM_SIZE idx = 0;
111 CSSM_SIZE stopIdx = inName->Length;
112 uint8 *p = inName->Data;
113 *outName = *inName;
114 if (outDomain) {
115 outDomain->Length = idx;
116 outDomain->Data = p;
117 }
118 if (!p) return;
119 while (idx < stopIdx) {
120 if (*p++ == '@') {
121 outName->Length = idx;
122 if (outDomain) {
123 outDomain->Length = inName->Length - (idx + 1);
124 outDomain->Data = p;
125 }
126 break;
127 }
128 idx++;
129 }
130 }
131
132 using namespace KeychainCore;
133
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),
147 mPrivKey(NULL),
148 mPubKey(NULL),
149 mEstTime(0),
150 mRefId(mAlloc),
151 mCertState(isNew ? CRS_New : CRS_Reconstructed),
152 mCertData(mAlloc),
153 mUserName(mAlloc),
154 mPassword(mAlloc),
155 mHostName(mAlloc),
156 mDomain(mAlloc),
157 mDoRenew(false),
158 mIsAsync(false),
159 mMutex(Mutex::recursive)
160 {
161 StLock<Mutex>_(mMutex);
162 certReqDbg("CertificateRequest construct");
163
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);
171 }
172 if(privateKeyItemRef) {
173 mPrivKey = privateKeyItemRef;
174 CFRetain(mPrivKey);
175 }
176 if(publicKeyItemRef) {
177 mPubKey = publicKeyItemRef;
178 CFRetain(mPubKey);
179 }
180
181 /* parse attr array */
182 if(attributeList == NULL) {
183 return;
184 }
185
186 bool doPendingRequest = false;
187 for(unsigned dex=0; dex<attributeList->count; dex++) {
188 const SecCertificateRequestAttribute *attr = &attributeList->attr[dex];
189
190 if((attr->oid.Data == NULL) || (attr->value.Data == NULL)) {
191 MacOSError::throwMe(errSecParam);
192 }
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;
200 }
201 mUserName.copy(userName);
202 mDomain.copy(domainName);
203 }
204 else if(nssCompareCssmData(&CSSMOID_DOTMAC_CERT_REQ_VALUE_PASSWORD, &attr->oid)) {
205 mPassword.copy(attr->value);
206 }
207 else if(nssCompareCssmData(&CSSMOID_DOTMAC_CERT_REQ_VALUE_HOSTNAME, &attr->oid)) {
208 mHostName.copy(attr->value);
209 }
210 else if(nssCompareCssmData(&CSSMOID_DOTMAC_CERT_REQ_VALUE_RENEW, &attr->oid)) {
211 /*
212 * any nonzero value means true
213 * FIXME: this is deprecated, Treadstone doesn't allow this. Reject this
214 * request? Ignore?
215 */
216 mDoRenew = attrBoolValue(attr);
217 }
218 else if(nssCompareCssmData(&CSSMOID_DOTMAC_CERT_REQ_VALUE_ASYNC, &attr->oid)) {
219 /* any nonzero value means true */
220 mIsAsync = attrBoolValue(attr);
221 }
222 else if(nssCompareCssmData(&CSSMOID_DOTMAC_CERT_REQ_VALUE_IS_PENDING, &attr->oid)) {
223 /* any nonzero value means true */
224 doPendingRequest = attrBoolValue(attr);
225 }
226
227 else {
228 certReqDbg("CertificateRequest(): unknown name/value oid");
229 MacOSError::throwMe(errSecParam);
230 }
231 }
232 if(mCertState == CRS_Reconstructed) {
233 /* see if we have a refId or maybe even a cert in prefs */
234 retrieveResults();
235 if(mCertData.data() != NULL) {
236 mCertState = CRS_HaveCert;
237 }
238 else if(mRefId.data() != NULL) {
239 mCertState = CRS_HaveRefId;
240 }
241 else if(doPendingRequest) {
242 /* ask the server if there's a request pending */
243 postPendingRequest();
244 /* NOT REACHED - that always throws */
245 }
246 else {
247 certReqDbg("CertificateRequest(): nothing in prefs");
248 /* Nothing found in prefs; nothing to go by */
249 MacOSError::throwMe(errSecItemNotFound);
250 }
251 }
252 }
253
254 CertificateRequest::~CertificateRequest() throw()
255 {
256 StLock<Mutex>_(mMutex);
257 certReqDbg("CertificateRequest destruct");
258
259 if(mPrivKey) {
260 CFRelease(mPrivKey);
261 }
262 if(mPubKey) {
263 CFRelease(mPubKey);
264 }
265 }
266
267 #pragma mark ----- cert request submit -----
268
269 void CertificateRequest::submit(
270 sint32 *estimatedTime)
271 {
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);
279 }
280 else {
281 /* shouldn't be here, we already validated policy in constructor */
282 assert(0);
283 certReqDbg("CertificateRequest::submit(): bad policy");
284 MacOSError::throwMe(errSecParam);
285 }
286 }
287
288 void CertificateRequest::submitDotMac(
289 sint32 *estimatedTime)
290 {
291 StLock<Mutex>_(mMutex);
292 CSSM_RETURN crtn;
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;
305 OSStatus ortn;
306
307 if(mCertState != CRS_New) {
308 certReqDbg("CertificateRequest: can only submit a new request");
309 MacOSError::throwMe(errSecParam);
310 }
311 if((mUserName.data() == NULL) || (mPassword.data() == NULL)) {
312 certReqDbg("CertificateRequest: user name and password required");
313 MacOSError::throwMe(errSecParam);
314 }
315
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);
320 }
321 ortn = SecKeyGetCSSMKey(mPrivKey, &privKey);
322 if(ortn) {
323 MacOSError::throwMe(ortn);
324 }
325 ortn = SecKeyGetCSSMKey(mPubKey, &pubKey);
326 if(ortn) {
327 MacOSError::throwMe(ortn);
328 }
329 ortn = SecKeyGetCSPHandle(mPrivKey, &cspHand);
330 if(ortn) {
331 MacOSError::throwMe(ortn);
332 }
333
334 /*
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".
338 */
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);
349
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();
361 if(mDoRenew) {
362 certReq.flags |= CSSM_DOTMAC_TP_SIGN_RENEW;
363 }
364 /* we don't deal with CSR here, input or output */
365
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));
379 if(ortn) {
380 certReqDbg("CertificateRequest: SecKeyGetCredentials error");
381 MacOSError::throwMe(ortn);
382 }
383
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();
390 } else {
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);
397 }
398 tpAuthPtr = &tpAuthority;
399
400 /* go */
401 crtn = CSSM_TP_SubmitCredRequest(mTP->handle(),
402 tpAuthPtr,
403 CSSM_TP_AUTHORITY_REQUEST_CERTISSUE,
404 &reqSet,
405 &callerAuth,
406 &mEstTime,
407 &refId); // CSSM_DATA_PTR ReferenceIdentifier
408
409 /* handle return, store results */
410 switch(crtn) {
411 case CSSM_OK:
412 /* refID is a cert, we have to store it in prefs for later retrieval. */
413 certReqDbg("submitDotMac: full success, storing cert");
414 if(!mIsAsync) {
415 /* store in prefs if not running in async mode */
416 ortn = storeResults(NULL, &refId);
417 if(ortn) {
418 crtn = ortn;
419 }
420 }
421 /* but keep a local copy too */
422 mCertData.copy(refId);
423 mCertState = CRS_HaveCert;
424 if(estimatedTime) {
425 /* it's ready right now */
426 *estimatedTime = 0;
427 }
428 break;
429
430 case CSSMERR_APPLE_DOTMAC_REQ_QUEUED:
431 /* refID is the blob we use in CSSM_TP_RetrieveCredResult() */
432 certReqDbg("submitDotMac: queued, storing refId");
433 mRefId.copy(refId);
434 /* return success - this crtn is not visible at API */
435 crtn = CSSM_OK;
436 if(!mIsAsync) {
437 /* store in prefs if not running in async mode */
438 ortn = storeResults(&refId, NULL);
439 if(ortn) {
440 crtn = ortn;
441 }
442 }
443 mCertState = CRS_HaveRefId;
444 if(estimatedTime) {
445 *estimatedTime = mEstTime;
446 }
447 break;
448
449 case CSSMERR_APPLE_DOTMAC_REQ_REDIRECT:
450 /* refID is a URL, caller obtains via getReturnData() */
451 certReqDbg("submitDotMac: redirect");
452 mRefId.copy(refId);
453 mCertState = CRS_HaveOtherData;
454 break;
455
456 default:
457 /* all others are fatal errors, thrown below */
458 break;
459 }
460 if(refId.Data) {
461 /* mallocd on our behalf by TP */
462 free(refId.Data);
463 }
464 if(crtn) {
465 CssmError::throwMe(crtn);
466 }
467 }
468
469 #pragma mark ----- cert request get result -----
470
471 void CertificateRequest::getResult(
472 sint32 *estimatedTime, // optional
473 CssmData &certData)
474 {
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);
482 }
483 else {
484 /* shouldn't be here, we already validated policy in constructor */
485 assert(0);
486 certReqDbg("CertificateRequest::getResult(): bad policy");
487 MacOSError::throwMe(errSecParam);
488 }
489 }
490
491 void CertificateRequest::getResultDotMac(
492 sint32 *estimatedTime, // optional
493 CssmData &certData)
494 {
495 StLock<Mutex>_(mMutex);
496 switch(mCertState) {
497 case CRS_HaveCert:
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();
502 if(estimatedTime) {
503 *estimatedTime = 0;
504 }
505 break;
506 case CRS_HaveRefId:
507 {
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;
513 CSSM_RETURN crtn;
514
515 crtn = CSSM_TP_RetrieveCredResult(mTP->handle(),
516 &mRefId.get(),
517 NULL, // CallerAuthCredentials
518 &mEstTime,
519 &ConfirmationRequired,
520 &resultSet);
521 switch(crtn) {
522 case CSSM_OK:
523 break;
524 case CSSMERR_TP_CERT_NOT_VALID_YET:
525 /*
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
530 * fake it.
531 */
532 certReqDbg("getResultDotMac: polled server, not ready yet");
533 if(estimatedTime) {
534 *estimatedTime = (mEstTime) ? mEstTime : 1;
535 }
536 MacOSError::throwMe(CSSMERR_APPLE_DOTMAC_REQ_IS_PENDING);
537 default:
538 certReqDbg("CSSM_TP_RetrieveCredResult error");
539 CssmError::throwMe(crtn);
540 }
541 if(resultSet == NULL) {
542 certReqDbg("***CSSM_TP_RetrieveCredResult OK, but no result set");
543 MacOSError::throwMe(errSecInternalComponent);
544 }
545 if(resultSet->NumberOfResults != 1) {
546 certReqDbg("***CSSM_TP_RetrieveCredResult OK, NumberOfResults (%lu)",
547 (unsigned long)resultSet->NumberOfResults);
548 MacOSError::throwMe(errSecInternalComponent);
549 }
550 if(resultSet->Results == NULL) {
551 certReqDbg("***CSSM_TP_RetrieveCredResult OK, but empty result set");
552 MacOSError::throwMe(errSecInternalComponent);
553 }
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);
559 }
560 mCertData.copy(*result);
561 certData = mCertData.get();
562 mCertState = CRS_HaveCert;
563 if(estimatedTime) {
564 *estimatedTime = 0;
565 }
566
567 /*
568 * Free the stuff allocated on our behalf by TP.
569 * FIXME - are we sure CssmClient is using alloc, free, etc.?
570 */
571 free(result->Data);
572 free(result);
573 free(resultSet);
574 break;
575 }
576 default:
577 /* what do we do with this? */
578 certReqDbg("CertificateRequest::getResultDotMac(): bad state");
579 MacOSError::throwMe(errSecInternalComponent);
580 }
581
582 /*
583 * One more thing: once we pass a cert back to caller, we erase
584 * the record of this transaction from prefs.
585 */
586 assert(mCertData.data() != NULL);
587 assert(mCertData.data() == certData.Data);
588 removeResults();
589 }
590
591 /*
592 * Obtain policy/error specific return data blob. We own the data, it's
593 * not copied.
594 */
595 void CertificateRequest::getReturnData(
596 CssmData &rtnData)
597 {
598 StLock<Mutex>_(mMutex);
599 rtnData = mRefId.get();
600 }
601
602 #pragma mark ----- preferences support -----
603
604 /* Current user as CFString, for use as key in per-policy dictionary */
605 CFStringRef CertificateRequest::createUserKey()
606 {
607 StLock<Mutex>_(mMutex);
608 return CFStringCreateWithBytes(NULL, (UInt8 *)mUserName.data(), mUserName.length(),
609 kCFStringEncodingUTF8, false);
610 }
611
612 #define MAX_OID_LEN 2048 // way big... */
613
614 /* current policy as CFString, for use as key in prefs dictionary */
615 CFStringRef CertificateRequest::createPolicyKey()
616 {
617 StLock<Mutex>_(mMutex);
618 char oidstr[MAX_OID_LEN];
619 unsigned char *inp = (unsigned char *)mPolicy.data();
620 char *outp = oidstr;
621 CFIndex len = mPolicy.length();
622 for(CFIndex dex=0; dex<len; dex++) {
623 sprintf(outp, "%02X ", *inp++);
624 outp += 3;
625 }
626 return CFStringCreateWithBytes(NULL, (UInt8 *)oidstr, len * 3,
627 kCFStringEncodingUTF8, false);
628 }
629
630 /*
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.
634 */
635 OSStatus CertificateRequest::storeResults(
636 const CSSM_DATA *refId, // optional, for queued requests
637 const CSSM_DATA *certData) // optional, for immediate completion
638 {
639 StLock<Mutex>_(mMutex);
640 assert(mPolicy.data() != NULL);
641 assert(mUserName.data() != NULL);
642 assert(mDomain.data() != NULL);
643
644 bool deleteEntry = ((refId == NULL) && (certData == NULL));
645
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)
649 {
650 prefsDict = new MutableDictionary();
651 }
652
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);
656
657 CFStringRef userKey = createUserKey();
658 if(deleteEntry) {
659 /* remove user dictionary from this policy dictionary */
660 policyDict->removeValue(userKey);
661 }
662 else {
663 /* get a mutable copy of the dictionary for this user, or a fresh empty one */
664 MutableDictionary *userDict = policyDict->copyMutableDictValue(userKey);
665
666 CFStringRef domainKey = CFStringCreateWithBytes(NULL, (UInt8 *)mDomain.data(), mDomain.length(), kCFStringEncodingUTF8, false);
667 userDict->setValue(CFSTR(DOT_MAC_DOMAIN_KEY), domainKey);
668 CFRelease(domainKey);
669
670 /* write refId and/or cert --> user dictionary */
671 if(refId) {
672 userDict->setDataValue(CFSTR(DOT_MAC_REF_ID_KEY), refId->Data, refId->Length);
673 }
674 if(certData) {
675 userDict->setDataValue(CFSTR(DOT_MAC_CERT_KEY), certData->Data, certData->Length);
676 }
677
678 /* new user dictionary --> policy dictionary */
679 policyDict->setValue(userKey, userDict->dict());
680 delete userDict;
681 }
682 CFRelease(userKey);
683
684 /* new policy dictionary to prefs dictionary, or nuke it */
685 if(policyDict->count() == 0) {
686 prefsDict->removeValue(policyKey);
687 }
688 else {
689 prefsDict->setValue(policyKey, policyDict->dict());
690 }
691 CFRelease(policyKey);
692 delete policyDict;
693
694 /* prefs --> disk */
695 OSStatus ortn = errSecSuccess;
696 if(!prefsDict->writePlistToPrefs(DOT_MAC_REQ_PREFS, Dictionary::US_User)) {
697 certReqDbg("storeResults: error writing prefs to disk");
698 ortn = errSecIO;
699 }
700 delete prefsDict;
701 return ortn;
702 }
703
704 /*
705 * Attempt to fetch mCertData or mRefId from preferences.
706 */
707 void CertificateRequest::retrieveResults()
708 {
709 StLock<Mutex>_(mMutex);
710 assert(mPolicy.data() != NULL);
711 assert(mUserName.data() != NULL);
712
713 /* get the .mac cert prefs as a dictionary */
714 Dictionary *pd = Dictionary::CreateDictionary(DOT_MAC_REQ_PREFS, Dictionary::US_User);
715 if (pd == NULL)
716 {
717 certReqDbg("retrieveResults: no prefs found");
718 return;
719 }
720
721 auto_ptr<Dictionary> prefsDict(pd);
722
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));
734 if(val) {
735 mCertData.copy(CFDataGetBytePtr(val), CFDataGetLength(val));
736 }
737
738 /* how about refId? */
739 val = userDict->getDataValue(CFSTR(DOT_MAC_REF_ID_KEY));
740 if(val) {
741 mRefId.copy(CFDataGetBytePtr(val), CFDataGetLength(val));
742 }
743 delete userDict;
744 }
745 CFRelease(userKey);
746 delete policyDict;
747 }
748 }
749
750 /*
751 * Remove all trace of current policy/user. Called when we successfully transferred
752 * the cert back to caller.
753 */
754 void CertificateRequest::removeResults()
755 {
756 StLock<Mutex>_(mMutex);
757 assert(mPolicy.data() != NULL);
758 assert(mUserName.data() != NULL);
759 storeResults(NULL, NULL);
760 }
761
762 /*
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
769 *
770 * The distinguishing features about this TP request are:
771 *
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
777 */
778 void CertificateRequest::postPendingRequest()
779 {
780 StLock<Mutex>_(mMutex);
781 CSSM_RETURN crtn;
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};
790
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);
795 }
796
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;
803
804 /* now the rest of the args for CSSM_TP_SubmitCredRequest() */
805 reqSet.Requests = &certReq;
806 reqSet.NumberOfRequests = 1;
807 /*
808 * This OID actually doesn't matter - right? This RPC doesn't know about
809 * which request we seek...
810 */
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 */
818
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;
825 }
826
827 /* go */
828 crtn = CSSM_TP_SubmitCredRequest(mTP->handle(),
829 tpAuthPtr,
830 CSSM_TP_AUTHORITY_REQUEST_CERTLOOKUP,
831 &reqSet,
832 &callerAuth,
833 &mEstTime,
834 &refId); // CSSM_DATA_PTR ReferenceIdentifier
835
836 if(refId.Data) {
837 /* shouldn't be any but just in case.... */
838 free(refId.Data);
839 }
840 switch(crtn) {
841 case CSSMERR_APPLE_DOTMAC_REQ_IS_PENDING:
842 certReqDbg("postPendingRequest: REQ_IS_PENDING");
843 break;
844 case CSSMERR_APPLE_DOTMAC_NO_REQ_PENDING:
845 certReqDbg("postPendingRequest: NO_REQ_PENDING");
846 break;
847 case CSSM_OK:
848 /* should never happen */
849 certReqDbg("postPendingRequest: unexpected success!");
850 crtn = errSecInternalComponent;
851 break;
852 default:
853 certReqDbg("postPendingRequest: unexpected rtn %lu", (unsigned long)crtn);
854 break;
855 }
856 CssmError::throwMe(crtn);
857 }
858