049d160028b878cdca32cbd02490c7c37e976d7d
[strongswan.git] / src / libcharon / plugins / eap_md5 / eap_md5.c
1 /*
2 * Copyright (C) 2007 Martin Willi
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 "eap_md5.h"
17
18 #include <daemon.h>
19 #include <library.h>
20 #include <crypto/hashers/hasher.h>
21
22 typedef struct private_eap_md5_t private_eap_md5_t;
23
24 /**
25 * Private data of an eap_md5_t object.
26 */
27 struct private_eap_md5_t {
28
29 /**
30 * Public authenticator_t interface.
31 */
32 eap_md5_t public;
33
34 /**
35 * ID of the server
36 */
37 identification_t *server;
38
39 /**
40 * ID of the peer
41 */
42 identification_t *peer;
43
44 /**
45 * challenge sent by the server
46 */
47 chunk_t challenge;
48
49 /**
50 * EAP message identifier
51 */
52 u_int8_t identifier;
53 };
54
55 typedef struct eap_md5_header_t eap_md5_header_t;
56
57 /**
58 * packed eap MD5 header struct
59 */
60 struct eap_md5_header_t {
61 /** EAP code (REQUEST/RESPONSE) */
62 u_int8_t code;
63 /** unique message identifier */
64 u_int8_t identifier;
65 /** length of whole message */
66 u_int16_t length;
67 /** EAP type */
68 u_int8_t type;
69 /** length of value (challenge) */
70 u_int8_t value_size;
71 /** actual value */
72 u_int8_t value[];
73 } __attribute__((__packed__));
74
75 #define CHALLENGE_LEN 16
76 #define PAYLOAD_LEN (CHALLENGE_LEN + sizeof(eap_md5_header_t))
77
78 /**
79 * Hash the challenge string, create response
80 */
81 static status_t hash_challenge(private_eap_md5_t *this, chunk_t *response,
82 identification_t *me, identification_t *other)
83 {
84 shared_key_t *shared;
85 chunk_t concat;
86 hasher_t *hasher;
87
88 shared = lib->credmgr->get_shared(lib->credmgr, SHARED_EAP, me, other);
89 if (shared == NULL)
90 {
91 DBG1(DBG_IKE, "no EAP key found for hosts '%Y' - '%Y'", me, other);
92 return NOT_FOUND;
93 }
94 concat = chunk_cata("ccc", chunk_from_thing(this->identifier),
95 shared->get_key(shared), this->challenge);
96 shared->destroy(shared);
97 hasher = lib->crypto->create_hasher(lib->crypto, HASH_MD5);
98 if (hasher == NULL)
99 {
100 DBG1(DBG_IKE, "EAP-MD5 failed, MD5 not supported");
101 return FAILED;
102 }
103 hasher->allocate_hash(hasher, concat, response);
104 hasher->destroy(hasher);
105 return SUCCESS;
106 }
107
108 /**
109 * Implementation of eap_method_t.initiate for the peer
110 */
111 static status_t initiate_peer(private_eap_md5_t *this, eap_payload_t **out)
112 {
113 /* peer never initiates */
114 return FAILED;
115 }
116
117 /**
118 * Implementation of eap_method_t.initiate for the server
119 */
120 static status_t initiate_server(private_eap_md5_t *this, eap_payload_t **out)
121 {
122 rng_t *rng;
123 eap_md5_header_t *req;
124
125 rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
126 if (!rng)
127 {
128 return FAILED;
129 }
130 rng->allocate_bytes(rng, CHALLENGE_LEN, &this->challenge);
131 rng->destroy(rng);
132
133 req = alloca(PAYLOAD_LEN);
134 req->length = htons(PAYLOAD_LEN);
135 req->code = EAP_REQUEST;
136 req->identifier = this->identifier;
137 req->type = EAP_MD5;
138 req->value_size = this->challenge.len;
139 memcpy(req->value, this->challenge.ptr, this->challenge.len);
140
141 *out = eap_payload_create_data(chunk_create((void*)req, PAYLOAD_LEN));
142 return NEED_MORE;
143 }
144
145 /**
146 * Implementation of eap_method_t.process for the peer
147 */
148 static status_t process_peer(private_eap_md5_t *this,
149 eap_payload_t *in, eap_payload_t **out)
150 {
151 chunk_t response;
152 chunk_t data;
153 eap_md5_header_t *req;
154
155 this->identifier = in->get_identifier(in);
156 data = in->get_data(in);
157 this->challenge = chunk_clone(chunk_skip(data, 6));
158 if (data.len < 6 || this->challenge.len < *(data.ptr + 5))
159 {
160 DBG1(DBG_IKE, "received invalid EAP-MD5 message");
161 return FAILED;
162 }
163 if (hash_challenge(this, &response, this->peer, this->server) != SUCCESS)
164 {
165 return FAILED;
166 }
167 req = alloca(PAYLOAD_LEN);
168 req->length = htons(PAYLOAD_LEN);
169 req->code = EAP_RESPONSE;
170 req->identifier = this->identifier;
171 req->type = EAP_MD5;
172 req->value_size = response.len;
173 memcpy(req->value, response.ptr, response.len);
174 chunk_free(&response);
175
176 *out = eap_payload_create_data(chunk_create((void*)req, PAYLOAD_LEN));
177 return NEED_MORE;
178 }
179
180 /**
181 * Implementation of eap_method_t.process for the server
182 */
183 static status_t process_server(private_eap_md5_t *this,
184 eap_payload_t *in, eap_payload_t **out)
185 {
186 chunk_t response, expected;
187 chunk_t data;
188
189 if (this->identifier != in->get_identifier(in))
190 {
191 DBG1(DBG_IKE, "received invalid EAP-MD5 message");
192 return FAILED;
193 }
194 if (hash_challenge(this, &expected, this->server, this->peer) != SUCCESS)
195 {
196 return FAILED;
197 }
198 data = in->get_data(in);
199 response = chunk_skip(data, 6);
200
201 if (response.len < expected.len ||
202 !memeq(response.ptr, expected.ptr, expected.len))
203 {
204 chunk_free(&expected);
205 DBG1(DBG_IKE, "EAP-MD5 verification failed");
206 return FAILED;
207 }
208 chunk_free(&expected);
209 return SUCCESS;
210 }
211
212 /**
213 * Implementation of eap_method_t.get_type.
214 */
215 static eap_type_t get_type(private_eap_md5_t *this, u_int32_t *vendor)
216 {
217 *vendor = 0;
218 return EAP_MD5;
219 }
220
221 /**
222 * Implementation of eap_method_t.get_msk.
223 */
224 static status_t get_msk(private_eap_md5_t *this, chunk_t *msk)
225 {
226 return FAILED;
227 }
228
229 /**
230 * Implementation of eap_method_t.is_mutual.
231 */
232 static bool is_mutual(private_eap_md5_t *this)
233 {
234 return FALSE;
235 }
236
237 /**
238 * Implementation of eap_method_t.destroy.
239 */
240 static void destroy(private_eap_md5_t *this)
241 {
242 this->peer->destroy(this->peer);
243 this->server->destroy(this->server);
244 chunk_free(&this->challenge);
245 free(this);
246 }
247
248 /**
249 * Generic constructor
250 */
251 static private_eap_md5_t *eap_md5_create_generic(identification_t *server,
252 identification_t *peer)
253 {
254 private_eap_md5_t *this = malloc_thing(private_eap_md5_t);
255
256 this->public.eap_method_interface.initiate = NULL;
257 this->public.eap_method_interface.process = NULL;
258 this->public.eap_method_interface.get_type = (eap_type_t(*)(eap_method_t*,u_int32_t*))get_type;
259 this->public.eap_method_interface.is_mutual = (bool(*)(eap_method_t*))is_mutual;
260 this->public.eap_method_interface.get_msk = (status_t(*)(eap_method_t*,chunk_t*))get_msk;
261 this->public.eap_method_interface.destroy = (void(*)(eap_method_t*))destroy;
262
263 /* private data */
264 this->peer = peer->clone(peer);
265 this->server = server->clone(server);
266 this->challenge = chunk_empty;
267 this->identifier = 0;
268
269 return this;
270 }
271
272 /*
273 * see header
274 */
275 eap_md5_t *eap_md5_create_server(identification_t *server, identification_t *peer)
276 {
277 private_eap_md5_t *this = eap_md5_create_generic(server, peer);
278
279 this->public.eap_method_interface.initiate = (status_t(*)(eap_method_t*,eap_payload_t**))initiate_server;
280 this->public.eap_method_interface.process = (status_t(*)(eap_method_t*,eap_payload_t*,eap_payload_t**))process_server;
281
282 /* generate a non-zero identifier */
283 do {
284 this->identifier = random();
285 } while (!this->identifier);
286
287 return &this->public;
288 }
289
290 /*
291 * see header
292 */
293 eap_md5_t *eap_md5_create_peer(identification_t *server, identification_t *peer)
294 {
295 private_eap_md5_t *this = eap_md5_create_generic(server, peer);
296
297 this->public.eap_method_interface.initiate = (status_t(*)(eap_method_t*,eap_payload_t**))initiate_peer;
298 this->public.eap_method_interface.process = (status_t(*)(eap_method_t*,eap_payload_t*,eap_payload_t**))process_peer;
299
300 return &this->public;
301 }
302