1 /* Certification Authority (CA) support for IKE authentication
2 * Copyright (C) 2002-2004 Andreas Steffen, Zuercher Hochschule Winterthur
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the
6 * Free Software Foundation; either version 2 of the License, or (at your
7 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
21 #include <sys/types.h>
23 #include <utils/identification.h>
27 #include "constants.h"
35 #include "smartcard.h"
37 /* chained list of X.509 authority certificates (ca, aa, and ocsp) */
39 static x509cert_t
*x509authcerts
= NULL
;
41 /* chained list of X.509 certification authority information records */
43 static ca_info_t
*ca_infos
= NULL
;
46 * Checks if CA a is trusted by CA b
48 bool trusted_ca(chunk_t a
, chunk_t b
, int *pathlen
)
52 /* no CA b specified -> any CA a is accepted */
55 *pathlen
= (a
.ptr
== NULL
)?
0 : MAX_CA_PATH_LEN
;
59 /* no CA a specified -> trust cannot be established */
62 *pathlen
= MAX_CA_PATH_LEN
;
68 /* CA a equals CA b -> we have a match */
74 /* CA a might be a subordinate CA of b */
75 lock_authcert_list("trusted_ca");
77 while ((*pathlen
)++ < MAX_CA_PATH_LEN
)
79 certificate_t
*certificate
;
80 identification_t
*issuer
;
84 cacert
= get_authcert(a
, chunk_empty
, X509_CA
);
89 certificate
= cacert
->cert
;
91 /* is the certificate self-signed? */
93 x509_t
*x509
= (x509_t
*)certificate
;
95 if (x509
->get_flags(x509
) & X509_SELF_SIGNED
)
101 /* does the issuer of CA a match CA b? */
102 issuer
= certificate
->get_issuer(certificate
);
103 issuer_dn
= issuer
->get_encoding(issuer
);
104 match
= same_dn(issuer_dn
, b
);
106 /* we have a match and exit the loop */
111 /* go one level up in the CA chain */
115 unlock_authcert_list("trusted_ca");
120 * does our CA match one of the requested CAs?
122 bool match_requested_ca(generalName_t
*requested_ca
, chunk_t our_ca
,
125 /* if no ca is requested than any ca will match */
126 if (requested_ca
== NULL
)
132 *our_pathlen
= MAX_CA_PATH_LEN
+ 1;
134 while (requested_ca
!= NULL
)
138 if (trusted_ca(our_ca
, requested_ca
->name
, &pathlen
)
139 && pathlen
< *our_pathlen
)
141 *our_pathlen
= pathlen
;
143 requested_ca
= requested_ca
->next
;
146 if (*our_pathlen
> MAX_CA_PATH_LEN
)
148 *our_pathlen
= MAX_CA_PATH_LEN
;
158 * free the first authority certificate in the chain
160 static void free_first_authcert(void)
162 x509cert_t
*first
= x509authcerts
;
163 x509authcerts
= first
->next
;
164 free_x509cert(first
);
168 * free all CA certificates
170 void free_authcerts(void)
172 lock_authcert_list("free_authcerts");
174 while (x509authcerts
!= NULL
)
175 free_first_authcert();
177 unlock_authcert_list("free_authcerts");
181 * get a X.509 authority certificate with a given subject or keyid
183 x509cert_t
* get_authcert(chunk_t subject
, chunk_t keyid
, x509_flag_t auth_flags
)
185 x509cert_t
*cert
, *prev_cert
= NULL
;
187 /* the authority certificate list is empty */
188 if (x509authcerts
== NULL
)
193 for (cert
= x509authcerts
; cert
!= NULL
; prev_cert
= cert
, cert
= cert
->next
)
195 certificate_t
*certificate
= cert
->cert
;
196 x509_t
*x509
= (x509_t
*)certificate
;
197 identification_t
*cert_subject
;
198 chunk_t cert_subject_dn
;
200 /* skip non-matching types of authority certificates */
201 if (!(x509
->get_flags(x509
) & auth_flags
))
206 /* compare the keyid with the certificate's subjectKeyIdentifier */
209 chunk_t subjectKeyId
;
211 subjectKeyId
= x509
->get_subjectKeyIdentifier(x509
);
212 if (subjectKeyId
.ptr
&& !chunk_equals(keyid
, subjectKeyId
))
218 /* compare the subjectDistinguishedNames */
219 cert_subject
= certificate
->get_subject(certificate
);
220 cert_subject_dn
= cert_subject
->get_encoding(cert_subject
);
221 if (!same_dn(subject
, cert_subject_dn
))
226 /* found the authcert */
227 if (cert
!= x509authcerts
)
229 /* bring the certificate up front */
230 prev_cert
->next
= cert
->next
;
231 cert
->next
= x509authcerts
;
232 x509authcerts
= cert
;
240 * add an authority certificate to the chained list
242 x509cert_t
* add_authcert(x509cert_t
*cert
, x509_flag_t auth_flags
)
244 certificate_t
*certificate
= cert
->cert
;
245 x509_t
*x509
= (x509_t
*)certificate
;
246 identification_t
*cert_subject
= certificate
->get_subject(certificate
);
247 chunk_t cert_subject_dn
= cert_subject
->get_encoding(cert_subject
);
248 x509cert_t
*old_cert
;
250 lock_authcert_list("add_authcert");
252 old_cert
= get_authcert(cert_subject_dn
,
253 x509
->get_subjectKeyIdentifier(x509
),
255 if (old_cert
!= NULL
)
257 if (certificate
->equals(certificate
, old_cert
->cert
))
259 DBG(DBG_CONTROL
| DBG_PARSING
,
260 DBG_log(" authcert is already present and identical")
262 unlock_authcert_list("add_authcert");
269 /* cert is already present but will be replaced by new cert */
270 free_first_authcert();
271 DBG(DBG_CONTROL
| DBG_PARSING
,
272 DBG_log(" existing authcert deleted")
277 /* add new authcert to chained list */
278 cert
->next
= x509authcerts
;
279 x509authcerts
= cert
;
280 share_x509cert(cert
); /* set count to one */
281 DBG(DBG_CONTROL
| DBG_PARSING
,
282 DBG_log(" authcert inserted")
284 unlock_authcert_list("add_authcert");
289 * Loads authority certificates
291 void load_authcerts(const char *type
, const char *path
, x509_flag_t auth_flags
)
293 struct dirent
**filelist
;
298 /* change directory to specified path */
299 save_dir
= getcwd(buf
, BUF_LEN
);
303 plog("Could not change to directory '%s'", path
);
307 plog("Changing to directory '%s'", path
);
308 n
= scandir(path
, &filelist
, file_select
, alphasort
);
311 plog(" scandir() error");
318 if (load_cert(filelist
[n
]->d_name
, type
, auth_flags
, &cert
))
320 add_authcert(cert
.u
.x509
, auth_flags
);
327 /* restore directory path */
328 ignore_result(chdir(save_dir
));
332 * list all X.509 authcerts with given auth flags in a chained list
334 void list_authcerts(const char *caption
, x509_flag_t auth_flags
, bool utc
)
336 lock_authcert_list("list_authcerts");
337 list_x509cert_chain(caption
, x509authcerts
, auth_flags
, utc
);
338 unlock_authcert_list("list_authcerts");
342 * get a cacert with a given subject or keyid from an alternative list
344 static const x509cert_t
* get_alt_cacert(chunk_t subject
, chunk_t keyid
,
345 const x509cert_t
*cert
)
352 for (; cert
!= NULL
; cert
= cert
->next
)
354 certificate_t
*certificate
= cert
->cert
;
355 identification_t
*cert_subject
;
356 chunk_t cert_subject_dn
;
358 /* compare the keyid with the certificate's subjectKeyIdentifier */
361 x509_t
*x509
= (x509_t
*)certificate
;
362 chunk_t subjectKeyId
;
364 subjectKeyId
= x509
->get_subjectKeyIdentifier(x509
);
365 if (subjectKeyId
.ptr
&& !chunk_equals(keyid
, subjectKeyId
))
371 /* compare the subjectDistinguishedNames */
372 cert_subject
= certificate
->get_subject(certificate
);
373 cert_subject_dn
= cert_subject
->get_encoding(cert_subject
);
374 if (!same_dn(subject
, cert_subject_dn
))
379 /* we found the cacert */
385 /* establish trust into a candidate authcert by going up the trust chain.
386 * validity and revocation status are not checked.
388 bool trust_authcert_candidate(const x509cert_t
*cert
, const x509cert_t
*alt_chain
)
392 lock_authcert_list("trust_authcert_candidate");
394 for (pathlen
= 0; pathlen
< MAX_CA_PATH_LEN
; pathlen
++)
396 certificate_t
*certificate
= cert
->cert
;
397 x509_t
*x509
= (x509_t
*)certificate
;
398 identification_t
*subject
= certificate
->get_subject(certificate
);
399 identification_t
*issuer
= certificate
->get_issuer(certificate
);
400 chunk_t issuer_dn
= issuer
->get_encoding(issuer
);
401 chunk_t authKeyID
= x509
->get_authKeyIdentifier(x509
);
402 const x509cert_t
*authcert
= NULL
;
405 DBG_log("subject: '%Y'", subject
);
406 DBG_log("issuer: '%Y'", issuer
);
407 if (authKeyID
.ptr
!= NULL
)
409 DBG_log("authkey: %#B", &authKeyID
);
413 /* search in alternative chain first */
414 authcert
= get_alt_cacert(issuer_dn
, authKeyID
, alt_chain
);
416 if (authcert
!= NULL
)
419 DBG_log("issuer cacert found in alternative chain")
424 /* search in trusted chain */
425 authcert
= get_authcert(issuer_dn
, authKeyID
, X509_CA
);
427 if (authcert
!= NULL
)
430 DBG_log("issuer cacert found")
435 plog("issuer cacert not found");
436 unlock_authcert_list("trust_authcert_candidate");
441 if (!certificate
->issued_by(certificate
, authcert
->cert
))
443 plog("certificate signature is invalid");
444 unlock_authcert_list("trust_authcert_candidate");
448 DBG_log("certificate signature is valid")
451 /* check if cert is a self-signed root ca */
452 if (pathlen
> 0 && (x509
->get_flags(x509
) & X509_SELF_SIGNED
))
455 DBG_log("reached self-signed root ca")
457 unlock_authcert_list("trust_authcert_candidate");
461 /* go up one step in the trust chain */
464 plog("maximum ca path length of %d levels exceeded", MAX_CA_PATH_LEN
);
465 unlock_authcert_list("trust_authcert_candidate");
470 * get a CA info record with a given authName or authKeyID
472 ca_info_t
* get_ca_info(chunk_t authname
, chunk_t keyid
)
474 ca_info_t
*ca
= ca_infos
;
478 if ((keyid
.ptr
!= NULL
) ?
same_keyid(keyid
, ca
->authKeyID
)
479 : same_dn(authname
, ca
->authName
))
490 * free the dynamic memory used by a ca_info record
493 free_ca_info(ca_info_t
* ca_info
)
499 ca_info
->crluris
->destroy_function(ca_info
->crluris
, free
);
501 free(ca_info
->ldaphost
);
502 free(ca_info
->ldapbase
);
503 free(ca_info
->ocspuri
);
504 free(ca_info
->authName
.ptr
);
505 free(ca_info
->authKeyID
.ptr
);
510 * free all CA certificates
512 void free_ca_infos(void)
514 while (ca_infos
!= NULL
)
516 ca_info_t
*ca
= ca_infos
;
518 ca_infos
= ca_infos
->next
;
524 * find a CA information record by name and optionally delete it
526 bool find_ca_info_by_name(const char *name
, bool delete)
528 ca_info_t
**ca_p
= &ca_infos
;
529 ca_info_t
*ca
= *ca_p
;
533 /* is there already an entry? */
534 if (streq(name
, ca
->name
))
538 lock_ca_info_list("find_ca_info_by_name");
541 plog("deleting ca description \"%s\"", name
);
542 unlock_ca_info_list("find_ca_info_by_name");
553 * Create an empty ca_info_t record
555 ca_info_t
* create_ca_info(void)
557 ca_info_t
*ca_info
= malloc_thing(ca_info_t
);
559 memset(ca_info
, 0, sizeof(ca_info_t
));
560 ca_info
->crluris
= linked_list_create();
566 * Adds a CA description to a chained list
568 void add_ca_info(const whack_message_t
*msg
)
570 smartcard_t
*sc
= NULL
;
572 bool valid_cert
= FALSE
;
573 bool cached_cert
= FALSE
;
575 if (find_ca_info_by_name(msg
->name
, FALSE
))
577 loglog(RC_DUPNAME
, "attempt to redefine ca record \"%s\"", msg
->name
);
581 if (scx_on_smartcard(msg
->cacert
))
583 /* load CA cert from smartcard */
584 valid_cert
= scx_load_cert(msg
->cacert
, &sc
, &cert
, &cached_cert
);
588 /* load CA cert from file */
589 valid_cert
= load_ca_cert(msg
->cacert
, &cert
);
594 x509cert_t
*cacert
= cert
.u
.x509
;
595 certificate_t
*certificate
= cacert
->cert
;
596 x509_t
*x509
= (x509_t
*)certificate
;
597 identification_t
*subject
= certificate
->get_subject(certificate
);
598 chunk_t subject_dn
= subject
->get_encoding(subject
);
599 chunk_t subjectKeyID
= x509
->get_subjectKeyIdentifier(x509
);
600 ca_info_t
*ca
= NULL
;
602 /* does the authname already exist? */
603 ca
= get_ca_info(subject_dn
, subjectKeyID
);
607 /* ca_info is already present */
608 loglog(RC_DUPNAME
, " duplicate ca information in record \"%s\" found,"
609 "ignoring \"%s\"", ca
->name
, msg
->name
);
610 free_x509cert(cacert
);
614 plog("added ca description \"%s\"", msg
->name
);
616 /* create and initialize new ca_info record */
617 ca
= create_ca_info();
620 ca
->name
= clone_str(msg
->name
);
623 ca
->authName
= chunk_clone(subject_dn
);
625 DBG_log("authname: '%Y'", subject
)
629 if (subjectKeyID
.ptr
)
631 ca
->authKeyID
= chunk_clone(subjectKeyID
);
632 DBG(DBG_CONTROL
| DBG_PARSING
,
633 DBG_log("authkey: %#B", &subjectKeyID
)
638 ca
->ldaphost
= clone_str(msg
->ldaphost
);
641 ca
->ldapbase
= clone_str(msg
->ldapbase
);
644 if (msg
->ocspuri
!= NULL
)
646 if (strncasecmp(msg
->ocspuri
, "http", 4) == 0)
647 ca
->ocspuri
= clone_str(msg
->ocspuri
);
649 plog(" ignoring ocspuri with unkown protocol");
653 add_distribution_point(ca
->crluris
, msg
->crluri
);
654 add_distribution_point(ca
->crluris
, msg
->crluri2
);
657 ca
->strictcrlpolicy
= msg
->whack_strict
;
659 /* insert ca_info record into the chained list */
660 lock_ca_info_list("add_ca_info");
664 ca
->installed
= time(NULL
);
666 unlock_ca_info_list("add_ca_info");
668 /* add cacert to list of authcerts */
669 cacert
= add_authcert(cacert
, X509_CA
);
670 if (!cached_cert
&& sc
!= NULL
)
672 if (sc
->last_cert
.type
== CERT_X509_SIGNATURE
)
673 sc
->last_cert
.u
.x509
->count
--;
674 sc
->last_cert
.u
.x509
= cacert
;
675 share_cert(sc
->last_cert
);
678 time(&sc
->last_load
);
683 * list all ca_info records in the chained list
685 void list_ca_infos(bool utc
)
687 ca_info_t
*ca
= ca_infos
;
691 whack_log(RC_COMMENT
, " ");
692 whack_log(RC_COMMENT
, "List of X.509 CA Information Records:");
693 whack_log(RC_COMMENT
, " ");
700 /* strictpolicy per CA not supported yet
702 whack_log(RC_COMMENT, "%T, \"%s\", strictcrlpolicy: %s"
703 , &ca->installed, utc, ca->name
704 , ca->strictcrlpolicy? "yes":"no");
706 whack_log(RC_COMMENT
, "%T, \"%s\"", &ca
->installed
, utc
, ca
->name
);
707 dntoa(buf
, BUF_LEN
, ca
->authName
);
708 whack_log(RC_COMMENT
, " authname: '%s'", buf
);
709 if (ca
->ldaphost
!= NULL
)
710 whack_log(RC_COMMENT
, " ldaphost: '%s'", ca
->ldaphost
);
711 if (ca
->ldapbase
!= NULL
)
712 whack_log(RC_COMMENT
, " ldapbase: '%s'", ca
->ldapbase
);
713 if (ca
->ocspuri
!= NULL
)
714 whack_log(RC_COMMENT
, " ocspuri: '%s'", ca
->ocspuri
);
716 list_distribution_points(ca
->crluris
);
718 if (ca
->authKeyID
.ptr
!= NULL
)
720 datatot(ca
->authKeyID
.ptr
, ca
->authKeyID
.len
, ':'
722 whack_log(RC_COMMENT
, " authkey: %s", buf
);