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