1/*
2 * Copyright (c) 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/*
26 * DotMacCredRequest.cpp - public session functions for Submit/Retrieve cred result
27 */
28
29#include "AppleDotMacTPSession.h"
30#include "dotMacTpDebug.h"
31#include "dotMacTpUtils.h"
32#include "dotMacTpRpcGlue.h"
33#include "dotMacTp.h"
34#include <security_asn1/nssUtils.h>
35#include <security_asn1/SecNssCoder.h>
36#include <Security/oidsalg.h>
37#include <security_cdsa_utils/cuPem.h>
38#include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
39
40void AppleDotMacTPSession::RetrieveCredResult(
41	const CssmData &ReferenceIdentifier,
42	const CSSM_TP_CALLERAUTH_CONTEXT *CallerAuthCredentials,
43	sint32 &EstimatedTime,
44	CSSM_BOOL &ConfirmationRequired,
45	CSSM_TP_RESULT_SET_PTR &RetrieveOutput)
46{
47	/*
48	 * The only input we use is the RefId, which we created in SubmitCredRequest()
49	 * in the case of a CSSMERR_APPLE_DOTMAC_REQ_QUEUED status.
50	 */
51	if((ReferenceIdentifier.Data == NULL) || (ReferenceIdentifier.Length == 0)) {
52		dotMacErrorLog("RetrieveCredResult: NULL ReferenceIdentifier\n");
53		CssmError::throwMe(CSSMERR_TP_INVALID_IDENTIFIER);
54	}
55	if(CallerAuthCredentials != NULL) {
56		dotMacErrorLog("RetrieveCredResult: CallerAuthCredentials not used\n");
57		CssmError::throwMe(CSSMERR_TP_INVALID_CALLERAUTH_CONTEXT_POINTER);
58	}
59
60	/*
61	 * Decode the RefId to get the fields we need to fetch the cert.
62	 */
63	SecNssCoder coder;
64	CSSM_DATA userName;
65	CSSM_DATA domain;
66	DotMacCertTypeTag certType;
67	OSStatus ortn = dotMacDecodeRefId(coder, ReferenceIdentifier, userName, domain, &certType);
68	if(ortn) {
69		dotMacErrorLog("RetrieveCredResult: Invalid RefID\n");
70		CssmError::throwMe(CSSMERR_TP_INVALID_IDENTIFIER);
71	}
72
73	/* Fetch the cert. */
74	CSSM_DATA certData = {0, NULL};
75	CSSM_RETURN crtn;
76	crtn = dotMacTpCertFetch(userName, domain, certType, *this, certData);
77	if(crtn) {
78		/* FIXME error handling here, including no data */
79		CssmError::throwMe(crtn);
80	}
81
82	ConfirmationRequired = CSSM_FALSE;
83	EstimatedTime = 0;
84
85	/* Return cert data as the single Results in CSSM_TP_RESULT_SET. */
86	if(certData.Length == 0) {
87		/* The spec says this is OK, it just means that no cert is available yet */
88		dotMacErrorLog("RetrieveCredResult: no data after successful GET\n");
89		CssmError::throwMe(CSSMERR_TP_CERT_NOT_VALID_YET);
90	}
91
92	RetrieveOutput = (CSSM_TP_RESULT_SET_PTR)malloc(sizeof(CSSM_TP_RESULT_SET));
93	CSSM_DATA *resultData = (CSSM_DATA *)malloc(sizeof(CSSM_DATA));
94	*resultData = certData;
95	RetrieveOutput->NumberOfResults = 1;
96	RetrieveOutput->Results = resultData;
97}
98
99/*
100 * All archive requests (store, fetch, list, remove) go through here.
101 * All are synchronous (i.e., no RetrieveCredResult is needed).
102 */
103void AppleDotMacTPSession::SubmitArchiveRequest(
104	DotMacArchiveType archiveType,					// OID preparsed
105	const CSSM_DATA &hostName,						// required
106	CSSM_TP_AUTHORITY_REQUEST_TYPE RequestType,
107	const CSSM_TP_REQUEST_SET &RequestInput,
108	const CSSM_TP_CALLERAUTH_CONTEXT *CallerAuthContext,
109	sint32 &EstimatedTime,
110	CssmData &ReferenceIdentifier)
111{
112	CSSM_APPLE_DOTMAC_TP_ARCHIVE_REQUEST *archReq =
113		(CSSM_APPLE_DOTMAC_TP_ARCHIVE_REQUEST *)RequestInput.Requests;
114	const CSSM_DATA *pfxIn = NULL;
115	CSSM_DATA *pfxOut = NULL;
116	const CSSM_DATA *archiveName = NULL;
117	const CSSM_DATA *timeString = NULL;
118	uint32 version;
119
120	if((archReq == NULL) ||
121	   (archReq->userName.Data == NULL) ||
122	   (archReq->password.Data == NULL)) {
123		dotMacErrorLog("SubmitArchiveRequest: bad username/pwd\n");
124		CssmError::throwMe(CSSMERR_TP_INVALID_REQUEST_INPUTS);
125	}
126
127	/* Version and cert type - infer certType if v. 1 */
128	version = archReq->version;
129	const CSSM_DATA *serialNumber = NULL;
130	DotMacArchive_v2 **archives_v2 = NULL;
131	DotMacArchive **archives_v1 = NULL;
132	DotMacCertTypeTag certTypeTag;
133	if(version >= CSSM_DOT_MAC_TP_ARCHIVE_REQ_VERSION_v2) {
134		certTypeTag = archReq->certTypeTag;
135		archives_v2 = &archReq->archives_v2;
136		serialNumber = &archReq->serialNumber;
137	}
138	else {
139		/* backwards compatibility */
140		certTypeTag = CSSM_DOT_MAC_TYPE_ICHAT;
141		archives_v1 = &archReq->archives;
142	}
143
144	/* further verification per request type */
145	bool badInputs = false;
146	switch(archiveType) {
147		case DMAT_List:
148			/* nothing further needed */
149			break;
150		case DMAT_Store:
151			if((archReq->archiveName.Data == NULL) ||
152			   (archReq->timeString.Data == NULL) ||
153			   (archReq->pfx.Data == NULL)) {
154				badInputs = true;
155			}
156			pfxIn = &archReq->pfx;
157			archiveName = &archReq->archiveName;
158			timeString = &archReq->timeString;
159			break;
160		case DMAT_Fetch:
161			pfxOut = &archReq->pfx;
162			/* and drop thru */
163		case DMAT_Remove:
164			if(archReq->archiveName.Data == NULL) {
165				badInputs = true;
166			}
167			archiveName = &archReq->archiveName;
168			break;
169		default:
170			assert(0);
171			CssmError::throwMe(internalComponentErr);
172	}
173	if(badInputs) {
174		dotMacErrorLog("SubmitArchiveRequest: bad per-method inputs\n");
175		CssmError::throwMe(CSSMERR_TP_INVALID_REQUEST_INPUTS);
176	}
177
178	CSSM_RETURN crtn;
179
180	crtn = dotMacPostArchiveReq(version, certTypeTag,
181		archiveType,
182		archReq->userName, archReq->password, hostName,		// all required
183		archiveName, pfxIn, timeString, serialNumber, pfxOut,
184		&archReq->numArchives, archives_v1, archives_v2,
185		*this);
186	if(crtn) {
187		CssmError::throwMe(crtn);
188	}
189}
190
191typedef enum {
192	CRO_Sign,
193	CRO_Archive,
194	CRO_Lookup
195} CredReqOp;
196
197/*
198 * This is the primary entry point to initiate all operations performed
199 * by this module.
200 */
201void AppleDotMacTPSession::SubmitCredRequest(
202	const CSSM_TP_AUTHORITY_ID *PreferredAuthority,		// optional
203	CSSM_TP_AUTHORITY_REQUEST_TYPE RequestType,
204	const CSSM_TP_REQUEST_SET &RequestInput,
205	const CSSM_TP_CALLERAUTH_CONTEXT *CallerAuthContext,
206	sint32 &EstimatedTime,
207	CssmData &ReferenceIdentifier)
208{
209	CSSM_DATA hostName = { strlen(DOT_MAC_SIGN_HOST_NAME), (uint8 *)DOT_MAC_SIGN_HOST_NAME };
210    CSSM_DATA domainName = { strlen(DOT_MAC_DOMAIN), (uint8 *)DOT_MAC_DOMAIN };
211	CSSM_DATA fullName = { 0, NULL };
212	CSSM_DATA *altHost = NULL;
213
214	CredReqOp op = CRO_Sign;
215	switch(RequestType) {
216		case CSSM_TP_AUTHORITY_REQUEST_CERTISSUE:
217			break;
218		case CSSM_TP_AUTHORITY_REQUEST_CERTLOOKUP:
219			op = CRO_Lookup;
220            hostName.Length = strlen(DOT_MAC_LOOKUP_HOST_NAME);
221            hostName.Data =  (uint8 *)DOT_MAC_LOOKUP_HOST_NAME;
222			break;
223		default:
224			CssmError::throwMe(CSSMERR_TP_UNSUPPORTED_SERVICE);
225	}
226
227	/* default host, overridable via PreferredAuthority or username specification */
228    CssmAutoData fullHostName(Allocator::standard());
229	fullHostName.malloc(hostName.Length + 1 + domainName.Length);
230	fullName = fullHostName.get();
231	memmove(fullName.Data, hostName.Data, hostName.Length);
232	memmove(fullName.Data + hostName.Length, ".", 1);
233	memmove(fullName.Data + hostName.Length + 1, domainName.Data, domainName.Length);
234
235	/* qualify inputs */
236	if(PreferredAuthority) {
237		/* only valid option: host name */
238		if(PreferredAuthority->AuthorityCert != NULL) {
239			dotMacErrorLog("SubmitCredRequest: AuthorityCert illegal\n");
240			CssmError::throwMe(CSSMERR_TP_INVALID_AUTHORITY);
241		}
242		if(PreferredAuthority->AuthorityLocation == NULL) {
243			dotMacErrorLog("SubmitCredRequest: AuthorityLocation invalid\n");
244			CssmError::throwMe(CSSMERR_TP_INVALID_AUTHORITY);
245		}
246		if(PreferredAuthority->AuthorityLocation->AddressType != CSSM_ADDR_NAME) {
247			dotMacErrorLog("SubmitCredRequest: AddressType invalid\n");
248			CssmError::throwMe(CSSMERR_TP_INVALID_AUTHORITY);
249		}
250		fullName = PreferredAuthority->AuthorityLocation->Address;
251		if(fullName.Data == NULL) {
252			dotMacErrorLog("SubmitCredRequest: Address invalid\n");
253			CssmError::throwMe(CSSMERR_TP_INVALID_AUTHORITY);
254		}
255		/* for archive only (it has a different default) */
256		altHost = &fullName;
257		dotMacTokenizeHostName(fullName, hostName, domainName);
258	}
259
260	if(CallerAuthContext == NULL) {
261		CssmError::throwMe(CSSMERR_TP_INVALID_CALLERAUTH_CONTEXT_POINTER);
262	}
263	if((RequestInput.NumberOfRequests != 1) ||
264	   (RequestInput.Requests == NULL)) {
265		CssmError::throwMe(CSSMERR_TP_INVALID_REQUEST_INPUTS);
266	}
267	const CSSM_TP_POLICYINFO *tpPolicy = &CallerAuthContext->Policy;
268	if((tpPolicy->NumberOfPolicyIds != 1) || (tpPolicy->PolicyIds == NULL)) {
269		CssmError::throwMe(CSSMERR_TP_INVALID_POLICY_IDENTIFIERS);
270	}
271
272	DotMacCertTypeTag certType = CSSM_DOT_MAC_TYPE_UNSPECIFIED;
273	DotMacArchiveType archiveType = DMAT_List;
274	CSSM_DATA csr = {0, NULL};
275
276	/*
277	 * Map policy to op and (if op is CRO_Sign) cert type.
278	 * For CRO_Archive ops, certType is in CSSM_APPLE_DOTMAC_TP_ARCHIVE_REQUEST.
279	 */
280	const CSSM_OID *oid = &tpPolicy->PolicyIds->FieldOid;
281	if(nssCompareCssmData(oid, &CSSMOID_DOTMAC_CERT_REQ_IDENTITY)) {
282		certType = CSSM_DOT_MAC_TYPE_ICHAT;
283	}
284	else if(nssCompareCssmData(oid, &CSSMOID_DOTMAC_CERT_REQ_EMAIL_SIGN)) {
285		certType = CSSM_DOT_MAC_TYPE_EMAIL_SIGNING;
286	}
287	else if(nssCompareCssmData(oid, &CSSMOID_DOTMAC_CERT_REQ_EMAIL_ENCRYPT)) {
288		certType = CSSM_DOT_MAC_TYPE_EMAIL_ENCRYPT;
289	}
290	else if(nssCompareCssmData(oid, &CSSMOID_DOTMAC_CERT_REQ_SHARED_SERVICES)) {
291		certType = CSSM_DOT_MAC_TYPE_SHARED_SERVICES;
292	}
293	/* Archive ops: certType is in CSSM_APPLE_DOTMAC_TP_ARCHIVE_REQUEST */
294	else if(nssCompareCssmData(oid, &CSSMOID_DOTMAC_CERT_REQ_ARCHIVE_LIST)) {
295		op = CRO_Archive;
296		archiveType = DMAT_List;
297	}
298	else if(nssCompareCssmData(oid, &CSSMOID_DOTMAC_CERT_REQ_ARCHIVE_STORE)) {
299		op = CRO_Archive;
300		archiveType = DMAT_Store;
301	}
302	else if(nssCompareCssmData(oid, &CSSMOID_DOTMAC_CERT_REQ_ARCHIVE_FETCH)) {
303		op = CRO_Archive;
304		archiveType = DMAT_Fetch;
305	}
306	else if(nssCompareCssmData(oid, &CSSMOID_DOTMAC_CERT_REQ_ARCHIVE_REMOVE)) {
307		op = CRO_Archive;
308		archiveType = DMAT_Remove;
309	}
310	else {
311		CssmError::throwMe(CSSMERR_TP_INVALID_POLICY_IDENTIFIERS);
312	}
313
314	switch(op) {
315		case CRO_Archive:
316        {
317			CSSM_APPLE_DOTMAC_TP_ARCHIVE_REQUEST *archReq =
318				(CSSM_APPLE_DOTMAC_TP_ARCHIVE_REQUEST *)RequestInput.Requests;
319			if(!archReq || !archReq->userName.Data || !archReq->userName.Length) {
320				dotMacErrorLog("SubmitCredRequest(Archive): bad username\n");
321				CssmError::throwMe(CSSMERR_TP_INVALID_REQUEST_INPUTS);
322			}
323            if (!altHost) {
324                CSSM_DATA archReqDomain = domainName;
325                dotMacTokenizeUserName(archReq->userName, archReq->userName, archReqDomain);
326                if (archReqDomain.Length && archReqDomain.Data) {
327                    domainName = archReqDomain;
328                    fullHostName.reset();
329                    fullHostName.malloc(hostName.Length + 1 + domainName.Length);
330                    fullName = fullHostName.get();
331                    memmove(fullName.Data, hostName.Data, hostName.Length);
332                    memmove(fullName.Data + hostName.Length, ".", 1);
333                    memmove(fullName.Data + hostName.Length + 1, domainName.Data, domainName.Length);
334                }
335            }
336			SubmitArchiveRequest(archiveType, fullName, RequestType,
337				RequestInput, CallerAuthContext, EstimatedTime, ReferenceIdentifier);
338			return;
339        }
340		case CRO_Lookup:
341		{
342			CSSM_APPLE_DOTMAC_TP_CERT_REQUEST *certReq =
343				(CSSM_APPLE_DOTMAC_TP_CERT_REQUEST *)RequestInput.Requests;
344			if(!certReq || !certReq->userName.Data || !certReq->userName.Length) {
345				dotMacErrorLog("SubmitCredRequest(Lookup): bad username\n");
346				CssmError::throwMe(CSSMERR_TP_INVALID_REQUEST_INPUTS);
347			}
348            if (!altHost) {
349                CSSM_DATA certReqDomain = domainName;
350                dotMacTokenizeUserName(certReq->userName, certReq->userName, certReqDomain);
351                if (certReqDomain.Length && certReqDomain.Data) {
352                    domainName = certReqDomain;
353                    fullHostName.reset();
354                    fullHostName.malloc(hostName.Length + 1 + domainName.Length);
355                    fullName = fullHostName.get();
356                    memmove(fullName.Data, hostName.Data, hostName.Length);
357                    memmove(fullName.Data + hostName.Length, ".", 1);
358                    memmove(fullName.Data + hostName.Length + 1, domainName.Data, domainName.Length);
359                }
360            }
361
362			if(certReq->flags & CSSM_DOTMAC_TP_IS_REQ_PENDING) {
363				/*
364				 * We're just asking the server if there is a request pending
365				 * for this user
366				 */
367				if(certReq->password.Data == NULL) {
368					dotMacErrorLog("TP_IS_REQ_PENDING: no passphrase\n");
369					CssmError::throwMe(CSSMERR_TP_INVALID_REQUEST_INPUTS);
370				}
371				CSSM_RETURN crtn = dotMacPostReqPendingPing(certType,
372					certReq->userName,
373					certReq->password,
374					fullName);
375				/* this RPC does not have a "success" return */
376				assert(crtn != CSSM_OK);
377				if(crtn) {
378					CssmError::throwMe(crtn);
379				}
380			}
381			else {
382				/*
383				 * Note this is a path which apps could use to have us do a standard
384				 * cert fetch via HTTP, without any "pending request" state, but
385				 * currently (9/20/06) no code in the system uses this. The only
386				 * time CertificateRequest (in libsecurity_keychain) does a
387				 * CSSM_TP_AUTHORITY_REQUEST_CERTLOOKUP op is when it's doing
388				 * a CSSM_DOTMAC_TP_IS_REQ_PENDING poll op, which is handled just
389				 * above this block.
390				 */
391				CSSM_RETURN crtn = dotMacTpCertFetch(certReq->userName, domainName,
392					certType, *this, ReferenceIdentifier);
393				if(crtn) {
394					dotMacErrorLog("SubmitCredRequest(Lookup): error on fetch\n");
395					CssmError::throwMe(crtn);
396				}
397			}
398			return;
399		}
400		case CRO_Sign:
401			/* proceed to main body */
402			break;
403	}
404
405	/* op = CRO_Sign */
406
407	CSSM_APPLE_DOTMAC_TP_CERT_REQUEST *certReq =
408		(CSSM_APPLE_DOTMAC_TP_CERT_REQUEST *)RequestInput.Requests;
409	if((certReq->userName.Data == NULL) ||
410	   (certReq->password.Data == NULL)) {
411		dotMacErrorLog("SubmitCredRequest: bad username/pwd\n");
412		CssmError::throwMe(CSSMERR_TP_INVALID_REQUEST_INPUTS);
413	}
414	if(!(certReq->flags & CSSM_DOTMAC_TP_EXIST_CSR)) {
415		/* Generating a CSR requires even more... */
416		if((certReq->cspHand == 0) ||
417		   (certReq->clHand == 0) ||
418		   (certReq->numTypeValuePairs == 0) ||
419		   (certReq->typeValuePairs == NULL) ||
420		   (certReq->publicKey == NULL) ||
421		   (certReq->privateKey == NULL)) {
422			dotMacErrorLog("SubmitCredRequest: bad CSR generating params\n");
423			CssmError::throwMe(CSSMERR_TP_INVALID_REQUEST_INPUTS);
424		}
425	}
426	else if(certReq->csr.Data == NULL) {
427		/* using existing CSR */
428		dotMacErrorLog("SubmitCredRequest: bad incoming CSR\n");
429		CssmError::throwMe(CSSMERR_TP_INVALID_REQUEST_INPUTS);
430	}
431
432	/* local vars prior to goto */
433	SecNssCoder			coder;
434	CSSM_X509_NAME		x509Name;
435	const CSSM_KEY		*pubKey = certReq->publicKey;
436	const CSSM_KEY		*actPubKey = NULL;
437	CSSM_BOOL			freeRawKey = CSSM_FALSE;
438	CSSM_KEY			rawPubKey;
439	unsigned char		*pemCsr = NULL;
440	unsigned			pemCsrLen;
441	CSSM_RETURN			crtn = CSSM_OK;
442	CSSM_CC_HANDLE		sigHand = 0;
443	CSSM_DATA_PTR		csrPtr = NULL;
444
445	if(certReq->flags & CSSM_DOTMAC_TP_EXIST_CSR) {
446		/* Skip the CSR gen */
447		csr = certReq->csr;
448		goto doPost;
449	}
450
451	/***
452	 *** Create a PEM-encoded PKCS12 CSR using the CL handle provided.
453	 ***/
454
455	/* Build an X509Name for the CL */
456	dotMacTpbuildX509Name(coder, certReq->numTypeValuePairs, certReq->typeValuePairs,
457		x509Name);
458
459	/* convert possible ref key to raw; CL requires this */
460
461	switch(pubKey->KeyHeader.BlobType) {
462		case CSSM_KEYBLOB_RAW:
463			actPubKey = pubKey;
464			break;
465		case CSSM_KEYBLOB_REFERENCE:
466			dotMacRefKeyToRaw(certReq->cspHand, pubKey, &rawPubKey);
467			actPubKey = &rawPubKey;
468			freeRawKey = CSSM_TRUE;
469			break;
470		default:
471			dotMacErrorLog("SubmitCredRequest: bad key blob type (%u)",
472				(unsigned)pubKey->KeyHeader.BlobType);
473			CssmError::throwMe(CSSMERR_TP_INVALID_REQUEST_INPUTS);
474	}
475	/* subsequent errors to errOut: */
476
477	/* cook up a CL-passthrough-specific request */
478	CSSM_APPLE_CL_CSR_REQUEST clReq;
479	memset(&clReq, 0, sizeof(clReq));
480	clReq.subjectNameX509 	= &x509Name;
481	clReq.signatureAlg		= DOT_MAC_CSR_SIGNATURE_ALGID;
482	clReq.signatureOid		= DOT_MAC_CSR_SIGNATURE_ALGOID;
483	clReq.cspHand 			= certReq->cspHand;
484	clReq.subjectPublicKey  = actPubKey;
485	clReq.subjectPrivateKey = certReq->privateKey;
486
487	/* A crypto handle to pass to the CL */
488	crtn = CSSM_CSP_CreateSignatureContext(certReq->cspHand,
489			clReq.signatureAlg,
490			(CallerAuthContext ? CallerAuthContext->CallerCredentials : NULL),
491			certReq->privateKey,
492			&sigHand);
493	if(crtn) {
494		dotMacErrorLog("CSSM_CSP_CreateSignatureContext returned %ld", (long)crtn);
495		goto errOut;
496	}
497
498	/* down to the CL to do the actual work */
499	crtn = CSSM_CL_PassThrough(certReq->clHand,
500		sigHand,
501		CSSM_APPLEX509CL_OBTAIN_CSR,
502		&clReq,
503		(void **)&csrPtr);
504	if(crtn) {
505		dotMacErrorLog("CSSM_CL_PassThrough returned %ld", (long)crtn);
506		goto errOut;
507	}
508	if(csrPtr == NULL) {
509		dotMacErrorLog("SubmitCredRequest: CL returned NULL CSR\n");
510		crtn = internalComponentErr;
511		goto errOut;
512	}
513
514	/* base64 encode */
515	if(pemEncode(csrPtr->Data, csrPtr->Length, &pemCsr, &pemCsrLen,
516		"CERTIFICATE REQUEST")) {
517		dotMacErrorLog("***Error on PEM encode of CSR\n");
518		crtn = memFullErr;
519		goto errOut;
520	}
521
522	if(certReq->flags & CSSM_DOTMAC_TP_RETURN_CSR) {
523		/* caller wants a copy of the CSR */
524		certReq->csr.Data = (uint8 *)malloc(pemCsrLen);
525		memmove(certReq->csr.Data, pemCsr, pemCsrLen);
526		certReq->csr.Length = pemCsrLen;
527	}
528	csr.Data = pemCsr;
529	csr.Length = pemCsrLen;
530
531doPost:
532	if(!(certReq->flags & CSSM_DOTMAC_TP_DO_NOT_POST)) {
533
534		/* do the net request */
535		CSSM_DATA resultBody;
536        if (!altHost) {
537            CSSM_DATA certReqDomain = domainName;
538            dotMacTokenizeUserName(certReq->userName, certReq->userName, certReqDomain);
539            if (certReqDomain.Length && certReqDomain.Data) {
540                domainName = certReqDomain;
541                fullHostName.reset();
542                fullHostName.malloc(hostName.Length + 1 + domainName.Length);
543                fullName = fullHostName.get();
544                memmove(fullName.Data, hostName.Data, hostName.Length);
545                memmove(fullName.Data + hostName.Length, ".", 1);
546                memmove(fullName.Data + hostName.Length + 1, domainName.Data, domainName.Length);
547            }
548        }
549
550		crtn = dotMacPostCertReq(certType,
551			certReq->userName,
552			certReq->password,
553			fullName,
554			certReq->flags & CSSM_DOTMAC_TP_SIGN_RENEW ? true : false,
555			csr,
556			coder,
557			EstimatedTime,
558			resultBody);
559		switch(crtn) {
560			/* Some cases return data to caller */
561			case noErr:								/* resultBody = PEM-encoded cert */
562			case CSSMERR_APPLE_DOTMAC_REQ_REDIRECT: /* resultBody = URL */
563			case CSSMERR_APPLE_DOTMAC_REQ_QUEUED:   /* resultBody = opaque data we'll use later */
564				if(resultBody.Data != NULL) {
565					ReferenceIdentifier.Data = (uint8 *)malloc(resultBody.Length);
566					ReferenceIdentifier.Length = resultBody.Length;
567					memmove(ReferenceIdentifier.Data, resultBody.Data, resultBody.Length);
568
569					/* skip trailing NULL - it's just ASCII data */
570					if(resultBody.Data[resultBody.Length-1] == '\0') {
571						ReferenceIdentifier.Length--;
572					}
573				}
574				else {
575					dotMacErrorLog("***SubmitCredReq: expected RefId.Data, got none\n");
576				}
577				break;
578			default:
579				break;
580		}
581	}
582
583errOut:
584	/* free local resources */
585	if(sigHand) {
586		CSSM_DeleteContext(sigHand);
587	}
588	if(freeRawKey) {
589		CSSM_FreeKey(certReq->cspHand, NULL, &rawPubKey, CSSM_FALSE);
590	}
591	if(pemCsr) {
592		free(pemCsr);
593	}
594	if(crtn) {
595		CssmError::throwMe(crtn);
596	}
597
598}
599