Query secrets in EAP-MD5 with me/other identities, fixing lookup in NetworkManager
[strongswan.git] / src / charon / 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 identififier
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 = charon->credentials->get_shared(charon->credentials, SHARED_EAP,
89 me, other);
90 if (shared == NULL)
91 {
92 DBG1(DBG_IKE, "no EAP key found for hosts '%Y' - '%Y'", me, other);
93 return NOT_FOUND;
94 }
95 concat = chunk_cata("ccc", chunk_from_thing(this->identifier),
96 shared->get_key(shared), this->challenge);
97 shared->destroy(shared);
98 hasher = lib->crypto->create_hasher(lib->crypto, HASH_MD5);
99 if (hasher == NULL)
100 {
101 DBG1(DBG_IKE, "EAP-MD5 failed, MD5 not supported");
102 return FAILED;
103 }
104 hasher->allocate_hash(hasher, concat, response);
105 hasher->destroy(hasher);
106 return SUCCESS;
107 }
108
109 /**
110 * Implementation of eap_method_t.initiate for the peer
111 */
112 static status_t initiate_peer(private_eap_md5_t *this, eap_payload_t **out)
113 {
114 /* peer never initiates */
115 return FAILED;
116 }
117
118 /**
119 * Implementation of eap_method_t.initiate for the server
120 */
121 static status_t initiate_server(private_eap_md5_t *this, eap_payload_t **out)
122 {
123 rng_t *rng;
124 eap_md5_header_t *req;
125
126 rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
127 if (!rng)
128 {
129 return FAILED;
130 }
131 rng->allocate_bytes(rng, CHALLENGE_LEN, &this->challenge);
132 rng->destroy(rng);
133
134 req = alloca(PAYLOAD_LEN);
135 req->length = htons(PAYLOAD_LEN);
136 req->code = EAP_REQUEST;
137 req->identifier = this->identifier;
138 req->type = EAP_MD5;
139 req->value_size = this->challenge.len;
140 memcpy(req->value, this->challenge.ptr, this->challenge.len);
141
142 *out = eap_payload_create_data(chunk_create((void*)req, PAYLOAD_LEN));
143 return NEED_MORE;
144 }
145
146 /**
147 * Implementation of eap_method_t.process for the peer
148 */
149 static status_t process_peer(private_eap_md5_t *this,
150 eap_payload_t *in, eap_payload_t **out)
151 {
152 chunk_t response;
153 chunk_t data;
154 eap_md5_header_t *req;
155
156 this->identifier = in->get_identifier(in);
157 data = in->get_data(in);
158 this->challenge = chunk_clone(chunk_skip(data, 6));
159 if (data.len < 6 || this->challenge.len < *(data.ptr + 5))
160 {
161 DBG1(DBG_IKE, "received invalid EAP-MD5 message");
162 return FAILED;
163 }
164 if (hash_challenge(this, &response, this->peer, this->server) != SUCCESS)
165 {
166 return FAILED;
167 }
168 req = alloca(PAYLOAD_LEN);
169 req->length = htons(PAYLOAD_LEN);
170 req->code = EAP_RESPONSE;
171 req->identifier = this->identifier;
172 req->type = EAP_MD5;
173 req->value_size = response.len;
174 memcpy(req->value, response.ptr, response.len);
175 chunk_free(&response);
176
177 *out = eap_payload_create_data(chunk_create((void*)req, PAYLOAD_LEN));
178 return NEED_MORE;
179 }
180
181 /**
182 * Implementation of eap_method_t.process for the server
183 */
184 static status_t process_server(private_eap_md5_t *this,
185 eap_payload_t *in, eap_payload_t **out)
186 {
187 chunk_t response, expected;
188 chunk_t data;
189
190 if (this->identifier != in->get_identifier(in))
191 {
192 DBG1(DBG_IKE, "received invalid EAP-MD5 message");
193 return FAILED;
194 }
195 if (hash_challenge(this, &expected, this->server, this->peer) != SUCCESS)
196 {
197 return FAILED;
198 }
199 data = in->get_data(in);
200 response = chunk_skip(data, 6);
201
202 if (response.len < expected.len ||
203 !memeq(response.ptr, expected.ptr, expected.len))
204 {
205 chunk_free(&expected);
206 DBG1(DBG_IKE, "EAP-MD5 verification failed");
207 return FAILED;
208 }
209 chunk_free(&expected);
210 return SUCCESS;
211 }
212
213 /**
214 * Implementation of eap_method_t.get_type.
215 */
216 static eap_type_t get_type(private_eap_md5_t *this, u_int32_t *vendor)
217 {
218 *vendor = 0;
219 return EAP_MD5;
220 }
221
222 /**
223 * Implementation of eap_method_t.get_msk.
224 */
225 static status_t get_msk(private_eap_md5_t *this, chunk_t *msk)
226 {
227 return FAILED;
228 }
229
230 /**
231 * Implementation of eap_method_t.is_mutual.
232 */
233 static bool is_mutual(private_eap_md5_t *this)
234 {
235 return FALSE;
236 }
237
238 /**
239 * Implementation of eap_method_t.destroy.
240 */
241 static void destroy(private_eap_md5_t *this)
242 {
243 this->peer->destroy(this->peer);
244 this->server->destroy(this->server);
245 chunk_free(&this->challenge);
246 free(this);
247 }
248
249 /**
250 * Generic constructor
251 */
252 static private_eap_md5_t *eap_md5_create_generic(identification_t *server,
253 identification_t *peer)
254 {
255 private_eap_md5_t *this = malloc_thing(private_eap_md5_t);
256
257 this->public.eap_method_interface.initiate = NULL;
258 this->public.eap_method_interface.process = NULL;
259 this->public.eap_method_interface.get_type = (eap_type_t(*)(eap_method_t*,u_int32_t*))get_type;
260 this->public.eap_method_interface.is_mutual = (bool(*)(eap_method_t*))is_mutual;
261 this->public.eap_method_interface.get_msk = (status_t(*)(eap_method_t*,chunk_t*))get_msk;
262 this->public.eap_method_interface.destroy = (void(*)(eap_method_t*))destroy;
263
264 /* private data */
265 this->peer = peer->clone(peer);
266 this->server = server->clone(server);
267 this->challenge = chunk_empty;
268 this->identifier = 0;
269
270 return this;
271 }
272
273 /*
274 * see header
275 */
276 eap_md5_t *eap_md5_create_server(identification_t *server, identification_t *peer)
277 {
278 private_eap_md5_t *this = eap_md5_create_generic(server, peer);
279
280 this->public.eap_method_interface.initiate = (status_t(*)(eap_method_t*,eap_payload_t**))initiate_server;
281 this->public.eap_method_interface.process = (status_t(*)(eap_method_t*,eap_payload_t*,eap_payload_t**))process_server;
282
283 /* generate a non-zero identifier */
284 do {
285 this->identifier = random();
286 } while (!this->identifier);
287
288 return &this->public;
289 }
290
291 /*
292 * see header
293 */
294 eap_md5_t *eap_md5_create_peer(identification_t *server, identification_t *peer)
295 {
296 private_eap_md5_t *this = eap_md5_create_generic(server, peer);
297
298 this->public.eap_method_interface.initiate = (status_t(*)(eap_method_t*,eap_payload_t**))initiate_peer;
299 this->public.eap_method_interface.process = (status_t(*)(eap_method_t*,eap_payload_t*,eap_payload_t**))process_peer;
300
301 return &this->public;
302 }
303