]> git.saurik.com Git - apple/security.git/blob - AppleX509TP/certGroupUtils.cpp
Security-177.tar.gz
[apple/security.git] / AppleX509TP / certGroupUtils.cpp
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
32 #include "certGroupUtils.h"
33 #include "tpdebugging.h"
34 #include "tpTime.h"
35
36 #include <string.h> /* for memcmp */
37
38
39 /*
40 * Copy one CSSM_DATA to another, mallocing destination.
41 */
42 void tpCopyCssmData(
43 CssmAllocator &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 CssmAllocator &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 CssmAllocator &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)
93 {
94 if((data1 == NULL) || (data1->Data == NULL) ||
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 */
130 CSSM_DATA_PTR tp_CertGetPublicKey(
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;
137
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 */
159 CSSM_X509_ALGORITHM_IDENTIFIER_PTR tp_CertGetAlgId(
160 TPCertInfo *cert,
161 CSSM_DATA_PTR *valueToFree) // used in tp_CertFreeAlgId
162 {
163 CSSM_RETURN crtn;
164 CSSM_DATA_PTR val;
165
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 /*
184 * Determine if two certs - passed in encoded form - are equivalent.
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 /*
195 * Given a aignature OID, return the corresponding CSSM_ALGID for the
196 * signature the required key.
197 */
198 CSSM_ALGORITHMS tpOidToAldId(
199 const CSSM_OID *oid,
200 CSSM_ALGORITHMS *keyAlg) // RETURNED
201 {
202 *keyAlg = CSSM_ALGID_RSA; // default
203 if(tpCompareOids(oid, &CSSMOID_MD2WithRSA)) {
204 return CSSM_ALGID_MD2WithRSA;
205 }
206 else if(tpCompareOids(oid, &CSSMOID_MD5WithRSA)) {
207 return CSSM_ALGID_MD5WithRSA;
208 }
209 else if(tpCompareOids(oid, &CSSMOID_SHA1WithRSA)) {
210 return CSSM_ALGID_SHA1WithRSA;
211 }
212 else if(tpCompareOids(oid, &CSSMOID_SHA1WithRSA_OIW)) {
213 return CSSM_ALGID_SHA1WithRSA;
214 }
215 else if(tpCompareOids(oid, &CSSMOID_SHA1WithDSA)) {
216 *keyAlg = CSSM_ALGID_DSA;
217 return CSSM_ALGID_SHA1WithDSA;
218 }
219 else if(tpCompareOids(oid, &CSSMOID_APPLE_FEE_MD5)) {
220 *keyAlg = CSSM_ALGID_FEE;
221 return CSSM_ALGID_FEE_MD5;
222 }
223 else if(tpCompareOids(oid, &CSSMOID_APPLE_FEE_SHA1)) {
224 *keyAlg = CSSM_ALGID_FEE;
225 return CSSM_ALGID_FEE_SHA1;
226 }
227 else if(tpCompareOids(oid, &CSSMOID_APPLE_ECDSA)) {
228 *keyAlg = CSSM_ALGID_FEE;
229 return CSSM_ALGID_SHA1WithECDSA;
230 }
231 else {
232 *keyAlg = CSSM_ALGID_NONE;
233 return CSSM_ALGID_NONE;
234 }
235 }
236
237 /*
238 * Convert a C string to lower case in place. NULL terminator not needed.
239 */
240 void tpToLower(
241 char *str,
242 unsigned strLen)
243 {
244 for(unsigned i=0; i<strLen; i++) {
245 *str = tolower(*str);
246 str++;
247 }
248 }
249
250 /*
251 * Normalize an RFC822 addr-spec. This consists of converting
252 * all characters following the '@' character to lower case.
253 */
254 void tpNormalizeAddrSpec(
255 char *addr,
256 unsigned addrLen)
257 {
258 while((*addr != '@') && (addrLen != 0)) {
259 addr++;
260 addrLen--;
261 }
262 if(addrLen == 0) {
263 tpPolicyError("tpNormalizeAddrSpec: bad addr-spec");
264 return;
265 }
266 tpToLower(addr, addrLen);
267 }
268
269 /***
270 *** dnsName compare support.
271 *** Please do not make any changes to this code without talking to
272 *** dmitch about updating (if necessary) and running (always)
273 *** regression tests which specifically test this logic.
274 ***/
275
276 /*
277 * Max length of a distinguished name component (label) we handle.
278 * Various RFCs spec this out at 63 bytes; we're just allocating space
279 * for these on the stack, so why not cut some slack.
280 */
281 #define MAX_DNS_COMP_LEN 128
282
283 /*
284 * Obtain the next component from a DNS Name.
285 * Caller mallocs outBuf, size >= MAX_DNS_COMP_LEN.
286 * Returns true if a component was found.
287 */
288 static bool tpNextDnsComp(
289 const char *inBuf,
290 uint32 &inBufLen, // IN/OUT
291 char *outBuf, // component RETURNED here
292 uint32 &outBufLen) // RETURNED length of component
293 {
294 outBufLen = 0;
295 if(inBufLen == 0) {
296 return false;
297 }
298
299 /* skip over leading '.' */
300 if(*inBuf == '.') {
301 inBuf++;
302 if(--inBufLen == 0) {
303 return false;
304 }
305 }
306
307 /* copy chars until out of data or next '.' found */
308 do {
309 if(*inBuf == '.') {
310 break;
311 }
312 *outBuf++ = *inBuf++;
313 inBufLen--;
314 outBufLen++;
315 if(outBufLen >= MAX_DNS_COMP_LEN) {
316 /* abort */
317 break;
318 }
319 } while(inBufLen != 0);
320 if(outBufLen) {
321 return true;
322 }
323 else {
324 return false;
325 }
326 }
327
328 /*
329 * Find location of specified substring in given bigstring. Returns
330 * pointer to start of substring in bigstring, else returns NULL.
331 */
332 static const char *tpSubStr(
333 const char *bigstr,
334 uint32 bigstrLen,
335 const char *substr,
336 uint32 substrLen)
337 {
338 /* stop searching substrLen chars before end of bigstr */
339 const char *endBigStr = bigstr + bigstrLen - substrLen;
340 for( ; bigstr <= endBigStr; ) {
341 if(*bigstr == *substr) {
342 /* first char match - remainder? */
343 if(substrLen == 1) {
344 /* don't count on memcmp(a,b,0) */
345 return bigstr;
346 }
347 if(!memcmp(bigstr+1, substr+1, substrLen - 1)) {
348 return bigstr;
349 }
350 }
351 bigstr++;
352 }
353 return NULL;
354 }
355
356 /*
357 * Compare two DNS components, with full wildcard check. We assume
358 * that no '.' chars exist (per the processing performed in
359 * tpNextDnsComp()). Returns CSSM_TRUE on match, else CSSM_FALSE.
360 */
361 static CSSM_BOOL tpCompareComps(
362 const char *hostComp, // no wildcards
363 uint32 hostCompLen,
364 const char *certComp, // wildcards OK here
365 uint32 certCompLen)
366 {
367 const char *endCertComp = certComp + certCompLen;
368 const char *endHostComp = hostComp + hostCompLen;
369 do {
370 /* wild card in cert name? */
371 const char *wildCard = tpSubStr(certComp, certCompLen,
372 "*", 1);
373 if(wildCard == NULL) {
374 /* no, require perfect literal match right now */
375 if((hostCompLen == certCompLen) &&
376 !memcmp(hostComp, certComp, certCompLen)) {
377 return CSSM_TRUE;
378 }
379 else {
380 return CSSM_FALSE;
381 }
382 }
383
384 if(wildCard != certComp) {
385 /*
386 * Require literal match of hostComp with certComp
387 * up until (but not including) the wildcard
388 */
389 uint32 subStrLen = wildCard - certComp;
390 if(subStrLen > hostCompLen) {
391 /* out of host name chars */
392 return CSSM_FALSE;
393 }
394 if(memcmp(certComp, hostComp, subStrLen)) {
395 return CSSM_FALSE;
396 }
397 /* OK, skip over substring */
398 hostComp += subStrLen;
399 hostCompLen -= subStrLen;
400 /* start parsing at the wildcard itself */
401 certComp = wildCard;
402 certCompLen -= subStrLen;
403 continue;
404 }
405
406 /*
407 * Currently looking at a wildcard.
408 *
409 * Find substring in hostComp which matches from the char after
410 * the wildcard up to whichever of these comes next:
411 *
412 * -- end of certComp
413 * -- another wildcard
414 */
415 wildCard++;
416 if(wildCard == endCertComp) {
417 /*
418 * -- Wild card at end of cert's DNS
419 * -- nothing else to match - rest of hostComp is the wildcard
420 * match
421 * -- done, success
422 */
423 return CSSM_TRUE;
424 }
425
426 const char *afterSubStr; // in certComp
427 afterSubStr = tpSubStr(wildCard, endCertComp - wildCard,
428 "*", 1);
429 if(afterSubStr == NULL) {
430 /* no more wildcards - use end of certComp */
431 afterSubStr = endCertComp;
432 }
433 uint32 subStrLen = afterSubStr - wildCard;
434 const char *foundSub = tpSubStr(hostComp, hostCompLen,
435 wildCard, subStrLen);
436 if(foundSub == NULL) {
437 /* No match of explicit chars */
438 return CSSM_FALSE;
439 }
440
441 /* found it - skip past this substring */
442 hostComp = foundSub + subStrLen;
443 hostCompLen = endHostComp - hostComp;
444 certComp = afterSubStr;
445 certCompLen = endCertComp - afterSubStr;
446
447 } while((hostCompLen != 0) || (certCompLen != 0));
448 if((hostCompLen == 0) && (certCompLen == 0)) {
449 return CSSM_TRUE;
450 }
451 else {
452 /* end of one but not the other */
453 return CSSM_FALSE;
454 }
455 }
456
457 /*
458 * Compare hostname, is presented to the TP in
459 * CSSM_APPLE_TP_SSL_OPTIONS.ServerName, to a server name obtained
460 * from the server's cert (i.e., from subjectAltName or commonName).
461 * Limited wildcard checking is performed here.
462 *
463 * The incoming hostname is assumed to have been processed by tpToLower();
464 * we'll perform that processing on certName here.
465 *
466 * Returns CSSM_TRUE on match, else CSSM_FALSE.
467 */
468 CSSM_BOOL tpCompareHostNames(
469 const char *hostName, // spec'd by app, tpToLower'd
470 uint32 hostNameLen,
471 char *certName, // from cert, we tpToLower
472 uint32 certNameLen)
473 {
474 tpToLower(certName, certNameLen);
475
476 /* tolerate optional NULL terminators for both */
477 if(hostName[hostNameLen - 1] == '\0') {
478 hostNameLen--;
479 }
480 if(certName[certNameLen - 1] == '\0') {
481 certNameLen--;
482 }
483
484 /* case 1: exact match */
485 if((certNameLen == hostNameLen) &&
486 !memcmp(certName, hostName, certNameLen)) {
487 return CSSM_TRUE;
488 }
489
490 /*
491 * Case 2: Compare one component at a time, handling wildcards in
492 * cert's server name. The characters implicitly matched by a
493 * wildcard span only one component of a dnsName.
494 */
495 do {
496 /* get next component from each dnsName */
497 char hostComp[MAX_DNS_COMP_LEN];
498 char certComp[MAX_DNS_COMP_LEN];
499 uint32 hostCompLen;
500 uint32 certCompLen;
501
502 bool foundHost = tpNextDnsComp(hostName, hostNameLen,
503 hostComp, hostCompLen);
504 bool foundCert = tpNextDnsComp(certName, certNameLen,
505 certComp, certCompLen);
506 if(foundHost != foundCert) {
507 /* unequal number of components */
508 tpPolicyError("tpCompareHostNames: wildcard mismatch (1)");
509 return CSSM_FALSE;
510 }
511 if(!foundHost) {
512 /* normal successful termination */
513 return CSSM_TRUE;
514 }
515
516 /* compare individual components */
517 if(!tpCompareComps(hostComp, hostCompLen,
518 certComp, certCompLen)) {
519 tpPolicyError("tpCompareHostNames: wildcard mismatch (2)");
520 return CSSM_FALSE;
521 }
522
523 /* skip over this component */
524 hostName += hostCompLen;
525 certName += certCompLen;
526 } while(1);
527 /* NOT REACHED */
528 //assert(0):
529 return CSSM_FALSE;
530 }
531
532 /*
533 * Compare email address, is presented to the TP in
534 * CSSM_APPLE_TP_SMIME_OPTIONS.SenderEmail, to a string obtained
535 * from the sender's cert (i.e., from subjectAltName or Subject DN).
536 *
537 * Returns CSSM_TRUE on match, else CSSM_FALSE.
538 *
539 * Incomiong appEmail string has already been tpNormalizeAddrSpec'd.
540 * We do that for certEmail string here.
541 */
542 CSSM_BOOL tpCompareEmailAddr(
543 const char *appEmail, // spec'd by app, normalized
544 uint32 appEmailLen,
545 char *certEmail, // from cert, we normalize
546 uint32 certEmailLen)
547 {
548 tpNormalizeAddrSpec(certEmail, certEmailLen);
549
550 /* tolerate optional NULL terminators for both */
551 if(appEmail[appEmailLen - 1] == '\0') {
552 appEmailLen--;
553 }
554 if(certEmail[certEmailLen - 1] == '\0') {
555 certEmailLen--;
556 }
557 if((certEmailLen == appEmailLen) &&
558 !memcmp(certEmail, appEmail, certEmailLen)) {
559 return CSSM_TRUE;
560 }
561 else {
562 /* mismatch */
563 tpPolicyError("tpCompareEmailAddr: app/cert email addrs mismatch");
564 return CSSM_FALSE;
565 }
566 }