]> git.saurik.com Git - apple/security.git/blob - libsecurity_apple_x509_tp/lib/certGroupUtils.cpp
Security-55163.44.tar.gz
[apple/security.git] / libsecurity_apple_x509_tp / lib / certGroupUtils.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 certGroupUtils.cpp
21
22 Created 10/9/2000 by Doug Mitchell.
23 */
24
25 #include <Security/cssmtype.h>
26 #include <Security/cssmapi.h>
27 #include <Security/x509defs.h>
28 #include <Security/oidscert.h>
29 #include <Security/oidsalg.h>
30 #include <Security/cssmapple.h>
31 #include <Security/SecAsn1Coder.h>
32 #include <Security/keyTemplates.h>
33
34 #include "certGroupUtils.h"
35 #include "tpdebugging.h"
36 #include "tpTime.h"
37
38 #include <string.h> /* for memcmp */
39
40
41 /*
42 * Copy one CSSM_DATA to another, mallocing destination.
43 */
44 void tpCopyCssmData(
45 Allocator &alloc,
46 const CSSM_DATA *src,
47 CSSM_DATA_PTR dst)
48 {
49 dst->Data = (uint8 *)alloc.malloc(src->Length);
50 dst->Length = src->Length;
51 memmove(dst->Data, src->Data, src->Length);
52 }
53
54 /*
55 * Malloc a CSSM_DATA, copy another one to it.
56 */
57 CSSM_DATA_PTR tpMallocCopyCssmData(
58 Allocator &alloc,
59 const CSSM_DATA *src)
60 {
61 CSSM_DATA_PTR dst = (CSSM_DATA_PTR)alloc.malloc(sizeof(CSSM_DATA));
62 tpCopyCssmData(alloc, src, dst);
63 return dst;
64 }
65
66 /*
67 * Free the data referenced by a CSSM data, and optionally, the struct itself.
68 */
69 void tpFreeCssmData(
70 Allocator &alloc,
71 CSSM_DATA_PTR data,
72 CSSM_BOOL freeStruct)
73 {
74 if(data == NULL) {
75 return;
76 }
77 if(data->Length != 0) {
78 tpFree(alloc, data->Data);
79 }
80 if(freeStruct) {
81 tpFree(alloc, data);
82 }
83 else {
84 data->Length = 0;
85 data->Data = NULL;
86 }
87 }
88
89 /*
90 * Compare two CSSM_DATAs, return CSSM_TRUE if identical.
91 */
92 CSSM_BOOL tpCompareCssmData(
93 const CSSM_DATA *data1,
94 const CSSM_DATA *data2)
95 {
96 if((data1 == NULL) || (data1->Data == NULL) ||
97 (data2 == NULL) || (data2->Data == NULL) ||
98 (data1->Length != data2->Length)) {
99 return CSSM_FALSE;
100 }
101 if(data1->Length != data2->Length) {
102 return CSSM_FALSE;
103 }
104 if(memcmp(data1->Data, data2->Data, data1->Length) == 0) {
105 return CSSM_TRUE;
106 }
107 else {
108 return CSSM_FALSE;
109 }
110 }
111
112 /*
113 * Free memory via specified plugin's app-level allocator
114 */
115 void tpFreePluginMemory(
116 CSSM_HANDLE hand,
117 void *p)
118 {
119 CSSM_API_MEMORY_FUNCS memFuncs;
120 CSSM_RETURN crtn = CSSM_GetAPIMemoryFunctions(hand, &memFuncs);
121 if(crtn) {
122 tpErrorLog("CSSM_GetAPIMemoryFunctions failure\n");
123 /* oh well, leak and continue */
124 return;
125 }
126 memFuncs.free_func(p, memFuncs.AllocRef);
127 }
128
129 /*
130 * Obtain the public key blob from a cert.
131 */
132 CSSM_DATA_PTR tp_CertGetPublicKey(
133 TPCertInfo *cert,
134 CSSM_DATA_PTR *valueToFree) // used in tp_CertFreePublicKey
135 {
136 CSSM_RETURN crtn;
137 CSSM_DATA_PTR val;
138 CSSM_X509_SUBJECT_PUBLIC_KEY_INFO *keyInfo;
139
140 *valueToFree = NULL;
141 crtn = cert->fetchField(&CSSMOID_X509V1SubjectPublicKeyCStruct, &val);
142 if(crtn) {
143 tpErrorLog("Error on CSSM_CL_CertGetFirstFieldValue(PublicKeyCStruct)\n");
144 return NULL;
145 }
146 *valueToFree = val;
147 keyInfo = (CSSM_X509_SUBJECT_PUBLIC_KEY_INFO *)val->Data;
148 return &keyInfo->subjectPublicKey;
149 }
150
151 void tp_CertFreePublicKey(
152 CSSM_CL_HANDLE clHand,
153 CSSM_DATA_PTR value)
154 {
155 CSSM_CL_FreeFieldValue(clHand, &CSSMOID_X509V1SubjectPublicKeyCStruct, value);
156 }
157
158 /*
159 * Obtain signature algorithm info from a cert.
160 */
161 CSSM_X509_ALGORITHM_IDENTIFIER_PTR tp_CertGetAlgId(
162 TPCertInfo *cert,
163 CSSM_DATA_PTR *valueToFree) // used in tp_CertFreeAlgId
164 {
165 CSSM_RETURN crtn;
166 CSSM_DATA_PTR val;
167
168 *valueToFree = NULL;
169 crtn = cert->fetchField(&CSSMOID_X509V1SignatureAlgorithm, &val);
170 if(crtn) {
171 tpErrorLog("Error on fetchField(CSSMOID_X509V1SignatureAlgorithm)\n");
172 return NULL;
173 }
174 *valueToFree = val;
175 return (CSSM_X509_ALGORITHM_IDENTIFIER_PTR)val->Data;
176 }
177
178 void tp_CertFreeAlgId(
179 CSSM_CL_HANDLE clHand,
180 CSSM_DATA_PTR value)
181 {
182 CSSM_CL_FreeFieldValue(clHand, &CSSMOID_X509V1SignatureAlgorithm, value);
183 }
184
185 /*
186 * Determine if two certs - passed in encoded form - are equivalent.
187 */
188 CSSM_BOOL tp_CompareCerts(
189 const CSSM_DATA *cert1,
190 const CSSM_DATA *cert2)
191 {
192 return tpCompareCssmData(cert1, cert2);
193 }
194
195 /*
196 * Convert a C string to lower case in place. NULL terminator not needed.
197 */
198 void tpToLower(
199 char *str,
200 unsigned strLen)
201 {
202 for(unsigned i=0; i<strLen; i++) {
203 *str = tolower(*str);
204 str++;
205 }
206 }
207
208 /*
209 * Normalize an RFC822 addr-spec. This consists of converting
210 * all characters following the '@' character to lower case.
211 * A true normalizeAll results in lower-casing all characters
212 * (e.g. for iChat).
213 */
214 void tpNormalizeAddrSpec(
215 char *addr,
216 unsigned addrLen,
217 bool normalizeAll)
218 {
219 if (addr == NULL) {
220 tpPolicyError("tpNormalizeAddrSpec: bad addr");
221 return;
222 }
223 if(!normalizeAll) {
224 while((addrLen != 0) && (*addr != '@')) {
225 addr++;
226 addrLen--;
227 }
228 if(addrLen == 0) {
229 tpPolicyError("tpNormalizeAddrSpec: bad addr-spec");
230 return;
231 }
232 }
233 tpToLower(addr, addrLen);
234 }
235
236 /***
237 *** dnsName compare support.
238 *** Please do not make any changes to this code without talking to
239 *** dmitch about updating (if necessary) and running (always)
240 *** regression tests which specifically test this logic.
241 ***/
242
243 /*
244 * Max length of a distinguished name component (label) we handle.
245 * Various RFCs spec this out at 63 bytes; we're just allocating space
246 * for these on the stack, so why not cut some slack.
247 */
248 #define MAX_DNS_COMP_LEN 128
249
250 /*
251 * Obtain the next component from a DNS Name.
252 * Caller mallocs outBuf, size >= MAX_DNS_COMP_LEN.
253 * Returns true if a component was found.
254 */
255 static bool tpNextDnsComp(
256 const char *inBuf,
257 uint32 &inBufLen, // IN/OUT
258 char *outBuf, // component RETURNED here
259 uint32 &outBufLen) // RETURNED length of component
260 {
261 outBufLen = 0;
262 if(inBufLen == 0) {
263 return false;
264 }
265
266 /* skip over leading '.' */
267 if(*inBuf == '.') {
268 inBuf++;
269 if(--inBufLen == 0) {
270 return false;
271 }
272 }
273
274 /* copy chars until out of data or next '.' found */
275 do {
276 if(*inBuf == '.') {
277 break;
278 }
279 *outBuf++ = *inBuf++;
280 inBufLen--;
281 outBufLen++;
282 if(outBufLen >= MAX_DNS_COMP_LEN) {
283 /* abort */
284 break;
285 }
286 } while(inBufLen != 0);
287 if(outBufLen) {
288 return true;
289 }
290 else {
291 return false;
292 }
293 }
294
295 /*
296 * Find location of specified substring in given bigstring. Returns
297 * pointer to start of substring in bigstring, else returns NULL.
298 */
299 static const char *tpSubStr(
300 const char *bigstr,
301 uint32 bigstrLen,
302 const char *substr,
303 uint32 substrLen)
304 {
305 /* stop searching substrLen chars before end of bigstr */
306 const char *endBigStr = bigstr + bigstrLen - substrLen;
307 for( ; bigstr <= endBigStr; ) {
308 if(*bigstr == *substr) {
309 /* first char match - remainder? */
310 if(substrLen == 1) {
311 /* don't count on memcmp(a,b,0) */
312 return bigstr;
313 }
314 if(!memcmp(bigstr+1, substr+1, substrLen - 1)) {
315 return bigstr;
316 }
317 }
318 bigstr++;
319 }
320 return NULL;
321 }
322
323 /*
324 * Compare two DNS components, with full wildcard check. We assume
325 * that no '.' chars exist (per the processing performed in
326 * tpNextDnsComp()). Returns CSSM_TRUE on match, else CSSM_FALSE.
327 */
328 static CSSM_BOOL tpCompareComps(
329 const char *hostComp, // no wildcards
330 uint32 hostCompLen,
331 const char *certComp, // wildcards OK here
332 uint32 certCompLen)
333 {
334 const char *endCertComp = certComp + certCompLen;
335 const char *endHostComp = hostComp + hostCompLen;
336 do {
337 /* wild card in cert name? */
338 const char *wildCard = tpSubStr(certComp, certCompLen,
339 "*", 1);
340 if(wildCard == NULL) {
341 /* no, require perfect literal match right now */
342 if((hostCompLen == certCompLen) &&
343 !memcmp(hostComp, certComp, certCompLen)) {
344 return CSSM_TRUE;
345 }
346 else {
347 return CSSM_FALSE;
348 }
349 }
350
351 if(wildCard != certComp) {
352 /*
353 * Require literal match of hostComp with certComp
354 * up until (but not including) the wildcard
355 */
356 uint32 subStrLen = wildCard - certComp;
357 if(subStrLen > hostCompLen) {
358 /* out of host name chars */
359 return CSSM_FALSE;
360 }
361 if(memcmp(certComp, hostComp, subStrLen)) {
362 return CSSM_FALSE;
363 }
364 /* OK, skip over substring */
365 hostComp += subStrLen;
366 hostCompLen -= subStrLen;
367 /* start parsing at the wildcard itself */
368 certComp = wildCard;
369 certCompLen -= subStrLen;
370 continue;
371 }
372
373 /*
374 * Currently looking at a wildcard.
375 *
376 * Find substring in hostComp which matches from the char after
377 * the wildcard up to whichever of these comes next:
378 *
379 * -- end of certComp
380 * -- another wildcard
381 */
382 wildCard++;
383 if(wildCard == endCertComp) {
384 /*
385 * -- Wild card at end of cert's DNS
386 * -- nothing else to match - rest of hostComp is the wildcard
387 * match
388 * -- done, success
389 */
390 return CSSM_TRUE;
391 }
392
393 const char *afterSubStr; // in certComp
394 afterSubStr = tpSubStr(wildCard, endCertComp - wildCard,
395 "*", 1);
396 if(afterSubStr == NULL) {
397 /* no more wildcards - use end of certComp */
398 afterSubStr = endCertComp;
399 }
400 uint32 subStrLen = afterSubStr - wildCard;
401 const char *foundSub = tpSubStr(hostComp, hostCompLen,
402 wildCard, subStrLen);
403 if(foundSub == NULL) {
404 /* No match of explicit chars */
405 return CSSM_FALSE;
406 }
407
408 /* found it - skip past this substring */
409 hostComp = foundSub + subStrLen;
410 hostCompLen = endHostComp - hostComp;
411 certComp = afterSubStr;
412 certCompLen = endCertComp - afterSubStr;
413
414 } while((hostCompLen != 0) || (certCompLen != 0));
415 if((hostCompLen == 0) && (certCompLen == 0)) {
416 return CSSM_TRUE;
417 }
418 else {
419 /* end of one but not the other */
420 return CSSM_FALSE;
421 }
422 }
423
424 /*
425 * Compare hostname, is presented to the TP in
426 * CSSM_APPLE_TP_SSL_OPTIONS.ServerName, to a server name obtained
427 * from the server's cert (i.e., from subjectAltName or commonName).
428 * Limited wildcard checking is performed here.
429 *
430 * The incoming hostname is assumed to have been processed by tpToLower();
431 * we'll perform that processing on certName here.
432 *
433 * Trailing '.' characters in both host names will be ignored per Radar 3996792.
434 *
435 * Returns CSSM_TRUE on match, else CSSM_FALSE.
436 */
437 CSSM_BOOL tpCompareHostNames(
438 const char *hostName, // spec'd by app, tpToLower'd
439 uint32 hostNameLen,
440 char *certName, // from cert, we tpToLower
441 uint32 certNameLen)
442 {
443 tpToLower(certName, certNameLen);
444
445 /* tolerate optional NULL terminators for both */
446 if(hostNameLen && (hostName[hostNameLen - 1] == '\0')) {
447 hostNameLen--;
448 }
449 if(certNameLen && (certName[certNameLen - 1] == '\0')) {
450 certNameLen--;
451 }
452
453 if((hostNameLen == 0) || (certNameLen == 0)) {
454 /* trivial case with at least one empty name */
455 if(hostNameLen == certNameLen) {
456 return CSSM_TRUE;
457 }
458 else {
459 return CSSM_FALSE;
460 }
461 }
462
463 /* trim off trailing dots */
464 if(hostName[hostNameLen - 1] == '.') {
465 hostNameLen--;
466 }
467 if(certName[certNameLen - 1] == '.') {
468 certNameLen--;
469 }
470
471 /* Case 1: exact match */
472 if((certNameLen == hostNameLen) &&
473 !memcmp(certName, hostName, certNameLen)) {
474 return CSSM_TRUE;
475 }
476
477 /*
478 * Case 2: Compare one component at a time, handling wildcards in
479 * cert's server name. The characters implicitly matched by a
480 * wildcard span only one component of a dnsName.
481 */
482 do {
483 /* get next component from each dnsName */
484 char hostComp[MAX_DNS_COMP_LEN];
485 char certComp[MAX_DNS_COMP_LEN];
486 uint32 hostCompLen;
487 uint32 certCompLen;
488
489 bool foundHost = tpNextDnsComp(hostName, hostNameLen,
490 hostComp, hostCompLen);
491 bool foundCert = tpNextDnsComp(certName, certNameLen,
492 certComp, certCompLen);
493 if(foundHost != foundCert) {
494 /* unequal number of components */
495 tpPolicyError("tpCompareHostNames: wildcard mismatch (1)");
496 return CSSM_FALSE;
497 }
498 if(!foundHost) {
499 /* normal successful termination */
500 return CSSM_TRUE;
501 }
502
503 /* compare individual components */
504 if(!tpCompareComps(hostComp, hostCompLen,
505 certComp, certCompLen)) {
506 tpPolicyError("tpCompareHostNames: wildcard mismatch (2)");
507 return CSSM_FALSE;
508 }
509
510 /* skip over this component
511 * (note: since tpNextDnsComp will first skip over a leading '.',
512 * we must make sure to skip over it here as well.)
513 */
514 if(*hostName == '.') hostName++;
515 hostName += hostCompLen;
516 if(*certName == '.') certName++;
517 certName += certCompLen;
518 } while(1);
519 /* NOT REACHED */
520 //assert(0):
521 return CSSM_FALSE;
522 }
523
524 /*
525 * Compare email address, is presented to the TP in
526 * CSSM_APPLE_TP_SMIME_OPTIONS.SenderEmail, to a string obtained
527 * from the sender's cert (i.e., from subjectAltName or Subject DN).
528 *
529 * Returns CSSM_TRUE on match, else CSSM_FALSE.
530 *
531 * Incoming appEmail string has already been tpNormalizeAddrSpec'd.
532 * We do that for certEmail string here.
533 */
534 CSSM_BOOL tpCompareEmailAddr(
535 const char *appEmail, // spec'd by app, normalized
536 uint32 appEmailLen,
537 char *certEmail, // from cert, we normalize
538 uint32 certEmailLen,
539 bool normalizeAll) // true : lower-case all certEmail characters
540
541 {
542 tpNormalizeAddrSpec(certEmail, certEmailLen, normalizeAll);
543
544 /* tolerate optional NULL terminators for both */
545 if(appEmailLen > 0 && appEmail[appEmailLen - 1] == '\0') {
546 appEmailLen--;
547 }
548 if(certEmailLen > 0 && certEmail[certEmailLen - 1] == '\0') {
549 certEmailLen--;
550 }
551 if((certEmailLen == appEmailLen) &&
552 !memcmp(certEmail, appEmail, certEmailLen)) {
553 return CSSM_TRUE;
554 }
555 else {
556 /* mismatch */
557 tpPolicyError("tpCompareEmailAddr: app/cert email addrs mismatch");
558 return CSSM_FALSE;
559 }
560 }
561
562 /*
563 * Following a CSSMOID_ECDSA_WithSpecified algorithm is an encoded
564 * ECDSA_SigAlgParams containing the digest agorithm OID. Decode and return
565 * a unified ECDSA/digest alg (e.g. CSSM_ALGID_SHA512WithECDSA).
566 * Returns nonzero on error.
567 */
568 int decodeECDSA_SigAlgParams(
569 const CSSM_DATA *params,
570 CSSM_ALGORITHMS *cssmAlg) /* RETURNED */
571 {
572 SecAsn1CoderRef coder = NULL;
573 if(SecAsn1CoderCreate(&coder)) {
574 tpErrorLog("***Error in SecAsn1CoderCreate()\n");
575 return -1;
576 }
577 CSSM_X509_ALGORITHM_IDENTIFIER algParams;
578 memset(&algParams, 0, sizeof(algParams));
579 int ourRtn = 0;
580 bool algFound = false;
581 if(SecAsn1DecodeData(coder, params, kSecAsn1AlgorithmIDTemplate,
582 &algParams)) {
583 tpErrorLog("***Error decoding CSSM_X509_ALGORITHM_IDENTIFIER\n");
584 ourRtn = -1;
585 goto errOut;
586 }
587 CSSM_ALGORITHMS digestAlg;
588 algFound = cssmOidToAlg(&algParams.algorithm, &digestAlg);
589 if(!algFound) {
590 tpErrorLog("***Unknown algorithm in CSSM_X509_ALGORITHM_IDENTIFIER\n");
591 ourRtn = -1;
592 goto errOut;
593 }
594 switch(digestAlg) {
595 case CSSM_ALGID_SHA1:
596 *cssmAlg = CSSM_ALGID_SHA1WithECDSA;
597 break;
598 case CSSM_ALGID_SHA224:
599 *cssmAlg = CSSM_ALGID_SHA224WithECDSA;
600 break;
601 case CSSM_ALGID_SHA256:
602 *cssmAlg = CSSM_ALGID_SHA256WithECDSA;
603 break;
604 case CSSM_ALGID_SHA384:
605 *cssmAlg = CSSM_ALGID_SHA384WithECDSA;
606 break;
607 case CSSM_ALGID_SHA512:
608 *cssmAlg = CSSM_ALGID_SHA512WithECDSA;
609 break;
610 default:
611 tpErrorLog("***Unknown algorithm in ECDSA_SigAlgParams\n");
612 ourRtn = -1;
613 }
614 errOut:
615 SecAsn1CoderRelease(coder);
616 return ourRtn;
617 }
618