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
);