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