2 * Copyright (c) 2000-2001 Apple Computer, 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.
22 Created 10/9/2000 by Doug Mitchell.
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>
34 #include "certGroupUtils.h"
35 #include "tpdebugging.h"
38 #include <string.h> /* for memcmp */
42 * Copy one CSSM_DATA to another, mallocing destination.
49 dst
->Data
= (uint8
*)alloc
.malloc(src
->Length
);
50 dst
->Length
= src
->Length
;
51 memmove(dst
->Data
, src
->Data
, src
->Length
);
55 * Malloc a CSSM_DATA, copy another one to it.
57 CSSM_DATA_PTR
tpMallocCopyCssmData(
61 CSSM_DATA_PTR dst
= (CSSM_DATA_PTR
)alloc
.malloc(sizeof(CSSM_DATA
));
62 tpCopyCssmData(alloc
, src
, dst
);
67 * Free the data referenced by a CSSM data, and optionally, the struct itself.
77 if(data
->Length
!= 0) {
78 tpFree(alloc
, data
->Data
);
90 * Compare two CSSM_DATAs, return CSSM_TRUE if identical.
92 CSSM_BOOL
tpCompareCssmData(
93 const CSSM_DATA
*data1
,
94 const CSSM_DATA
*data2
)
96 if((data1
== NULL
) || (data1
->Data
== NULL
) ||
97 (data2
== NULL
) || (data2
->Data
== NULL
) ||
98 (data1
->Length
!= data2
->Length
)) {
101 if(data1
->Length
!= data2
->Length
) {
104 if(memcmp(data1
->Data
, data2
->Data
, data1
->Length
) == 0) {
113 * Free memory via specified plugin's app-level allocator
115 void tpFreePluginMemory(
119 CSSM_API_MEMORY_FUNCS memFuncs
;
120 CSSM_RETURN crtn
= CSSM_GetAPIMemoryFunctions(hand
, &memFuncs
);
122 tpErrorLog("CSSM_GetAPIMemoryFunctions failure\n");
123 /* oh well, leak and continue */
126 memFuncs
.free_func(p
, memFuncs
.AllocRef
);
130 * Obtain the public key blob from a cert.
132 CSSM_DATA_PTR
tp_CertGetPublicKey(
134 CSSM_DATA_PTR
*valueToFree
) // used in tp_CertFreePublicKey
138 CSSM_X509_SUBJECT_PUBLIC_KEY_INFO
*keyInfo
;
141 crtn
= cert
->fetchField(&CSSMOID_X509V1SubjectPublicKeyCStruct
, &val
);
143 tpErrorLog("Error on CSSM_CL_CertGetFirstFieldValue(PublicKeyCStruct)\n");
147 keyInfo
= (CSSM_X509_SUBJECT_PUBLIC_KEY_INFO
*)val
->Data
;
148 return &keyInfo
->subjectPublicKey
;
151 void tp_CertFreePublicKey(
152 CSSM_CL_HANDLE clHand
,
155 CSSM_CL_FreeFieldValue(clHand
, &CSSMOID_X509V1SubjectPublicKeyCStruct
, value
);
159 * Obtain signature algorithm info from a cert.
161 CSSM_X509_ALGORITHM_IDENTIFIER_PTR
tp_CertGetAlgId(
163 CSSM_DATA_PTR
*valueToFree
) // used in tp_CertFreeAlgId
169 crtn
= cert
->fetchField(&CSSMOID_X509V1SignatureAlgorithm
, &val
);
171 tpErrorLog("Error on fetchField(CSSMOID_X509V1SignatureAlgorithm)\n");
175 return (CSSM_X509_ALGORITHM_IDENTIFIER_PTR
)val
->Data
;
178 void tp_CertFreeAlgId(
179 CSSM_CL_HANDLE clHand
,
182 CSSM_CL_FreeFieldValue(clHand
, &CSSMOID_X509V1SignatureAlgorithm
, value
);
186 * Determine if two certs - passed in encoded form - are equivalent.
188 CSSM_BOOL
tp_CompareCerts(
189 const CSSM_DATA
*cert1
,
190 const CSSM_DATA
*cert2
)
192 return tpCompareCssmData(cert1
, cert2
);
196 * Convert a C string to lower case in place. NULL terminator not needed.
202 for(unsigned i
=0; i
<strLen
; i
++) {
203 *str
= tolower(*str
);
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
214 void tpNormalizeAddrSpec(
220 tpPolicyError("tpNormalizeAddrSpec: bad addr");
224 while((addrLen
!= 0) && (*addr
!= '@')) {
229 tpPolicyError("tpNormalizeAddrSpec: bad addr-spec");
233 tpToLower(addr
, addrLen
);
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.
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.
248 #define MAX_DNS_COMP_LEN 128
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.
255 static bool tpNextDnsComp(
257 uint32
&inBufLen
, // IN/OUT
258 char *outBuf
, // component RETURNED here
259 uint32
&outBufLen
) // RETURNED length of component
266 /* skip over leading '.' */
269 if(--inBufLen
== 0) {
274 /* copy chars until out of data or next '.' found */
279 *outBuf
++ = *inBuf
++;
282 if(outBufLen
>= MAX_DNS_COMP_LEN
) {
286 } while(inBufLen
!= 0);
296 * Find location of specified substring in given bigstring. Returns
297 * pointer to start of substring in bigstring, else returns NULL.
299 static const char *tpSubStr(
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? */
311 /* don't count on memcmp(a,b,0) */
314 if(!memcmp(bigstr
+1, substr
+1, substrLen
- 1)) {
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.
328 static CSSM_BOOL
tpCompareComps(
329 const char *hostComp
, // no wildcards
331 const char *certComp
, // wildcards OK here
334 const char *endCertComp
= certComp
+ certCompLen
;
335 const char *endHostComp
= hostComp
+ hostCompLen
;
337 /* wild card in cert name? */
338 const char *wildCard
= tpSubStr(certComp
, certCompLen
,
340 if(wildCard
== NULL
) {
341 /* no, require perfect literal match right now */
342 if((hostCompLen
== certCompLen
) &&
343 !memcmp(hostComp
, certComp
, certCompLen
)) {
351 if(wildCard
!= certComp
) {
353 * Require literal match of hostComp with certComp
354 * up until (but not including) the wildcard
356 uint32 subStrLen
= wildCard
- certComp
;
357 if(subStrLen
> hostCompLen
) {
358 /* out of host name chars */
361 if(memcmp(certComp
, hostComp
, subStrLen
)) {
364 /* OK, skip over substring */
365 hostComp
+= subStrLen
;
366 hostCompLen
-= subStrLen
;
367 /* start parsing at the wildcard itself */
369 certCompLen
-= subStrLen
;
374 * Currently looking at a wildcard.
376 * Find substring in hostComp which matches from the char after
377 * the wildcard up to whichever of these comes next:
380 * -- another wildcard
383 if(wildCard
== endCertComp
) {
385 * -- Wild card at end of cert's DNS
386 * -- nothing else to match - rest of hostComp is the wildcard
393 const char *afterSubStr
; // in certComp
394 afterSubStr
= tpSubStr(wildCard
, endCertComp
- wildCard
,
396 if(afterSubStr
== NULL
) {
397 /* no more wildcards - use end of certComp */
398 afterSubStr
= endCertComp
;
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 */
408 /* found it - skip past this substring */
409 hostComp
= foundSub
+ subStrLen
;
410 hostCompLen
= endHostComp
- hostComp
;
411 certComp
= afterSubStr
;
412 certCompLen
= endCertComp
- afterSubStr
;
414 } while((hostCompLen
!= 0) || (certCompLen
!= 0));
415 if((hostCompLen
== 0) && (certCompLen
== 0)) {
419 /* end of one but not the other */
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.
430 * The incoming hostname is assumed to have been processed by tpToLower();
431 * we'll perform that processing on certName here.
433 * Trailing '.' characters in both host names will be ignored per Radar 3996792.
435 * Returns CSSM_TRUE on match, else CSSM_FALSE.
437 CSSM_BOOL
tpCompareHostNames(
438 const char *hostName
, // spec'd by app, tpToLower'd
440 char *certName
, // from cert, we tpToLower
443 tpToLower(certName
, certNameLen
);
445 /* tolerate optional NULL terminators for both */
446 if(hostNameLen
&& (hostName
[hostNameLen
- 1] == '\0')) {
449 if(certNameLen
&& (certName
[certNameLen
- 1] == '\0')) {
453 if((hostNameLen
== 0) || (certNameLen
== 0)) {
454 /* trivial case with at least one empty name */
455 if(hostNameLen
== certNameLen
) {
463 /* trim off trailing dots */
464 if(hostName
[hostNameLen
- 1] == '.') {
467 if(certName
[certNameLen
- 1] == '.') {
471 /* Case 1: exact match */
472 if((certNameLen
== hostNameLen
) &&
473 !memcmp(certName
, hostName
, certNameLen
)) {
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.
483 /* get next component from each dnsName */
484 char hostComp
[MAX_DNS_COMP_LEN
];
485 char certComp
[MAX_DNS_COMP_LEN
];
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)");
499 /* normal successful termination */
503 /* compare individual components */
504 if(!tpCompareComps(hostComp
, hostCompLen
,
505 certComp
, certCompLen
)) {
506 tpPolicyError("tpCompareHostNames: wildcard mismatch (2)");
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.)
514 if(*hostName
== '.') hostName
++;
515 hostName
+= hostCompLen
;
516 if(*certName
== '.') certName
++;
517 certName
+= certCompLen
;
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).
529 * Returns CSSM_TRUE on match, else CSSM_FALSE.
531 * Incoming appEmail string has already been tpNormalizeAddrSpec'd.
532 * We do that for certEmail string here.
534 CSSM_BOOL
tpCompareEmailAddr(
535 const char *appEmail
, // spec'd by app, normalized
537 char *certEmail
, // from cert, we normalize
539 bool normalizeAll
) // true : lower-case all certEmail characters
542 tpNormalizeAddrSpec(certEmail
, certEmailLen
, normalizeAll
);
544 /* tolerate optional NULL terminators for both */
545 if(appEmailLen
> 0 && appEmail
[appEmailLen
- 1] == '\0') {
548 if(certEmailLen
> 0 && certEmail
[certEmailLen
- 1] == '\0') {
551 if((certEmailLen
== appEmailLen
) &&
552 !memcmp(certEmail
, appEmail
, certEmailLen
)) {
557 tpPolicyError("tpCompareEmailAddr: app/cert email addrs mismatch");
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.
568 int decodeECDSA_SigAlgParams(
569 const CSSM_DATA
*params
,
570 CSSM_ALGORITHMS
*cssmAlg
) /* RETURNED */
572 SecAsn1CoderRef coder
= NULL
;
573 if(SecAsn1CoderCreate(&coder
)) {
574 tpErrorLog("***Error in SecAsn1CoderCreate()\n");
577 CSSM_X509_ALGORITHM_IDENTIFIER algParams
;
578 memset(&algParams
, 0, sizeof(algParams
));
580 bool algFound
= false;
581 if(SecAsn1DecodeData(coder
, params
, kSecAsn1AlgorithmIDTemplate
,
583 tpErrorLog("***Error decoding CSSM_X509_ALGORITHM_IDENTIFIER\n");
587 CSSM_ALGORITHMS digestAlg
;
588 algFound
= cssmOidToAlg(&algParams
.algorithm
, &digestAlg
);
590 tpErrorLog("***Unknown algorithm in CSSM_X509_ALGORITHM_IDENTIFIER\n");
595 case CSSM_ALGID_SHA1
:
596 *cssmAlg
= CSSM_ALGID_SHA1WithECDSA
;
598 case CSSM_ALGID_SHA224
:
599 *cssmAlg
= CSSM_ALGID_SHA224WithECDSA
;
601 case CSSM_ALGID_SHA256
:
602 *cssmAlg
= CSSM_ALGID_SHA256WithECDSA
;
604 case CSSM_ALGID_SHA384
:
605 *cssmAlg
= CSSM_ALGID_SHA384WithECDSA
;
607 case CSSM_ALGID_SHA512
:
608 *cssmAlg
= CSSM_ALGID_SHA512WithECDSA
;
611 tpErrorLog("***Unknown algorithm in ECDSA_SigAlgParams\n");
615 SecAsn1CoderRelease(coder
);