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