ipseckey: Refactor creation of certificate enumerator
[strongswan.git] / src / libcharon / plugins / ipseckey / ipseckey_cred.c
1 /*
2 * Copyright (C) 2013 Tobias Brunner
3 * Copyright (C) 2012 Reto Guadagnini
4 * Hochschule fuer Technik Rapperswil
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 #define _GNU_SOURCE
18 #include <stdio.h>
19 #include <string.h>
20
21 #include "ipseckey_cred.h"
22 #include "ipseckey.h"
23
24 #include <bio/bio_reader.h>
25
26 typedef struct private_ipseckey_cred_t private_ipseckey_cred_t;
27
28 /**
29 * Private data of an ipseckey_cred_t object
30 */
31 struct private_ipseckey_cred_t {
32
33 /**
34 * Public part
35 */
36 ipseckey_cred_t public;
37
38 /**
39 * DNS resolver
40 */
41 resolver_t *res;
42 };
43
44 /**
45 * enumerator over certificates
46 */
47 typedef struct {
48 /** implements enumerator interface */
49 enumerator_t public;
50 /** inner enumerator (enumerates IPSECKEY resource records) */
51 enumerator_t *inner;
52 /** response of the DNS resolver which contains the IPSECKEYs */
53 resolver_response_t *response;
54 /* IPSECKEYs are not valid before this point in time */
55 time_t notBefore;
56 /* IPSECKEYs are not valid after this point in time */
57 time_t notAfter;
58 /* identity to which the IPSECKEY belongs */
59 identification_t *identity;
60 } cert_enumerator_t;
61
62 METHOD(enumerator_t, cert_enumerator_enumerate, bool,
63 cert_enumerator_t *this, certificate_t **cert)
64 {
65 rr_t *cur_rr = NULL;
66 ipseckey_t *cur_ipseckey = NULL;
67 chunk_t pub_key;
68 public_key_t * key = NULL;
69 bool supported_ipseckey_found = FALSE;
70
71 /* Get the next supported IPSECKEY using the inner enumerator. */
72 while (this->inner->enumerate(this->inner, &cur_rr) &&
73 !supported_ipseckey_found)
74 {
75 supported_ipseckey_found = TRUE;
76
77 cur_ipseckey = ipseckey_create_frm_rr(cur_rr);
78
79 if (!cur_ipseckey)
80 {
81 DBG1(DBG_CFG, "failed to parse ipseckey - skipping this key");
82 supported_ipseckey_found = FALSE;
83 }
84
85 if (cur_ipseckey &&
86 cur_ipseckey->get_algorithm(cur_ipseckey) != IPSECKEY_ALGORITHM_RSA)
87 {
88 DBG1(DBG_CFG, "unsupported ipseckey algorithm - skipping this key");
89 cur_ipseckey->destroy(cur_ipseckey);
90 supported_ipseckey_found = FALSE;
91 }
92 }
93
94 if (supported_ipseckey_found)
95 {
96 /*
97 * Wrap the key of the IPSECKEY in a certificate and return this
98 * certificate.
99 */
100 pub_key = cur_ipseckey->get_public_key(cur_ipseckey);
101
102 key = lib->creds->create(lib->creds, CRED_PUBLIC_KEY, KEY_RSA,
103 BUILD_BLOB_DNSKEY, pub_key,
104 BUILD_END);
105
106 if (!key)
107 {
108 DBG1(DBG_CFG, "failed to create public key from ipseckey");
109 cur_ipseckey->destroy(cur_ipseckey);
110 return FALSE;
111 }
112
113 *cert = lib->creds->create(lib->creds, CRED_CERTIFICATE,
114 CERT_TRUSTED_PUBKEY,
115 BUILD_PUBLIC_KEY, key,
116 BUILD_SUBJECT, this->identity,
117 BUILD_NOT_BEFORE_TIME, this->notBefore,
118 BUILD_NOT_AFTER_TIME, this->notAfter,
119 BUILD_END);
120 return TRUE;
121 }
122
123 return FALSE;
124 }
125
126 METHOD(enumerator_t, cert_enumerator_destroy, void,
127 cert_enumerator_t *this)
128 {
129 this->inner->destroy(this->inner);
130 this->response->destroy(this->response);
131 free(this);
132 }
133
134 METHOD(credential_set_t, create_cert_enumerator, enumerator_t*,
135 private_ipseckey_cred_t *this, certificate_type_t cert, key_type_t key,
136 identification_t *id, bool trusted)
137 {
138 resolver_response_t *response;
139 enumerator_t *rrsig_enum;
140 cert_enumerator_t *e;
141 rr_set_t *rrset;
142 rr_t *rrsig;
143 bio_reader_t *reader;
144 u_int32_t nBefore, nAfter;
145 chunk_t ignore;
146 char *fqdn;
147
148 if (!id || id->get_type(id) != ID_FQDN)
149 {
150 return enumerator_create_empty();
151 }
152
153 /* query the DNS for the required IPSECKEY RRs */
154 if (asprintf(&fqdn, "%Y", id) <= 0)
155 {
156 DBG1(DBG_CFG, "failed to determine FQDN to retrieve IPSECKEY RRs");
157 return enumerator_create_empty();
158 }
159 DBG1(DBG_CFG, "performing a DNS query for IPSECKEY RRs of '%s'", fqdn);
160 response = this->res->query(this->res, fqdn, RR_CLASS_IN, RR_TYPE_IPSECKEY);
161 if (!response)
162 {
163 DBG1(DBG_CFG, " query for IPSECKEY RRs failed");
164 free(fqdn);
165 return enumerator_create_empty();
166 }
167 free(fqdn);
168
169 if (!response->has_data(response) ||
170 !response->query_name_exist(response))
171 {
172 DBG1(DBG_CFG, " unable to retrieve IPSECKEY RRs from the DNS");
173 response->destroy(response);
174 return enumerator_create_empty();
175 }
176
177 if (response->get_security_state(response) != SECURE)
178 {
179 DBG1(DBG_CFG, " DNSSEC state of IPSECKEY RRs is not secure");
180 response->destroy(response);
181 return enumerator_create_empty();
182 }
183
184 /* determine the validity period of the retrieved IPSECKEYs
185 *
186 * we use the "Signature Inception" and "Signature Expiration" field
187 * of the first RRSIG RR to determine the validity period of the
188 * IPSECKEY RRs.
189 * TODO: Take multiple RRSIGs into account. */
190 rrset = response->get_rr_set(response);
191 rrsig_enum = rrset->create_rrsig_enumerator(rrset);
192 if (!rrsig_enum || !rrsig_enum->enumerate(rrsig_enum, &rrsig))
193 {
194 DBG1(DBG_CFG, " unable to determine the validity period of "
195 "IPSECKEY RRs because no RRSIGs are present");
196 DESTROY_IF(rrsig_enum);
197 response->destroy(response);
198 return enumerator_create_empty();
199 }
200 rrsig_enum->destroy(rrsig_enum);
201
202 /* parse the RRSIG for its validity period (RFC 4034) */
203 reader = bio_reader_create(rrsig->get_rdata(rrsig));
204 if (!reader->read_data(reader, 8, &ignore) ||
205 !reader->read_uint32(reader, &nAfter) ||
206 !reader->read_uint32(reader, &nBefore))
207 {
208 DBG1(DBG_CFG, " unable to determine the validity period of RRSIG RRs");
209 reader->destroy(reader);
210 response->destroy(response);
211 return enumerator_create_empty();
212 }
213 reader->destroy(reader);
214
215 INIT(e,
216 .public = {
217 .enumerate = (void*)_cert_enumerator_enumerate,
218 .destroy = _cert_enumerator_destroy,
219 },
220 .inner = rrset->create_rr_enumerator(rrset),
221 .response = response,
222 .notBefore = nBefore,
223 .notAfter = nAfter,
224 .identity = id,
225 );
226 return &e->public;
227 }
228
229 METHOD(ipseckey_cred_t, destroy, void,
230 private_ipseckey_cred_t *this)
231 {
232 this->res->destroy(this->res);
233 free(this);
234 }
235
236 /**
237 * Described in header.
238 */
239 ipseckey_cred_t *ipseckey_cred_create(resolver_t *res)
240 {
241 private_ipseckey_cred_t *this;
242
243 INIT(this,
244 .public = {
245 .set = {
246 .create_private_enumerator = (void*)return_null,
247 .create_cert_enumerator = _create_cert_enumerator,
248 .create_shared_enumerator = (void*)return_null,
249 .create_cdp_enumerator = (void*)return_null,
250 .cache_cert = (void*)nop,
251 },
252 .destroy = _destroy,
253 },
254 .res = res,
255 );
256
257 return &this->public;
258 }