]> git.saurik.com Git - apple/security.git/blob - AppleX509CL/SnaccUtils.cpp
Security-29.tar.gz
[apple/security.git] / AppleX509CL / SnaccUtils.cpp
1 /*
2 * Copyright (c) 2000-2001 Apple Computer, Inc. All Rights Reserved.
3 *
4 * The contents of this file constitute Original Code as defined in and are
5 * subject to the Apple Public Source License Version 1.2 (the 'License').
6 * You may not use this file except in compliance with the License. Please obtain
7 * a copy of the License at http://www.apple.com/publicsource and read it before
8 * using this file.
9 *
10 * This Original Code and all software distributed under the License are
11 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS
12 * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT
13 * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
14 * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the
15 * specific language governing rights and limitations under the License.
16 */
17
18
19 /*
20 * CertSNACC.cpp - snacc-related cert functions
21 *
22 * Created 9/1/2000 by Doug Mitchell.
23 * Copyright (c) 2000 by Apple Computer.
24 */
25
26 #include "SnaccUtils.h"
27 #include "cldebugging.h"
28 #include <Security/pkcs1oids.h>
29 #include <Security/cdsaUtils.h>
30 #include <Security/cssmapple.h>
31 #include <Security/appleoids.h>
32 #include <Security/globalizer.h>
33
34 #define DEBUG_DECODE 0
35 #if DEBUG_DECODE
36 #define ddprintf(x) printf x
37 #else
38 #define ddprintf(x)
39 #endif
40
41 /*
42 * AsnOid "constants" which we construct and cache on demand to avoid the
43 * somewhat expensive op of constructing them every time we test for equality
44 * in CL_snaccOidToCssmAlg.
45 */
46 class AlgOidCache
47 {
48 public:
49 AlgOidCache() :
50 mRsaEncryption(rsaEncryption_arc),
51 mMd2WithRSAEncryption(md2WithRSAEncryption_arc),
52 mMd5WithRSAEncryption(md5WithRSAEncryption_arc),
53 mSha1withRSAEncryption(sha1withRSAEncryption_arc),
54 mId_dsa(id_dsa_arc),
55 mId_dsa_with_sha1(id_dsa_with_sha1_arc),
56 mAppleFee(appleFee_arc),
57 mAppleAsc(appleAsc_arc),
58 mAppleFeeMD5(appleFeeMD5_arc),
59 mAppleFeeSHA1(appleFeeSHA1_arc),
60 mAppleFeed(appleFeed_arc),
61 mAppleFeedExp(appleFeedExp_arc),
62 mAppleECDSA(appleECDSA_arc)
63 { }
64
65 AsnOid mRsaEncryption;
66 AsnOid mMd2WithRSAEncryption;
67 AsnOid mMd5WithRSAEncryption;
68 AsnOid mSha1withRSAEncryption;
69 AsnOid mId_dsa;
70 AsnOid mId_dsa_with_sha1;
71 AsnOid mAppleFee;
72 AsnOid mAppleAsc;
73 AsnOid mAppleFeeMD5;
74 AsnOid mAppleFeeSHA1;
75 AsnOid mAppleFeed;
76 AsnOid mAppleFeedExp;
77 AsnOid mAppleECDSA;
78 };
79
80 static ModuleNexus<AlgOidCache> algOidCache;
81
82 /*
83 * To ensure a secure means of signing and verifying TBSCert blobs, we
84 * provide these functions to encode and decode just the top-level
85 * elements of a certificate. Snacc doesn't allow you to specify, for
86 * example, a fully encoded TBSCert prior to encoding the whole cert after
87 * signing it - you have to decode the TBSCert, put it and the other
88 * components into a Cert, and then encode the whole thing. Unfortunately
89 * there is no guarantee that when you decode and re-encode a TBSCert blob,
90 * you get the same thing you started with (although with DER rules, as
91 * opposed to BER rules, you should). Thus when signing, we sign the TBSCert
92 * and encode the signed cert here without ever decoding the TBSCert (or,
93 * at least, without using the decoded version to get the encoded TBS blob).
94 */
95
96 void
97 CL_certDecodeComponents(
98 const CssmData &signedCert, // DER-encoded
99 CssmOwnedData &TBSCert, // still DER-encoded
100 CssmOwnedData &algId, // ditto
101 CssmOwnedData &rawSig) // raw bits (not an encoded AsnBits)
102 {
103 CssmAutoData encodedSig(rawSig.allocator);
104
105 /* drop signedCert into an AsnBuf for processing */
106 AsnBuf buf;
107 buf.InstallData(reinterpret_cast<char *>(signedCert.data()), signedCert.length());
108
109 /* based on snacc-generated Certificate::BDec() and BDecContent() */
110 AsnTag tag;
111 AsnLen bytesDecoded = 0;
112 AsnLen decLen; // from BDecLen
113 AsnLen totalLen; // including tag and ASN length
114 char *elemStart; // ptr to start of element, including tag
115
116 int rtn;
117 ENV_TYPE env;
118 if ((rtn = setjmp (env)) == 0) {
119 tag = BDecTag (buf, bytesDecoded, env);
120 if (tag != MAKE_TAG_ID (UNIV, CONS, SEQ_TAG_CODE)) {
121 errorLog1("CL_CertDecodeComponents: bad first-level tag (0x%x)\n", tag);
122 CssmError::throwMe(CSSMERR_CL_UNKNOWN_FORMAT);
123 }
124 decLen = BDecLen (buf, bytesDecoded, env); // of total
125 /* FIXME - we should be able to ensure right here that we have enough */
126
127 /* First element, TBSCert */
128 /* Note we need to include the tag and content in the outgoing blobs */
129 elemStart = buf.DataPtr() + bytesDecoded;
130 tag = BDecTag (buf, bytesDecoded, env);
131 if(tag != MAKE_TAG_ID (UNIV, CONS, SEQ_TAG_CODE)) {
132 errorLog1("CL_CertDecodeComponents: bad TBSCert tag (0x%x)\n", tag);
133 CssmError::throwMe(CSSMERR_CL_UNKNOWN_FORMAT);
134 }
135
136 decLen = BDecLen (buf, bytesDecoded, env); // DER 'length'
137 /* buf now at first content byte; simulate grabbing content */
138 totalLen = decLen + (bytesDecoded - (elemStart - buf.DataPtr()));
139 buf.Skip(decLen);
140 bytesDecoded += decLen;
141 TBSCert.copy(elemStart, totalLen);
142 ddprintf(("CL_certDecodeComponents: TBS len %d; data %02x %02x %02x %02x...\n",
143 totalLen, ((uint8 *)elemStart)[0], ((uint8 *)elemStart)[1],
144 ((uint8 *)elemStart)[2], ((uint8 *)elemStart)[3]));
145
146 /* next element, algId */
147 elemStart = buf.DataPtr() + bytesDecoded;
148 tag = BDecTag (buf, bytesDecoded, env);
149 if(tag != MAKE_TAG_ID (UNIV, CONS, SEQ_TAG_CODE)) {
150 errorLog1("CL_CertDecodeComponents: bad AlgId tag (0x%x)\n", tag);
151 CssmError::throwMe(CSSMERR_CL_UNKNOWN_FORMAT);
152 }
153 decLen = BDecLen (buf, bytesDecoded, env);
154 totalLen = decLen + (bytesDecoded - (elemStart - buf.DataPtr()));
155 buf.Skip(decLen);
156 bytesDecoded += decLen;
157 algId.copy(elemStart, totalLen);
158 ddprintf(("CL_certDecodeComponents: algId len %d; data %02x %02x %02x...\n",
159 totalLen, ((uint8 *)elemStart)[0], ((uint8 *)elemStart)[1],
160 ((uint8 *)elemStart)[2]));
161
162 /* next element, signature */
163 elemStart = buf.DataPtr() + bytesDecoded;
164 tag = BDecTag (buf, bytesDecoded, env);
165 if((tag != MAKE_TAG_ID (UNIV, CONS, BITSTRING_TAG_CODE)) &&
166 (tag != MAKE_TAG_ID (UNIV, PRIM, BITSTRING_TAG_CODE))) {
167 errorLog1("CL_CertDecodeComponents: bad sig tag 0x%x\n", tag);
168 CssmError::throwMe(CSSMERR_CL_UNKNOWN_FORMAT);
169 }
170 decLen = BDecLen (buf, bytesDecoded, env);
171 totalLen = decLen + (bytesDecoded - (elemStart - buf.DataPtr()));
172 encodedSig.copy(elemStart, totalLen);
173 ddprintf(("CL_certDecodeComponents: encodedSig len %d; data %02x %02x "
174 "%02x %02x...\n",
175 totalLen, ((uint8 *)elemStart)[0], ((uint8 *)elemStart)[1],
176 ((uint8 *)elemStart)[2], ((uint8 *)elemStart)[3]));
177
178 /*
179 * encodedSig is a DER-encoded AsnBits. Decode for caller.
180 */
181 SC_decodeAsnBitsToCssmData(encodedSig.get(), rawSig);
182 ddprintf(("CL_certDecodeComponents: rawSig len %d\n", rawSig.length()));
183 /*
184 * OK, if we get here, we can skip the remaining stuff from
185 * Certificate::BDecContent(), which involves getting to the end
186 * of indefinte-length data.
187 */
188 }
189 else {
190 errorLog0("CL_CertDecodeComponents: longjmp during decode\n");
191 TBSCert.reset();
192 algId.reset();
193 rawSig.reset();
194 CssmError::throwMe(CSSMERR_CL_UNKNOWN_FORMAT);
195 }
196 }
197
198 /*
199 * Given pre-DER-encoded blobs, do the final encode step for a signed cert.
200 */
201 void
202 CL_certEncodeComponents(
203 const CssmData &TBSCert, // DER-encoded
204 const CssmData &algId, // ditto
205 const CssmData &rawSig, // raw bits, not encoded
206 CssmOwnedData &signedCert) // DER-encoded
207 {
208 /* first BER-encode the signature */
209 AsnBits snaccSig(reinterpret_cast<char *>(rawSig.data()),
210 rawSig.length() * 8);
211 CssmAutoData encodedSig(signedCert.allocator);
212 SC_encodeAsnObj(snaccSig, encodedSig, rawSig.length() + 10);
213
214 /*
215 * OK, we have all three cert components already DER-encoded. The encoded
216 * cert is just (tag | contentLength | TBSCert | algId | encodedSig).
217 * To avoid an unneccessary copy at the end of the encode, figure out
218 * the length of tag and contentLength. The tag is known to be one byte.
219 */
220 size_t contentLen = TBSCert.length() + algId.length() + encodedSig.length();
221 size_t lenLen = SC_lengthOfLength(contentLen);
222 size_t totalLen = 1 /* tag */ + lenLen /* length bytes */ + contentLen;
223 signedCert.malloc(totalLen);
224
225 /* tag */
226 char *cp = (char *)signedCert.data();
227 *cp++ = UNIV | CONS | SEQ_TAG_CODE;
228
229 /* length */
230 SC_encodeLength(contentLen, cp, lenLen);
231 cp += lenLen;
232
233 /* concatenate the existing components */
234 memcpy(cp, TBSCert.data(), TBSCert.length());
235 cp += TBSCert.length();
236 memcpy(cp, algId.data(), algId.length());
237 cp += algId.length();
238 memcpy(cp, encodedSig.data(), encodedSig.length());
239 CASSERT((cp + encodedSig.length()) ==
240 ((char *)signedCert.data() + signedCert.length()));
241 }
242
243 /* malloc/copy a CsmmOid from a snacc-style AsnOid. */
244 void CL_snaccOidToCssm(
245 const AsnOid &inOid,
246 CssmOid &outOid,
247 CssmAllocator &alloc)
248 {
249 outOid.Data = (uint8 *)alloc.malloc(inOid.Len());
250 outOid.Length = inOid.Len();
251 const char *cp = inOid;
252 memcpy(outOid.Data, cp, outOid.Length);
253 }
254
255 /* convert algorithm identifier from CSSM format to snacc format */
256 void CL_cssmAlgIdToSnacc (
257 const CSSM_X509_ALGORITHM_IDENTIFIER &cssmAlgId,
258 AlgorithmIdentifier &snaccAlgId)
259 {
260 snaccAlgId.algorithm.Set(reinterpret_cast<char *>(
261 cssmAlgId.algorithm.Data), cssmAlgId.algorithm.Length);
262 if(cssmAlgId.parameters.Data != NULL) {
263 /* optional parameters, raw bytes */
264 /* FIXME - is that right? SHould we encode as a bit string?
265 * I've never seen this "ANY" type field used... */
266 snaccAlgId.parameters = new AsnAny;
267 CSM_Buffer *cbuf = new CSM_Buffer(
268 reinterpret_cast<char *>(cssmAlgId.parameters.Data),
269 cssmAlgId.parameters.Length);
270 snaccAlgId.parameters->value = cbuf;
271 }
272 else {
273 CL_nullAlgParams(snaccAlgId);
274 }
275 }
276
277 /* convert algorithm indentifier from snacc format to CSSM format */
278 void CL_snaccAlgIdToCssm (
279 const AlgorithmIdentifier &snaccAlgId,
280 CSSM_X509_ALGORITHM_IDENTIFIER &cssmAlgId,
281 CssmAllocator &alloc)
282 {
283 memset(&cssmAlgId, 0, sizeof(CSSM_X509_ALGORITHM_IDENTIFIER));
284
285 /* algorithm - required */
286 CssmOid &outOid = CssmOid::overlay(cssmAlgId.algorithm);
287 CL_snaccOidToCssm(snaccAlgId.algorithm, outOid, alloc);
288
289 /* parameters as AsnAny - optional - for now just pass back the raw bytes */
290 if(snaccAlgId.parameters != NULL) {
291 CSM_Buffer *cbuf = snaccAlgId.parameters->value;
292 cssmAlgId.parameters.Data = (uint8 *)alloc.malloc(cbuf->Length());
293 cssmAlgId.parameters.Length = cbuf->Length();
294 memmove(cssmAlgId.parameters.Data, cbuf->Access(),
295 cssmAlgId.parameters.Length);
296 }
297 }
298
299 /* convert between uint32-style CSSM algorithm and snacc-style AsnOid */
300 CSSM_ALGORITHMS CL_snaccOidToCssmAlg(
301 const AsnOid &oid)
302 {
303 AlgOidCache &oc = algOidCache();
304
305 CSSM_ALGORITHMS cssmAlg = 0;
306 if(oid == oc.mRsaEncryption) {
307 cssmAlg = CSSM_ALGID_RSA;
308 }
309 else if(oid == oc.mMd2WithRSAEncryption) {
310 cssmAlg = CSSM_ALGID_MD2WithRSA;
311 }
312 else if(oid == oc.mMd5WithRSAEncryption) {
313 cssmAlg = CSSM_ALGID_MD5WithRSA;
314 }
315 else if(oid == oc.mSha1withRSAEncryption) {
316 cssmAlg = CSSM_ALGID_SHA1WithRSA;
317 }
318 else if(oid == oc.mId_dsa) {
319 cssmAlg = CSSM_ALGID_DSA;
320 }
321 else if(oid == oc.mId_dsa_with_sha1) {
322 cssmAlg = CSSM_ALGID_SHA1WithDSA;
323 }
324 else if(oid == oc.mAppleFee) {
325 cssmAlg = CSSM_ALGID_FEE;
326 }
327 else if(oid == oc.mAppleAsc) {
328 cssmAlg = CSSM_ALGID_ASC;
329 }
330 else if(oid == oc.mAppleFeeMD5) {
331 cssmAlg = CSSM_ALGID_FEE_MD5;
332 }
333 else if(oid == oc.mAppleFeeSHA1) {
334 cssmAlg = CSSM_ALGID_FEE_SHA1;
335 }
336 else if(oid == oc.mAppleFeed) {
337 cssmAlg = CSSM_ALGID_FEED;
338 }
339 else if(oid == oc.mAppleFeedExp) {
340 cssmAlg = CSSM_ALGID_FEEDEXP;
341 }
342 else if(oid == oc.mAppleECDSA) {
343 cssmAlg = CSSM_ALGID_SHA1WithECDSA;
344 }
345 /* etc. */
346 else {
347 errorLog0("snaccOidToCssmAlg: unknown alg\n");
348 #ifndef NDEBUG
349 printf("Bogus OID: "); oid.Print(cout);
350 printf("\n");
351 #endif
352 CssmError::throwMe(CSSMERR_CL_UNKNOWN_FORMAT);
353 }
354 return cssmAlg;
355 }
356
357 void CL_cssmAlgToSnaccOid(
358 CSSM_ALGORITHMS cssmAlg,
359 AsnOid &oid)
360 {
361 switch(cssmAlg) {
362 case CSSM_ALGID_RSA:
363 oid.ReSet(rsaEncryption_arc);
364 break;
365 case CSSM_ALGID_MD2WithRSA:
366 oid.ReSet(md2WithRSAEncryption_arc);
367 break;
368 case CSSM_ALGID_MD5WithRSA:
369 oid.ReSet(md2WithRSAEncryption_arc);
370 break;
371 case CSSM_ALGID_SHA1WithRSA:
372 oid.ReSet(sha1withRSAEncryption_arc);
373 break;
374 case CSSM_ALGID_DSA:
375 oid.ReSet(id_dsa_arc);
376 break;
377 case CSSM_ALGID_SHA1WithDSA:
378 oid.ReSet(id_dsa_with_sha1_arc);
379 break;
380 case CSSM_ALGID_FEE:
381 oid.ReSet(appleFee_arc);
382 break;
383 case CSSM_ALGID_ASC:
384 oid.ReSet(appleAsc_arc);
385 break;
386 case CSSM_ALGID_FEE_MD5:
387 oid.ReSet(appleFeeMD5_arc);
388 break;
389 case CSSM_ALGID_FEE_SHA1:
390 oid.ReSet(appleFeeSHA1_arc);
391 break;
392 case CSSM_ALGID_FEED:
393 oid.ReSet(appleFeed_arc);
394 break;
395 case CSSM_ALGID_FEEDEXP:
396 oid.ReSet(appleFeedExp_arc);
397 break;
398 case CSSM_ALGID_SHA1WithECDSA:
399 oid.ReSet(appleECDSA_arc);
400 break;
401 /* etc. */
402 default:
403 errorLog1("cssmAlgToSnaccOid: unknown alg (%d)\n", (int)cssmAlg);
404 CssmError::throwMe(CSSMERR_CL_UNKNOWN_FORMAT);
405 }
406 }
407
408 /* set up a encoded NULL for AlgorithmIdentifier.parameters */
409 void CL_nullAlgParams(
410 AlgorithmIdentifier &snaccAlgId)
411 {
412 snaccAlgId.parameters = new AsnAny;
413 char encodedNull[2] = {NULLTYPE_TAG_CODE, 0};
414 CSM_Buffer *cbuf = new CSM_Buffer(encodedNull, 2);
415 snaccAlgId.parameters->value = cbuf;
416 }
417
418 /* AsnOcts --> CSSM_DATA */
419 void CL_AsnOctsToCssmData(
420 const AsnOcts &octs,
421 CSSM_DATA &cdata,
422 CssmAllocator &alloc)
423 {
424 const char *cp = octs;
425 CssmAutoData aData(alloc, (uint8 *)cp, octs.Len());
426 cdata = aData.release();
427 }
428
429 #define MAX_NAME_SIZE (4 * 1024)
430
431 /* snacc-style GeneralNames --> CE_GeneralNames */
432 /* GeneralNames from sm_x509cmn.h */
433 void CL_snaccGeneralNamesToCdsa(
434 GeneralNames &snaccObj,
435 CE_GeneralNames &cdsaObj,
436 CssmAllocator &alloc)
437 {
438 cdsaObj.numNames = snaccObj.Count();
439 if(cdsaObj.numNames == 0) {
440 cdsaObj.generalName = NULL;
441 return;
442 }
443 cdsaObj.generalName = (CE_GeneralName *)alloc.malloc(
444 cdsaObj.numNames * sizeof(CE_GeneralName));
445 snaccObj.SetCurrToFirst();
446 CssmAutoData aData(alloc);
447 for(unsigned i=0; i<cdsaObj.numNames; i++) {
448 CE_GeneralName *currCdsaName = &cdsaObj.generalName[i];
449 GeneralName *currSnaccName = snaccObj.Curr();
450
451 /* just take the simple ones for now */
452 char *src = NULL;
453 unsigned len = 0;
454 AsnType *toBeEncoded = NULL;
455 switch(currSnaccName->choiceId) {
456 case GeneralName::otherNameCid:
457 /* OTHER_NAME, AsnOid */
458 currCdsaName->nameType = GNT_OtherName;
459 src = *currSnaccName->otherName;
460 len = currSnaccName->otherName->Len();
461 break;
462 case GeneralName::rfc822NameCid:
463 /* IA5String, AsnOcts */
464 currCdsaName->nameType = GNT_RFC822Name;
465 src = *currSnaccName->rfc822Name;
466 len = currSnaccName->rfc822Name->Len();
467 break;
468 case GeneralName::dNSNameCid:
469 /* IA5String, AsnOcts */
470 currCdsaName->nameType = GNT_DNSName;
471 src = *currSnaccName->dNSName;
472 len = currSnaccName->dNSName->Len();
473 break;
474 case GeneralName::x400AddressCid:
475 /* ORAddress from sm_x411mtsas */
476 currCdsaName->nameType = GNT_X400Address;
477 toBeEncoded = currSnaccName->x400Address;
478 break;
479 case GeneralName::directoryNameCid:
480 /* Name from sm_x501if */
481 /* We actually have to to deal with this in CertFields.cpp;
482 * it'll be easy to support this (with a mod to
483 * CE_GeneralName).
484 */
485 currCdsaName->nameType = GNT_DirectoryName;
486 toBeEncoded = currSnaccName->directoryName;
487 break;
488 case GeneralName::ediPartyNameCid:
489 /* EDIPartyName from sm_x509cmn */
490 currCdsaName->nameType = GNT_EdiPartyName;
491 toBeEncoded = currSnaccName->ediPartyName;
492 break;
493 case GeneralName::uniformResourceIdentifierCid:
494 /* IA5String, AsnOcts */
495 currCdsaName->nameType = GNT_URI;
496 src = *currSnaccName->uniformResourceIdentifier;
497 len = currSnaccName->uniformResourceIdentifier->Len();
498 break;
499 case GeneralName::iPAddressCid:
500 /* AsnOcts */
501 currCdsaName->nameType = GNT_IPAddress;
502 src = *currSnaccName->iPAddress;
503 len = currSnaccName->iPAddress->Len();
504 break;
505 case GeneralName::registeredIDCid:
506 /* AsnOid */
507 currCdsaName->nameType = GNT_RegisteredID;
508 src = *currSnaccName->registeredID;
509 len = currSnaccName->registeredID->Len();
510 break;
511 }
512 if(src == NULL) {
513 /* punt - encode the complex object and give caller the encoded
514 * bytes */
515 CASSERT(toBeEncoded != NULL);
516 SC_encodeAsnObj(*toBeEncoded, aData, MAX_NAME_SIZE);
517 src = aData;
518 len = aData.length();
519 aData.release();
520 currCdsaName->berEncoded = CSSM_TRUE;
521 }
522 else {
523 CASSERT(toBeEncoded == NULL);
524 currCdsaName->berEncoded = CSSM_FALSE;
525 }
526
527 /* src --> currCdsaName->name */
528 currCdsaName->name.Data = (uint8 *)alloc.malloc(len);
529 currCdsaName->name.Length = len;
530 memmove(currCdsaName->name.Data, src, len);
531
532 snaccObj.GoNext();
533 }
534 }
535
536 /* CE_GeneralNames --> snacc-style GeneralNames */
537 /* GeneralNames from sm_x509cmn.h */
538 GeneralNames *CL_cdsaGeneralNamesToSnacc(
539 CE_GeneralNames &cdsaObj)
540 {
541 GeneralNames *snaccObj = new GeneralNames;
542 bool abortFlag = false; // true --> invalid incoming field
543 CssmAllocator &alloc = CssmAllocator::standard();
544
545 for(unsigned i=0; i<cdsaObj.numNames; i++) {
546 CE_GeneralName *currCdsaName = &cdsaObj.generalName[i];
547 char *rawData = reinterpret_cast<char *>(currCdsaName->name.Data);
548 unsigned rawDataLen = currCdsaName->name.Length;
549 GeneralName *currSnaccName = snaccObj->Append();
550 CssmData &berCdata = CssmData::overlay(currCdsaName->name);
551 CssmRemoteData berData(alloc, berCdata);
552 switch(currCdsaName->nameType) {
553 case GNT_OtherName:
554 /* OTHER_NAME, AsnOid */
555 if(currCdsaName->berEncoded) {
556 abortFlag = true;
557 break;
558 }
559 currSnaccName->choiceId = GeneralName::otherNameCid;
560 currSnaccName->otherName = new AsnOid(rawData, rawDataLen);
561 break;
562
563 case GNT_RFC822Name:
564 /* IA5String */
565 if(currCdsaName->berEncoded) {
566 abortFlag = true;
567 break;
568 }
569 currSnaccName->choiceId = GeneralName::rfc822NameCid;
570 currSnaccName->rfc822Name = new IA5String(rawData, rawDataLen);
571 break;
572 case GNT_DNSName:
573 /* IA5String */
574 if(currCdsaName->berEncoded) {
575 abortFlag = true;
576 break;
577 }
578 currSnaccName->choiceId = GeneralName::dNSNameCid;
579 currSnaccName->rfc822Name = new IA5String(rawData, rawDataLen);
580 break;
581
582 case GNT_X400Address:
583 /* ORAddress from sm_x411mtsas */
584 if(!currCdsaName->berEncoded) {
585 abortFlag = true;
586 break;
587 }
588 currSnaccName->choiceId = GeneralName::x400AddressCid;
589 currSnaccName->x400Address = new ORAddress;
590 try {
591 SC_decodeAsnObj(berData, *currSnaccName->x400Address);
592 }
593 catch(...) {
594 abortFlag = true;
595 }
596 break;
597 case GNT_DirectoryName:
598 /* Name from sm_x501if */
599 /* We actually have to to deal with this in CertFields.cpp;
600 * it'll be easy to support this (with a mod to
601 * CE_GeneralName).
602 */
603 if(!currCdsaName->berEncoded) {
604 abortFlag = true;
605 break;
606 }
607 currSnaccName->choiceId = GeneralName::directoryNameCid;
608 currSnaccName->directoryName = new Name;
609 try {
610 SC_decodeAsnObj(berData, *currSnaccName->directoryName);
611 }
612 catch(...) {
613 abortFlag = true;
614 }
615 break;
616
617 case GNT_EdiPartyName:
618 /* EDIPartyName from sm_x509cmn */
619 if(!currCdsaName->berEncoded) {
620 abortFlag = true;
621 break;
622 }
623 currSnaccName->choiceId = GeneralName::ediPartyNameCid;
624 currSnaccName->ediPartyName = new EDIPartyName;
625 try {
626 SC_decodeAsnObj(berData, *currSnaccName->ediPartyName);
627 }
628 catch(...) {
629 abortFlag = true;
630 }
631 break;
632
633 case GNT_URI:
634 /* IA5String */
635 if(currCdsaName->berEncoded) {
636 abortFlag = true;
637 break;
638 }
639 currSnaccName->choiceId = GeneralName::uniformResourceIdentifierCid;
640 currSnaccName->uniformResourceIdentifier =
641 new IA5String(rawData, rawDataLen);
642 break;
643
644 case GNT_IPAddress:
645 /* AsnOcts */
646 if(currCdsaName->berEncoded) {
647 abortFlag = true;
648 break;
649 }
650 currSnaccName->choiceId = GeneralName::iPAddressCid;
651 currSnaccName->iPAddress = new AsnOcts(rawData, rawDataLen);
652 break;
653 case GNT_RegisteredID:
654 /* AsnOid */
655 if(currCdsaName->berEncoded) {
656 abortFlag = true;
657 break;
658 }
659 currSnaccName->choiceId = GeneralName::registeredIDCid;
660 currSnaccName->registeredID = new AsnOid(rawData, rawDataLen);
661 break;
662 }
663 berData.release();
664 if(abortFlag) {
665 break;
666 }
667 }
668 if(abortFlag) {
669 delete snaccObj;
670 CssmError::throwMe(CSSMERR_CL_INVALID_FIELD_POINTER);
671 }
672 return snaccObj;
673 }
674
675 void CL_normalizeString(
676 char *strPtr,
677 int &strLen)
678 {
679 char *pCh = strPtr; // working ptr
680 char *pD = pCh; // start of good string chars
681 char *pEos = pCh + strLen - 1;
682
683 if(strLen == 0) {
684 return;
685 }
686
687 /* adjust if Length included NULL terminator */
688 while(*pEos == 0) {
689 pEos--;
690 }
691
692 /* Remove trailing spaces */
693 while(isspace(*pEos)) {
694 pEos--;
695 }
696
697 /* Point to one past last non-space character */
698 pEos++;
699
700 /* upper case */
701 while(pCh < pEos) {
702 *pCh++ = toupper(*pCh);
703 }
704
705 /* clean out whitespace */
706 /*
707 * 1. skip all leading whitespace
708 */
709 pCh = pD;
710 while(isspace(*pCh) && (pCh < pEos)) {
711 pCh++;
712 }
713
714 /*
715 * 2. eliminate multiple whitespace.
716 * pCh points to first non-white char.
717 * pD still points to start of string
718 */
719 char ch;
720 while(pCh < pEos) {
721 ch = *pCh++;
722 *pD++ = ch; // normal case
723 if( isspace(ch) ){
724 /* skip 'til next nonwhite */
725 while(isspace(*pCh) && (pCh < pEos)) {
726 pCh++;
727 }
728 }
729 };
730
731 strLen = pD - strPtr;
732 }
733
734 /*
735 * Normalize an RDN. Per RFC2459 (4.1.2.4), printable strings are case
736 * insensitive and we're supposed to ignore leading and trailing
737 * whitespace, and collapse multiple whitespace characters into one.
738 */
739 void CL_normalizeX509Name(
740 Name &name,
741 CssmAllocator &alloc)
742 {
743 RDNSequence *rdns = name.rDNSequence;
744 int numRdns = rdns->Count();
745 if((rdns == NULL) || (numRdns == 0)) {
746 /* not technically an error */
747 return;
748 }
749
750 rdns->SetCurrElmt(0);
751 for(int rdnDex=0; rdnDex<numRdns; rdnDex++) {
752 /* from sm_x501if */
753 RelativeDistinguishedName *rdn = rdns->Curr();
754 if(rdn == NULL) {
755 /* not sure how this can happen... */
756 dprintf1("clNormalizeX509Name: NULL rdn at index %d\n", rdnDex);
757 rdns->GoNext();
758 continue;
759 }
760 int numAttrs = rdn->Count();
761 if(numAttrs == 0) {
762 dprintf1("clNormalizeX509Name: zero numAttrs at index %d\n", rdnDex);
763 rdns->GoNext();
764 continue;
765 }
766
767 /* descend into array of attribute/values */
768 rdn->SetCurrElmt(0);
769 for(int attrDex=0; attrDex<numAttrs; attrDex++) {
770 /* from sm_x501if */
771 AttributeTypeAndDistinguishedValue *att = rdn->Curr();
772 if(att == NULL) {
773 /* not sure how this can happen... */
774 dprintf1("clNormalizeX509Name: NULL att at index %d\n", attrDex);
775 rdn->GoNext();
776 continue;
777 }
778
779 /*
780 * att->value is an AsnAny (CSM_Buffer) containing an encoded
781 * string - supposedly a DirectoryString, but some certs put an
782 * IA5String here which is not handled by DirectoryString.
783 *
784 * (See e.g. the Thawte serverbasic cert, which has an email
785 * address in IA5String format.) In the IA5String case we skip the
786 * normalization.
787 *
788 * Anyway, figure out what's there, snag the raw string, normalize the
789 * string, cook up an appropriate DirectoryString for it, encode the
790 * result, and put the encoding back in att->value.
791 */
792 CSM_Buffer *cbuf = att->value.value;
793 DirectoryString dirStr;
794 char *cbufData = const_cast<char *>(cbuf->Access());
795 CssmData encodedStr(cbufData, cbuf->Length());
796
797 /* avoid exception if this is an IA5String... */
798 char tagByte = cbufData[0];
799 if((tagByte == (UNIV | PRIM | IA5STRING_TAG_CODE)) ||
800 (tagByte == (UNIV | CONS | IA5STRING_TAG_CODE))) {
801 /* can't normalize */
802 return;
803 }
804 try {
805 SC_decodeAsnObj(encodedStr, dirStr);
806 }
807 catch (...) {
808 /* can't normalize */
809 errorLog0("clNormalizeX509Name: malformed DirectoryString (1)\n");
810 return;
811 }
812
813 /* normalize, we don't need to know what kind of string it is */
814 char *strPtr = *dirStr.teletexString;
815 int newLen = dirStr.teletexString->Len();
816 CL_normalizeString(strPtr, newLen);
817
818 /* set new AsnOcts data from normalized version, freeing old */
819 dirStr.teletexString->ReSet(strPtr, newLen);
820
821 /* encode result */
822 CssmAutoData normEncoded(alloc);
823 SC_encodeAsnObj(dirStr, normEncoded, newLen + 8);
824
825 /* set new AsnAny data */
826 cbuf->Set((char *)normEncoded.data(), normEncoded.length());
827
828 rdn->GoNext();
829 } /* for each attribute/value */
830 rdns->GoNext();
831 } /* for each RDN */
832 }
833
834