]>
Commit | Line | Data |
---|---|---|
b1ab9ed8 | 1 | /* |
d8f41ccd | 2 | * Copyright (c) 2000-2001,2011-2014 Apple Inc. All Rights Reserved. |
427c49bc | 3 | * |
b1ab9ed8 A |
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. | |
427c49bc | 9 | * |
b1ab9ed8 A |
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 | |
b1ab9ed8 A |
21 | */ |
22 | ||
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> | |
31 | ||
427c49bc | 32 | #include "certGroupUtils.h" |
b1ab9ed8 A |
33 | #include "tpdebugging.h" |
34 | #include "tpTime.h" | |
35 | ||
36 | #include <string.h> /* for memcmp */ | |
37 | ||
38 | ||
39 | /* | |
427c49bc | 40 | * Copy one CSSM_DATA to another, mallocing destination. |
b1ab9ed8 A |
41 | */ |
42 | void tpCopyCssmData( | |
43 | Allocator &alloc, | |
44 | const CSSM_DATA *src, | |
45 | CSSM_DATA_PTR dst) | |
46 | { | |
47 | dst->Data = (uint8 *)alloc.malloc(src->Length); | |
48 | dst->Length = src->Length; | |
49 | memmove(dst->Data, src->Data, src->Length); | |
50 | } | |
51 | ||
52 | /* | |
53 | * Malloc a CSSM_DATA, copy another one to it. | |
54 | */ | |
55 | CSSM_DATA_PTR tpMallocCopyCssmData( | |
56 | Allocator &alloc, | |
57 | const CSSM_DATA *src) | |
58 | { | |
59 | CSSM_DATA_PTR dst = (CSSM_DATA_PTR)alloc.malloc(sizeof(CSSM_DATA)); | |
60 | tpCopyCssmData(alloc, src, dst); | |
61 | return dst; | |
62 | } | |
63 | ||
64 | /* | |
65 | * Free the data referenced by a CSSM data, and optionally, the struct itself. | |
66 | */ | |
67 | void tpFreeCssmData( | |
68 | Allocator &alloc, | |
69 | CSSM_DATA_PTR data, | |
70 | CSSM_BOOL freeStruct) | |
71 | { | |
72 | if(data == NULL) { | |
73 | return; | |
74 | } | |
75 | if(data->Length != 0) { | |
76 | tpFree(alloc, data->Data); | |
77 | } | |
78 | if(freeStruct) { | |
79 | tpFree(alloc, data); | |
80 | } | |
81 | else { | |
82 | data->Length = 0; | |
83 | data->Data = NULL; | |
84 | } | |
85 | } | |
86 | ||
87 | /* | |
88 | * Compare two CSSM_DATAs, return CSSM_TRUE if identical. | |
89 | */ | |
90 | CSSM_BOOL tpCompareCssmData( | |
91 | const CSSM_DATA *data1, | |
92 | const CSSM_DATA *data2) | |
427c49bc A |
93 | { |
94 | if((data1 == NULL) || (data1->Data == NULL) || | |
b1ab9ed8 A |
95 | (data2 == NULL) || (data2->Data == NULL) || |
96 | (data1->Length != data2->Length)) { | |
97 | return CSSM_FALSE; | |
98 | } | |
99 | if(data1->Length != data2->Length) { | |
100 | return CSSM_FALSE; | |
101 | } | |
102 | if(memcmp(data1->Data, data2->Data, data1->Length) == 0) { | |
103 | return CSSM_TRUE; | |
104 | } | |
105 | else { | |
106 | return CSSM_FALSE; | |
107 | } | |
108 | } | |
109 | ||
110 | /* | |
111 | * Free memory via specified plugin's app-level allocator | |
112 | */ | |
113 | void tpFreePluginMemory( | |
114 | CSSM_HANDLE hand, | |
115 | void *p) | |
116 | { | |
117 | CSSM_API_MEMORY_FUNCS memFuncs; | |
118 | CSSM_RETURN crtn = CSSM_GetAPIMemoryFunctions(hand, &memFuncs); | |
119 | if(crtn) { | |
120 | tpErrorLog("CSSM_GetAPIMemoryFunctions failure\n"); | |
121 | /* oh well, leak and continue */ | |
122 | return; | |
123 | } | |
124 | memFuncs.free_func(p, memFuncs.AllocRef); | |
125 | } | |
126 | ||
127 | /* | |
128 | * Obtain the public key blob from a cert. | |
129 | */ | |
427c49bc | 130 | CSSM_DATA_PTR tp_CertGetPublicKey( |
b1ab9ed8 A |
131 | TPCertInfo *cert, |
132 | CSSM_DATA_PTR *valueToFree) // used in tp_CertFreePublicKey | |
133 | { | |
134 | CSSM_RETURN crtn; | |
135 | CSSM_DATA_PTR val; | |
136 | CSSM_X509_SUBJECT_PUBLIC_KEY_INFO *keyInfo; | |
427c49bc | 137 | |
b1ab9ed8 A |
138 | *valueToFree = NULL; |
139 | crtn = cert->fetchField(&CSSMOID_X509V1SubjectPublicKeyCStruct, &val); | |
140 | if(crtn) { | |
141 | tpErrorLog("Error on CSSM_CL_CertGetFirstFieldValue(PublicKeyCStruct)\n"); | |
142 | return NULL; | |
143 | } | |
144 | *valueToFree = val; | |
145 | keyInfo = (CSSM_X509_SUBJECT_PUBLIC_KEY_INFO *)val->Data; | |
146 | return &keyInfo->subjectPublicKey; | |
147 | } | |
148 | ||
149 | void tp_CertFreePublicKey( | |
150 | CSSM_CL_HANDLE clHand, | |
151 | CSSM_DATA_PTR value) | |
152 | { | |
153 | CSSM_CL_FreeFieldValue(clHand, &CSSMOID_X509V1SubjectPublicKeyCStruct, value); | |
154 | } | |
155 | ||
156 | /* | |
157 | * Obtain signature algorithm info from a cert. | |
158 | */ | |
427c49bc | 159 | CSSM_X509_ALGORITHM_IDENTIFIER_PTR tp_CertGetAlgId( |
b1ab9ed8 A |
160 | TPCertInfo *cert, |
161 | CSSM_DATA_PTR *valueToFree) // used in tp_CertFreeAlgId | |
162 | { | |
163 | CSSM_RETURN crtn; | |
164 | CSSM_DATA_PTR val; | |
427c49bc | 165 | |
b1ab9ed8 A |
166 | *valueToFree = NULL; |
167 | crtn = cert->fetchField(&CSSMOID_X509V1SignatureAlgorithm, &val); | |
168 | if(crtn) { | |
169 | tpErrorLog("Error on fetchField(CSSMOID_X509V1SignatureAlgorithm)\n"); | |
170 | return NULL; | |
171 | } | |
172 | *valueToFree = val; | |
173 | return (CSSM_X509_ALGORITHM_IDENTIFIER_PTR)val->Data; | |
174 | } | |
175 | ||
176 | void tp_CertFreeAlgId( | |
177 | CSSM_CL_HANDLE clHand, | |
178 | CSSM_DATA_PTR value) | |
179 | { | |
180 | CSSM_CL_FreeFieldValue(clHand, &CSSMOID_X509V1SignatureAlgorithm, value); | |
181 | } | |
182 | ||
183 | /* | |
427c49bc | 184 | * Determine if two certs - passed in encoded form - are equivalent. |
b1ab9ed8 A |
185 | */ |
186 | CSSM_BOOL tp_CompareCerts( | |
187 | const CSSM_DATA *cert1, | |
188 | const CSSM_DATA *cert2) | |
189 | { | |
190 | return tpCompareCssmData(cert1, cert2); | |
191 | } | |
192 | ||
193 | /* | |
194 | * Convert a C string to lower case in place. NULL terminator not needed. | |
195 | */ | |
196 | void tpToLower( | |
197 | char *str, | |
198 | unsigned strLen) | |
199 | { | |
200 | for(unsigned i=0; i<strLen; i++) { | |
201 | *str = tolower(*str); | |
202 | str++; | |
203 | } | |
204 | } | |
205 | ||
206 | /* | |
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 | |
427c49bc | 210 | * (e.g. for iChat). |
b1ab9ed8 A |
211 | */ |
212 | void tpNormalizeAddrSpec( | |
213 | char *addr, | |
214 | unsigned addrLen, | |
215 | bool normalizeAll) | |
216 | { | |
217 | if (addr == NULL) { | |
218 | tpPolicyError("tpNormalizeAddrSpec: bad addr"); | |
219 | return; | |
220 | } | |
221 | if(!normalizeAll) { | |
222 | while((addrLen != 0) && (*addr != '@')) { | |
223 | addr++; | |
224 | addrLen--; | |
225 | } | |
226 | if(addrLen == 0) { | |
227 | tpPolicyError("tpNormalizeAddrSpec: bad addr-spec"); | |
228 | return; | |
229 | } | |
230 | } | |
231 | tpToLower(addr, addrLen); | |
232 | } | |
233 | ||
234 | /*** | |
235 | *** dnsName compare support. | |
427c49bc | 236 | *** Please do not make any changes to this code without talking to |
b1ab9ed8 A |
237 | *** dmitch about updating (if necessary) and running (always) |
238 | *** regression tests which specifically test this logic. | |
239 | ***/ | |
427c49bc | 240 | |
b1ab9ed8 A |
241 | /* |
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 | |
427c49bc | 244 | * for these on the stack, so why not cut some slack. |
b1ab9ed8 A |
245 | */ |
246 | #define MAX_DNS_COMP_LEN 128 | |
247 | ||
248 | /* | |
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. | |
252 | */ | |
253 | static bool tpNextDnsComp( | |
254 | const char *inBuf, | |
255 | uint32 &inBufLen, // IN/OUT | |
256 | char *outBuf, // component RETURNED here | |
257 | uint32 &outBufLen) // RETURNED length of component | |
258 | { | |
259 | outBufLen = 0; | |
260 | if(inBufLen == 0) { | |
261 | return false; | |
262 | } | |
427c49bc | 263 | |
b1ab9ed8 A |
264 | /* skip over leading '.' */ |
265 | if(*inBuf == '.') { | |
266 | inBuf++; | |
267 | if(--inBufLen == 0) { | |
268 | return false; | |
269 | } | |
270 | } | |
427c49bc | 271 | |
b1ab9ed8 A |
272 | /* copy chars until out of data or next '.' found */ |
273 | do { | |
274 | if(*inBuf == '.') { | |
275 | break; | |
276 | } | |
277 | *outBuf++ = *inBuf++; | |
278 | inBufLen--; | |
279 | outBufLen++; | |
280 | if(outBufLen >= MAX_DNS_COMP_LEN) { | |
281 | /* abort */ | |
282 | break; | |
283 | } | |
284 | } while(inBufLen != 0); | |
285 | if(outBufLen) { | |
286 | return true; | |
287 | } | |
288 | else { | |
289 | return false; | |
290 | } | |
291 | } | |
292 | ||
293 | /* | |
294 | * Find location of specified substring in given bigstring. Returns | |
295 | * pointer to start of substring in bigstring, else returns NULL. | |
296 | */ | |
297 | static const char *tpSubStr( | |
298 | const char *bigstr, | |
299 | uint32 bigstrLen, | |
300 | const char *substr, | |
301 | uint32 substrLen) | |
302 | { | |
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? */ | |
308 | if(substrLen == 1) { | |
309 | /* don't count on memcmp(a,b,0) */ | |
310 | return bigstr; | |
311 | } | |
312 | if(!memcmp(bigstr+1, substr+1, substrLen - 1)) { | |
313 | return bigstr; | |
427c49bc | 314 | } |
b1ab9ed8 A |
315 | } |
316 | bigstr++; | |
427c49bc | 317 | } |
b1ab9ed8 A |
318 | return NULL; |
319 | } | |
320 | ||
321 | /* | |
322 | * Compare two DNS components, with full wildcard check. We assume | |
427c49bc | 323 | * that no '.' chars exist (per the processing performed in |
b1ab9ed8 A |
324 | * tpNextDnsComp()). Returns CSSM_TRUE on match, else CSSM_FALSE. |
325 | */ | |
326 | static CSSM_BOOL tpCompareComps( | |
327 | const char *hostComp, // no wildcards | |
427c49bc | 328 | uint32 hostCompLen, |
b1ab9ed8 A |
329 | const char *certComp, // wildcards OK here |
330 | uint32 certCompLen) | |
331 | { | |
332 | const char *endCertComp = certComp + certCompLen; | |
333 | const char *endHostComp = hostComp + hostCompLen; | |
334 | do { | |
335 | /* wild card in cert name? */ | |
336 | const char *wildCard = tpSubStr(certComp, certCompLen, | |
337 | "*", 1); | |
338 | if(wildCard == NULL) { | |
339 | /* no, require perfect literal match right now */ | |
340 | if((hostCompLen == certCompLen) && | |
341 | !memcmp(hostComp, certComp, certCompLen)) { | |
342 | return CSSM_TRUE; | |
343 | } | |
344 | else { | |
345 | return CSSM_FALSE; | |
346 | } | |
347 | } | |
427c49bc | 348 | |
b1ab9ed8 | 349 | if(wildCard != certComp) { |
427c49bc | 350 | /* |
b1ab9ed8 A |
351 | * Require literal match of hostComp with certComp |
352 | * up until (but not including) the wildcard | |
353 | */ | |
427c49bc | 354 | ptrdiff_t subStrLen = wildCard - certComp; |
b1ab9ed8 A |
355 | if(subStrLen > hostCompLen) { |
356 | /* out of host name chars */ | |
357 | return CSSM_FALSE; | |
358 | } | |
359 | if(memcmp(certComp, hostComp, subStrLen)) { | |
360 | return CSSM_FALSE; | |
361 | } | |
362 | /* OK, skip over substring */ | |
363 | hostComp += subStrLen; | |
364 | hostCompLen -= subStrLen; | |
365 | /* start parsing at the wildcard itself */ | |
366 | certComp = wildCard; | |
367 | certCompLen -= subStrLen; | |
368 | continue; | |
369 | } | |
427c49bc | 370 | |
b1ab9ed8 A |
371 | /* |
372 | * Currently looking at a wildcard. | |
373 | * | |
374 | * Find substring in hostComp which matches from the char after | |
375 | * the wildcard up to whichever of these comes next: | |
376 | * | |
427c49bc | 377 | * -- end of certComp |
b1ab9ed8 A |
378 | * -- another wildcard |
379 | */ | |
427c49bc | 380 | wildCard++; |
b1ab9ed8 | 381 | if(wildCard == endCertComp) { |
427c49bc | 382 | /* |
b1ab9ed8 A |
383 | * -- Wild card at end of cert's DNS |
384 | * -- nothing else to match - rest of hostComp is the wildcard | |
385 | * match | |
427c49bc | 386 | * -- done, success |
b1ab9ed8 A |
387 | */ |
388 | return CSSM_TRUE; | |
389 | } | |
427c49bc | 390 | |
b1ab9ed8 | 391 | const char *afterSubStr; // in certComp |
427c49bc | 392 | afterSubStr = tpSubStr(wildCard, (uint32)(endCertComp - wildCard), |
b1ab9ed8 A |
393 | "*", 1); |
394 | if(afterSubStr == NULL) { | |
395 | /* no more wildcards - use end of certComp */ | |
396 | afterSubStr = endCertComp; | |
397 | } | |
427c49bc | 398 | uint32 subStrLen = (uint32)(afterSubStr - wildCard); |
b1ab9ed8 A |
399 | const char *foundSub = tpSubStr(hostComp, hostCompLen, |
400 | wildCard, subStrLen); | |
401 | if(foundSub == NULL) { | |
402 | /* No match of explicit chars */ | |
403 | return CSSM_FALSE; | |
404 | } | |
427c49bc | 405 | |
b1ab9ed8 A |
406 | /* found it - skip past this substring */ |
407 | hostComp = foundSub + subStrLen; | |
427c49bc | 408 | hostCompLen = (uint32)(endHostComp - hostComp); |
b1ab9ed8 | 409 | certComp = afterSubStr; |
427c49bc A |
410 | certCompLen = (uint32)(endCertComp - afterSubStr); |
411 | ||
b1ab9ed8 A |
412 | } while((hostCompLen != 0) || (certCompLen != 0)); |
413 | if((hostCompLen == 0) && (certCompLen == 0)) { | |
414 | return CSSM_TRUE; | |
415 | } | |
416 | else { | |
417 | /* end of one but not the other */ | |
418 | return CSSM_FALSE; | |
419 | } | |
420 | } | |
421 | ||
422 | /* | |
427c49bc | 423 | * Compare hostname, is presented to the TP in |
b1ab9ed8 A |
424 | * CSSM_APPLE_TP_SSL_OPTIONS.ServerName, to a server name obtained |
425 | * from the server's cert (i.e., from subjectAltName or commonName). | |
427c49bc | 426 | * Limited wildcard checking is performed here. |
b1ab9ed8 A |
427 | * |
428 | * The incoming hostname is assumed to have been processed by tpToLower(); | |
427c49bc | 429 | * we'll perform that processing on certName here. |
b1ab9ed8 A |
430 | * |
431 | * Trailing '.' characters in both host names will be ignored per Radar 3996792. | |
432 | * | |
433 | * Returns CSSM_TRUE on match, else CSSM_FALSE. | |
434 | */ | |
435 | CSSM_BOOL tpCompareHostNames( | |
436 | const char *hostName, // spec'd by app, tpToLower'd | |
437 | uint32 hostNameLen, | |
438 | char *certName, // from cert, we tpToLower | |
439 | uint32 certNameLen) | |
440 | { | |
441 | tpToLower(certName, certNameLen); | |
442 | ||
443 | /* tolerate optional NULL terminators for both */ | |
444 | if(hostNameLen && (hostName[hostNameLen - 1] == '\0')) { | |
445 | hostNameLen--; | |
446 | } | |
447 | if(certNameLen && (certName[certNameLen - 1] == '\0')) { | |
448 | certNameLen--; | |
449 | } | |
427c49bc | 450 | |
b1ab9ed8 A |
451 | if((hostNameLen == 0) || (certNameLen == 0)) { |
452 | /* trivial case with at least one empty name */ | |
453 | if(hostNameLen == certNameLen) { | |
454 | return CSSM_TRUE; | |
455 | } | |
456 | else { | |
457 | return CSSM_FALSE; | |
458 | } | |
459 | } | |
427c49bc | 460 | |
b1ab9ed8 A |
461 | /* trim off trailing dots */ |
462 | if(hostName[hostNameLen - 1] == '.') { | |
463 | hostNameLen--; | |
464 | } | |
465 | if(certName[certNameLen - 1] == '.') { | |
466 | certNameLen--; | |
467 | } | |
427c49bc | 468 | |
b1ab9ed8 A |
469 | /* Case 1: exact match */ |
470 | if((certNameLen == hostNameLen) && | |
471 | !memcmp(certName, hostName, certNameLen)) { | |
472 | return CSSM_TRUE; | |
473 | } | |
427c49bc A |
474 | |
475 | /* | |
b1ab9ed8 | 476 | * Case 2: Compare one component at a time, handling wildcards in |
427c49bc | 477 | * cert's server name. The characters implicitly matched by a |
b1ab9ed8 A |
478 | * wildcard span only one component of a dnsName. |
479 | */ | |
480 | do { | |
481 | /* get next component from each dnsName */ | |
482 | char hostComp[MAX_DNS_COMP_LEN]; | |
483 | char certComp[MAX_DNS_COMP_LEN]; | |
484 | uint32 hostCompLen; | |
485 | uint32 certCompLen; | |
427c49bc | 486 | |
b1ab9ed8 A |
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)"); | |
494 | return CSSM_FALSE; | |
495 | } | |
496 | if(!foundHost) { | |
497 | /* normal successful termination */ | |
498 | return CSSM_TRUE; | |
499 | } | |
427c49bc | 500 | |
b1ab9ed8 | 501 | /* compare individual components */ |
427c49bc | 502 | if(!tpCompareComps(hostComp, hostCompLen, |
b1ab9ed8 A |
503 | certComp, certCompLen)) { |
504 | tpPolicyError("tpCompareHostNames: wildcard mismatch (2)"); | |
505 | return CSSM_FALSE; | |
506 | } | |
427c49bc | 507 | |
b1ab9ed8 A |
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.) | |
511 | */ | |
512 | if(*hostName == '.') hostName++; | |
513 | hostName += hostCompLen; | |
514 | if(*certName == '.') certName++; | |
515 | certName += certCompLen; | |
516 | } while(1); | |
517 | /* NOT REACHED */ | |
518 | //assert(0): | |
519 | return CSSM_FALSE; | |
520 | } | |
521 | ||
522 | /* | |
427c49bc | 523 | * Compare email address, is presented to the TP in |
b1ab9ed8 A |
524 | * CSSM_APPLE_TP_SMIME_OPTIONS.SenderEmail, to a string obtained |
525 | * from the sender's cert (i.e., from subjectAltName or Subject DN). | |
526 | * | |
527 | * Returns CSSM_TRUE on match, else CSSM_FALSE. | |
528 | * | |
529 | * Incoming appEmail string has already been tpNormalizeAddrSpec'd. | |
427c49bc | 530 | * We do that for certEmail string here. |
b1ab9ed8 A |
531 | */ |
532 | CSSM_BOOL tpCompareEmailAddr( | |
533 | const char *appEmail, // spec'd by app, normalized | |
534 | uint32 appEmailLen, | |
535 | char *certEmail, // from cert, we normalize | |
536 | uint32 certEmailLen, | |
537 | bool normalizeAll) // true : lower-case all certEmail characters | |
538 | ||
539 | { | |
540 | tpNormalizeAddrSpec(certEmail, certEmailLen, normalizeAll); | |
541 | ||
542 | /* tolerate optional NULL terminators for both */ | |
543 | if(appEmailLen > 0 && appEmail[appEmailLen - 1] == '\0') { | |
544 | appEmailLen--; | |
545 | } | |
546 | if(certEmailLen > 0 && certEmail[certEmailLen - 1] == '\0') { | |
547 | certEmailLen--; | |
548 | } | |
549 | if((certEmailLen == appEmailLen) && | |
550 | !memcmp(certEmail, appEmail, certEmailLen)) { | |
551 | return CSSM_TRUE; | |
552 | } | |
553 | else { | |
554 | /* mismatch */ | |
555 | tpPolicyError("tpCompareEmailAddr: app/cert email addrs mismatch"); | |
556 | return CSSM_FALSE; | |
557 | } | |
558 | } | |
559 | ||
d8f41ccd A |
560 | /* |
561 | * Check whether the provided hostName has a domainName suffix. | |
562 | * This function does not process wildcards, and allows hostName to match | |
563 | * any subdomain level of the provided domainName. | |
564 | * | |
565 | * To match, the last domainNameLen chars of hostName must equal domainName, | |
566 | * and the character immediately preceding domainName in hostName (if any) | |
567 | * must be a dot. This means that domainName 'bar.com' will match hostName | |
568 | * values 'host.bar.com' or 'host.sub.bar.com', but not 'host.foobar.com'. | |
569 | * | |
570 | * The incoming hostname is assumed to have been processed by tpToLower(); | |
571 | * we'll perform that processing on domainName here. | |
572 | * | |
573 | * Trailing '.' characters in both host names will be ignored per Radar 3996792. | |
574 | * | |
575 | * Returns CSSM_TRUE on match, else CSSM_FALSE. | |
576 | */ | |
577 | CSSM_BOOL tpCompareDomainSuffix( | |
578 | const char *hostName, // spec'd by app, tpToLower'd | |
579 | uint32 hostNameLen, | |
580 | char *domainName, // we tpToLower | |
581 | uint32 domainNameLen) | |
582 | { | |
583 | tpToLower(domainName, domainNameLen); | |
584 | ||
585 | /* tolerate optional NULL terminators for both */ | |
586 | if(hostNameLen && (hostName[hostNameLen - 1] == '\0')) { | |
587 | hostNameLen--; | |
588 | } | |
589 | if(domainNameLen && (domainName[domainNameLen - 1] == '\0')) { | |
590 | domainNameLen--; | |
591 | } | |
592 | ||
593 | if((hostNameLen == 0) || (domainNameLen == 0)) { | |
594 | /* trivial case with at least one empty name */ | |
595 | if(hostNameLen == domainNameLen) { | |
596 | return CSSM_TRUE; | |
597 | } | |
598 | else { | |
599 | return CSSM_FALSE; | |
600 | } | |
601 | } | |
602 | ||
603 | /* trim off trailing dots */ | |
604 | if(hostName[hostNameLen - 1] == '.') { | |
605 | hostNameLen--; | |
606 | } | |
607 | if(domainName[domainNameLen - 1] == '.') { | |
608 | domainNameLen--; | |
609 | } | |
610 | ||
611 | /* trim off leading dot in suffix, if present */ | |
612 | if((domainNameLen > 0) && (domainName[0] == '.')) { | |
613 | domainName++; | |
614 | domainNameLen--; | |
615 | } | |
616 | ||
617 | if(hostNameLen < domainNameLen) { | |
618 | return CSSM_FALSE; | |
619 | } | |
620 | ||
621 | if(memcmp(hostName+(hostNameLen-domainNameLen),domainName,domainNameLen)) { | |
622 | return CSSM_FALSE; | |
623 | } | |
624 | ||
625 | /* require a dot prior to domain suffix, unless host == domain */ | |
626 | if(hostNameLen > domainNameLen) { | |
627 | if(hostName[hostNameLen-(domainNameLen+1)] != '.') { | |
628 | return CSSM_FALSE; | |
629 | } | |
630 | } | |
631 | ||
632 | return CSSM_TRUE; | |
633 | } | |
634 | ||
427c49bc | 635 | /* |
b1ab9ed8 | 636 | * Following a CSSMOID_ECDSA_WithSpecified algorithm is an encoded |
949d2ff0 | 637 | * ECDSA_SigAlgParams containing the digest algorithm OID. Decode and return |
b1ab9ed8 A |
638 | * a unified ECDSA/digest alg (e.g. CSSM_ALGID_SHA512WithECDSA). |
639 | * Returns nonzero on error. | |
640 | */ | |
641 | int decodeECDSA_SigAlgParams( | |
642 | const CSSM_DATA *params, | |
643 | CSSM_ALGORITHMS *cssmAlg) /* RETURNED */ | |
644 | { | |
645 | SecAsn1CoderRef coder = NULL; | |
646 | if(SecAsn1CoderCreate(&coder)) { | |
647 | tpErrorLog("***Error in SecAsn1CoderCreate()\n"); | |
648 | return -1; | |
649 | } | |
650 | CSSM_X509_ALGORITHM_IDENTIFIER algParams; | |
651 | memset(&algParams, 0, sizeof(algParams)); | |
652 | int ourRtn = 0; | |
653 | bool algFound = false; | |
654 | if(SecAsn1DecodeData(coder, params, kSecAsn1AlgorithmIDTemplate, | |
655 | &algParams)) { | |
656 | tpErrorLog("***Error decoding CSSM_X509_ALGORITHM_IDENTIFIER\n"); | |
657 | ourRtn = -1; | |
658 | goto errOut; | |
659 | } | |
660 | CSSM_ALGORITHMS digestAlg; | |
661 | algFound = cssmOidToAlg(&algParams.algorithm, &digestAlg); | |
662 | if(!algFound) { | |
663 | tpErrorLog("***Unknown algorithm in CSSM_X509_ALGORITHM_IDENTIFIER\n"); | |
664 | ourRtn = -1; | |
665 | goto errOut; | |
666 | } | |
667 | switch(digestAlg) { | |
668 | case CSSM_ALGID_SHA1: | |
669 | *cssmAlg = CSSM_ALGID_SHA1WithECDSA; | |
670 | break; | |
671 | case CSSM_ALGID_SHA224: | |
672 | *cssmAlg = CSSM_ALGID_SHA224WithECDSA; | |
673 | break; | |
674 | case CSSM_ALGID_SHA256: | |
675 | *cssmAlg = CSSM_ALGID_SHA256WithECDSA; | |
676 | break; | |
677 | case CSSM_ALGID_SHA384: | |
678 | *cssmAlg = CSSM_ALGID_SHA384WithECDSA; | |
679 | break; | |
680 | case CSSM_ALGID_SHA512: | |
681 | *cssmAlg = CSSM_ALGID_SHA512WithECDSA; | |
682 | break; | |
683 | default: | |
684 | tpErrorLog("***Unknown algorithm in ECDSA_SigAlgParams\n"); | |
685 | ourRtn = -1; | |
686 | } | |
687 | errOut: | |
688 | SecAsn1CoderRelease(coder); | |
689 | return ourRtn; | |
690 | } | |
691 |