Check rng return value when generating challenges in eap-md5 and mschapv2 plugins
[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 METHOD(eap_method_t, initiate_peer, status_t,
109 private_eap_md5_t *this, eap_payload_t **out)
110 {
111 /* peer never initiates */
112 return FAILED;
113 }
114
115 METHOD(eap_method_t, initiate_server, status_t,
116 private_eap_md5_t *this, eap_payload_t **out)
117 {
118 rng_t *rng;
119 eap_md5_header_t *req;
120
121 rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
122 if (!rng || !rng->allocate_bytes(rng, CHALLENGE_LEN, &this->challenge))
123 {
124 DESTROY_IF(rng);
125 return FAILED;
126 }
127 rng->destroy(rng);
128
129 req = alloca(PAYLOAD_LEN);
130 req->length = htons(PAYLOAD_LEN);
131 req->code = EAP_REQUEST;
132 req->identifier = this->identifier;
133 req->type = EAP_MD5;
134 req->value_size = this->challenge.len;
135 memcpy(req->value, this->challenge.ptr, this->challenge.len);
136
137 *out = eap_payload_create_data(chunk_create((void*)req, PAYLOAD_LEN));
138 return NEED_MORE;
139 }
140
141 METHOD(eap_method_t, process_peer, status_t,
142 private_eap_md5_t *this, eap_payload_t *in, eap_payload_t **out)
143 {
144 chunk_t response;
145 chunk_t data;
146 eap_md5_header_t *req;
147
148 this->identifier = in->get_identifier(in);
149 data = in->get_data(in);
150 if (data.len < 6 || data.ptr[5] + 6 > data.len)
151 {
152 DBG1(DBG_IKE, "received invalid EAP-MD5 message");
153 return FAILED;
154 }
155 this->challenge = chunk_clone(chunk_create(data.ptr + 6, data.ptr[5]));
156 if (hash_challenge(this, &response, this->peer, this->server) != SUCCESS)
157 {
158 return FAILED;
159 }
160 req = alloca(PAYLOAD_LEN);
161 req->length = htons(PAYLOAD_LEN);
162 req->code = EAP_RESPONSE;
163 req->identifier = this->identifier;
164 req->type = EAP_MD5;
165 req->value_size = response.len;
166 memcpy(req->value, response.ptr, response.len);
167 chunk_free(&response);
168
169 *out = eap_payload_create_data(chunk_create((void*)req, PAYLOAD_LEN));
170 return NEED_MORE;
171 }
172
173 METHOD(eap_method_t, process_server, status_t,
174 private_eap_md5_t *this, eap_payload_t *in, eap_payload_t **out)
175 {
176 chunk_t response, expected;
177 chunk_t data;
178
179 data = in->get_data(in);
180 if (this->identifier != in->get_identifier(in) ||
181 data.len < 6 || data.ptr[5] + 6 > data.len)
182 {
183 DBG1(DBG_IKE, "received invalid EAP-MD5 message");
184 return FAILED;
185 }
186 if (hash_challenge(this, &expected, this->server, this->peer) != SUCCESS)
187 {
188 return FAILED;
189 }
190 response = chunk_create(data.ptr + 6, data.ptr[5]);
191 if (response.len < expected.len ||
192 !memeq(response.ptr, expected.ptr, expected.len))
193 {
194 chunk_free(&expected);
195 DBG1(DBG_IKE, "EAP-MD5 verification failed");
196 return FAILED;
197 }
198 chunk_free(&expected);
199 return SUCCESS;
200 }
201
202 METHOD(eap_method_t, get_type, eap_type_t,
203 private_eap_md5_t *this, u_int32_t *vendor)
204 {
205 *vendor = 0;
206 return EAP_MD5;
207 }
208
209 METHOD(eap_method_t, get_msk, status_t,
210 private_eap_md5_t *this, chunk_t *msk)
211 {
212 return FAILED;
213 }
214
215 METHOD(eap_method_t, is_mutual, bool,
216 private_eap_md5_t *this)
217 {
218 return FALSE;
219 }
220
221 METHOD(eap_method_t, get_identifier, u_int8_t,
222 private_eap_md5_t *this)
223 {
224 return this->identifier;
225 }
226
227 METHOD(eap_method_t, set_identifier, void,
228 private_eap_md5_t *this, u_int8_t identifier)
229 {
230 this->identifier = identifier;
231 }
232
233 METHOD(eap_method_t, destroy, void,
234 private_eap_md5_t *this)
235 {
236 this->peer->destroy(this->peer);
237 this->server->destroy(this->server);
238 chunk_free(&this->challenge);
239 free(this);
240 }
241
242 /*
243 * See header
244 */
245 eap_md5_t *eap_md5_create_server(identification_t *server, identification_t *peer)
246 {
247 private_eap_md5_t *this;
248
249 INIT(this,
250 .public = {
251 .eap_method = {
252 .initiate = _initiate_server,
253 .process = _process_server,
254 .get_type = _get_type,
255 .is_mutual = _is_mutual,
256 .get_msk = _get_msk,
257 .get_identifier = _get_identifier,
258 .set_identifier = _set_identifier,
259 .destroy = _destroy,
260 },
261 },
262 .peer = peer->clone(peer),
263 .server = server->clone(server),
264 );
265
266 /* generate a non-zero identifier */
267 do {
268 this->identifier = random();
269 } while (!this->identifier);
270
271 return &this->public;
272 }
273
274 /*
275 * See header
276 */
277 eap_md5_t *eap_md5_create_peer(identification_t *server, identification_t *peer)
278 {
279 private_eap_md5_t *this;
280
281 INIT(this,
282 .public = {
283 .eap_method = {
284 .initiate = _initiate_peer,
285 .process = _process_peer,
286 .get_type = _get_type,
287 .is_mutual = _is_mutual,
288 .get_msk = _get_msk,
289 .destroy = _destroy,
290 },
291 },
292 .peer = peer->clone(peer),
293 .server = server->clone(server),
294 );
295
296 return &this->public;
297 }
298