ipseckey: Properly free enumerated certificates
[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 /** most recently enumerated certificate */
61 certificate_t *cert;
62 } cert_enumerator_t;
63
64 METHOD(enumerator_t, cert_enumerator_enumerate, bool,
65 cert_enumerator_t *this, certificate_t **cert)
66 {
67 ipseckey_t *cur_ipseckey;
68 public_key_t *public;
69 rr_t *cur_rr;
70 chunk_t key;
71
72 /* Get the next supported IPSECKEY using the inner enumerator. */
73 while (this->inner->enumerate(this->inner, &cur_rr))
74 {
75 cur_ipseckey = ipseckey_create_frm_rr(cur_rr);
76
77 if (!cur_ipseckey)
78 {
79 DBG1(DBG_CFG, " failed to parse IPSECKEY, skipping");
80 continue;
81 }
82
83 if (cur_ipseckey->get_algorithm(cur_ipseckey) != IPSECKEY_ALGORITHM_RSA)
84 {
85 DBG1(DBG_CFG, " unsupported IPSECKEY algorithm, skipping");
86 cur_ipseckey->destroy(cur_ipseckey);
87 continue;
88 }
89
90 /* wrap the key of the IPSECKEY in a certificate and return this
91 * certificate */
92 key = cur_ipseckey->get_public_key(cur_ipseckey);
93 public = lib->creds->create(lib->creds, CRED_PUBLIC_KEY, KEY_RSA,
94 BUILD_BLOB_DNSKEY, key,
95 BUILD_END);
96 cur_ipseckey->destroy(cur_ipseckey);
97 if (!public)
98 {
99 DBG1(DBG_CFG, " failed to create public key from IPSECKEY");
100 continue;
101 }
102 DESTROY_IF(this->cert);
103 this->cert = lib->creds->create(lib->creds, CRED_CERTIFICATE,
104 CERT_TRUSTED_PUBKEY,
105 BUILD_PUBLIC_KEY, public,
106 BUILD_SUBJECT, this->identity,
107 BUILD_NOT_BEFORE_TIME, this->notBefore,
108 BUILD_NOT_AFTER_TIME, this->notAfter,
109 BUILD_END);
110 public->destroy(public);
111 if (!this->cert)
112 {
113 DBG1(DBG_CFG, " failed to create certificate from IPSECKEY");
114 continue;
115 }
116 *cert = this->cert;
117 return TRUE;
118 }
119 return FALSE;
120 }
121
122 METHOD(enumerator_t, cert_enumerator_destroy, void,
123 cert_enumerator_t *this)
124 {
125 DESTROY_IF(this->cert);
126 this->inner->destroy(this->inner);
127 this->response->destroy(this->response);
128 free(this);
129 }
130
131 METHOD(credential_set_t, create_cert_enumerator, enumerator_t*,
132 private_ipseckey_cred_t *this, certificate_type_t cert, key_type_t key,
133 identification_t *id, bool trusted)
134 {
135 resolver_response_t *response;
136 enumerator_t *rrsig_enum;
137 cert_enumerator_t *e;
138 rr_set_t *rrset;
139 rr_t *rrsig;
140 bio_reader_t *reader;
141 uint32_t nBefore, nAfter;
142 chunk_t ignore;
143 char *fqdn;
144
145 if (!id || id->get_type(id) != ID_FQDN)
146 {
147 return enumerator_create_empty();
148 }
149
150 /* query the DNS for the required IPSECKEY RRs */
151 if (asprintf(&fqdn, "%Y", id) <= 0)
152 {
153 DBG1(DBG_CFG, "failed to determine FQDN to retrieve IPSECKEY RRs");
154 return enumerator_create_empty();
155 }
156 DBG1(DBG_CFG, "performing a DNS query for IPSECKEY RRs of '%s'", fqdn);
157 response = this->res->query(this->res, fqdn, RR_CLASS_IN, RR_TYPE_IPSECKEY);
158 if (!response)
159 {
160 DBG1(DBG_CFG, " query for IPSECKEY RRs failed");
161 free(fqdn);
162 return enumerator_create_empty();
163 }
164 free(fqdn);
165
166 if (!response->has_data(response) ||
167 !response->query_name_exist(response))
168 {
169 DBG1(DBG_CFG, " unable to retrieve IPSECKEY RRs from the DNS");
170 response->destroy(response);
171 return enumerator_create_empty();
172 }
173
174 if (response->get_security_state(response) != SECURE)
175 {
176 DBG1(DBG_CFG, " DNSSEC state of IPSECKEY RRs is not secure");
177 response->destroy(response);
178 return enumerator_create_empty();
179 }
180
181 /* determine the validity period of the retrieved IPSECKEYs
182 *
183 * we use the "Signature Inception" and "Signature Expiration" field
184 * of the first RRSIG RR to determine the validity period of the
185 * IPSECKEY RRs.
186 * TODO: Take multiple RRSIGs into account. */
187 rrset = response->get_rr_set(response);
188 rrsig_enum = rrset->create_rrsig_enumerator(rrset);
189 if (!rrsig_enum || !rrsig_enum->enumerate(rrsig_enum, &rrsig))
190 {
191 DBG1(DBG_CFG, " unable to determine the validity period of "
192 "IPSECKEY RRs because no RRSIGs are present");
193 DESTROY_IF(rrsig_enum);
194 response->destroy(response);
195 return enumerator_create_empty();
196 }
197 rrsig_enum->destroy(rrsig_enum);
198
199 /* parse the RRSIG for its validity period (RFC 4034) */
200 reader = bio_reader_create(rrsig->get_rdata(rrsig));
201 if (!reader->read_data(reader, 8, &ignore) ||
202 !reader->read_uint32(reader, &nAfter) ||
203 !reader->read_uint32(reader, &nBefore))
204 {
205 DBG1(DBG_CFG, " unable to determine the validity period of RRSIG RRs");
206 reader->destroy(reader);
207 response->destroy(response);
208 return enumerator_create_empty();
209 }
210 reader->destroy(reader);
211
212 INIT(e,
213 .public = {
214 .enumerate = (void*)_cert_enumerator_enumerate,
215 .destroy = _cert_enumerator_destroy,
216 },
217 .inner = rrset->create_rr_enumerator(rrset),
218 .response = response,
219 .notBefore = nBefore,
220 .notAfter = nAfter,
221 .identity = id,
222 );
223 return &e->public;
224 }
225
226 METHOD(ipseckey_cred_t, destroy, void,
227 private_ipseckey_cred_t *this)
228 {
229 this->res->destroy(this->res);
230 free(this);
231 }
232
233 /**
234 * Described in header.
235 */
236 ipseckey_cred_t *ipseckey_cred_create(resolver_t *res)
237 {
238 private_ipseckey_cred_t *this;
239
240 INIT(this,
241 .public = {
242 .set = {
243 .create_private_enumerator = (void*)return_null,
244 .create_cert_enumerator = _create_cert_enumerator,
245 .create_shared_enumerator = (void*)return_null,
246 .create_cdp_enumerator = (void*)return_null,
247 .cache_cert = (void*)nop,
248 },
249 .destroy = _destroy,
250 },
251 .res = res,
252 );
253
254 return &this->public;
255 }