Moved data structures to new collections subfolder
[strongswan.git] / src / libstrongswan / plugins / constraints / constraints_validator.c
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 <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/RFC822 constraint matches (suffix match)
56 */
57 static bool suffix_matches(identification_t *constraint, identification_t *id)
58 {
59 chunk_t c, i;
60
61 c = constraint->get_encoding(constraint);
62 i = id->get_encoding(id);
63
64 return i.len >= c.len && chunk_equals(c, chunk_skip(i, i.len - c.len));
65 }
66
67 /**
68 * Check if a DN constraint matches (RDN prefix match)
69 */
70 static bool dn_matches(identification_t *constraint, identification_t *id)
71 {
72 enumerator_t *ec, *ei;
73 id_part_t pc, pi;
74 chunk_t cc, ci;
75 bool match = TRUE;
76
77 ec = constraint->create_part_enumerator(constraint);
78 ei = id->create_part_enumerator(id);
79 while (ec->enumerate(ec, &pc, &cc))
80 {
81 if (!ei->enumerate(ei, &pi, &ci) ||
82 pi != pc || !chunk_equals(cc, ci))
83 {
84 match = FALSE;
85 break;
86 }
87 }
88 ec->destroy(ec);
89 ei->destroy(ei);
90
91 return match;
92 }
93
94 /**
95 * Check if a certificate matches to a NameConstraint
96 */
97 static bool name_constraint_matches(identification_t *constraint,
98 certificate_t *cert, bool permitted)
99 {
100 x509_t *x509 = (x509_t*)cert;
101 enumerator_t *enumerator;
102 identification_t *id;
103 id_type_t type;
104 bool matches = permitted;
105
106 type = constraint->get_type(constraint);
107 if (type == ID_DER_ASN1_DN)
108 {
109 matches = dn_matches(constraint, cert->get_subject(cert));
110 if (matches != permitted)
111 {
112 return matches;
113 }
114 }
115
116 enumerator = x509->create_subjectAltName_enumerator(x509);
117 while (enumerator->enumerate(enumerator, &id))
118 {
119 if (id->get_type(id) == type)
120 {
121 switch (type)
122 {
123 case ID_FQDN:
124 case ID_RFC822_ADDR:
125 matches = suffix_matches(constraint, id);
126 break;
127 case ID_DER_ASN1_DN:
128 matches = dn_matches(constraint, id);
129 break;
130 default:
131 DBG1(DBG_CFG, "%N NameConstraint matching not implemented",
132 id_type_names, type);
133 matches = FALSE;
134 break;
135 }
136 }
137 if (matches != permitted)
138 {
139 break;
140 }
141 }
142 enumerator->destroy(enumerator);
143
144 return matches;
145 }
146
147 /**
148 * Check if a permitted or excluded NameConstraint has been inherited to sub-CA
149 */
150 static bool name_constraint_inherited(identification_t *constraint,
151 x509_t *x509, bool permitted)
152 {
153 enumerator_t *enumerator;
154 identification_t *id;
155 bool inherited = FALSE;
156 id_type_t type;
157
158 if (!(x509->get_flags(x509) & X509_CA))
159 { /* not a sub-CA, not required */
160 return TRUE;
161 }
162
163 type = constraint->get_type(constraint);
164 enumerator = x509->create_name_constraint_enumerator(x509, permitted);
165 while (enumerator->enumerate(enumerator, &id))
166 {
167 if (id->get_type(id) == type)
168 {
169 switch (type)
170 {
171 case ID_FQDN:
172 case ID_RFC822_ADDR:
173 if (permitted)
174 { /* permitted constraint can be narrowed */
175 inherited = suffix_matches(constraint, id);
176 }
177 else
178 { /* excluded constraint can be widened */
179 inherited = suffix_matches(id, constraint);
180 }
181 break;
182 case ID_DER_ASN1_DN:
183 if (permitted)
184 {
185 inherited = dn_matches(constraint, id);
186 }
187 else
188 {
189 inherited = dn_matches(id, constraint);
190 }
191 break;
192 default:
193 DBG1(DBG_CFG, "%N NameConstraint matching not implemented",
194 id_type_names, type);
195 inherited = FALSE;
196 break;
197 }
198 }
199 if (inherited)
200 {
201 break;
202 }
203 }
204 enumerator->destroy(enumerator);
205 return inherited;
206 }
207
208 /**
209 * Check name constraints
210 */
211 static bool check_name_constraints(certificate_t *subject, x509_t *issuer)
212 {
213 enumerator_t *enumerator;
214 identification_t *constraint;
215
216 enumerator = issuer->create_name_constraint_enumerator(issuer, TRUE);
217 while (enumerator->enumerate(enumerator, &constraint))
218 {
219 if (!name_constraint_matches(constraint, subject, TRUE))
220 {
221 DBG1(DBG_CFG, "certificate '%Y' does not match permitted name "
222 "constraint '%Y'", subject->get_subject(subject), constraint);
223 enumerator->destroy(enumerator);
224 return FALSE;
225 }
226 if (!name_constraint_inherited(constraint, (x509_t*)subject, TRUE))
227 {
228 DBG1(DBG_CFG, "intermediate CA '%Y' does not inherit permitted name "
229 "constraint '%Y'", subject->get_subject(subject), constraint);
230 enumerator->destroy(enumerator);
231 return FALSE;
232 }
233 }
234 enumerator->destroy(enumerator);
235
236 enumerator = issuer->create_name_constraint_enumerator(issuer, FALSE);
237 while (enumerator->enumerate(enumerator, &constraint))
238 {
239 if (name_constraint_matches(constraint, subject, FALSE))
240 {
241 DBG1(DBG_CFG, "certificate '%Y' matches excluded name "
242 "constraint '%Y'", subject->get_subject(subject), constraint);
243 enumerator->destroy(enumerator);
244 return FALSE;
245 }
246 if (!name_constraint_inherited(constraint, (x509_t*)subject, FALSE))
247 {
248 DBG1(DBG_CFG, "intermediate CA '%Y' does not inherit excluded name "
249 "constraint '%Y'", subject->get_subject(subject), constraint);
250 enumerator->destroy(enumerator);
251 return FALSE;
252 }
253 }
254 enumerator->destroy(enumerator);
255 return TRUE;
256 }
257
258 /**
259 * Special OID for anyPolicy
260 */
261 static chunk_t any_policy = chunk_from_chars(0x55,0x1d,0x20,0x00);
262
263 /**
264 * Check if an issuer certificate has a given policy OID
265 */
266 static bool has_policy(x509_t *issuer, chunk_t oid)
267 {
268 x509_policy_mapping_t *mapping;
269 x509_cert_policy_t *policy;
270 enumerator_t *enumerator;
271
272 enumerator = issuer->create_cert_policy_enumerator(issuer);
273 while (enumerator->enumerate(enumerator, &policy))
274 {
275 if (chunk_equals(oid, policy->oid) ||
276 chunk_equals(any_policy, policy->oid))
277 {
278 enumerator->destroy(enumerator);
279 return TRUE;
280 }
281 }
282 enumerator->destroy(enumerator);
283
284 /* fall back to a mapped policy */
285 enumerator = issuer->create_policy_mapping_enumerator(issuer);
286 while (enumerator->enumerate(enumerator, &mapping))
287 {
288 if (chunk_equals(mapping->subject, oid))
289 {
290 enumerator->destroy(enumerator);
291 return TRUE;
292 }
293 }
294 enumerator->destroy(enumerator);
295 return FALSE;
296 }
297
298 /**
299 * Check certificatePolicies.
300 */
301 static bool check_policy(x509_t *subject, x509_t *issuer, bool check,
302 auth_cfg_t *auth)
303 {
304 certificate_t *cert = (certificate_t*)subject;
305 x509_policy_mapping_t *mapping;
306 x509_cert_policy_t *policy;
307 enumerator_t *enumerator;
308 char *oid;
309
310 /* verify if policyMappings in subject are valid */
311 enumerator = subject->create_policy_mapping_enumerator(subject);
312 while (enumerator->enumerate(enumerator, &mapping))
313 {
314 if (!has_policy(issuer, mapping->issuer))
315 {
316 oid = asn1_oid_to_string(mapping->issuer);
317 DBG1(DBG_CFG, "certificate '%Y' maps policy from %s, but issuer "
318 "misses it", cert->get_subject(cert), oid);
319 free(oid);
320 enumerator->destroy(enumerator);
321 return FALSE;
322 }
323 }
324 enumerator->destroy(enumerator);
325
326 if (check)
327 {
328 enumerator = subject->create_cert_policy_enumerator(subject);
329 while (enumerator->enumerate(enumerator, &policy))
330 {
331 if (!has_policy(issuer, policy->oid))
332 {
333 oid = asn1_oid_to_string(policy->oid);
334 DBG1(DBG_CFG, "policy %s missing in issuing certificate '%Y'",
335 oid, cert->get_issuer(cert));
336 free(oid);
337 enumerator->destroy(enumerator);
338 return FALSE;
339 }
340 if (auth)
341 {
342 oid = asn1_oid_to_string(policy->oid);
343 if (oid)
344 {
345 auth->add(auth, AUTH_RULE_CERT_POLICY, oid);
346 }
347 }
348 }
349 enumerator->destroy(enumerator);
350 }
351
352 return TRUE;
353 }
354
355 /**
356 * Check len certificates in trustchain for inherited policies
357 */
358 static bool has_policy_chain(linked_list_t *chain, x509_t *subject, int len)
359 {
360 enumerator_t *enumerator;
361 x509_t *issuer;
362 bool valid = TRUE;
363
364 enumerator = chain->create_enumerator(chain);
365 while (len-- > 0 && enumerator->enumerate(enumerator, &issuer))
366 {
367 if (!check_policy(subject, issuer, TRUE, NULL))
368 {
369 valid = FALSE;
370 break;
371 }
372 subject = issuer;
373 }
374 enumerator->destroy(enumerator);
375 return valid;
376 }
377
378 /**
379 * Check len certificates in trustchain to have no policyMappings
380 */
381 static bool has_no_policy_mapping(linked_list_t *chain, int len)
382 {
383 enumerator_t *enumerator, *mappings;
384 x509_policy_mapping_t *mapping;
385 certificate_t *cert;
386 x509_t *x509;
387 bool valid = TRUE;
388
389 enumerator = chain->create_enumerator(chain);
390 while (len-- > 0 && enumerator->enumerate(enumerator, &x509))
391 {
392 mappings = x509->create_policy_mapping_enumerator(x509);
393 valid = !mappings->enumerate(mappings, &mapping);
394 mappings->destroy(mappings);
395 if (!valid)
396 {
397 cert = (certificate_t*)x509;
398 DBG1(DBG_CFG, "found policyMapping in certificate '%Y', but "
399 "inhibitPolicyMapping in effect", cert->get_subject(cert));
400 break;
401 }
402 }
403 enumerator->destroy(enumerator);
404 return valid;
405 }
406
407 /**
408 * Check len certificates in trustchain to have no anyPolicies
409 */
410 static bool has_no_any_policy(linked_list_t *chain, int len)
411 {
412 enumerator_t *enumerator, *policies;
413 x509_cert_policy_t *policy;
414 certificate_t *cert;
415 x509_t *x509;
416 bool valid = TRUE;
417
418 enumerator = chain->create_enumerator(chain);
419 while (len-- > 0 && enumerator->enumerate(enumerator, &x509))
420 {
421 policies = x509->create_cert_policy_enumerator(x509);
422 while (policies->enumerate(policies, &policy))
423 {
424 if (chunk_equals(policy->oid, any_policy))
425 {
426 cert = (certificate_t*)x509;
427 DBG1(DBG_CFG, "found anyPolicy in certificate '%Y', but "
428 "inhibitAnyPolicy in effect", cert->get_subject(cert));
429 valid = FALSE;
430 break;
431 }
432 }
433 policies->destroy(policies);
434 }
435 enumerator->destroy(enumerator);
436 return valid;
437 }
438
439 /**
440 * Check requireExplicitPolicy and inhibitPolicyMapping constraints
441 */
442 static bool check_policy_constraints(x509_t *issuer, u_int pathlen,
443 auth_cfg_t *auth)
444 {
445 certificate_t *subject;
446 bool valid = TRUE;
447
448 subject = auth->get(auth, AUTH_RULE_SUBJECT_CERT);
449 if (subject)
450 {
451 if (subject->get_type(subject) == CERT_X509)
452 {
453 enumerator_t *enumerator;
454 linked_list_t *chain;
455 certificate_t *cert;
456 auth_rule_t rule;
457 x509_t *x509;
458 int len = 0;
459 u_int expl, inh;
460
461 /* prepare trustchain to validate */
462 chain = linked_list_create();
463 enumerator = auth->create_enumerator(auth);
464 while (enumerator->enumerate(enumerator, &rule, &cert))
465 {
466 if (rule == AUTH_RULE_IM_CERT &&
467 cert->get_type(cert) == CERT_X509)
468 {
469 chain->insert_last(chain, cert);
470 }
471 }
472 enumerator->destroy(enumerator);
473 chain->insert_last(chain, issuer);
474
475 /* search for requireExplicitPolicy constraints */
476 enumerator = chain->create_enumerator(chain);
477 while (enumerator->enumerate(enumerator, &x509))
478 {
479 expl = x509->get_constraint(x509, X509_REQUIRE_EXPLICIT_POLICY);
480 if (expl != X509_NO_CONSTRAINT)
481 {
482 if (!has_policy_chain(chain, (x509_t*)subject, len - expl))
483 {
484 valid = FALSE;
485 break;
486 }
487 }
488 len++;
489 }
490 enumerator->destroy(enumerator);
491
492 /* search for inhibitPolicyMapping/inhibitAnyPolicy constraints */
493 len = 0;
494 chain->insert_first(chain, subject);
495 enumerator = chain->create_enumerator(chain);
496 while (enumerator->enumerate(enumerator, &x509))
497 {
498 inh = x509->get_constraint(x509, X509_INHIBIT_POLICY_MAPPING);
499 if (inh != X509_NO_CONSTRAINT)
500 {
501 if (!has_no_policy_mapping(chain, len - inh))
502 {
503 valid = FALSE;
504 break;
505 }
506 }
507 inh = x509->get_constraint(x509, X509_INHIBIT_ANY_POLICY);
508 if (inh != X509_NO_CONSTRAINT)
509 {
510 if (!has_no_any_policy(chain, len - inh))
511 {
512 valid = FALSE;
513 break;
514 }
515 }
516 len++;
517 }
518 enumerator->destroy(enumerator);
519
520 chain->destroy(chain);
521 }
522 }
523 return valid;
524 }
525
526 METHOD(cert_validator_t, validate, bool,
527 private_constraints_validator_t *this, certificate_t *subject,
528 certificate_t *issuer, bool online, u_int pathlen, bool anchor,
529 auth_cfg_t *auth)
530 {
531 if (issuer->get_type(issuer) == CERT_X509 &&
532 subject->get_type(subject) == CERT_X509)
533 {
534 if (!check_pathlen((x509_t*)issuer, pathlen))
535 {
536 return FALSE;
537 }
538 if (!check_name_constraints(subject, (x509_t*)issuer))
539 {
540 return FALSE;
541 }
542 if (!check_policy((x509_t*)subject, (x509_t*)issuer, !pathlen, auth))
543 {
544 return FALSE;
545 }
546 if (anchor)
547 {
548 if (!check_policy_constraints((x509_t*)issuer, pathlen, auth))
549 {
550 return FALSE;
551 }
552 }
553 }
554 return TRUE;
555 }
556
557 METHOD(constraints_validator_t, destroy, void,
558 private_constraints_validator_t *this)
559 {
560 free(this);
561 }
562
563 /**
564 * See header
565 */
566 constraints_validator_t *constraints_validator_create()
567 {
568 private_constraints_validator_t *this;
569
570 INIT(this,
571 .public = {
572 .validator.validate = _validate,
573 .destroy = _destroy,
574 },
575 );
576
577 return &this->public;
578 }