Added an XAuth plugin that forwards authentication to EAP methods
[strongswan.git] / src / libcharon / plugins / xauth_eap / xauth_eap.c
1 /*
2 * Copyright (C) 2011 Martin Willi
3 * Copyright (C) 2011 revosec AG
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 "xauth_eap.h"
17
18 #include <daemon.h>
19
20 #include <library.h>
21 #include <credentials/sets/callback_cred.h>
22
23 typedef struct private_xauth_eap_t private_xauth_eap_t;
24
25 /**
26 * Private data of an xauth_eap_t object.
27 */
28 struct private_xauth_eap_t {
29
30 /**
31 * Public interface.
32 */
33 xauth_eap_t public;
34
35 /**
36 * ID of the server
37 */
38 identification_t *server;
39
40 /**
41 * ID of the peer
42 */
43 identification_t *peer;
44
45 /**
46 * Callback credential set
47 */
48 callback_cred_t *cred;
49
50 /**
51 * XAuth password
52 */
53 chunk_t pass;
54 };
55
56 /**
57 * Callback credential set function
58 */
59 static shared_key_t* shared_cb(private_xauth_eap_t *this, shared_key_type_t type,
60 identification_t *me, identification_t *other,
61 id_match_t *match_me, id_match_t *match_other)
62 {
63 shared_key_t *shared;
64
65 if (!this->pass.len)
66 {
67 return NULL;
68 }
69 if (type != SHARED_EAP && type != SHARED_ANY)
70 {
71 return NULL;
72 }
73 if (me)
74 {
75 if (!this->peer->equals(this->peer, me))
76 {
77 return NULL;
78 }
79 if (match_me)
80 {
81 *match_me = ID_MATCH_PERFECT;
82 }
83 }
84 else if (match_me)
85 {
86 *match_me = ID_MATCH_ANY;
87 }
88 if (other)
89 {
90 if (!this->server->equals(this->server, other))
91 {
92 return NULL;
93 }
94 if (match_other)
95 {
96 *match_other = ID_MATCH_PERFECT;
97 }
98 }
99 else if (match_other)
100 {
101 *match_other = ID_MATCH_ANY;
102 }
103 shared = shared_key_create(SHARED_EAP, chunk_clone(this->pass));
104 this->pass = chunk_empty;
105 return shared;
106 }
107
108 /**
109 * Do EAP exchanges to verify secret
110 */
111 static bool verify_eap(private_xauth_eap_t *this, eap_method_t *backend)
112 {
113 eap_payload_t *request, *response;
114 eap_method_t *frontend;
115 eap_type_t type;
116 u_int32_t vendor;
117 status_t status;
118
119 if (backend->initiate(backend, &request) != NEED_MORE)
120 {
121 return FALSE;
122 }
123 type = request->get_type(request, &vendor);
124 frontend = charon->eap->create_instance(charon->eap, type, vendor,
125 EAP_PEER, this->server, this->peer);
126 if (!frontend)
127 {
128 DBG1(DBG_IKE, "XAuth-EAP backend requested %N, but not supported",
129 eap_type_names, type);
130 request->destroy(request);
131 return FALSE;
132 }
133 while (TRUE)
134 {
135 /* credential set is active in frontend only, but not in backend */
136 lib->credmgr->add_local_set(lib->credmgr, &this->cred->set, TRUE);
137 status = frontend->process(frontend, request, &response);
138 lib->credmgr->remove_local_set(lib->credmgr, &this->cred->set);
139 request->destroy(request);
140 if (status != NEED_MORE)
141 { /* clients should never return SUCCESS */
142 frontend->destroy(frontend);
143 return FALSE;
144 }
145 status = backend->process(backend, response, &request);
146 response->destroy(response);
147 switch (status)
148 {
149 case SUCCESS:
150 frontend->destroy(frontend);
151 return TRUE;
152 case NEED_MORE:
153 break;
154 default:
155 frontend->destroy(frontend);
156 return FALSE;
157 }
158 }
159 }
160
161 METHOD(xauth_method_t, initiate, status_t,
162 private_xauth_eap_t *this, cp_payload_t **out)
163 {
164 cp_payload_t *cp;
165
166 cp = cp_payload_create_type(CONFIGURATION_V1, CFG_REQUEST);
167 cp->add_attribute(cp, configuration_attribute_create_chunk(
168 CONFIGURATION_ATTRIBUTE_V1, XAUTH_USER_NAME, chunk_empty));
169 cp->add_attribute(cp, configuration_attribute_create_chunk(
170 CONFIGURATION_ATTRIBUTE_V1, XAUTH_USER_PASSWORD, chunk_empty));
171 *out = cp;
172 return NEED_MORE;
173 }
174
175 METHOD(xauth_method_t, process, status_t,
176 private_xauth_eap_t *this, cp_payload_t *in, cp_payload_t **out)
177 {
178 configuration_attribute_t *attr;
179 enumerator_t *enumerator;
180 identification_t *id;
181 chunk_t user = chunk_empty;
182 eap_method_t *backend;
183 eap_type_t type;
184 char *name;
185 bool ok;
186
187 enumerator = in->create_attribute_enumerator(in);
188 while (enumerator->enumerate(enumerator, &attr))
189 {
190 switch (attr->get_type(attr))
191 {
192 case XAUTH_USER_NAME:
193 user = attr->get_chunk(attr);
194 break;
195 case XAUTH_USER_PASSWORD:
196 this->pass = attr->get_chunk(attr);
197 break;
198 default:
199 break;
200 }
201 }
202 enumerator->destroy(enumerator);
203
204 if (!user.ptr || !this->pass.ptr)
205 {
206 DBG1(DBG_IKE, "peer did not respond to our XAuth request");
207 return FAILED;
208 }
209 if (user.len)
210 {
211 id = identification_create_from_data(user);
212 if (!id)
213 {
214 DBG1(DBG_IKE, "failed to parse provided XAuth username");
215 return FAILED;
216 }
217 this->peer->destroy(this->peer);
218 this->peer = id;
219 }
220
221 name = lib->settings->get_str(lib->settings,
222 "charon.plugins.xauth-eap.backend", "radius");
223 type = eap_type_from_string(name);
224 if (!type)
225 {
226 DBG1(DBG_CFG, "Unknown XAuth-EAP method: %s", name);
227 return FAILED;
228 }
229 backend = charon->eap->create_instance(charon->eap, type, 0, EAP_SERVER,
230 this->server, this->peer);
231 if (!backend)
232 {
233 DBG1(DBG_CFG, "XAuth-EAP method backend not supported: %s", name);
234 return FAILED;
235 }
236 ok = verify_eap(this, backend);
237 backend->destroy(backend);
238 if (ok)
239 {
240 return SUCCESS;
241 }
242 return FAILED;
243 }
244
245 METHOD(xauth_method_t, get_identity, identification_t*,
246 private_xauth_eap_t *this)
247 {
248 return this->peer;
249 }
250
251 METHOD(xauth_method_t, destroy, void,
252 private_xauth_eap_t *this)
253 {
254 this->cred->destroy(this->cred);
255 this->server->destroy(this->server);
256 this->peer->destroy(this->peer);
257 free(this);
258 }
259
260 /*
261 * Described in header.
262 */
263 xauth_eap_t *xauth_eap_create_server(identification_t *server,
264 identification_t *peer)
265 {
266 private_xauth_eap_t *this;
267
268 INIT(this,
269 .public = {
270 .xauth_method = {
271 .initiate = _initiate,
272 .process = _process,
273 .get_identity = _get_identity,
274 .destroy = _destroy,
275 },
276 },
277 .server = server->clone(server),
278 .peer = peer->clone(peer),
279 );
280
281 this->cred = callback_cred_create_shared((void*)shared_cb, this);
282
283 return &this->public;
284 }