Annotation of embedaddon/strongswan/src/libstrongswan/plugins/constraints/constraints_validator.c, revision 1.1.1.1
1.1 misho 1: /*
2: * Copyright (C) 2010 Martin Willi
3: * Copyright (C) 2010 revosec AG
4: *
5: * This program is free software; you can redistribute it and/or modify it
6: * under the terms of the GNU General Public License as published by the
7: * Free Software Foundation; either version 2 of the License, or (at your
8: * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9: *
10: * This program is distributed in the hope that it will be useful, but
11: * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12: * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13: * for more details.
14: */
15:
16: #include "constraints_validator.h"
17:
18: #include <utils/debug.h>
19: #include <asn1/asn1.h>
20: #include <collections/linked_list.h>
21: #include <credentials/certificates/x509.h>
22:
23: typedef struct private_constraints_validator_t private_constraints_validator_t;
24:
25: /**
26: * Private data of an constraints_validator_t object.
27: */
28: struct private_constraints_validator_t {
29:
30: /**
31: * Public constraints_validator_t interface.
32: */
33: constraints_validator_t public;
34: };
35:
36: /**
37: * Check pathlen constraint of issuer certificate
38: */
39: static bool check_pathlen(x509_t *issuer, int pathlen)
40: {
41: u_int pathlen_constraint;
42:
43: pathlen_constraint = issuer->get_constraint(issuer, X509_PATH_LEN);
44: if (pathlen_constraint != X509_NO_CONSTRAINT &&
45: pathlen > pathlen_constraint)
46: {
47: DBG1(DBG_CFG, "path length of %d violates constraint of %d",
48: pathlen, pathlen_constraint);
49: return FALSE;
50: }
51: return TRUE;
52: }
53:
54: /**
55: * Check if a FQDN constraint matches
56: */
57: static bool fqdn_matches(identification_t *constraint, identification_t *id)
58: {
59: chunk_t c, i, diff;
60:
61: c = constraint->get_encoding(constraint);
62: i = id->get_encoding(id);
63:
64: if (!c.len || i.len < c.len)
65: {
66: return FALSE;
67: }
68: diff = chunk_create(i.ptr, i.len - c.len);
69: if (!chunk_equals(c, chunk_skip(i, diff.len)))
70: {
71: return FALSE;
72: }
73: if (!diff.len)
74: {
75: return TRUE;
76: }
77: if (c.ptr[0] == '.' || diff.ptr[diff.len - 1] == '.')
78: {
79: return TRUE;
80: }
81: return FALSE;
82: }
83:
84: /**
85: * Check if a RFC822 constraint matches
86: */
87: static bool email_matches(identification_t *constraint, identification_t *id)
88: {
89: chunk_t c, i, diff;
90:
91: c = constraint->get_encoding(constraint);
92: i = id->get_encoding(id);
93:
94: if (!c.len || i.len < c.len)
95: {
96: return FALSE;
97: }
98: if (memchr(c.ptr, '@', c.len))
99: { /* constraint is a full email address */
100: return chunk_equals(c, i);
101: }
102: diff = chunk_create(i.ptr, i.len - c.len);
103: if (!diff.len || !chunk_equals(c, chunk_skip(i, diff.len)))
104: {
105: return FALSE;
106: }
107: if (c.ptr[0] == '.')
108: { /* constraint is domain, suffix match */
109: return TRUE;
110: }
111: if (diff.ptr[diff.len - 1] == '@')
112: { /* constraint is host specific, only username can be appended */
113: return TRUE;
114: }
115: return FALSE;
116: }
117:
118: /**
119: * Check if a DN constraint matches (RDN prefix match)
120: */
121: static bool dn_matches(identification_t *constraint, identification_t *id)
122: {
123: enumerator_t *ec, *ei;
124: id_part_t pc, pi;
125: chunk_t cc, ci;
126: bool match = TRUE;
127:
128: ec = constraint->create_part_enumerator(constraint);
129: ei = id->create_part_enumerator(id);
130: while (ec->enumerate(ec, &pc, &cc))
131: {
132: if (!ei->enumerate(ei, &pi, &ci) ||
133: pi != pc || !chunk_equals(cc, ci))
134: {
135: match = FALSE;
136: break;
137: }
138: }
139: ec->destroy(ec);
140: ei->destroy(ei);
141:
142: return match;
143: }
144:
145: /**
146: * Check if a certificate matches to a NameConstraint
147: */
148: static bool name_constraint_matches(identification_t *constraint,
149: certificate_t *cert, bool permitted)
150: {
151: x509_t *x509 = (x509_t*)cert;
152: enumerator_t *enumerator;
153: identification_t *id;
154: id_type_t type;
155: bool matches = permitted;
156:
157: type = constraint->get_type(constraint);
158: if (type == ID_DER_ASN1_DN)
159: {
160: matches = dn_matches(constraint, cert->get_subject(cert));
161: if (matches != permitted)
162: {
163: return matches;
164: }
165: }
166:
167: enumerator = x509->create_subjectAltName_enumerator(x509);
168: while (enumerator->enumerate(enumerator, &id))
169: {
170: if (id->get_type(id) == type)
171: {
172: switch (type)
173: {
174: case ID_FQDN:
175: matches = fqdn_matches(constraint, id);
176: break;
177: case ID_RFC822_ADDR:
178: matches = email_matches(constraint, id);
179: break;
180: case ID_DER_ASN1_DN:
181: matches = dn_matches(constraint, id);
182: break;
183: default:
184: DBG1(DBG_CFG, "%N NameConstraint matching not implemented",
185: id_type_names, type);
186: matches = FALSE;
187: break;
188: }
189: }
190: if (matches != permitted)
191: {
192: break;
193: }
194: }
195: enumerator->destroy(enumerator);
196:
197: return matches;
198: }
199:
200: /**
201: * Check if a permitted or excluded NameConstraint has been inherited to sub-CA
202: */
203: static bool name_constraint_inherited(identification_t *constraint,
204: x509_t *x509, bool permitted)
205: {
206: enumerator_t *enumerator;
207: identification_t *id, *a, *b;
208: bool inherited = FALSE;
209: id_type_t type;
210:
211: if (!(x509->get_flags(x509) & X509_CA))
212: { /* not a sub-CA, not required */
213: return TRUE;
214: }
215:
216: type = constraint->get_type(constraint);
217: enumerator = x509->create_name_constraint_enumerator(x509, permitted);
218: while (enumerator->enumerate(enumerator, &id))
219: {
220: if (id->get_type(id) == type)
221: {
222: if (permitted)
223: { /* permitted constraint can be narrowed */
224: a = constraint;
225: b = id;
226: }
227: else
228: { /* excluded constraint can be widened */
229: a = id;
230: b = constraint;
231: }
232: switch (type)
233: {
234: case ID_FQDN:
235: inherited = fqdn_matches(a, b);
236: break;
237: case ID_RFC822_ADDR:
238: inherited = email_matches(a, b);
239: break;
240: case ID_DER_ASN1_DN:
241: inherited = dn_matches(a, b);
242: break;
243: default:
244: DBG1(DBG_CFG, "%N NameConstraint matching not implemented",
245: id_type_names, type);
246: inherited = FALSE;
247: break;
248: }
249: }
250: if (inherited)
251: {
252: break;
253: }
254: }
255: enumerator->destroy(enumerator);
256: return inherited;
257: }
258:
259: /**
260: * Check name constraints
261: */
262: static bool check_name_constraints(certificate_t *subject, x509_t *issuer)
263: {
264: enumerator_t *enumerator;
265: identification_t *constraint;
266:
267: enumerator = issuer->create_name_constraint_enumerator(issuer, TRUE);
268: while (enumerator->enumerate(enumerator, &constraint))
269: {
270: if (!name_constraint_matches(constraint, subject, TRUE))
271: {
272: DBG1(DBG_CFG, "certificate '%Y' does not match permitted name "
273: "constraint '%Y'", subject->get_subject(subject), constraint);
274: enumerator->destroy(enumerator);
275: return FALSE;
276: }
277: if (!name_constraint_inherited(constraint, (x509_t*)subject, TRUE))
278: {
279: DBG1(DBG_CFG, "intermediate CA '%Y' does not inherit permitted name "
280: "constraint '%Y'", subject->get_subject(subject), constraint);
281: enumerator->destroy(enumerator);
282: return FALSE;
283: }
284: }
285: enumerator->destroy(enumerator);
286:
287: enumerator = issuer->create_name_constraint_enumerator(issuer, FALSE);
288: while (enumerator->enumerate(enumerator, &constraint))
289: {
290: if (name_constraint_matches(constraint, subject, FALSE))
291: {
292: DBG1(DBG_CFG, "certificate '%Y' matches excluded name "
293: "constraint '%Y'", subject->get_subject(subject), constraint);
294: enumerator->destroy(enumerator);
295: return FALSE;
296: }
297: if (!name_constraint_inherited(constraint, (x509_t*)subject, FALSE))
298: {
299: DBG1(DBG_CFG, "intermediate CA '%Y' does not inherit excluded name "
300: "constraint '%Y'", subject->get_subject(subject), constraint);
301: enumerator->destroy(enumerator);
302: return FALSE;
303: }
304: }
305: enumerator->destroy(enumerator);
306: return TRUE;
307: }
308:
309: /**
310: * Special OID for anyPolicy
311: */
312: static chunk_t any_policy = chunk_from_chars(0x55,0x1d,0x20,0x00);
313:
314: /**
315: * Check if an issuer certificate has a given policy OID
316: */
317: static bool has_policy(x509_t *issuer, chunk_t oid)
318: {
319: x509_policy_mapping_t *mapping;
320: x509_cert_policy_t *policy;
321: enumerator_t *enumerator;
322:
323: enumerator = issuer->create_cert_policy_enumerator(issuer);
324: while (enumerator->enumerate(enumerator, &policy))
325: {
326: if (chunk_equals(oid, policy->oid) ||
327: chunk_equals(any_policy, policy->oid))
328: {
329: enumerator->destroy(enumerator);
330: return TRUE;
331: }
332: }
333: enumerator->destroy(enumerator);
334:
335: /* fall back to a mapped policy */
336: enumerator = issuer->create_policy_mapping_enumerator(issuer);
337: while (enumerator->enumerate(enumerator, &mapping))
338: {
339: if (chunk_equals(mapping->subject, oid))
340: {
341: enumerator->destroy(enumerator);
342: return TRUE;
343: }
344: }
345: enumerator->destroy(enumerator);
346: return FALSE;
347: }
348:
349: /**
350: * Check certificatePolicies.
351: */
352: static bool check_policy(x509_t *subject, x509_t *issuer)
353: {
354: certificate_t *cert = (certificate_t*)subject;
355: x509_policy_mapping_t *mapping;
356: x509_cert_policy_t *policy;
357: enumerator_t *enumerator;
358: char *oid;
359:
360: /* verify if policyMappings in subject are valid */
361: enumerator = subject->create_policy_mapping_enumerator(subject);
362: while (enumerator->enumerate(enumerator, &mapping))
363: {
364: if (!has_policy(issuer, mapping->issuer))
365: {
366: oid = asn1_oid_to_string(mapping->issuer);
367: DBG1(DBG_CFG, "certificate '%Y' maps policy from %s, but issuer "
368: "misses it", cert->get_subject(cert), oid);
369: free(oid);
370: enumerator->destroy(enumerator);
371: return FALSE;
372: }
373: }
374: enumerator->destroy(enumerator);
375:
376: enumerator = subject->create_cert_policy_enumerator(subject);
377: while (enumerator->enumerate(enumerator, &policy))
378: {
379: if (!has_policy(issuer, policy->oid))
380: {
381: oid = asn1_oid_to_string(policy->oid);
382: DBG1(DBG_CFG, "policy %s missing in issuing certificate '%Y'",
383: oid, cert->get_issuer(cert));
384: free(oid);
385: enumerator->destroy(enumerator);
386: return FALSE;
387: }
388: }
389: enumerator->destroy(enumerator);
390:
391: return TRUE;
392: }
393:
394: /**
395: * Check if a given policy is valid under a trustchain
396: */
397: static bool is_policy_valid(linked_list_t *chain, chunk_t oid)
398: {
399: x509_policy_mapping_t *mapping;
400: x509_cert_policy_t *policy;
401: x509_t *issuer;
402: enumerator_t *issuers, *policies, *mappings;
403: bool found = TRUE;
404:
405: issuers = chain->create_enumerator(chain);
406: while (issuers->enumerate(issuers, &issuer))
407: {
408: int maxmap = 8;
409:
410: while (found)
411: {
412: found = FALSE;
413:
414: policies = issuer->create_cert_policy_enumerator(issuer);
415: while (policies->enumerate(policies, &policy))
416: {
417: if (chunk_equals(oid, policy->oid) ||
418: chunk_equals(any_policy, policy->oid))
419: {
420: found = TRUE;
421: break;
422: }
423: }
424: policies->destroy(policies);
425: if (found)
426: {
427: break;
428: }
429: /* fall back to a mapped policy */
430: mappings = issuer->create_policy_mapping_enumerator(issuer);
431: while (mappings->enumerate(mappings, &mapping))
432: {
433: if (chunk_equals(mapping->subject, oid))
434: {
435: oid = mapping->issuer;
436: found = TRUE;
437: break;
438: }
439: }
440: mappings->destroy(mappings);
441: if (--maxmap == 0)
442: {
443: found = FALSE;
444: break;
445: }
446: }
447: if (!found)
448: {
449: break;
450: }
451: }
452: issuers->destroy(issuers);
453:
454: return found;
455: }
456:
457: /**
458: * Check len certificates in trustchain for inherited policies
459: */
460: static bool has_policy_chain(linked_list_t *chain, x509_t *subject, int len)
461: {
462: enumerator_t *enumerator;
463: x509_t *issuer;
464: bool valid = TRUE;
465:
466: enumerator = chain->create_enumerator(chain);
467: while (len-- > 0 && enumerator->enumerate(enumerator, &issuer))
468: {
469: if (!check_policy(subject, issuer))
470: {
471: valid = FALSE;
472: break;
473: }
474: subject = issuer;
475: }
476: enumerator->destroy(enumerator);
477: return valid;
478: }
479:
480: /**
481: * Check len certificates in trustchain to have no policyMappings
482: */
483: static bool has_no_policy_mapping(linked_list_t *chain, int len)
484: {
485: enumerator_t *enumerator, *mappings;
486: x509_policy_mapping_t *mapping;
487: certificate_t *cert;
488: x509_t *x509;
489: bool valid = TRUE;
490:
491: enumerator = chain->create_enumerator(chain);
492: while (len-- > 0 && enumerator->enumerate(enumerator, &x509))
493: {
494: mappings = x509->create_policy_mapping_enumerator(x509);
495: valid = !mappings->enumerate(mappings, &mapping);
496: mappings->destroy(mappings);
497: if (!valid)
498: {
499: cert = (certificate_t*)x509;
500: DBG1(DBG_CFG, "found policyMapping in certificate '%Y', but "
501: "inhibitPolicyMapping in effect", cert->get_subject(cert));
502: break;
503: }
504: }
505: enumerator->destroy(enumerator);
506: return valid;
507: }
508:
509: /**
510: * Check len certificates in trustchain to have no anyPolicies
511: */
512: static bool has_no_any_policy(linked_list_t *chain, int len)
513: {
514: enumerator_t *enumerator, *policies;
515: x509_cert_policy_t *policy;
516: certificate_t *cert;
517: x509_t *x509;
518: bool valid = TRUE;
519:
520: enumerator = chain->create_enumerator(chain);
521: while (len-- > 0 && enumerator->enumerate(enumerator, &x509))
522: {
523: policies = x509->create_cert_policy_enumerator(x509);
524: while (policies->enumerate(policies, &policy))
525: {
526: if (chunk_equals(policy->oid, any_policy))
527: {
528: cert = (certificate_t*)x509;
529: DBG1(DBG_CFG, "found anyPolicy in certificate '%Y', but "
530: "inhibitAnyPolicy in effect", cert->get_subject(cert));
531: valid = FALSE;
532: break;
533: }
534: }
535: policies->destroy(policies);
536: }
537: enumerator->destroy(enumerator);
538: return valid;
539: }
540:
541: /**
542: * Check requireExplicitPolicy and inhibitPolicyMapping constraints
543: */
544: static bool check_policy_constraints(x509_t *issuer, u_int pathlen,
545: auth_cfg_t *auth)
546: {
547: certificate_t *subject;
548: bool valid = TRUE;
549:
550: subject = auth->get(auth, AUTH_RULE_SUBJECT_CERT);
551: if (subject)
552: {
553: if (subject->get_type(subject) == CERT_X509)
554: {
555: x509_cert_policy_t *policy;
556: enumerator_t *enumerator;
557: linked_list_t *chain;
558: certificate_t *cert;
559: auth_rule_t rule;
560: x509_t *x509;
561: int len = 0;
562: u_int expl, inh;
563: char *oid;
564:
565: /* prepare trustchain to validate */
566: chain = linked_list_create();
567: enumerator = auth->create_enumerator(auth);
568: while (enumerator->enumerate(enumerator, &rule, &cert))
569: {
570: if (rule == AUTH_RULE_IM_CERT &&
571: cert->get_type(cert) == CERT_X509)
572: {
573: chain->insert_last(chain, cert);
574: }
575: }
576: enumerator->destroy(enumerator);
577: chain->insert_last(chain, issuer);
578:
579: /* search for requireExplicitPolicy constraints */
580: enumerator = chain->create_enumerator(chain);
581: while (enumerator->enumerate(enumerator, &x509))
582: {
583: expl = x509->get_constraint(x509, X509_REQUIRE_EXPLICIT_POLICY);
584: if (expl != X509_NO_CONSTRAINT)
585: {
586: if (!has_policy_chain(chain, (x509_t*)subject, len - expl))
587: {
588: valid = FALSE;
589: break;
590: }
591: }
592: len++;
593: }
594: enumerator->destroy(enumerator);
595:
596: /* search for inhibitPolicyMapping/inhibitAnyPolicy constraints */
597: len = 0;
598: chain->insert_first(chain, subject);
599: enumerator = chain->create_enumerator(chain);
600: while (enumerator->enumerate(enumerator, &x509))
601: {
602: inh = x509->get_constraint(x509, X509_INHIBIT_POLICY_MAPPING);
603: if (inh != X509_NO_CONSTRAINT)
604: {
605: if (!has_no_policy_mapping(chain, len - inh))
606: {
607: valid = FALSE;
608: break;
609: }
610: }
611: inh = x509->get_constraint(x509, X509_INHIBIT_ANY_POLICY);
612: if (inh != X509_NO_CONSTRAINT)
613: {
614: if (!has_no_any_policy(chain, len - inh))
615: {
616: valid = FALSE;
617: break;
618: }
619: }
620: len++;
621: }
622: enumerator->destroy(enumerator);
623:
624: if (valid)
625: {
626: x509 = (x509_t*)subject;
627:
628: enumerator = x509->create_cert_policy_enumerator(x509);
629: while (enumerator->enumerate(enumerator, &policy))
630: {
631: oid = asn1_oid_to_string(policy->oid);
632: if (oid)
633: {
634: if (is_policy_valid(chain, policy->oid))
635: {
636: auth->add(auth, AUTH_RULE_CERT_POLICY, oid);
637: }
638: else
639: {
640: DBG1(DBG_CFG, "certificate policy %s for '%Y' "
641: "not allowed by trustchain, ignored",
642: oid, subject->get_subject(subject));
643: free(oid);
644: }
645: }
646: }
647: enumerator->destroy(enumerator);
648: }
649: chain->destroy(chain);
650: }
651: }
652: return valid;
653: }
654:
655: METHOD(cert_validator_t, validate, bool,
656: private_constraints_validator_t *this, certificate_t *subject,
657: certificate_t *issuer, bool online, u_int pathlen, bool anchor,
658: auth_cfg_t *auth)
659: {
660: if (issuer->get_type(issuer) == CERT_X509 &&
661: subject->get_type(subject) == CERT_X509)
662: {
663: if (!check_pathlen((x509_t*)issuer, pathlen))
664: {
665: lib->credmgr->call_hook(lib->credmgr, CRED_HOOK_EXCEEDED_PATH_LEN,
666: subject);
667: return FALSE;
668: }
669: if (!check_name_constraints(subject, (x509_t*)issuer))
670: {
671: lib->credmgr->call_hook(lib->credmgr, CRED_HOOK_POLICY_VIOLATION,
672: subject);
673: return FALSE;
674: }
675: if (anchor)
676: {
677: if (!check_policy_constraints((x509_t*)issuer, pathlen, auth))
678: {
679: lib->credmgr->call_hook(lib->credmgr,
680: CRED_HOOK_POLICY_VIOLATION, issuer);
681: return FALSE;
682: }
683: }
684: }
685: return TRUE;
686: }
687:
688: METHOD(constraints_validator_t, destroy, void,
689: private_constraints_validator_t *this)
690: {
691: free(this);
692: }
693:
694: /**
695: * See header
696: */
697: constraints_validator_t *constraints_validator_create()
698: {
699: private_constraints_validator_t *this;
700:
701: INIT(this,
702: .public = {
703: .validator.validate = _validate,
704: .destroy = _destroy,
705: },
706: );
707:
708: return &this->public;
709: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>