Migrated eap_md5 plugin to INIT/METHOD macros
[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)
123 {
124 return FAILED;
125 }
126 rng->allocate_bytes(rng, CHALLENGE_LEN, &this->challenge);
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 this->challenge = chunk_clone(chunk_skip(data, 6));
151 if (data.len < 6 || this->challenge.len < *(data.ptr + 5))
152 {
153 DBG1(DBG_IKE, "received invalid EAP-MD5 message");
154 return FAILED;
155 }
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 if (this->identifier != in->get_identifier(in))
180 {
181 DBG1(DBG_IKE, "received invalid EAP-MD5 message");
182 return FAILED;
183 }
184 if (hash_challenge(this, &expected, this->server, this->peer) != SUCCESS)
185 {
186 return FAILED;
187 }
188 data = in->get_data(in);
189 response = chunk_skip(data, 6);
190
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, destroy, void,
222 private_eap_md5_t *this)
223 {
224 this->peer->destroy(this->peer);
225 this->server->destroy(this->server);
226 chunk_free(&this->challenge);
227 free(this);
228 }
229
230 /*
231 * See header
232 */
233 eap_md5_t *eap_md5_create_server(identification_t *server, identification_t *peer)
234 {
235 private_eap_md5_t *this;
236
237 INIT(this,
238 .public = {
239 .eap_method_interface = {
240 .initiate = _initiate_server,
241 .process = _process_server,
242 .get_type = _get_type,
243 .is_mutual = _is_mutual,
244 .get_msk = _get_msk,
245 .destroy = _destroy,
246 },
247 },
248 .peer = peer->clone(peer),
249 .server = server->clone(server),
250 .challenge = chunk_empty,
251 .identifier = 0,
252 );
253
254 /* generate a non-zero identifier */
255 do {
256 this->identifier = random();
257 } while (!this->identifier);
258
259 return &this->public;
260 }
261
262 /*
263 * See header
264 */
265 eap_md5_t *eap_md5_create_peer(identification_t *server, identification_t *peer)
266 {
267 private_eap_md5_t *this;
268
269 INIT(this,
270 .public = {
271 .eap_method_interface = {
272 .initiate = _initiate_peer,
273 .process = _process_peer,
274 .get_type = _get_type,
275 .is_mutual = _is_mutual,
276 .get_msk = _get_msk,
277 .destroy = _destroy,
278 },
279 },
280 .peer = peer->clone(peer),
281 .server = server->clone(server),
282 .challenge = chunk_empty,
283 .identifier = 0,
284 );
285
286 return &this->public;
287 }
288