2 * Copyright (c) 2000-2001,2011-2014 Apple Inc. All Rights Reserved.
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
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.
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>
32 #include "certGroupUtils.h"
33 #include "tpdebugging.h"
36 #include <string.h> /* for memcmp */
40 * Copy one CSSM_DATA to another, mallocing destination.
47 dst
->Data
= (uint8
*)alloc
.malloc(src
->Length
);
48 dst
->Length
= src
->Length
;
49 memmove(dst
->Data
, src
->Data
, src
->Length
);
53 * Malloc a CSSM_DATA, copy another one to it.
55 CSSM_DATA_PTR
tpMallocCopyCssmData(
59 CSSM_DATA_PTR dst
= (CSSM_DATA_PTR
)alloc
.malloc(sizeof(CSSM_DATA
));
60 tpCopyCssmData(alloc
, src
, dst
);
65 * Free the data referenced by a CSSM data, and optionally, the struct itself.
75 if(data
->Length
!= 0) {
76 tpFree(alloc
, data
->Data
);
88 * Compare two CSSM_DATAs, return CSSM_TRUE if identical.
90 CSSM_BOOL
tpCompareCssmData(
91 const CSSM_DATA
*data1
,
92 const CSSM_DATA
*data2
)
94 if((data1
== NULL
) || (data1
->Data
== NULL
) ||
95 (data2
== NULL
) || (data2
->Data
== NULL
) ||
96 (data1
->Length
!= data2
->Length
)) {
99 if(data1
->Length
!= data2
->Length
) {
102 if(memcmp(data1
->Data
, data2
->Data
, data1
->Length
) == 0) {
111 * Free memory via specified plugin's app-level allocator
113 void tpFreePluginMemory(
117 CSSM_API_MEMORY_FUNCS memFuncs
;
118 CSSM_RETURN crtn
= CSSM_GetAPIMemoryFunctions(hand
, &memFuncs
);
120 tpErrorLog("CSSM_GetAPIMemoryFunctions failure\n");
121 /* oh well, leak and continue */
124 memFuncs
.free_func(p
, memFuncs
.AllocRef
);
128 * Obtain the public key blob from a cert.
130 CSSM_DATA_PTR
tp_CertGetPublicKey(
132 CSSM_DATA_PTR
*valueToFree
) // used in tp_CertFreePublicKey
136 CSSM_X509_SUBJECT_PUBLIC_KEY_INFO
*keyInfo
;
139 crtn
= cert
->fetchField(&CSSMOID_X509V1SubjectPublicKeyCStruct
, &val
);
141 tpErrorLog("Error on CSSM_CL_CertGetFirstFieldValue(PublicKeyCStruct)\n");
145 keyInfo
= (CSSM_X509_SUBJECT_PUBLIC_KEY_INFO
*)val
->Data
;
146 return &keyInfo
->subjectPublicKey
;
149 void tp_CertFreePublicKey(
150 CSSM_CL_HANDLE clHand
,
153 CSSM_CL_FreeFieldValue(clHand
, &CSSMOID_X509V1SubjectPublicKeyCStruct
, value
);
157 * Obtain signature algorithm info from a cert.
159 CSSM_X509_ALGORITHM_IDENTIFIER_PTR
tp_CertGetAlgId(
161 CSSM_DATA_PTR
*valueToFree
) // used in tp_CertFreeAlgId
167 crtn
= cert
->fetchField(&CSSMOID_X509V1SignatureAlgorithm
, &val
);
169 tpErrorLog("Error on fetchField(CSSMOID_X509V1SignatureAlgorithm)\n");
173 return (CSSM_X509_ALGORITHM_IDENTIFIER_PTR
)val
->Data
;
176 void tp_CertFreeAlgId(
177 CSSM_CL_HANDLE clHand
,
180 CSSM_CL_FreeFieldValue(clHand
, &CSSMOID_X509V1SignatureAlgorithm
, value
);
184 * Determine if two certs - passed in encoded form - are equivalent.
186 CSSM_BOOL
tp_CompareCerts(
187 const CSSM_DATA
*cert1
,
188 const CSSM_DATA
*cert2
)
190 return tpCompareCssmData(cert1
, cert2
);
194 * Convert a C string to lower case in place. NULL terminator not needed.
200 for(unsigned i
=0; i
<strLen
; i
++) {
201 *str
= tolower(*str
);
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
212 void tpNormalizeAddrSpec(
218 tpPolicyError("tpNormalizeAddrSpec: bad addr");
222 while((addrLen
!= 0) && (*addr
!= '@')) {
227 tpPolicyError("tpNormalizeAddrSpec: bad addr-spec");
231 tpToLower(addr
, addrLen
);
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.
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.
246 #define MAX_DNS_COMP_LEN 128
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.
253 static bool tpNextDnsComp(
255 uint32
&inBufLen
, // IN/OUT
256 char *outBuf
, // component RETURNED here
257 uint32
&outBufLen
) // RETURNED length of component
264 /* skip over leading '.' */
267 if(--inBufLen
== 0) {
272 /* copy chars until out of data or next '.' found */
277 *outBuf
++ = *inBuf
++;
280 if(outBufLen
>= MAX_DNS_COMP_LEN
) {
284 } while(inBufLen
!= 0);
294 * Find location of specified substring in given bigstring. Returns
295 * pointer to start of substring in bigstring, else returns NULL.
297 static const char *tpSubStr(
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? */
309 /* don't count on memcmp(a,b,0) */
312 if(!memcmp(bigstr
+1, substr
+1, substrLen
- 1)) {
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.
326 static CSSM_BOOL
tpCompareComps(
327 const char *hostComp
, // no wildcards
329 const char *certComp
, // wildcards OK here
332 const char *endCertComp
= certComp
+ certCompLen
;
333 const char *endHostComp
= hostComp
+ hostCompLen
;
335 /* wild card in cert name? */
336 const char *wildCard
= tpSubStr(certComp
, certCompLen
,
338 if(wildCard
== NULL
) {
339 /* no, require perfect literal match right now */
340 if((hostCompLen
== certCompLen
) &&
341 !memcmp(hostComp
, certComp
, certCompLen
)) {
349 if(wildCard
!= certComp
) {
351 * Require literal match of hostComp with certComp
352 * up until (but not including) the wildcard
354 ptrdiff_t subStrLen
= wildCard
- certComp
;
355 if(subStrLen
> hostCompLen
) {
356 /* out of host name chars */
359 if(memcmp(certComp
, hostComp
, subStrLen
)) {
362 /* OK, skip over substring */
363 hostComp
+= subStrLen
;
364 hostCompLen
-= subStrLen
;
365 /* start parsing at the wildcard itself */
367 certCompLen
-= subStrLen
;
372 * Currently looking at a wildcard.
374 * Find substring in hostComp which matches from the char after
375 * the wildcard up to whichever of these comes next:
378 * -- another wildcard
381 if(wildCard
== endCertComp
) {
383 * -- Wild card at end of cert's DNS
384 * -- nothing else to match - rest of hostComp is the wildcard
391 const char *afterSubStr
; // in certComp
392 afterSubStr
= tpSubStr(wildCard
, (uint32
)(endCertComp
- wildCard
),
394 if(afterSubStr
== NULL
) {
395 /* no more wildcards - use end of certComp */
396 afterSubStr
= endCertComp
;
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 */
406 /* found it - skip past this substring */
407 hostComp
= foundSub
+ subStrLen
;
408 hostCompLen
= (uint32
)(endHostComp
- hostComp
);
409 certComp
= afterSubStr
;
410 certCompLen
= (uint32
)(endCertComp
- afterSubStr
);
412 } while((hostCompLen
!= 0) || (certCompLen
!= 0));
413 if((hostCompLen
== 0) && (certCompLen
== 0)) {
417 /* end of one but not the other */
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.
428 * The incoming hostname is assumed to have been processed by tpToLower();
429 * we'll perform that processing on certName here.
431 * Trailing '.' characters in both host names will be ignored per Radar 3996792.
433 * Returns CSSM_TRUE on match, else CSSM_FALSE.
435 CSSM_BOOL
tpCompareHostNames(
436 const char *hostName
, // spec'd by app, tpToLower'd
438 char *certName
, // from cert, we tpToLower
441 tpToLower(certName
, certNameLen
);
443 /* tolerate optional NULL terminators for both */
444 if(hostNameLen
&& (hostName
[hostNameLen
- 1] == '\0')) {
447 if(certNameLen
&& (certName
[certNameLen
- 1] == '\0')) {
451 if((hostNameLen
== 0) || (certNameLen
== 0)) {
452 /* trivial case with at least one empty name */
453 if(hostNameLen
== certNameLen
) {
461 /* trim off trailing dots */
462 if(hostName
[hostNameLen
- 1] == '.') {
465 if(certName
[certNameLen
- 1] == '.') {
469 /* Case 1: exact match */
470 if((certNameLen
== hostNameLen
) &&
471 !memcmp(certName
, hostName
, certNameLen
)) {
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.
481 /* get next component from each dnsName */
482 char hostComp
[MAX_DNS_COMP_LEN
];
483 char certComp
[MAX_DNS_COMP_LEN
];
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)");
497 /* normal successful termination */
501 /* compare individual components */
502 if(!tpCompareComps(hostComp
, hostCompLen
,
503 certComp
, certCompLen
)) {
504 tpPolicyError("tpCompareHostNames: wildcard mismatch (2)");
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.)
512 if(*hostName
== '.') hostName
++;
513 hostName
+= hostCompLen
;
514 if(*certName
== '.') certName
++;
515 certName
+= certCompLen
;
522 * Compare email address, is presented to the TP in
523 * CSSM_APPLE_TP_SMIME_OPTIONS.SenderEmail, to a string obtained
524 * from the sender's cert (i.e., from subjectAltName or Subject DN).
526 * Returns CSSM_TRUE on match, else CSSM_FALSE.
528 * Incoming appEmail string has already been tpNormalizeAddrSpec'd.
529 * We do that for certEmail string here.
531 CSSM_BOOL
tpCompareEmailAddr(
532 const char *appEmail
, // spec'd by app, normalized
534 char *certEmail
, // from cert, we normalize
536 bool normalizeAll
) // true : lower-case all certEmail characters
539 tpNormalizeAddrSpec(certEmail
, certEmailLen
, normalizeAll
);
541 /* tolerate optional NULL terminators for both */
542 if(appEmailLen
> 0 && appEmail
[appEmailLen
- 1] == '\0') {
545 if(certEmailLen
> 0 && certEmail
[certEmailLen
- 1] == '\0') {
548 if((certEmailLen
== appEmailLen
) &&
549 !memcmp(certEmail
, appEmail
, certEmailLen
)) {
554 tpPolicyError("tpCompareEmailAddr: app/cert email addrs mismatch");
560 * Check whether the provided hostName has a domainName suffix.
561 * This function does not process wildcards, and allows hostName to match
562 * any subdomain level of the provided domainName.
564 * To match, the last domainNameLen chars of hostName must equal domainName,
565 * and the character immediately preceding domainName in hostName (if any)
566 * must be a dot. This means that domainName 'bar.com' will match hostName
567 * values 'host.bar.com' or 'host.sub.bar.com', but not 'host.foobar.com'.
569 * The incoming hostname is assumed to have been processed by tpToLower();
570 * we'll perform that processing on domainName here.
572 * Trailing '.' characters in both host names will be ignored per Radar 3996792.
574 * Returns CSSM_TRUE on match, else CSSM_FALSE.
576 CSSM_BOOL
tpCompareDomainSuffix(
577 const char *hostName
, // spec'd by app, tpToLower'd
579 char *domainName
, // we tpToLower
580 uint32 domainNameLen
)
582 tpToLower(domainName
, domainNameLen
);
584 /* tolerate optional NULL terminators for both */
585 if(hostNameLen
&& (hostName
[hostNameLen
- 1] == '\0')) {
588 if(domainNameLen
&& (domainName
[domainNameLen
- 1] == '\0')) {
592 if((hostNameLen
== 0) || (domainNameLen
== 0)) {
593 /* trivial case with at least one empty name */
594 if(hostNameLen
== domainNameLen
) {
602 /* trim off trailing dots */
603 if(hostName
[hostNameLen
- 1] == '.') {
606 if(domainName
[domainNameLen
- 1] == '.') {
610 /* trim off leading dot in suffix, if present */
611 if((domainNameLen
> 0) && (domainName
[0] == '.')) {
616 if(hostNameLen
< domainNameLen
) {
620 if(memcmp(hostName
+(hostNameLen
-domainNameLen
),domainName
,domainNameLen
)) {
624 /* require a dot prior to domain suffix, unless host == domain */
625 if(hostNameLen
> domainNameLen
) {
626 if(hostName
[hostNameLen
-(domainNameLen
+1)] != '.') {
635 * Following a CSSMOID_ECDSA_WithSpecified algorithm is an encoded
636 * ECDSA_SigAlgParams containing the digest algorithm OID. Decode and return
637 * a unified ECDSA/digest alg (e.g. CSSM_ALGID_SHA512WithECDSA).
638 * Returns nonzero on error.
640 int decodeECDSA_SigAlgParams(
641 const CSSM_DATA
*params
,
642 CSSM_ALGORITHMS
*cssmAlg
) /* RETURNED */
644 SecAsn1CoderRef coder
= NULL
;
645 if(SecAsn1CoderCreate(&coder
)) {
646 tpErrorLog("***Error in SecAsn1CoderCreate()\n");
649 CSSM_X509_ALGORITHM_IDENTIFIER algParams
;
650 memset(&algParams
, 0, sizeof(algParams
));
652 bool algFound
= false;
653 if(SecAsn1DecodeData(coder
, params
, kSecAsn1AlgorithmIDTemplate
,
655 tpErrorLog("***Error decoding CSSM_X509_ALGORITHM_IDENTIFIER\n");
659 CSSM_ALGORITHMS digestAlg
;
660 algFound
= cssmOidToAlg(&algParams
.algorithm
, &digestAlg
);
662 tpErrorLog("***Unknown algorithm in CSSM_X509_ALGORITHM_IDENTIFIER\n");
667 case CSSM_ALGID_SHA1
:
668 *cssmAlg
= CSSM_ALGID_SHA1WithECDSA
;
670 case CSSM_ALGID_SHA224
:
671 *cssmAlg
= CSSM_ALGID_SHA224WithECDSA
;
673 case CSSM_ALGID_SHA256
:
674 *cssmAlg
= CSSM_ALGID_SHA256WithECDSA
;
676 case CSSM_ALGID_SHA384
:
677 *cssmAlg
= CSSM_ALGID_SHA384WithECDSA
;
679 case CSSM_ALGID_SHA512
:
680 *cssmAlg
= CSSM_ALGID_SHA512WithECDSA
;
683 tpErrorLog("***Unknown algorithm in ECDSA_SigAlgParams\n");
687 SecAsn1CoderRelease(coder
);