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 if(!bigstr
|| !substr
|| bigstrLen
== 0 || substrLen
== 0) {
307 /* stop searching substrLen chars before end of bigstr */
308 const char *endBigStr
= bigstr
+ bigstrLen
- substrLen
;
309 for( ; bigstr
<= endBigStr
; ) {
310 if(*bigstr
== *substr
) {
311 /* first char match - remainder? */
313 /* don't count on memcmp(a,b,0) */
316 if(!memcmp(bigstr
+1, substr
+1, substrLen
- 1)) {
326 * Compare two DNS components, with full wildcard check. We assume
327 * that no '.' chars exist (per the processing performed in
328 * tpNextDnsComp()). Returns CSSM_TRUE on match, else CSSM_FALSE.
330 static CSSM_BOOL
tpCompareComps(
331 const char *hostComp
, // no wildcards
333 const char *certComp
, // wildcards OK here
336 const char *endCertComp
= certComp
+ certCompLen
;
337 const char *endHostComp
= hostComp
+ hostCompLen
;
339 /* wild card in cert name? */
340 const char *wildCard
= tpSubStr(certComp
, certCompLen
,
342 if(wildCard
== NULL
) {
343 /* no, require perfect literal match right now */
344 if((hostCompLen
== certCompLen
) &&
345 !memcmp(hostComp
, certComp
, certCompLen
)) {
353 if(wildCard
!= certComp
) {
355 * Require literal match of hostComp with certComp
356 * up until (but not including) the wildcard
358 ptrdiff_t subStrLen
= wildCard
- certComp
;
359 if(subStrLen
> hostCompLen
) {
360 /* out of host name chars */
363 if(memcmp(certComp
, hostComp
, subStrLen
)) {
366 /* OK, skip over substring */
367 hostComp
+= subStrLen
;
368 hostCompLen
-= subStrLen
;
369 /* start parsing at the wildcard itself */
371 certCompLen
-= subStrLen
;
376 * Currently looking at a wildcard.
378 * Find substring in hostComp which matches from the char after
379 * the wildcard up to whichever of these comes next:
382 * -- another wildcard
385 if(wildCard
== endCertComp
) {
387 * -- Wild card at end of cert's DNS
388 * -- nothing else to match - rest of hostComp is the wildcard
395 const char *afterSubStr
; // in certComp
396 afterSubStr
= tpSubStr(wildCard
, (uint32
)(endCertComp
- wildCard
),
398 if(afterSubStr
== NULL
) {
399 /* no more wildcards - use end of certComp */
400 afterSubStr
= endCertComp
;
402 uint32 subStrLen
= (uint32
)(afterSubStr
- wildCard
);
403 const char *foundSub
= tpSubStr(hostComp
, hostCompLen
,
404 wildCard
, subStrLen
);
405 if(foundSub
== NULL
) {
406 /* No match of explicit chars */
410 /* found it - skip past this substring */
411 hostComp
= foundSub
+ subStrLen
;
412 hostCompLen
= (uint32
)(endHostComp
- hostComp
);
413 certComp
= afterSubStr
;
414 certCompLen
= (uint32
)(endCertComp
- afterSubStr
);
416 } while((hostCompLen
!= 0) || (certCompLen
!= 0));
417 if((hostCompLen
== 0) && (certCompLen
== 0)) {
421 /* end of one but not the other */
427 * Compare hostname, is presented to the TP in
428 * CSSM_APPLE_TP_SSL_OPTIONS.ServerName, to a server name obtained
429 * from the server's cert (i.e., from subjectAltName or commonName).
430 * Limited wildcard checking is performed here.
432 * The incoming hostname is assumed to have been processed by tpToLower();
433 * we'll perform that processing on certName here.
435 * Trailing '.' characters in both host names will be ignored per Radar 3996792.
437 * Returns CSSM_TRUE on match, else CSSM_FALSE.
439 CSSM_BOOL
tpCompareHostNames(
440 const char *hostName
, // spec'd by app, tpToLower'd
442 char *certName
, // from cert, we tpToLower
445 tpToLower(certName
, certNameLen
);
447 /* tolerate optional NULL terminators for both */
448 if(hostNameLen
&& (hostName
[hostNameLen
- 1] == '\0')) {
451 if(certNameLen
&& (certName
[certNameLen
- 1] == '\0')) {
455 if((hostNameLen
== 0) || (certNameLen
== 0)) {
456 /* trivial case with at least one empty name */
457 if(hostNameLen
== certNameLen
) {
465 /* trim off trailing dots */
466 if(hostName
[hostNameLen
- 1] == '.') {
469 if(certName
[certNameLen
- 1] == '.') {
473 /* Case 1: exact match */
474 if((certNameLen
== hostNameLen
) &&
475 !memcmp(certName
, hostName
, certNameLen
)) {
480 * Case 2: Compare one component at a time, handling wildcards in
481 * cert's server name. The characters implicitly matched by a
482 * wildcard span only one component of a dnsName.
485 /* get next component from each dnsName */
486 char hostComp
[MAX_DNS_COMP_LEN
];
487 char certComp
[MAX_DNS_COMP_LEN
];
491 bool foundHost
= tpNextDnsComp(hostName
, hostNameLen
,
492 hostComp
, hostCompLen
);
493 bool foundCert
= tpNextDnsComp(certName
, certNameLen
,
494 certComp
, certCompLen
);
495 if(foundHost
!= foundCert
) {
496 /* unequal number of components */
497 tpPolicyError("tpCompareHostNames: wildcard mismatch (1)");
501 /* normal successful termination */
505 /* compare individual components */
506 if(!tpCompareComps(hostComp
, hostCompLen
,
507 certComp
, certCompLen
)) {
508 tpPolicyError("tpCompareHostNames: wildcard mismatch (2)");
512 /* skip over this component
513 * (note: since tpNextDnsComp will first skip over a leading '.',
514 * we must make sure to skip over it here as well.)
516 if(*hostName
== '.') hostName
++;
517 hostName
+= hostCompLen
;
518 if(*certName
== '.') certName
++;
519 certName
+= certCompLen
;
526 * Compare email address, is presented to the TP in
527 * CSSM_APPLE_TP_SMIME_OPTIONS.SenderEmail, to a string obtained
528 * from the sender's cert (i.e., from subjectAltName or Subject DN).
530 * Returns CSSM_TRUE on match, else CSSM_FALSE.
532 * Incoming appEmail string has already been tpNormalizeAddrSpec'd.
533 * We do that for certEmail string here.
535 CSSM_BOOL
tpCompareEmailAddr(
536 const char *appEmail
, // spec'd by app, normalized
538 char *certEmail
, // from cert, we normalize
540 bool normalizeAll
) // true : lower-case all certEmail characters
543 tpNormalizeAddrSpec(certEmail
, certEmailLen
, normalizeAll
);
545 /* tolerate optional NULL terminators for both */
546 if(appEmailLen
> 0 && appEmail
[appEmailLen
- 1] == '\0') {
549 if(certEmailLen
> 0 && certEmail
[certEmailLen
- 1] == '\0') {
552 if((certEmailLen
== appEmailLen
) &&
553 !memcmp(certEmail
, appEmail
, certEmailLen
)) {
558 tpPolicyError("tpCompareEmailAddr: app/cert email addrs mismatch");
564 * Check whether the provided hostName has a domainName suffix.
565 * This function does not process wildcards, and allows hostName to match
566 * any subdomain level of the provided domainName.
568 * To match, the last domainNameLen chars of hostName must equal domainName,
569 * and the character immediately preceding domainName in hostName (if any)
570 * must be a dot. This means that domainName 'bar.com' will match hostName
571 * values 'host.bar.com' or 'host.sub.bar.com', but not 'host.foobar.com'.
573 * The incoming hostname is assumed to have been processed by tpToLower();
574 * we'll perform that processing on domainName here.
576 * Trailing '.' characters in both host names will be ignored per Radar 3996792.
578 * Returns CSSM_TRUE on match, else CSSM_FALSE.
580 CSSM_BOOL
tpCompareDomainSuffix(
581 const char *hostName
, // spec'd by app, tpToLower'd
583 char *domainName
, // we tpToLower
584 uint32 domainNameLen
)
586 tpToLower(domainName
, domainNameLen
);
588 /* tolerate optional NULL terminators for both */
589 if(hostNameLen
&& (hostName
[hostNameLen
- 1] == '\0')) {
592 if(domainNameLen
&& (domainName
[domainNameLen
- 1] == '\0')) {
596 if((hostNameLen
== 0) || (domainNameLen
== 0)) {
597 /* trivial case with at least one empty name */
598 if(hostNameLen
== domainNameLen
) {
606 /* trim off trailing dots */
607 if(hostName
[hostNameLen
- 1] == '.') {
610 if(domainName
[domainNameLen
- 1] == '.') {
614 /* trim off leading dot in suffix, if present */
615 if((domainNameLen
> 0) && (domainName
[0] == '.')) {
620 if(hostNameLen
< domainNameLen
) {
624 if(memcmp(hostName
+(hostNameLen
-domainNameLen
),domainName
,domainNameLen
)) {
628 /* require a dot prior to domain suffix, unless host == domain */
629 if(hostNameLen
> domainNameLen
) {
630 if(hostName
[hostNameLen
-(domainNameLen
+1)] != '.') {
639 * Following a CSSMOID_ECDSA_WithSpecified algorithm is an encoded
640 * ECDSA_SigAlgParams containing the digest algorithm OID. Decode and return
641 * a unified ECDSA/digest alg (e.g. CSSM_ALGID_SHA512WithECDSA).
642 * Returns nonzero on error.
644 int decodeECDSA_SigAlgParams(
645 const CSSM_DATA
*params
,
646 CSSM_ALGORITHMS
*cssmAlg
) /* RETURNED */
648 SecAsn1CoderRef coder
= NULL
;
649 if(SecAsn1CoderCreate(&coder
)) {
650 tpErrorLog("***Error in SecAsn1CoderCreate()\n");
653 CSSM_X509_ALGORITHM_IDENTIFIER algParams
;
654 memset(&algParams
, 0, sizeof(algParams
));
656 bool algFound
= false;
657 if(SecAsn1DecodeData(coder
, params
, kSecAsn1AlgorithmIDTemplate
,
659 tpErrorLog("***Error decoding CSSM_X509_ALGORITHM_IDENTIFIER\n");
663 CSSM_ALGORITHMS digestAlg
;
664 algFound
= cssmOidToAlg(&algParams
.algorithm
, &digestAlg
);
666 tpErrorLog("***Unknown algorithm in CSSM_X509_ALGORITHM_IDENTIFIER\n");
671 case CSSM_ALGID_SHA1
:
672 *cssmAlg
= CSSM_ALGID_SHA1WithECDSA
;
674 case CSSM_ALGID_SHA224
:
675 *cssmAlg
= CSSM_ALGID_SHA224WithECDSA
;
677 case CSSM_ALGID_SHA256
:
678 *cssmAlg
= CSSM_ALGID_SHA256WithECDSA
;
680 case CSSM_ALGID_SHA384
:
681 *cssmAlg
= CSSM_ALGID_SHA384WithECDSA
;
683 case CSSM_ALGID_SHA512
:
684 *cssmAlg
= CSSM_ALGID_SHA512WithECDSA
;
687 tpErrorLog("***Unknown algorithm in ECDSA_SigAlgParams\n");
691 SecAsn1CoderRelease(coder
);