redirect-manager: Add helper function to create and parse REDIRECT notify data
[strongswan.git] / src / libcharon / sa / redirect_manager.c
1 /*
2 * Copyright (C) 2015 Tobias Brunner
3 * Hochschule fuer Technik Rapperswil
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 */
15
16 #include "redirect_manager.h"
17
18 #include <collections/linked_list.h>
19 #include <threading/rwlock.h>
20 #include <bio/bio_reader.h>
21 #include <bio/bio_writer.h>
22
23 typedef struct private_redirect_manager_t private_redirect_manager_t;
24
25 /**
26 * Private data
27 */
28 struct private_redirect_manager_t {
29
30 /**
31 * Public interface
32 */
33 redirect_manager_t public;
34
35 /**
36 * Registered providers
37 */
38 linked_list_t *providers;
39
40 /**
41 * Lock to access list of providers
42 */
43 rwlock_t *lock;
44 };
45
46
47 /**
48 * Gateway identify types
49 *
50 * The encoding is the same as that for corresponding ID payloads.
51 */
52 typedef enum {
53 /** IPv4 address of the VPN gateway */
54 GATEWAY_ID_TYPE_IPV4 = 1,
55 /** IPv6 address of the VPN gateway */
56 GATEWAY_ID_TYPE_IPV6 = 2,
57 /** FQDN of the VPN gateway */
58 GATEWAY_ID_TYPE_FQDN = 3,
59 } gateway_id_type_t;
60
61 /**
62 * Mapping of gateway identity types to identity types
63 */
64 static id_type_t gateway_to_id_type(gateway_id_type_t type)
65 {
66 switch (type)
67 {
68 case GATEWAY_ID_TYPE_IPV4:
69 return ID_IPV4_ADDR;
70 case GATEWAY_ID_TYPE_IPV6:
71 return ID_IPV6_ADDR;
72 case GATEWAY_ID_TYPE_FQDN:
73 return ID_FQDN;
74 default:
75 return 0;
76 }
77 }
78
79 /**
80 * Mapping of identity types to gateway identity types
81 */
82 static gateway_id_type_t id_type_to_gateway(id_type_t type)
83 {
84 switch (type)
85 {
86 case ID_IPV4_ADDR:
87 return GATEWAY_ID_TYPE_IPV4;
88 case ID_IPV6_ADDR:
89 return GATEWAY_ID_TYPE_IPV6;
90 case ID_FQDN:
91 return GATEWAY_ID_TYPE_FQDN;
92 default:
93 return 0;
94 }
95 }
96
97 METHOD(redirect_manager_t, add_provider, void,
98 private_redirect_manager_t *this, redirect_provider_t *provider)
99 {
100 this->lock->write_lock(this->lock);
101 this->providers->insert_last(this->providers, provider);
102 this->lock->unlock(this->lock);
103 }
104
105 METHOD(redirect_manager_t, remove_provider, void,
106 private_redirect_manager_t *this, redirect_provider_t *provider)
107 {
108 this->lock->write_lock(this->lock);
109 this->providers->remove(this->providers, provider, NULL);
110 this->lock->unlock(this->lock);
111 }
112
113 /**
114 * Determine whether a client should be redirected using the callback with the
115 * given offset into the redirect_provider_t interface.
116 */
117 static bool should_redirect(private_redirect_manager_t *this, ike_sa_t *ike_sa,
118 identification_t **gateway, size_t offset)
119 {
120 enumerator_t *enumerator;
121 void *provider;
122 bool redirect = FALSE;
123
124 this->lock->read_lock(this->lock);
125 enumerator = this->providers->create_enumerator(this->providers);
126 while (enumerator->enumerate(enumerator, &provider))
127 {
128 bool (**method)(void*,ike_sa_t*,identification_t**) = provider + offset;
129 if (*method && (*method)(provider, ike_sa, gateway))
130 {
131 if (*gateway && id_type_to_gateway((*gateway)->get_type(*gateway)))
132 {
133 redirect = TRUE;
134 break;
135 }
136 else
137 {
138 DBG1(DBG_CFG, "redirect provider returned invalid gateway ID");
139 DESTROY_IF(*gateway);
140 }
141 }
142 }
143 enumerator->destroy(enumerator);
144 this->lock->unlock(this->lock);
145 return redirect;
146 }
147
148 METHOD(redirect_manager_t, redirect_on_init, bool,
149 private_redirect_manager_t *this, ike_sa_t *ike_sa,
150 identification_t **gateway)
151 {
152 return should_redirect(this, ike_sa, gateway,
153 offsetof(redirect_provider_t, redirect_on_init));
154 }
155
156 METHOD(redirect_manager_t, redirect_on_auth, bool,
157 private_redirect_manager_t *this, ike_sa_t *ike_sa,
158 identification_t **gateway)
159 {
160 return should_redirect(this, ike_sa, gateway,
161 offsetof(redirect_provider_t, redirect_on_auth));
162 }
163
164 METHOD(redirect_manager_t, destroy, void,
165 private_redirect_manager_t *this)
166 {
167 this->providers->destroy(this->providers);
168 this->lock->destroy(this->lock);
169 free(this);
170 }
171
172 /*
173 * Described in header
174 */
175 redirect_manager_t *redirect_manager_create()
176 {
177 private_redirect_manager_t *this;
178
179 INIT(this,
180 .public = {
181 .add_provider = _add_provider,
182 .remove_provider = _remove_provider,
183 .redirect_on_init = _redirect_on_init,
184 .redirect_on_auth = _redirect_on_auth,
185 .destroy = _destroy,
186 },
187 .providers = linked_list_create(),
188 .lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
189 );
190
191 return &this->public;
192 }
193
194 /*
195 * Encoding of a REDIRECT or REDIRECTED_FROM notify
196 *
197 1 2 3
198 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
199 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
200 | Next Payload |C| RESERVED | Payload Length |
201 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
202 |Protocol ID(=0)| SPI Size (=0) | Notify Message Type |
203 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
204 | GW Ident Type | GW Ident Len | |
205 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ~
206 ~ New Responder GW Identity ~
207 | |
208 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
209 | |
210 ~ Nonce Data ~
211 | |
212 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
213 */
214
215 /*
216 * Described in header
217 */
218 chunk_t redirect_data_create(identification_t *gw, chunk_t nonce)
219 {
220 gateway_id_type_t type;
221 bio_writer_t *writer;
222 chunk_t data;
223
224 type = id_type_to_gateway(gw->get_type(gw));
225 if (!type)
226 {
227 return chunk_empty;
228 }
229
230 writer = bio_writer_create(0);
231 writer->write_uint8(writer, type);
232 writer->write_data8(writer, gw->get_encoding(gw));
233 if (nonce.ptr)
234 {
235 writer->write_data(writer, nonce);
236 }
237
238 data = writer->extract_buf(writer);
239 writer->destroy(writer);
240 return data;
241 }
242
243 /*
244 * Described in header
245 */
246 identification_t *redirect_data_parse(chunk_t data, chunk_t *nonce)
247 {
248 bio_reader_t *reader;
249 id_type_t id_type;
250 chunk_t gateway;
251 u_int8_t type;
252
253 reader = bio_reader_create(data);
254 if (!reader->read_uint8(reader, &type) ||
255 !reader->read_data8(reader, &gateway))
256 {
257 DBG1(DBG_ENC, "invalid REDIRECT notify data");
258 reader->destroy(reader);
259 return NULL;
260 }
261 id_type = gateway_to_id_type(type);
262 if (!id_type)
263 {
264 DBG1(DBG_ENC, "invalid gateway ID type (%d) in REDIRECT notify", type);
265 reader->destroy(reader);
266 return NULL;
267 }
268 if (nonce)
269 {
270 *nonce = chunk_clone(reader->peek(reader));
271 }
272 reader->destroy(reader);
273 return identification_create_from_encoding(id_type, gateway);
274 }