]>
Commit | Line | Data |
---|---|---|
5728c6aa | 1 | /* |
28bcbc29 TB |
2 | * Copyright (C) 2011 Tobias Brunner |
3 | * Hochschule fuer Technik Rapperswil | |
4 | * | |
5728c6aa MW |
5 | * Copyright (C) 2010 Martin Willi |
6 | * Copyright (C) 2010 revosec AG | |
7 | * | |
8 | * This program is free software; you can redistribute it and/or modify it | |
9 | * under the terms of the GNU General Public License as published by the | |
10 | * Free Software Foundation; either version 2 of the License, or (at your | |
11 | * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. | |
12 | * | |
13 | * This program is distributed in the hope that it will be useful, but | |
14 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | |
15 | * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
16 | * for more details. | |
17 | */ | |
18 | ||
19 | /* | |
5e4b1ad2 MR |
20 | * Copyright (C) 2013 Michael Rossberg |
21 | * Copyright (C) 2013 Technische Universität Ilmenau | |
22 | * | |
5728c6aa MW |
23 | * Copyright (C) 2010 secunet Security Networks AG |
24 | * Copyright (C) 2010 Thomas Egerer | |
25 | * | |
26 | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
27 | * of this software and associated documentation files (the "Software"), to deal | |
28 | * in the Software without restriction, including without limitation the rights | |
29 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
30 | * copies of the Software, and to permit persons to whom the Software is | |
31 | * furnished to do so, subject to the following conditions: | |
32 | * | |
33 | * The above copyright notice and this permission notice shall be included in | |
34 | * all copies or substantial portions of the Software. | |
35 | * | |
36 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
37 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
38 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
39 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
40 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
41 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
42 | * THE SOFTWARE. | |
43 | */ | |
44 | ||
45 | #define _GNU_SOURCE | |
46 | #include <stdio.h> | |
47 | #include <openssl/x509.h> | |
48 | #include <openssl/x509v3.h> | |
49 | ||
50 | #include "openssl_x509.h" | |
51 | #include "openssl_util.h" | |
52 | ||
f05b4272 | 53 | #include <utils/debug.h> |
5728c6aa | 54 | #include <asn1/oid.h> |
12642a68 | 55 | #include <collections/linked_list.h> |
5e4b1ad2 | 56 | #include <selectors/traffic_selector.h> |
5728c6aa | 57 | |
5e4b1ad2 MR |
58 | /* IP Addr block extension support was introduced with 0.9.8e */ |
59 | #if OPENSSL_VERSION_NUMBER < 0x0090805fL | |
60 | #define OPENSSL_NO_RFC3779 | |
61 | #endif | |
5728c6aa MW |
62 | |
63 | typedef struct private_openssl_x509_t private_openssl_x509_t; | |
64 | ||
65 | /** | |
66 | * Private data of an openssl_x509_t object. | |
67 | */ | |
68 | struct private_openssl_x509_t { | |
69 | ||
70 | /** | |
71 | * Public openssl_x509_t interface. | |
72 | */ | |
73 | openssl_x509_t public; | |
74 | ||
75 | /** | |
76 | * OpenSSL certificate representation | |
77 | */ | |
78 | X509 *x509; | |
79 | ||
80 | /** | |
81 | * DER encoded certificate | |
82 | */ | |
83 | chunk_t encoding; | |
84 | ||
85 | /** | |
86 | * SHA1 hash of the certificate | |
87 | */ | |
88 | chunk_t hash; | |
89 | ||
90 | /** | |
91 | * X509 flags | |
92 | */ | |
93 | x509_flag_t flags; | |
94 | ||
95 | /** | |
96 | * Pathlen constraint | |
97 | */ | |
d390b3b9 | 98 | u_char pathlen; |
5728c6aa MW |
99 | |
100 | /** | |
101 | * certificate subject | |
102 | */ | |
103 | identification_t *subject; | |
104 | ||
105 | /** | |
106 | * certificate issuer | |
107 | */ | |
108 | identification_t *issuer; | |
109 | ||
110 | /** | |
111 | * Certificates public key | |
112 | */ | |
113 | public_key_t *pubkey; | |
114 | ||
115 | /** | |
116 | * subjectKeyIdentifier as read from cert | |
117 | */ | |
118 | chunk_t subjectKeyIdentifier; | |
119 | ||
120 | /** | |
121 | * authorityKeyIdentifier as read from cert | |
122 | */ | |
123 | chunk_t authKeyIdentifier; | |
124 | ||
125 | /** | |
126 | * Start time of certificate validity | |
127 | */ | |
128 | time_t notBefore; | |
129 | ||
130 | /** | |
131 | * End time of certificate validity | |
132 | */ | |
133 | time_t notAfter; | |
134 | ||
135 | /** | |
136 | * Signature scheme of the certificate | |
137 | */ | |
138 | signature_scheme_t scheme; | |
139 | ||
140 | /** | |
141 | * subjectAltNames | |
142 | */ | |
143 | linked_list_t *subjectAltNames; | |
144 | ||
145 | /** | |
146 | * issuerAltNames | |
147 | */ | |
148 | linked_list_t *issuerAltNames; | |
149 | ||
150 | /** | |
a6478a04 | 151 | * List of CRL URIs, as x509_cdp_t |
5728c6aa MW |
152 | */ |
153 | linked_list_t *crl_uris; | |
154 | ||
155 | /** | |
156 | * List of OCSP URIs | |
157 | */ | |
158 | linked_list_t *ocsp_uris; | |
159 | ||
5e4b1ad2 MR |
160 | /** |
161 | * List of ipAddrBlocks as traffic_selector_t | |
162 | */ | |
163 | linked_list_t *ipAddrBlocks; | |
164 | ||
165 | ||
5728c6aa MW |
166 | /** |
167 | * References to this cert | |
168 | */ | |
169 | refcount_t ref; | |
170 | }; | |
171 | ||
4e508517 MW |
172 | /** |
173 | * Destroy a CRL URI struct | |
174 | */ | |
a6478a04 | 175 | static void crl_uri_destroy(x509_cdp_t *this) |
4e508517 | 176 | { |
a6478a04 | 177 | free(this->uri); |
4e508517 MW |
178 | DESTROY_IF(this->issuer); |
179 | free(this); | |
180 | } | |
181 | ||
5728c6aa MW |
182 | /** |
183 | * Convert a GeneralName to an identification_t. | |
184 | */ | |
185 | static identification_t *general_name2id(GENERAL_NAME *name) | |
186 | { | |
187 | if (!name) | |
188 | { | |
189 | return NULL; | |
190 | } | |
191 | switch (name->type) | |
192 | { | |
193 | case GEN_EMAIL: | |
194 | return identification_create_from_encoding(ID_RFC822_ADDR, | |
195 | openssl_asn1_str2chunk(name->d.rfc822Name)); | |
196 | case GEN_DNS: | |
197 | return identification_create_from_encoding(ID_FQDN, | |
198 | openssl_asn1_str2chunk(name->d.dNSName)); | |
199 | case GEN_URI: | |
200 | return identification_create_from_encoding(ID_DER_ASN1_GN_URI, | |
201 | openssl_asn1_str2chunk(name->d.uniformResourceIdentifier)); | |
202 | case GEN_IPADD: | |
07c5aacc MW |
203 | { |
204 | chunk_t chunk = openssl_asn1_str2chunk(name->d.iPAddress); | |
205 | if (chunk.len == 4) | |
206 | { | |
207 | return identification_create_from_encoding(ID_IPV4_ADDR, chunk); | |
208 | } | |
209 | if (chunk.len == 16) | |
210 | { | |
211 | return identification_create_from_encoding(ID_IPV6_ADDR, chunk); | |
212 | } | |
213 | return NULL; | |
214 | } | |
5728c6aa MW |
215 | case GEN_DIRNAME : |
216 | return openssl_x509_name2id(name->d.directoryName); | |
a0a8aaaf MW |
217 | case GEN_OTHERNAME: |
218 | if (OBJ_obj2nid(name->d.otherName->type_id) == NID_ms_upn && | |
219 | name->d.otherName->value->type == V_ASN1_UTF8STRING) | |
220 | { | |
221 | return identification_create_from_encoding(ID_RFC822_ADDR, | |
222 | openssl_asn1_str2chunk( | |
223 | name->d.otherName->value->value.utf8string)); | |
224 | } | |
225 | return NULL; | |
5728c6aa MW |
226 | default: |
227 | return NULL; | |
228 | } | |
229 | } | |
230 | ||
231 | METHOD(x509_t, get_flags, x509_flag_t, | |
232 | private_openssl_x509_t *this) | |
233 | { | |
234 | return this->flags; | |
235 | } | |
236 | ||
237 | METHOD(x509_t, get_serial, chunk_t, | |
238 | private_openssl_x509_t *this) | |
239 | { | |
240 | return openssl_asn1_str2chunk(X509_get_serialNumber(this->x509)); | |
241 | } | |
242 | ||
243 | METHOD(x509_t, get_subjectKeyIdentifier, chunk_t, | |
244 | private_openssl_x509_t *this) | |
245 | { | |
246 | chunk_t fingerprint; | |
247 | ||
248 | if (this->subjectKeyIdentifier.len) | |
249 | { | |
250 | return this->subjectKeyIdentifier; | |
251 | } | |
da9724e6 | 252 | if (this->pubkey->get_fingerprint(this->pubkey, KEYID_PUBKEY_SHA1, |
5728c6aa MW |
253 | &fingerprint)) |
254 | { | |
255 | return fingerprint; | |
256 | } | |
257 | return chunk_empty; | |
258 | } | |
259 | ||
260 | METHOD(x509_t, get_authKeyIdentifier, chunk_t, | |
261 | private_openssl_x509_t *this) | |
262 | { | |
263 | if (this->authKeyIdentifier.len) | |
264 | { | |
265 | return this->authKeyIdentifier; | |
266 | } | |
267 | return chunk_empty; | |
268 | } | |
269 | ||
d390b3b9 | 270 | METHOD(x509_t, get_constraint, u_int, |
b3d359e5 | 271 | private_openssl_x509_t *this, x509_constraint_t type) |
a742d97f | 272 | { |
b3d359e5 MW |
273 | switch (type) |
274 | { | |
275 | case X509_PATH_LEN: | |
276 | return this->pathlen; | |
277 | default: | |
278 | return X509_NO_CONSTRAINT; | |
279 | } | |
a742d97f MW |
280 | } |
281 | ||
5728c6aa MW |
282 | METHOD(x509_t, create_subjectAltName_enumerator, enumerator_t*, |
283 | private_openssl_x509_t *this) | |
284 | { | |
285 | return this->subjectAltNames->create_enumerator(this->subjectAltNames); | |
286 | } | |
287 | ||
288 | METHOD(x509_t, create_crl_uri_enumerator, enumerator_t*, | |
289 | private_openssl_x509_t *this) | |
290 | { | |
a6478a04 | 291 | return this->crl_uris->create_enumerator(this->crl_uris); |
5728c6aa MW |
292 | } |
293 | ||
294 | METHOD(x509_t, create_ocsp_uri_enumerator, enumerator_t*, | |
295 | private_openssl_x509_t *this) | |
296 | { | |
297 | return this->ocsp_uris->create_enumerator(this->ocsp_uris); | |
298 | } | |
299 | ||
5e4b1ad2 MR |
300 | METHOD(x509_t, create_ipAddrBlock_enumerator, enumerator_t*, |
301 | private_openssl_x509_t *this) | |
302 | { | |
303 | return this->ipAddrBlocks->create_enumerator(this->ipAddrBlocks); | |
304 | } | |
305 | ||
5728c6aa MW |
306 | METHOD(certificate_t, get_type, certificate_type_t, |
307 | private_openssl_x509_t *this) | |
308 | { | |
309 | return CERT_X509; | |
310 | } | |
311 | ||
312 | METHOD(certificate_t, get_subject, identification_t*, | |
313 | private_openssl_x509_t *this) | |
314 | { | |
315 | return this->subject; | |
316 | } | |
317 | ||
318 | METHOD(certificate_t, get_issuer, identification_t*, | |
319 | private_openssl_x509_t *this) | |
320 | { | |
321 | return this->issuer; | |
322 | } | |
323 | ||
324 | METHOD(certificate_t, has_subject, id_match_t, | |
325 | private_openssl_x509_t *this, identification_t *subject) | |
326 | { | |
327 | identification_t *current; | |
328 | enumerator_t *enumerator; | |
329 | id_match_t match, best; | |
663e7355 | 330 | chunk_t encoding; |
5728c6aa MW |
331 | |
332 | if (subject->get_type(subject) == ID_KEY_ID) | |
333 | { | |
663e7355 MW |
334 | encoding = subject->get_encoding(subject); |
335 | ||
336 | if (chunk_equals(this->hash, encoding)) | |
337 | { | |
338 | return ID_MATCH_PERFECT; | |
339 | } | |
340 | if (this->subjectKeyIdentifier.len && | |
341 | chunk_equals(this->subjectKeyIdentifier, encoding)) | |
342 | { | |
343 | return ID_MATCH_PERFECT; | |
344 | } | |
345 | if (this->pubkey && | |
346 | this->pubkey->has_fingerprint(this->pubkey, encoding)) | |
5728c6aa MW |
347 | { |
348 | return ID_MATCH_PERFECT; | |
349 | } | |
ed1c4303 MW |
350 | if (chunk_equals(get_serial(this), encoding)) |
351 | { | |
352 | return ID_MATCH_PERFECT; | |
353 | } | |
5728c6aa MW |
354 | } |
355 | best = this->subject->matches(this->subject, subject); | |
356 | enumerator = create_subjectAltName_enumerator(this); | |
357 | while (enumerator->enumerate(enumerator, ¤t)) | |
358 | { | |
359 | match = current->matches(current, subject); | |
360 | if (match > best) | |
361 | { | |
362 | best = match; | |
363 | } | |
364 | } | |
365 | enumerator->destroy(enumerator); | |
366 | return best; | |
367 | } | |
368 | ||
369 | METHOD(certificate_t, has_issuer, id_match_t, | |
370 | private_openssl_x509_t *this, identification_t *issuer) | |
371 | { | |
372 | /* issuerAltNames currently not supported */ | |
373 | return this->issuer->matches(this->issuer, issuer); | |
374 | } | |
375 | ||
376 | METHOD(certificate_t, issued_by, bool, | |
a37f2d20 MW |
377 | private_openssl_x509_t *this, certificate_t *issuer, |
378 | signature_scheme_t *scheme) | |
5728c6aa MW |
379 | { |
380 | public_key_t *key; | |
381 | bool valid; | |
382 | x509_t *x509 = (x509_t*)issuer; | |
383 | chunk_t tbs; | |
384 | ||
385 | if (&this->public.x509.interface == issuer) | |
386 | { | |
387 | if (this->flags & X509_SELF_SIGNED) | |
388 | { | |
389 | return TRUE; | |
390 | } | |
391 | } | |
392 | else | |
393 | { | |
394 | if (issuer->get_type(issuer) != CERT_X509) | |
395 | { | |
396 | return FALSE; | |
397 | } | |
398 | if (!(x509->get_flags(x509) & X509_CA)) | |
399 | { | |
400 | return FALSE; | |
401 | } | |
402 | if (!this->issuer->equals(this->issuer, issuer->get_subject(issuer))) | |
403 | { | |
404 | return FALSE; | |
405 | } | |
406 | } | |
407 | if (this->scheme == SIGN_UNKNOWN) | |
408 | { | |
409 | return FALSE; | |
410 | } | |
411 | key = issuer->get_public_key(issuer); | |
412 | if (!key) | |
413 | { | |
414 | return FALSE; | |
415 | } | |
416 | tbs = openssl_i2chunk(X509_CINF, this->x509->cert_info); | |
417 | valid = key->verify(key, this->scheme, tbs, | |
418 | openssl_asn1_str2chunk(this->x509->signature)); | |
419 | free(tbs.ptr); | |
420 | key->destroy(key); | |
a37f2d20 MW |
421 | if (valid && scheme) |
422 | { | |
423 | *scheme = this->scheme; | |
424 | } | |
5728c6aa MW |
425 | return valid; |
426 | } | |
427 | ||
428 | METHOD(certificate_t, get_public_key, public_key_t*, | |
429 | private_openssl_x509_t *this) | |
430 | { | |
431 | return this->pubkey->get_ref(this->pubkey); | |
432 | } | |
433 | ||
434 | METHOD(certificate_t, get_validity, bool, | |
435 | private_openssl_x509_t *this, | |
436 | time_t *when, time_t *not_before, time_t *not_after) | |
437 | { | |
438 | time_t t; | |
439 | ||
440 | if (when) | |
441 | { | |
442 | t = *when; | |
443 | } | |
444 | else | |
445 | { | |
446 | t = time(NULL); | |
447 | } | |
448 | if (not_before) | |
449 | { | |
450 | *not_before = this->notBefore; | |
451 | } | |
452 | if (not_after) | |
453 | { | |
454 | *not_after = this->notAfter; | |
455 | } | |
456 | return (t >= this->notBefore && t <= this->notAfter); | |
457 | } | |
458 | ||
0406eeaa MW |
459 | METHOD(certificate_t, get_encoding, bool, |
460 | private_openssl_x509_t *this, cred_encoding_type_t type, chunk_t *encoding) | |
5728c6aa | 461 | { |
0406eeaa MW |
462 | if (type == CERT_ASN1_DER) |
463 | { | |
464 | *encoding = chunk_clone(this->encoding); | |
465 | return TRUE; | |
466 | } | |
467 | return lib->encoding->encode(lib->encoding, type, NULL, encoding, | |
468 | CRED_PART_X509_ASN1_DER, this->encoding, CRED_PART_END); | |
5728c6aa MW |
469 | } |
470 | ||
0406eeaa | 471 | |
5728c6aa MW |
472 | METHOD(certificate_t, equals, bool, |
473 | private_openssl_x509_t *this, certificate_t *other) | |
474 | { | |
475 | chunk_t encoding; | |
476 | bool equal; | |
477 | ||
478 | if (this == (private_openssl_x509_t*)other) | |
479 | { | |
480 | return TRUE; | |
481 | } | |
482 | if (other->get_type(other) != CERT_X509) | |
483 | { | |
484 | return FALSE; | |
485 | } | |
486 | if (other->equals == (void*)equals) | |
487 | { /* skip allocation if we have the same implementation */ | |
488 | encoding = ((private_openssl_x509_t*)other)->encoding; | |
489 | return chunk_equals(this->encoding, encoding); | |
490 | } | |
0406eeaa MW |
491 | if (!other->get_encoding(other, CERT_ASN1_DER, &encoding)) |
492 | { | |
493 | return FALSE; | |
494 | } | |
5728c6aa MW |
495 | equal = chunk_equals(this->encoding, encoding); |
496 | free(encoding.ptr); | |
497 | return equal; | |
498 | } | |
499 | ||
500 | METHOD(certificate_t, get_ref, certificate_t*, | |
501 | private_openssl_x509_t *this) | |
502 | { | |
503 | ref_get(&this->ref); | |
504 | return &this->public.x509.interface; | |
505 | } | |
506 | ||
507 | METHOD(certificate_t, destroy, void, | |
508 | private_openssl_x509_t *this) | |
509 | { | |
510 | if (ref_put(&this->ref)) | |
511 | { | |
512 | if (this->x509) | |
513 | { | |
514 | X509_free(this->x509); | |
515 | } | |
516 | DESTROY_IF(this->subject); | |
517 | DESTROY_IF(this->issuer); | |
518 | DESTROY_IF(this->pubkey); | |
519 | free(this->subjectKeyIdentifier.ptr); | |
520 | free(this->authKeyIdentifier.ptr); | |
521 | free(this->encoding.ptr); | |
522 | free(this->hash.ptr); | |
523 | this->subjectAltNames->destroy_offset(this->subjectAltNames, | |
524 | offsetof(identification_t, destroy)); | |
525 | this->issuerAltNames->destroy_offset(this->issuerAltNames, | |
526 | offsetof(identification_t, destroy)); | |
4e508517 | 527 | this->crl_uris->destroy_function(this->crl_uris, (void*)crl_uri_destroy); |
5728c6aa | 528 | this->ocsp_uris->destroy_function(this->ocsp_uris, free); |
5e4b1ad2 MR |
529 | this->ipAddrBlocks->destroy_offset(this->ipAddrBlocks, |
530 | offsetof(traffic_selector_t, destroy)); | |
5728c6aa MW |
531 | free(this); |
532 | } | |
533 | } | |
534 | ||
535 | /** | |
536 | * Create an empty certificate | |
537 | */ | |
538 | static private_openssl_x509_t *create_empty() | |
539 | { | |
540 | private_openssl_x509_t *this; | |
541 | ||
542 | INIT(this, | |
543 | .public = { | |
544 | .x509 = { | |
545 | .interface = { | |
546 | .get_type = _get_type, | |
547 | .get_subject = _get_subject, | |
548 | .get_issuer = _get_issuer, | |
549 | .has_subject = _has_subject, | |
550 | .has_issuer = _has_issuer, | |
551 | .issued_by = _issued_by, | |
552 | .get_public_key = _get_public_key, | |
553 | .get_validity = _get_validity, | |
5728c6aa MW |
554 | .get_encoding = _get_encoding, |
555 | .equals = _equals, | |
556 | .get_ref = _get_ref, | |
557 | .destroy = _destroy, | |
558 | }, | |
559 | .get_flags = _get_flags, | |
560 | .get_serial = _get_serial, | |
561 | .get_subjectKeyIdentifier = _get_subjectKeyIdentifier, | |
562 | .get_authKeyIdentifier = _get_authKeyIdentifier, | |
b3d359e5 | 563 | .get_constraint = _get_constraint, |
5728c6aa MW |
564 | .create_subjectAltName_enumerator = _create_subjectAltName_enumerator, |
565 | .create_crl_uri_enumerator = _create_crl_uri_enumerator, | |
566 | .create_ocsp_uri_enumerator = _create_ocsp_uri_enumerator, | |
5e4b1ad2 | 567 | .create_ipAddrBlock_enumerator = _create_ipAddrBlock_enumerator, |
dbfbbec3 | 568 | .create_name_constraint_enumerator = (void*)enumerator_create_empty, |
20bd7810 | 569 | .create_cert_policy_enumerator = (void*)enumerator_create_empty, |
5a0caa4b | 570 | .create_policy_mapping_enumerator = (void*)enumerator_create_empty, |
5728c6aa MW |
571 | }, |
572 | }, | |
573 | .subjectAltNames = linked_list_create(), | |
574 | .issuerAltNames = linked_list_create(), | |
575 | .crl_uris = linked_list_create(), | |
576 | .ocsp_uris = linked_list_create(), | |
5e4b1ad2 | 577 | .ipAddrBlocks = linked_list_create(), |
5dba5852 | 578 | .pathlen = X509_NO_CONSTRAINT, |
5728c6aa MW |
579 | .ref = 1, |
580 | ); | |
581 | ||
582 | return this; | |
583 | } | |
584 | ||
585 | /** | |
586 | * parse an extionsion containing GENERAL_NAMES into a list | |
587 | */ | |
588 | static bool parse_generalNames_ext(linked_list_t *list, | |
589 | X509_EXTENSION *ext) | |
590 | { | |
591 | GENERAL_NAMES *names; | |
592 | GENERAL_NAME *name; | |
593 | identification_t *id; | |
594 | int i, num; | |
595 | ||
596 | names = X509V3_EXT_d2i(ext); | |
597 | if (!names) | |
598 | { | |
599 | return FALSE; | |
600 | } | |
601 | ||
602 | num = sk_GENERAL_NAME_num(names); | |
603 | for (i = 0; i < num; i++) | |
604 | { | |
605 | name = sk_GENERAL_NAME_value(names, i); | |
606 | id = general_name2id(name); | |
607 | if (id) | |
608 | { | |
609 | list->insert_last(list, id); | |
610 | } | |
611 | GENERAL_NAME_free(name); | |
612 | } | |
613 | sk_GENERAL_NAME_free(names); | |
614 | return TRUE; | |
615 | } | |
616 | ||
617 | /** | |
618 | * parse basic constraints | |
619 | */ | |
620 | static bool parse_basicConstraints_ext(private_openssl_x509_t *this, | |
621 | X509_EXTENSION *ext) | |
622 | { | |
623 | BASIC_CONSTRAINTS *constraints; | |
d390b3b9 | 624 | long pathlen; |
5728c6aa MW |
625 | |
626 | constraints = (BASIC_CONSTRAINTS*)X509V3_EXT_d2i(ext); | |
627 | if (constraints) | |
628 | { | |
629 | if (constraints->ca) | |
630 | { | |
631 | this->flags |= X509_CA; | |
632 | } | |
633 | if (constraints->pathlen) | |
634 | { | |
28bcbc29 | 635 | |
d390b3b9 AS |
636 | pathlen = ASN1_INTEGER_get(constraints->pathlen); |
637 | this->pathlen = (pathlen >= 0 && pathlen < 128) ? | |
638 | pathlen : X509_NO_CONSTRAINT; | |
5728c6aa MW |
639 | } |
640 | BASIC_CONSTRAINTS_free(constraints); | |
641 | return TRUE; | |
642 | } | |
643 | return FALSE; | |
644 | } | |
645 | ||
28bcbc29 TB |
646 | /** |
647 | * parse key usage | |
648 | */ | |
649 | static bool parse_keyUsage_ext(private_openssl_x509_t *this, | |
650 | X509_EXTENSION *ext) | |
651 | { | |
652 | ASN1_BIT_STRING *usage; | |
653 | ||
654 | usage = X509V3_EXT_d2i(ext); | |
655 | if (usage) | |
656 | { | |
657 | if (usage->length > 0) | |
658 | { | |
659 | int flags = usage->data[0]; | |
660 | if (usage->length > 1) | |
661 | { | |
662 | flags |= usage->data[1] << 8; | |
663 | } | |
664 | switch (flags) | |
665 | { | |
666 | case X509v3_KU_CRL_SIGN: | |
667 | this->flags |= X509_CRL_SIGN; | |
668 | break; | |
669 | case X509v3_KU_KEY_CERT_SIGN: | |
670 | /* we use the caBasicContraint, MUST be set */ | |
671 | default: | |
672 | break; | |
673 | } | |
674 | } | |
675 | ASN1_BIT_STRING_free(usage); | |
676 | return TRUE; | |
677 | } | |
678 | return FALSE; | |
679 | } | |
680 | ||
c3e7b3de MW |
681 | /** |
682 | * Parse ExtendedKeyUsage | |
683 | */ | |
684 | static bool parse_extKeyUsage_ext(private_openssl_x509_t *this, | |
685 | X509_EXTENSION *ext) | |
686 | { | |
687 | EXTENDED_KEY_USAGE *usage; | |
688 | int i; | |
689 | ||
690 | usage = X509V3_EXT_d2i(ext); | |
691 | if (usage) | |
692 | { | |
693 | for (i = 0; i < sk_ASN1_OBJECT_num(usage); i++) | |
694 | { | |
695 | switch (OBJ_obj2nid(sk_ASN1_OBJECT_value(usage, i))) | |
696 | { | |
697 | case NID_server_auth: | |
698 | this->flags |= X509_SERVER_AUTH; | |
699 | break; | |
700 | case NID_client_auth: | |
701 | this->flags |= X509_CLIENT_AUTH; | |
702 | break; | |
703 | case NID_OCSP_sign: | |
704 | this->flags |= X509_OCSP_SIGNER; | |
705 | break; | |
706 | default: | |
707 | break; | |
708 | } | |
709 | } | |
710 | sk_ASN1_OBJECT_pop_free(usage, ASN1_OBJECT_free); | |
711 | return TRUE; | |
712 | } | |
713 | return FALSE; | |
714 | } | |
715 | ||
5728c6aa MW |
716 | /** |
717 | * Parse CRL distribution points | |
718 | */ | |
719 | static bool parse_crlDistributionPoints_ext(private_openssl_x509_t *this, | |
720 | X509_EXTENSION *ext) | |
721 | { | |
722 | CRL_DIST_POINTS *cdps; | |
723 | DIST_POINT *cdp; | |
a6478a04 MW |
724 | identification_t *id, *issuer; |
725 | x509_cdp_t *entry; | |
5728c6aa | 726 | char *uri; |
a6478a04 | 727 | int i, j, k, point_num, name_num, issuer_num; |
5728c6aa MW |
728 | |
729 | cdps = X509V3_EXT_d2i(ext); | |
730 | if (!cdps) | |
731 | { | |
732 | return FALSE; | |
733 | } | |
734 | point_num = sk_DIST_POINT_num(cdps); | |
735 | for (i = 0; i < point_num; i++) | |
736 | { | |
737 | cdp = sk_DIST_POINT_value(cdps, i); | |
738 | if (cdp) | |
739 | { | |
740 | if (cdp->distpoint && cdp->distpoint->type == 0 && | |
741 | cdp->distpoint->name.fullname) | |
742 | { | |
743 | name_num = sk_GENERAL_NAME_num(cdp->distpoint->name.fullname); | |
744 | for (j = 0; j < name_num; j++) | |
745 | { | |
746 | id = general_name2id(sk_GENERAL_NAME_value( | |
747 | cdp->distpoint->name.fullname, j)); | |
748 | if (id) | |
749 | { | |
750 | if (asprintf(&uri, "%Y", id) > 0) | |
751 | { | |
a6478a04 MW |
752 | if (cdp->CRLissuer) |
753 | { | |
754 | issuer_num = sk_GENERAL_NAME_num(cdp->CRLissuer); | |
755 | for (k = 0; k < issuer_num; k++) | |
756 | { | |
757 | issuer = general_name2id( | |
758 | sk_GENERAL_NAME_value(cdp->CRLissuer, k)); | |
759 | if (issuer) | |
760 | { | |
761 | INIT(entry, | |
762 | .uri = strdup(uri), | |
763 | .issuer = issuer, | |
764 | ); | |
765 | this->crl_uris->insert_last( | |
766 | this->crl_uris, entry); | |
767 | } | |
768 | } | |
769 | free(uri); | |
770 | } | |
771 | else | |
772 | { | |
773 | INIT(entry, | |
774 | .uri = uri, | |
775 | ); | |
776 | this->crl_uris->insert_last(this->crl_uris, entry); | |
777 | } | |
5728c6aa MW |
778 | } |
779 | id->destroy(id); | |
780 | } | |
781 | } | |
782 | } | |
a6478a04 | 783 | |
5728c6aa MW |
784 | DIST_POINT_free(cdp); |
785 | } | |
786 | } | |
787 | sk_DIST_POINT_free(cdps); | |
788 | return TRUE; | |
789 | } | |
790 | ||
791 | /** | |
792 | * Parse authorityInfoAccess with OCSP URIs | |
793 | */ | |
794 | static bool parse_authorityInfoAccess_ext(private_openssl_x509_t *this, | |
795 | X509_EXTENSION *ext) | |
796 | { | |
797 | AUTHORITY_INFO_ACCESS *infos; | |
798 | ACCESS_DESCRIPTION *desc; | |
799 | identification_t *id; | |
800 | int i, num; | |
801 | char *uri; | |
802 | ||
803 | infos = X509V3_EXT_d2i(ext); | |
804 | if (!infos) | |
805 | { | |
806 | return FALSE; | |
807 | } | |
808 | num = sk_ACCESS_DESCRIPTION_num(infos); | |
809 | for (i = 0; i < num; i++) | |
810 | { | |
811 | desc = sk_ACCESS_DESCRIPTION_value(infos, i); | |
812 | if (desc) | |
813 | { | |
814 | if (openssl_asn1_known_oid(desc->method) == OID_OCSP) | |
815 | { | |
816 | id = general_name2id(desc->location); | |
817 | if (id) | |
818 | { | |
819 | if (asprintf(&uri, "%Y", id) > 0) | |
820 | { | |
93818392 | 821 | this->ocsp_uris->insert_last(this->ocsp_uris, uri); |
5728c6aa MW |
822 | } |
823 | id->destroy(id); | |
824 | } | |
825 | } | |
826 | ACCESS_DESCRIPTION_free(desc); | |
827 | } | |
828 | } | |
829 | sk_ACCESS_DESCRIPTION_free(infos); | |
830 | return TRUE; | |
831 | } | |
832 | ||
5e4b1ad2 MR |
833 | #ifndef OPENSSL_NO_RFC3779 |
834 | ||
835 | /** | |
836 | * Parse a single block of ipAddrBlock extension | |
837 | */ | |
838 | static void parse_ipAddrBlock_ext_fam(private_openssl_x509_t *this, | |
839 | IPAddressFamily *fam) | |
840 | { | |
841 | const IPAddressOrRanges *list; | |
842 | IPAddressOrRange *aor; | |
843 | traffic_selector_t *ts; | |
844 | ts_type_t type; | |
845 | chunk_t from, to; | |
846 | int i, afi; | |
847 | ||
848 | if (fam->ipAddressChoice->type != IPAddressChoice_addressesOrRanges) | |
849 | { | |
850 | return; | |
851 | } | |
852 | ||
853 | afi = v3_addr_get_afi(fam); | |
854 | switch (afi) | |
855 | { | |
856 | case IANA_AFI_IPV4: | |
857 | from = chunk_alloca(4); | |
858 | to = chunk_alloca(4); | |
859 | type = TS_IPV4_ADDR_RANGE; | |
860 | break; | |
861 | case IANA_AFI_IPV6: | |
862 | from = chunk_alloca(16); | |
863 | to = chunk_alloca(16); | |
864 | type = TS_IPV6_ADDR_RANGE; | |
865 | break; | |
866 | default: | |
867 | return; | |
868 | } | |
869 | ||
870 | list = fam->ipAddressChoice->u.addressesOrRanges; | |
871 | for (i = 0; i < sk_IPAddressOrRange_num(list); i++) | |
872 | { | |
873 | aor = sk_IPAddressOrRange_value(list, i); | |
874 | if (v3_addr_get_range(aor, afi, from.ptr, to.ptr, from.len) > 0) | |
875 | { | |
876 | ts = traffic_selector_create_from_bytes(0, type, from, 0, to, 65535); | |
877 | if (ts) | |
878 | { | |
879 | this->ipAddrBlocks->insert_last(this->ipAddrBlocks, ts); | |
880 | } | |
881 | } | |
882 | } | |
883 | } | |
884 | ||
885 | /** | |
886 | * Parse ipAddrBlock extension | |
887 | */ | |
888 | static bool parse_ipAddrBlock_ext(private_openssl_x509_t *this, | |
889 | X509_EXTENSION *ext) | |
890 | { | |
891 | STACK_OF(IPAddressFamily) *blocks; | |
892 | IPAddressFamily *fam; | |
893 | ||
894 | blocks = (STACK_OF(IPAddressFamily)*)X509V3_EXT_d2i(ext); | |
895 | if (!blocks) | |
896 | { | |
897 | return FALSE; | |
898 | } | |
899 | ||
900 | if (!v3_addr_is_canonical(blocks)) | |
901 | { | |
902 | sk_IPAddressFamily_free(blocks); | |
903 | return FALSE; | |
904 | } | |
905 | ||
906 | while (sk_IPAddressFamily_num(blocks) > 0) | |
907 | { | |
908 | fam = sk_IPAddressFamily_pop(blocks); | |
909 | parse_ipAddrBlock_ext_fam(this, fam); | |
910 | IPAddressFamily_free(fam); | |
911 | } | |
912 | sk_IPAddressFamily_free(blocks); | |
913 | ||
914 | this->flags |= X509_IP_ADDR_BLOCKS; | |
915 | return TRUE; | |
916 | } | |
917 | #endif /* !OPENSSL_NO_RFC3779 */ | |
918 | ||
5728c6aa MW |
919 | /** |
920 | * Parse authorityKeyIdentifier extension | |
921 | */ | |
922 | static bool parse_authKeyIdentifier_ext(private_openssl_x509_t *this, | |
923 | X509_EXTENSION *ext) | |
924 | { | |
925 | AUTHORITY_KEYID *keyid; | |
926 | ||
927 | keyid = (AUTHORITY_KEYID*)X509V3_EXT_d2i(ext); | |
928 | if (keyid) | |
929 | { | |
930 | free(this->authKeyIdentifier.ptr); | |
931 | this->authKeyIdentifier = chunk_clone( | |
932 | openssl_asn1_str2chunk(keyid->keyid)); | |
933 | AUTHORITY_KEYID_free(keyid); | |
934 | return TRUE; | |
935 | } | |
936 | return FALSE; | |
937 | } | |
938 | ||
939 | /** | |
940 | * Parse subjectKeyIdentifier extension | |
941 | */ | |
942 | static bool parse_subjectKeyIdentifier_ext(private_openssl_x509_t *this, | |
943 | X509_EXTENSION *ext) | |
944 | { | |
f00a1015 MW |
945 | chunk_t ostr; |
946 | ||
947 | ostr = openssl_asn1_str2chunk(X509_EXTENSION_get_data(ext)); | |
948 | /* quick and dirty unwrap of octet string */ | |
949 | if (ostr.len > 2 && | |
950 | ostr.ptr[0] == V_ASN1_OCTET_STRING && ostr.ptr[1] == ostr.len - 2) | |
951 | { | |
952 | free(this->subjectKeyIdentifier.ptr); | |
953 | this->subjectKeyIdentifier = chunk_clone(chunk_skip(ostr, 2)); | |
954 | return TRUE; | |
955 | } | |
956 | return FALSE; | |
5728c6aa MW |
957 | } |
958 | ||
959 | /** | |
960 | * Parse X509 extensions we are interested in | |
961 | */ | |
962 | static bool parse_extensions(private_openssl_x509_t *this) | |
963 | { | |
964 | STACK_OF(X509_EXTENSION) *extensions; | |
965 | int i, num; | |
966 | ||
967 | extensions = this->x509->cert_info->extensions; | |
968 | if (extensions) | |
969 | { | |
970 | num = sk_X509_EXTENSION_num(extensions); | |
971 | ||
972 | for (i = 0; i < num; i++) | |
973 | { | |
974 | X509_EXTENSION *ext; | |
975 | bool ok; | |
976 | ||
977 | ext = sk_X509_EXTENSION_value(extensions, i); | |
978 | switch (OBJ_obj2nid(X509_EXTENSION_get_object(ext))) | |
979 | { | |
980 | case NID_info_access: | |
981 | ok = parse_authorityInfoAccess_ext(this, ext); | |
982 | break; | |
983 | case NID_authority_key_identifier: | |
984 | ok = parse_authKeyIdentifier_ext(this, ext); | |
985 | break; | |
986 | case NID_subject_key_identifier: | |
987 | ok = parse_subjectKeyIdentifier_ext(this, ext); | |
988 | break; | |
989 | case NID_subject_alt_name: | |
990 | ok = parse_generalNames_ext(this->subjectAltNames, ext); | |
991 | break; | |
992 | case NID_issuer_alt_name: | |
993 | ok = parse_generalNames_ext(this->issuerAltNames, ext); | |
994 | break; | |
995 | case NID_basic_constraints: | |
996 | ok = parse_basicConstraints_ext(this, ext); | |
997 | break; | |
28bcbc29 TB |
998 | case NID_key_usage: |
999 | ok = parse_keyUsage_ext(this, ext); | |
1000 | break; | |
c3e7b3de MW |
1001 | case NID_ext_key_usage: |
1002 | ok = parse_extKeyUsage_ext(this, ext); | |
1003 | break; | |
5728c6aa MW |
1004 | case NID_crl_distribution_points: |
1005 | ok = parse_crlDistributionPoints_ext(this, ext); | |
1006 | break; | |
5e4b1ad2 MR |
1007 | #ifndef OPENSSL_NO_RFC3779 |
1008 | case NID_sbgp_ipAddrBlock: | |
1009 | ok = parse_ipAddrBlock_ext(this, ext); | |
1010 | break; | |
1011 | #endif /* !OPENSSL_NO_RFC3779 */ | |
5728c6aa | 1012 | default: |
c4fd3b2f AS |
1013 | ok = X509_EXTENSION_get_critical(ext) == 0 || |
1014 | !lib->settings->get_bool(lib->settings, | |
1015 | "libstrongswan.x509.enforce_critical", TRUE); | |
b0892d09 MW |
1016 | if (!ok) |
1017 | { | |
3f55f203 MW |
1018 | char buf[80] = ""; |
1019 | ||
1020 | OBJ_obj2txt(buf, sizeof(buf), | |
1021 | X509_EXTENSION_get_object(ext), 0); | |
1022 | DBG1(DBG_LIB, "found unsupported critical X.509 " | |
1023 | "extension: %s", buf); | |
b0892d09 | 1024 | } |
5728c6aa MW |
1025 | break; |
1026 | } | |
1027 | if (!ok) | |
1028 | { | |
1029 | return FALSE; | |
1030 | } | |
1031 | } | |
1032 | } | |
1033 | return TRUE; | |
1034 | } | |
1035 | ||
1036 | /** | |
1037 | * Parse a DER encoded x509 certificate | |
1038 | */ | |
1039 | static bool parse_certificate(private_openssl_x509_t *this) | |
1040 | { | |
1041 | const unsigned char *ptr = this->encoding.ptr; | |
1042 | hasher_t *hasher; | |
1043 | chunk_t chunk; | |
1044 | ||
1045 | this->x509 = d2i_X509(NULL, &ptr, this->encoding.len); | |
1046 | if (!this->x509) | |
1047 | { | |
1048 | return FALSE; | |
1049 | } | |
a6850b84 MW |
1050 | if (X509_get_version(this->x509) < 0 || X509_get_version(this->x509) > 2) |
1051 | { | |
1052 | DBG1(DBG_LIB, "unsupported x509 version: %d", | |
1053 | X509_get_version(this->x509) + 1); | |
1054 | return FALSE; | |
1055 | } | |
1056 | ||
5728c6aa MW |
1057 | this->subject = openssl_x509_name2id(X509_get_subject_name(this->x509)); |
1058 | this->issuer = openssl_x509_name2id(X509_get_issuer_name(this->x509)); | |
1059 | ||
1060 | switch (openssl_asn1_known_oid(this->x509->cert_info->key->algor->algorithm)) | |
1061 | { | |
1062 | case OID_RSA_ENCRYPTION: | |
1063 | this->pubkey = lib->creds->create(lib->creds, | |
1064 | CRED_PUBLIC_KEY, KEY_RSA, BUILD_BLOB_ASN1_DER, | |
1065 | openssl_asn1_str2chunk(X509_get0_pubkey_bitstr(this->x509)), | |
1066 | BUILD_END); | |
1067 | break; | |
1068 | case OID_EC_PUBLICKEY: | |
1069 | /* for ECDSA, we need the full subjectPublicKeyInfo, as it contains | |
1070 | * the curve parameters. */ | |
1071 | chunk = openssl_i2chunk(X509_PUBKEY, X509_get_X509_PUBKEY(this->x509)); | |
1072 | this->pubkey = lib->creds->create(lib->creds, | |
1073 | CRED_PUBLIC_KEY, KEY_ECDSA, BUILD_BLOB_ASN1_DER, | |
1074 | chunk, BUILD_END); | |
1075 | free(chunk.ptr); | |
1076 | break; | |
1077 | default: | |
1078 | DBG1(DBG_LIB, "unsupported public key algorithm"); | |
1079 | break; | |
1080 | } | |
1081 | if (!this->subject || !this->issuer || !this->pubkey) | |
1082 | { | |
1083 | return FALSE; | |
1084 | } | |
1085 | ||
1086 | this->notBefore = openssl_asn1_to_time(X509_get_notBefore(this->x509)); | |
1087 | this->notAfter = openssl_asn1_to_time(X509_get_notAfter(this->x509)); | |
1088 | ||
1089 | if (!chunk_equals( | |
1090 | openssl_asn1_obj2chunk(this->x509->cert_info->signature->algorithm), | |
1091 | openssl_asn1_obj2chunk(this->x509->sig_alg->algorithm))) | |
1092 | { | |
1093 | return FALSE; | |
1094 | } | |
1095 | this->scheme = signature_scheme_from_oid(openssl_asn1_known_oid( | |
1096 | this->x509->sig_alg->algorithm)); | |
1097 | ||
1098 | if (!parse_extensions(this)) | |
1099 | { | |
c4fd3b2f | 1100 | return FALSE; |
5728c6aa MW |
1101 | } |
1102 | ||
1103 | hasher = lib->crypto->create_hasher(lib->crypto, HASH_SHA1); | |
87dd205b | 1104 | if (!hasher || !hasher->allocate_hash(hasher, this->encoding, &this->hash)) |
5728c6aa | 1105 | { |
87dd205b | 1106 | DESTROY_IF(hasher); |
5728c6aa MW |
1107 | return FALSE; |
1108 | } | |
5728c6aa MW |
1109 | hasher->destroy(hasher); |
1110 | ||
a37f2d20 | 1111 | if (issued_by(this, &this->public.x509.interface, NULL)) |
5728c6aa MW |
1112 | { |
1113 | this->flags |= X509_SELF_SIGNED; | |
1114 | } | |
1115 | return TRUE; | |
1116 | } | |
1117 | ||
1118 | openssl_x509_t *openssl_x509_load(certificate_type_t type, va_list args) | |
1119 | { | |
1120 | chunk_t blob = chunk_empty; | |
1121 | x509_flag_t flags = 0; | |
1122 | ||
1123 | while (TRUE) | |
1124 | { | |
1125 | switch (va_arg(args, builder_part_t)) | |
1126 | { | |
1127 | case BUILD_BLOB_ASN1_DER: | |
1128 | blob = va_arg(args, chunk_t); | |
1129 | continue; | |
1130 | case BUILD_X509_FLAG: | |
1131 | flags |= va_arg(args, x509_flag_t); | |
1132 | continue; | |
1133 | case BUILD_END: | |
1134 | break; | |
1135 | default: | |
1136 | return NULL; | |
1137 | } | |
1138 | break; | |
1139 | } | |
1140 | ||
1141 | if (blob.ptr) | |
1142 | { | |
1143 | private_openssl_x509_t *this; | |
1144 | ||
1145 | this = create_empty(); | |
1146 | this->encoding = chunk_clone(blob); | |
1147 | this->flags |= flags; | |
1148 | if (parse_certificate(this)) | |
1149 | { | |
1150 | return &this->public; | |
1151 | } | |
1152 | DBG1(DBG_LIB, "OpenSSL X.509 parsing failed"); | |
1153 | destroy(this); | |
1154 | } | |
1155 | return NULL; | |
1156 | } |