Merge branch 'modular-load'
[strongswan.git] / src / libcharon / plugins / eap_gtc / eap_gtc.c
1 /*
2 * Copyright (C) 2007-2012 Martin Willi
3 * Copyright (C) 2012 revosec AG
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 #include "eap_gtc.h"
18
19 #include <daemon.h>
20 #include <library.h>
21
22 #define GTC_REQUEST_MSG "password"
23
24 typedef struct private_eap_gtc_t private_eap_gtc_t;
25
26 /**
27 * Private data of an eap_gtc_t object.
28 */
29 struct private_eap_gtc_t {
30
31 /**
32 * Public authenticator_t interface.
33 */
34 eap_gtc_t public;
35
36 /**
37 * ID of the server
38 */
39 identification_t *server;
40
41 /**
42 * ID of the peer
43 */
44 identification_t *peer;
45
46 /**
47 * EAP message identififier
48 */
49 u_int8_t identifier;
50 };
51
52 typedef struct eap_gtc_header_t eap_gtc_header_t;
53
54 /**
55 * packed eap GTC header struct
56 */
57 struct eap_gtc_header_t {
58 /** EAP code (REQUEST/RESPONSE) */
59 u_int8_t code;
60 /** unique message identifier */
61 u_int8_t identifier;
62 /** length of whole message */
63 u_int16_t length;
64 /** EAP type */
65 u_int8_t type;
66 /** type data */
67 u_int8_t data[];
68 } __attribute__((__packed__));
69
70 METHOD(eap_method_t, initiate_peer, status_t,
71 private_eap_gtc_t *this, eap_payload_t **out)
72 {
73 /* peer never initiates */
74 return FAILED;
75 }
76
77 METHOD(eap_method_t, initiate_server, status_t,
78 private_eap_gtc_t *this, eap_payload_t **out)
79 {
80 eap_gtc_header_t *req;
81 size_t len;
82
83 len = strlen(GTC_REQUEST_MSG);
84 req = alloca(sizeof(eap_gtc_header_t) + len);
85 req->length = htons(sizeof(eap_gtc_header_t) + len);
86 req->code = EAP_REQUEST;
87 req->identifier = this->identifier;
88 req->type = EAP_GTC;
89 memcpy(req->data, GTC_REQUEST_MSG, len);
90
91 *out = eap_payload_create_data(chunk_create((void*)req,
92 sizeof(eap_gtc_header_t) + len));
93 return NEED_MORE;
94 }
95
96 METHOD(eap_method_t, process_peer, status_t,
97 private_eap_gtc_t *this, eap_payload_t *in, eap_payload_t **out)
98 {
99 eap_gtc_header_t *res;
100 shared_key_t *shared;
101 chunk_t key;
102 size_t len;
103
104 shared = lib->credmgr->get_shared(lib->credmgr, SHARED_EAP,
105 this->peer, this->server);
106 if (shared == NULL)
107 {
108 DBG1(DBG_IKE, "no EAP key found for '%Y' - '%Y'",
109 this->peer, this->server);
110 return FAILED;
111 }
112 key = shared->get_key(shared);
113 len = key.len;
114
115 /* TODO: According to the draft we should "SASLprep" password, RFC4013. */
116
117 this->identifier = in->get_identifier(in);
118 res = alloca(sizeof(eap_gtc_header_t) + len);
119 res->length = htons(sizeof(eap_gtc_header_t) + len);
120 res->code = EAP_RESPONSE;
121 res->identifier = this->identifier;
122 res->type = EAP_GTC;
123 memcpy(res->data, key.ptr, len);
124
125 shared->destroy(shared);
126
127 *out = eap_payload_create_data(chunk_create((void*)res,
128 sizeof(eap_gtc_header_t) + len));
129 return NEED_MORE;
130 }
131
132 METHOD(eap_method_t, process_server, status_t,
133 private_eap_gtc_t *this, eap_payload_t *in, eap_payload_t **out)
134 {
135 status_t status = FAILED;
136 chunk_t user, pass;
137 xauth_method_t *xauth;
138 cp_payload_t *ci, *co;
139 char *backend;
140
141 user = this->peer->get_encoding(this->peer);
142 pass = chunk_skip(in->get_data(in), 5);
143 if (this->identifier != in->get_identifier(in) || !pass.len)
144 {
145 DBG1(DBG_IKE, "received invalid EAP-GTC message");
146 return FAILED;
147 }
148
149 /* get XAuth backend to use for credential verification. Default to PAM
150 * to support legacy EAP-GTC configurations */
151 backend = lib->settings->get_str(lib->settings,
152 "%s.plugins.eap-gtc.backend", "pam", lib->ns);
153 xauth = charon->xauth->create_instance(charon->xauth, backend, XAUTH_SERVER,
154 this->server, this->peer);
155 if (!xauth)
156 {
157 DBG1(DBG_IKE, "creating EAP-GTC XAuth backend '%s' failed", backend);
158 return FAILED;
159 }
160 if (xauth->initiate(xauth, &co) == NEED_MORE)
161 {
162 /* assume that "out" contains username/password attributes */
163 co->destroy(co);
164 ci = cp_payload_create_type(CONFIGURATION_V1, CFG_REPLY);
165 ci->add_attribute(ci, configuration_attribute_create_chunk(
166 CONFIGURATION_ATTRIBUTE_V1, XAUTH_USER_NAME, user));
167 ci->add_attribute(ci, configuration_attribute_create_chunk(
168 CONFIGURATION_ATTRIBUTE_V1, XAUTH_USER_PASSWORD, pass));
169 switch (xauth->process(xauth, ci, &co))
170 {
171 case SUCCESS:
172 status = SUCCESS;
173 break;
174 case NEED_MORE:
175 /* TODO: multiple exchanges currently not supported */
176 co->destroy(co);
177 break;
178 case FAILED:
179 default:
180 break;
181 }
182 ci->destroy(ci);
183 }
184 xauth->destroy(xauth);
185 return status;
186 }
187
188 METHOD(eap_method_t, get_type, eap_type_t,
189 private_eap_gtc_t *this, u_int32_t *vendor)
190 {
191 *vendor = 0;
192 return EAP_GTC;
193 }
194
195 METHOD(eap_method_t, get_msk, status_t,
196 private_eap_gtc_t *this, chunk_t *msk)
197 {
198 return FAILED;
199 }
200
201 METHOD(eap_method_t, get_identifier, u_int8_t,
202 private_eap_gtc_t *this)
203 {
204 return this->identifier;
205 }
206
207 METHOD(eap_method_t, set_identifier, void,
208 private_eap_gtc_t *this, u_int8_t identifier)
209 {
210 this->identifier = identifier;
211 }
212
213 METHOD(eap_method_t, is_mutual, bool,
214 private_eap_gtc_t *this)
215 {
216 return FALSE;
217 }
218
219 METHOD(eap_method_t, destroy, void,
220 private_eap_gtc_t *this)
221 {
222 this->peer->destroy(this->peer);
223 this->server->destroy(this->server);
224 free(this);
225 }
226
227 /**
228 * Generic constructor
229 */
230 static private_eap_gtc_t *eap_gtc_create_generic(identification_t *server,
231 identification_t *peer)
232 {
233 private_eap_gtc_t *this;
234
235 INIT(this,
236 .public = {
237 .eap_method_interface = {
238 .get_type = _get_type,
239 .is_mutual = _is_mutual,
240 .get_msk = _get_msk,
241 .get_identifier = _get_identifier,
242 .set_identifier = _set_identifier,
243 .destroy = _destroy,
244 },
245 },
246 .peer = peer->clone(peer),
247 .server = server->clone(server),
248 );
249
250 return this;
251 }
252
253 /*
254 * see header
255 */
256 eap_gtc_t *eap_gtc_create_server(identification_t *server, identification_t *peer)
257 {
258 private_eap_gtc_t *this = eap_gtc_create_generic(server, peer);
259
260 this->public.eap_method_interface.initiate = _initiate_server;
261 this->public.eap_method_interface.process = _process_server;
262
263 /* generate a non-zero identifier */
264 do {
265 this->identifier = random();
266 } while (!this->identifier);
267
268 return &this->public;
269 }
270
271 /*
272 * see header
273 */
274 eap_gtc_t *eap_gtc_create_peer(identification_t *server, identification_t *peer)
275 {
276 private_eap_gtc_t *this = eap_gtc_create_generic(server, peer);
277
278 this->public.eap_method_interface.initiate = _initiate_peer;
279 this->public.eap_method_interface.process = _process_peer;
280
281 return &this->public;
282 }