3d5211ef8465ab5dfe01496b491b23587c418476
[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 <utils/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 int pathlen_constraint;
42
43 pathlen_constraint = issuer->get_pathLenConstraint(issuer);
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 * Check if an issuer certificate has a given policy OID
260 */
261 static bool has_policy(x509_t *issuer, chunk_t oid)
262 {
263 chunk_t any_policy = chunk_from_chars(0x55,0x1d,0x20,0x00);
264 x509_policy_mapping_t *mapping;
265 x509_cert_policy_t *policy;
266 enumerator_t *enumerator;
267
268 enumerator = issuer->create_cert_policy_enumerator(issuer);
269 while (enumerator->enumerate(enumerator, &policy))
270 {
271 if (chunk_equals(oid, policy->oid) ||
272 chunk_equals(any_policy, policy->oid))
273 {
274 enumerator->destroy(enumerator);
275 return TRUE;
276 }
277 }
278 enumerator->destroy(enumerator);
279
280 /* fall back to a mapped policy */
281 enumerator = issuer->create_policy_mapping_enumerator(issuer);
282 while (enumerator->enumerate(enumerator, &mapping))
283 {
284 if (chunk_equals(mapping->subject, oid))
285 {
286 enumerator->destroy(enumerator);
287 return TRUE;
288 }
289 }
290 enumerator->destroy(enumerator);
291 return FALSE;
292 }
293
294 /**
295 * Check certificatePolicies.
296 */
297 static bool check_policy(x509_t *subject, x509_t *issuer, bool check,
298 auth_cfg_t *auth)
299 {
300 certificate_t *cert = (certificate_t*)subject;
301 x509_policy_mapping_t *mapping;
302 x509_cert_policy_t *policy;
303 enumerator_t *enumerator;
304 char *oid;
305
306 /* verify if policyMappings in subject are valid */
307 enumerator = subject->create_policy_mapping_enumerator(subject);
308 while (enumerator->enumerate(enumerator, &mapping))
309 {
310 if (!has_policy(issuer, mapping->issuer))
311 {
312 oid = asn1_oid_to_string(mapping->issuer);
313 DBG1(DBG_CFG, "certificate '%Y' maps policy from %s, but issuer "
314 "misses it", cert->get_subject(cert), oid);
315 free(oid);
316 enumerator->destroy(enumerator);
317 return FALSE;
318 }
319 }
320 enumerator->destroy(enumerator);
321
322 if (check)
323 {
324 enumerator = subject->create_cert_policy_enumerator(subject);
325 while (enumerator->enumerate(enumerator, &policy))
326 {
327 if (!has_policy(issuer, policy->oid))
328 {
329 oid = asn1_oid_to_string(policy->oid);
330 DBG1(DBG_CFG, "policy %s missing in issuing certificate '%Y'",
331 oid, cert->get_issuer(cert));
332 free(oid);
333 enumerator->destroy(enumerator);
334 return FALSE;
335 }
336 if (auth)
337 {
338 oid = asn1_oid_to_string(policy->oid);
339 if (oid)
340 {
341 auth->add(auth, AUTH_RULE_CERT_POLICY, oid);
342 }
343 }
344 }
345 enumerator->destroy(enumerator);
346 }
347
348 return TRUE;
349 }
350
351 /**
352 * Check len certificates in trustchain for inherited policies
353 */
354 static bool has_policy_chain(linked_list_t *chain, x509_t *subject, int len)
355 {
356 enumerator_t *enumerator;
357 x509_t *issuer;
358 bool valid = TRUE;
359
360 enumerator = chain->create_enumerator(chain);
361 while (len-- > 0 && enumerator->enumerate(enumerator, &issuer))
362 {
363 if (!check_policy(subject, issuer, TRUE, NULL))
364 {
365 valid = FALSE;
366 break;
367 }
368 subject = issuer;
369 }
370 enumerator->destroy(enumerator);
371 return valid;
372 }
373
374 /**
375 * Check len certificates in trustchain have policyMappings
376 */
377 static bool has_policy_mapping(linked_list_t *chain, int len)
378 {
379 enumerator_t *enumerator, *mappings;
380 x509_policy_mapping_t *mapping;
381 certificate_t *cert;
382 x509_t *x509;
383 bool valid = TRUE;
384
385 enumerator = chain->create_enumerator(chain);
386 while (len-- > 0 && enumerator->enumerate(enumerator, &x509))
387 {
388 mappings = x509->create_policy_mapping_enumerator(x509);
389 valid = !mappings->enumerate(mappings, &mapping);
390 mappings->destroy(mappings);
391 if (!valid)
392 {
393 cert = (certificate_t*)x509;
394 DBG1(DBG_CFG, "found policyMapping in certificate '%Y', but "
395 "inhibitPolicyMapping in effect", cert->get_subject(cert));
396 break;
397 }
398 }
399 enumerator->destroy(enumerator);
400 return valid;
401 }
402
403 /**
404 * Check requireExplicitPolicy and inhibitPolicyMapping constraints
405 */
406 static bool check_policy_constraints(x509_t *issuer, int pathlen,
407 auth_cfg_t *auth)
408 {
409 certificate_t *subject;
410 bool valid = TRUE;
411
412 subject = auth->get(auth, AUTH_RULE_SUBJECT_CERT);
413 if (subject)
414 {
415 if (subject->get_type(subject) == CERT_X509)
416 {
417 enumerator_t *enumerator;
418 linked_list_t *chain;
419 certificate_t *cert;
420 auth_rule_t rule;
421 x509_t *x509;
422 int len = 0, expl;
423
424 /* prepare trustchain to validate */
425 chain = linked_list_create();
426 enumerator = auth->create_enumerator(auth);
427 while (enumerator->enumerate(enumerator, &rule, &cert))
428 {
429 if (rule == AUTH_RULE_IM_CERT &&
430 cert->get_type(cert) == CERT_X509)
431 {
432 chain->insert_last(chain, cert);
433 }
434 }
435 enumerator->destroy(enumerator);
436 chain->insert_last(chain, issuer);
437
438 /* search for requireExplicitPolicy constraints */
439 enumerator = chain->create_enumerator(chain);
440 while (enumerator->enumerate(enumerator, &x509))
441 {
442 expl = x509->get_policyConstraint(x509, FALSE);
443 if (expl != X509_NO_CONSTRAINT)
444 {
445 if (!has_policy_chain(chain, (x509_t*)subject, len - expl))
446 {
447 valid = FALSE;
448 break;
449 }
450 }
451 len++;
452 }
453 enumerator->destroy(enumerator);
454
455 /* search for inhibitPolicyMapping constraints */
456 len = 0;
457 chain->insert_first(chain, subject);
458 enumerator = chain->create_enumerator(chain);
459 while (enumerator->enumerate(enumerator, &x509))
460 {
461 expl = x509->get_policyConstraint(x509, TRUE);
462 if (expl != X509_NO_CONSTRAINT)
463 {
464 if (!has_policy_mapping(chain, len - expl))
465 {
466 valid = FALSE;
467 break;
468 }
469 }
470 len++;
471 }
472 enumerator->destroy(enumerator);
473
474 chain->destroy(chain);
475 }
476 }
477 return valid;
478 }
479
480 METHOD(cert_validator_t, validate, bool,
481 private_constraints_validator_t *this, certificate_t *subject,
482 certificate_t *issuer, bool online, int pathlen, bool anchor,
483 auth_cfg_t *auth)
484 {
485 if (issuer->get_type(issuer) == CERT_X509 &&
486 subject->get_type(subject) == CERT_X509)
487 {
488 if (!check_pathlen((x509_t*)issuer, pathlen))
489 {
490 return FALSE;
491 }
492 if (!check_name_constraints(subject, (x509_t*)issuer))
493 {
494 return FALSE;
495 }
496 if (!check_policy((x509_t*)subject, (x509_t*)issuer, !pathlen, auth))
497 {
498 return FALSE;
499 }
500 if (anchor)
501 {
502 if (!check_policy_constraints((x509_t*)issuer, pathlen, auth))
503 {
504 return FALSE;
505 }
506 }
507 }
508 return TRUE;
509 }
510
511 METHOD(constraints_validator_t, destroy, void,
512 private_constraints_validator_t *this)
513 {
514 free(this);
515 }
516
517 /**
518 * See header
519 */
520 constraints_validator_t *constraints_validator_create()
521 {
522 private_constraints_validator_t *this;
523
524 INIT(this,
525 .public = {
526 .validator.validate = _validate,
527 .destroy = _destroy,
528 },
529 );
530
531 return &this->public;
532 }