1/* 2 * Copyright (c) 2000-2001 Apple Computer, 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 Created 10/9/2000 by Doug Mitchell. 23*/ 24 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> 33 34#include "certGroupUtils.h" 35#include "tpdebugging.h" 36#include "tpTime.h" 37 38#include <string.h> /* for memcmp */ 39 40 41/* 42 * Copy one CSSM_DATA to another, mallocing destination. 43 */ 44void tpCopyCssmData( 45 Allocator &alloc, 46 const CSSM_DATA *src, 47 CSSM_DATA_PTR dst) 48{ 49 dst->Data = (uint8 *)alloc.malloc(src->Length); 50 dst->Length = src->Length; 51 memmove(dst->Data, src->Data, src->Length); 52} 53 54/* 55 * Malloc a CSSM_DATA, copy another one to it. 56 */ 57CSSM_DATA_PTR tpMallocCopyCssmData( 58 Allocator &alloc, 59 const CSSM_DATA *src) 60{ 61 CSSM_DATA_PTR dst = (CSSM_DATA_PTR)alloc.malloc(sizeof(CSSM_DATA)); 62 tpCopyCssmData(alloc, src, dst); 63 return dst; 64} 65 66/* 67 * Free the data referenced by a CSSM data, and optionally, the struct itself. 68 */ 69void tpFreeCssmData( 70 Allocator &alloc, 71 CSSM_DATA_PTR data, 72 CSSM_BOOL freeStruct) 73{ 74 if(data == NULL) { 75 return; 76 } 77 if(data->Length != 0) { 78 tpFree(alloc, data->Data); 79 } 80 if(freeStruct) { 81 tpFree(alloc, data); 82 } 83 else { 84 data->Length = 0; 85 data->Data = NULL; 86 } 87} 88 89/* 90 * Compare two CSSM_DATAs, return CSSM_TRUE if identical. 91 */ 92CSSM_BOOL tpCompareCssmData( 93 const CSSM_DATA *data1, 94 const CSSM_DATA *data2) 95{ 96 if((data1 == NULL) || (data1->Data == NULL) || 97 (data2 == NULL) || (data2->Data == NULL) || 98 (data1->Length != data2->Length)) { 99 return CSSM_FALSE; 100 } 101 if(data1->Length != data2->Length) { 102 return CSSM_FALSE; 103 } 104 if(memcmp(data1->Data, data2->Data, data1->Length) == 0) { 105 return CSSM_TRUE; 106 } 107 else { 108 return CSSM_FALSE; 109 } 110} 111 112/* 113 * Free memory via specified plugin's app-level allocator 114 */ 115void tpFreePluginMemory( 116 CSSM_HANDLE hand, 117 void *p) 118{ 119 CSSM_API_MEMORY_FUNCS memFuncs; 120 CSSM_RETURN crtn = CSSM_GetAPIMemoryFunctions(hand, &memFuncs); 121 if(crtn) { 122 tpErrorLog("CSSM_GetAPIMemoryFunctions failure\n"); 123 /* oh well, leak and continue */ 124 return; 125 } 126 memFuncs.free_func(p, memFuncs.AllocRef); 127} 128 129/* 130 * Obtain the public key blob from a cert. 131 */ 132CSSM_DATA_PTR tp_CertGetPublicKey( 133 TPCertInfo *cert, 134 CSSM_DATA_PTR *valueToFree) // used in tp_CertFreePublicKey 135{ 136 CSSM_RETURN crtn; 137 CSSM_DATA_PTR val; 138 CSSM_X509_SUBJECT_PUBLIC_KEY_INFO *keyInfo; 139 140 *valueToFree = NULL; 141 crtn = cert->fetchField(&CSSMOID_X509V1SubjectPublicKeyCStruct, &val); 142 if(crtn) { 143 tpErrorLog("Error on CSSM_CL_CertGetFirstFieldValue(PublicKeyCStruct)\n"); 144 return NULL; 145 } 146 *valueToFree = val; 147 keyInfo = (CSSM_X509_SUBJECT_PUBLIC_KEY_INFO *)val->Data; 148 return &keyInfo->subjectPublicKey; 149} 150 151void tp_CertFreePublicKey( 152 CSSM_CL_HANDLE clHand, 153 CSSM_DATA_PTR value) 154{ 155 CSSM_CL_FreeFieldValue(clHand, &CSSMOID_X509V1SubjectPublicKeyCStruct, value); 156} 157 158/* 159 * Obtain signature algorithm info from a cert. 160 */ 161CSSM_X509_ALGORITHM_IDENTIFIER_PTR tp_CertGetAlgId( 162 TPCertInfo *cert, 163 CSSM_DATA_PTR *valueToFree) // used in tp_CertFreeAlgId 164{ 165 CSSM_RETURN crtn; 166 CSSM_DATA_PTR val; 167 168 *valueToFree = NULL; 169 crtn = cert->fetchField(&CSSMOID_X509V1SignatureAlgorithm, &val); 170 if(crtn) { 171 tpErrorLog("Error on fetchField(CSSMOID_X509V1SignatureAlgorithm)\n"); 172 return NULL; 173 } 174 *valueToFree = val; 175 return (CSSM_X509_ALGORITHM_IDENTIFIER_PTR)val->Data; 176} 177 178void tp_CertFreeAlgId( 179 CSSM_CL_HANDLE clHand, 180 CSSM_DATA_PTR value) 181{ 182 CSSM_CL_FreeFieldValue(clHand, &CSSMOID_X509V1SignatureAlgorithm, value); 183} 184 185/* 186 * Determine if two certs - passed in encoded form - are equivalent. 187 */ 188CSSM_BOOL tp_CompareCerts( 189 const CSSM_DATA *cert1, 190 const CSSM_DATA *cert2) 191{ 192 return tpCompareCssmData(cert1, cert2); 193} 194 195/* 196 * Convert a C string to lower case in place. NULL terminator not needed. 197 */ 198void tpToLower( 199 char *str, 200 unsigned strLen) 201{ 202 for(unsigned i=0; i<strLen; i++) { 203 *str = tolower(*str); 204 str++; 205 } 206} 207 208/* 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 212 * (e.g. for iChat). 213 */ 214void tpNormalizeAddrSpec( 215 char *addr, 216 unsigned addrLen, 217 bool normalizeAll) 218{ 219 if (addr == NULL) { 220 tpPolicyError("tpNormalizeAddrSpec: bad addr"); 221 return; 222 } 223 if(!normalizeAll) { 224 while((addrLen != 0) && (*addr != '@')) { 225 addr++; 226 addrLen--; 227 } 228 if(addrLen == 0) { 229 tpPolicyError("tpNormalizeAddrSpec: bad addr-spec"); 230 return; 231 } 232 } 233 tpToLower(addr, addrLen); 234} 235 236/*** 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. 241 ***/ 242 243/* 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. 247 */ 248#define MAX_DNS_COMP_LEN 128 249 250/* 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. 254 */ 255static bool tpNextDnsComp( 256 const char *inBuf, 257 uint32 &inBufLen, // IN/OUT 258 char *outBuf, // component RETURNED here 259 uint32 &outBufLen) // RETURNED length of component 260{ 261 outBufLen = 0; 262 if(inBufLen == 0) { 263 return false; 264 } 265 266 /* skip over leading '.' */ 267 if(*inBuf == '.') { 268 inBuf++; 269 if(--inBufLen == 0) { 270 return false; 271 } 272 } 273 274 /* copy chars until out of data or next '.' found */ 275 do { 276 if(*inBuf == '.') { 277 break; 278 } 279 *outBuf++ = *inBuf++; 280 inBufLen--; 281 outBufLen++; 282 if(outBufLen >= MAX_DNS_COMP_LEN) { 283 /* abort */ 284 break; 285 } 286 } while(inBufLen != 0); 287 if(outBufLen) { 288 return true; 289 } 290 else { 291 return false; 292 } 293} 294 295/* 296 * Find location of specified substring in given bigstring. Returns 297 * pointer to start of substring in bigstring, else returns NULL. 298 */ 299static const char *tpSubStr( 300 const char *bigstr, 301 uint32 bigstrLen, 302 const char *substr, 303 uint32 substrLen) 304{ 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? */ 310 if(substrLen == 1) { 311 /* don't count on memcmp(a,b,0) */ 312 return bigstr; 313 } 314 if(!memcmp(bigstr+1, substr+1, substrLen - 1)) { 315 return bigstr; 316 } 317 } 318 bigstr++; 319 } 320 return NULL; 321} 322 323/* 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. 327 */ 328static CSSM_BOOL tpCompareComps( 329 const char *hostComp, // no wildcards 330 uint32 hostCompLen, 331 const char *certComp, // wildcards OK here 332 uint32 certCompLen) 333{ 334 const char *endCertComp = certComp + certCompLen; 335 const char *endHostComp = hostComp + hostCompLen; 336 do { 337 /* wild card in cert name? */ 338 const char *wildCard = tpSubStr(certComp, certCompLen, 339 "*", 1); 340 if(wildCard == NULL) { 341 /* no, require perfect literal match right now */ 342 if((hostCompLen == certCompLen) && 343 !memcmp(hostComp, certComp, certCompLen)) { 344 return CSSM_TRUE; 345 } 346 else { 347 return CSSM_FALSE; 348 } 349 } 350 351 if(wildCard != certComp) { 352 /* 353 * Require literal match of hostComp with certComp 354 * up until (but not including) the wildcard 355 */ 356 ptrdiff_t subStrLen = wildCard - certComp; 357 if(subStrLen > hostCompLen) { 358 /* out of host name chars */ 359 return CSSM_FALSE; 360 } 361 if(memcmp(certComp, hostComp, subStrLen)) { 362 return CSSM_FALSE; 363 } 364 /* OK, skip over substring */ 365 hostComp += subStrLen; 366 hostCompLen -= subStrLen; 367 /* start parsing at the wildcard itself */ 368 certComp = wildCard; 369 certCompLen -= subStrLen; 370 continue; 371 } 372 373 /* 374 * Currently looking at a wildcard. 375 * 376 * Find substring in hostComp which matches from the char after 377 * the wildcard up to whichever of these comes next: 378 * 379 * -- end of certComp 380 * -- another wildcard 381 */ 382 wildCard++; 383 if(wildCard == endCertComp) { 384 /* 385 * -- Wild card at end of cert's DNS 386 * -- nothing else to match - rest of hostComp is the wildcard 387 * match 388 * -- done, success 389 */ 390 return CSSM_TRUE; 391 } 392 393 const char *afterSubStr; // in certComp 394 afterSubStr = tpSubStr(wildCard, (uint32)(endCertComp - wildCard), 395 "*", 1); 396 if(afterSubStr == NULL) { 397 /* no more wildcards - use end of certComp */ 398 afterSubStr = endCertComp; 399 } 400 uint32 subStrLen = (uint32)(afterSubStr - wildCard); 401 const char *foundSub = tpSubStr(hostComp, hostCompLen, 402 wildCard, subStrLen); 403 if(foundSub == NULL) { 404 /* No match of explicit chars */ 405 return CSSM_FALSE; 406 } 407 408 /* found it - skip past this substring */ 409 hostComp = foundSub + subStrLen; 410 hostCompLen = (uint32)(endHostComp - hostComp); 411 certComp = afterSubStr; 412 certCompLen = (uint32)(endCertComp - afterSubStr); 413 414 } while((hostCompLen != 0) || (certCompLen != 0)); 415 if((hostCompLen == 0) && (certCompLen == 0)) { 416 return CSSM_TRUE; 417 } 418 else { 419 /* end of one but not the other */ 420 return CSSM_FALSE; 421 } 422} 423 424/* 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. 429 * 430 * The incoming hostname is assumed to have been processed by tpToLower(); 431 * we'll perform that processing on certName here. 432 * 433 * Trailing '.' characters in both host names will be ignored per Radar 3996792. 434 * 435 * Returns CSSM_TRUE on match, else CSSM_FALSE. 436 */ 437CSSM_BOOL tpCompareHostNames( 438 const char *hostName, // spec'd by app, tpToLower'd 439 uint32 hostNameLen, 440 char *certName, // from cert, we tpToLower 441 uint32 certNameLen) 442{ 443 tpToLower(certName, certNameLen); 444 445 /* tolerate optional NULL terminators for both */ 446 if(hostNameLen && (hostName[hostNameLen - 1] == '\0')) { 447 hostNameLen--; 448 } 449 if(certNameLen && (certName[certNameLen - 1] == '\0')) { 450 certNameLen--; 451 } 452 453 if((hostNameLen == 0) || (certNameLen == 0)) { 454 /* trivial case with at least one empty name */ 455 if(hostNameLen == certNameLen) { 456 return CSSM_TRUE; 457 } 458 else { 459 return CSSM_FALSE; 460 } 461 } 462 463 /* trim off trailing dots */ 464 if(hostName[hostNameLen - 1] == '.') { 465 hostNameLen--; 466 } 467 if(certName[certNameLen - 1] == '.') { 468 certNameLen--; 469 } 470 471 /* Case 1: exact match */ 472 if((certNameLen == hostNameLen) && 473 !memcmp(certName, hostName, certNameLen)) { 474 return CSSM_TRUE; 475 } 476 477 /* 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. 481 */ 482 do { 483 /* get next component from each dnsName */ 484 char hostComp[MAX_DNS_COMP_LEN]; 485 char certComp[MAX_DNS_COMP_LEN]; 486 uint32 hostCompLen; 487 uint32 certCompLen; 488 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)"); 496 return CSSM_FALSE; 497 } 498 if(!foundHost) { 499 /* normal successful termination */ 500 return CSSM_TRUE; 501 } 502 503 /* compare individual components */ 504 if(!tpCompareComps(hostComp, hostCompLen, 505 certComp, certCompLen)) { 506 tpPolicyError("tpCompareHostNames: wildcard mismatch (2)"); 507 return CSSM_FALSE; 508 } 509 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.) 513 */ 514 if(*hostName == '.') hostName++; 515 hostName += hostCompLen; 516 if(*certName == '.') certName++; 517 certName += certCompLen; 518 } while(1); 519 /* NOT REACHED */ 520 //assert(0): 521 return CSSM_FALSE; 522} 523 524/* 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). 528 * 529 * Returns CSSM_TRUE on match, else CSSM_FALSE. 530 * 531 * Incoming appEmail string has already been tpNormalizeAddrSpec'd. 532 * We do that for certEmail string here. 533 */ 534CSSM_BOOL tpCompareEmailAddr( 535 const char *appEmail, // spec'd by app, normalized 536 uint32 appEmailLen, 537 char *certEmail, // from cert, we normalize 538 uint32 certEmailLen, 539 bool normalizeAll) // true : lower-case all certEmail characters 540 541{ 542 tpNormalizeAddrSpec(certEmail, certEmailLen, normalizeAll); 543 544 /* tolerate optional NULL terminators for both */ 545 if(appEmailLen > 0 && appEmail[appEmailLen - 1] == '\0') { 546 appEmailLen--; 547 } 548 if(certEmailLen > 0 && certEmail[certEmailLen - 1] == '\0') { 549 certEmailLen--; 550 } 551 if((certEmailLen == appEmailLen) && 552 !memcmp(certEmail, appEmail, certEmailLen)) { 553 return CSSM_TRUE; 554 } 555 else { 556 /* mismatch */ 557 tpPolicyError("tpCompareEmailAddr: app/cert email addrs mismatch"); 558 return CSSM_FALSE; 559 } 560} 561 562/* 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. 567 */ 568int decodeECDSA_SigAlgParams( 569 const CSSM_DATA *params, 570 CSSM_ALGORITHMS *cssmAlg) /* RETURNED */ 571{ 572 SecAsn1CoderRef coder = NULL; 573 if(SecAsn1CoderCreate(&coder)) { 574 tpErrorLog("***Error in SecAsn1CoderCreate()\n"); 575 return -1; 576 } 577 CSSM_X509_ALGORITHM_IDENTIFIER algParams; 578 memset(&algParams, 0, sizeof(algParams)); 579 int ourRtn = 0; 580 bool algFound = false; 581 if(SecAsn1DecodeData(coder, params, kSecAsn1AlgorithmIDTemplate, 582 &algParams)) { 583 tpErrorLog("***Error decoding CSSM_X509_ALGORITHM_IDENTIFIER\n"); 584 ourRtn = -1; 585 goto errOut; 586 } 587 CSSM_ALGORITHMS digestAlg; 588 algFound = cssmOidToAlg(&algParams.algorithm, &digestAlg); 589 if(!algFound) { 590 tpErrorLog("***Unknown algorithm in CSSM_X509_ALGORITHM_IDENTIFIER\n"); 591 ourRtn = -1; 592 goto errOut; 593 } 594 switch(digestAlg) { 595 case CSSM_ALGID_SHA1: 596 *cssmAlg = CSSM_ALGID_SHA1WithECDSA; 597 break; 598 case CSSM_ALGID_SHA224: 599 *cssmAlg = CSSM_ALGID_SHA224WithECDSA; 600 break; 601 case CSSM_ALGID_SHA256: 602 *cssmAlg = CSSM_ALGID_SHA256WithECDSA; 603 break; 604 case CSSM_ALGID_SHA384: 605 *cssmAlg = CSSM_ALGID_SHA384WithECDSA; 606 break; 607 case CSSM_ALGID_SHA512: 608 *cssmAlg = CSSM_ALGID_SHA512WithECDSA; 609 break; 610 default: 611 tpErrorLog("***Unknown algorithm in ECDSA_SigAlgParams\n"); 612 ourRtn = -1; 613 } 614errOut: 615 SecAsn1CoderRelease(coder); 616 return ourRtn; 617} 618 619