merged EAP-MD5 into trunk
[strongswan.git] / src / charon / sa / authenticators / eap / eap_md5.c
1 /**
2 * @file eap_md5.c
3 *
4 * @brief Implementation of eap_md5_t.
5 *
6 */
7
8 /*
9 * Copyright (C) 2007 Martin Willi
10 * Hochschule fuer Technik Rapperswil
11 *
12 * This program is free software; you can redistribute it and/or modify it
13 * under the terms of the GNU General Public License as published by the
14 * Free Software Foundation; either version 2 of the License, or (at your
15 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
19 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
20 * for more details.
21 */
22
23 #include "eap_md5.h"
24
25 #include <daemon.h>
26 #include <library.h>
27
28 typedef struct private_eap_md5_t private_eap_md5_t;
29
30 /**
31 * Private data of an eap_md5_t object.
32 */
33 struct private_eap_md5_t {
34
35 /**
36 * Public authenticator_t interface.
37 */
38 eap_md5_t public;
39
40 /**
41 * ID of the server
42 */
43 identification_t *server;
44
45 /**
46 * ID of the peer
47 */
48 identification_t *peer;
49
50 /**
51 * challenge sent by the server
52 */
53 chunk_t challenge;
54
55 /**
56 * EAP message identififier
57 */
58 u_int8_t identifier;
59 };
60
61 typedef struct eap_md5_header_t eap_md5_header_t;
62
63 /**
64 * packed eap MD5 header struct
65 */
66 struct eap_md5_header_t {
67 /** EAP code (REQUEST/RESPONSE) */
68 u_int8_t code;
69 /** unique message identifier */
70 u_int8_t identifier;
71 /** length of whole message */
72 u_int16_t length;
73 /** EAP type */
74 u_int8_t type;
75 /** length of value (challenge) */
76 u_int8_t value_size;
77 /** actual value */
78 u_int8_t value[];
79 } __attribute__((__packed__));
80
81 #define CHALLENGE_LEN 16
82 #define PAYLOAD_LEN (CHALLENGE_LEN + sizeof(eap_md5_header_t))
83
84 /**
85 * Hash the challenge string, create response
86 */
87 static status_t hash_challenge(private_eap_md5_t *this, chunk_t *response)
88 {
89 chunk_t concat, secret;
90 hasher_t *hasher;
91
92 if (charon->credentials->get_eap_key(charon->credentials, this->server,
93 this->peer, &secret) != SUCCESS)
94 {
95 DBG1(DBG_IKE, "no EAP key found for hosts '%D' - '%D'",
96 this->server, this->peer);
97 return NOT_FOUND;
98 }
99 concat = chunk_cata("cmc", chunk_from_thing(this->identifier),
100 secret, this->challenge);
101 hasher = hasher_create(HASH_MD5);
102 hasher->allocate_hash(hasher, concat, response);
103 hasher->destroy(hasher);
104 return SUCCESS;
105 }
106
107 /**
108 * Implementation of eap_method_t.initiate for the peer
109 */
110 static status_t initiate_peer(private_eap_md5_t *this, eap_payload_t **out)
111 {
112 /* peer never initiates */
113 return FAILED;
114 }
115
116 /**
117 * Implementation of eap_method_t.initiate for the server
118 */
119 static status_t initiate_server(private_eap_md5_t *this, eap_payload_t **out)
120 {
121 randomizer_t *randomizer;
122 status_t status;
123 eap_md5_header_t *req;
124
125 randomizer = randomizer_create();
126 status = randomizer->allocate_pseudo_random_bytes(randomizer, CHALLENGE_LEN,
127 &this->challenge);
128 randomizer->destroy(randomizer);
129 if (status != SUCCESS)
130 {
131 return FAILED;
132 }
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) != 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) != SUCCESS)
196 {
197 return FAILED;
198 }
199 data = in->get_data(in);
200 response = chunk_skip(data, 6);
201
202 if (!chunk_equals(response, expected))
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)
216 {
217 return EAP_MD5;
218 }
219
220 /**
221 * Implementation of eap_method_t.get_msk.
222 */
223 static status_t get_msk(private_eap_md5_t *this, chunk_t *msk)
224 {
225 return FAILED;
226 }
227
228 /**
229 * Implementation of eap_method_t.is_mutual.
230 */
231 static bool is_mutual(private_eap_md5_t *this)
232 {
233 return FALSE;
234 }
235
236 /**
237 * Implementation of eap_method_t.destroy.
238 */
239 static void destroy(private_eap_md5_t *this)
240 {
241 chunk_free(&this->challenge);
242 free(this);
243 }
244
245 /*
246 * Described in header.
247 */
248 eap_md5_t *eap_create(eap_role_t role,
249 identification_t *server, identification_t *peer)
250 {
251 private_eap_md5_t *this = malloc_thing(private_eap_md5_t);
252
253 /* public functions */
254 switch (role)
255 {
256 case EAP_SERVER:
257 this->public.eap_method_interface.initiate = (status_t(*)(eap_method_t*,eap_payload_t**))initiate_server;
258 this->public.eap_method_interface.process = (status_t(*)(eap_method_t*,eap_payload_t*,eap_payload_t**))process_server;
259 break;
260 case EAP_PEER:
261 this->public.eap_method_interface.initiate = (status_t(*)(eap_method_t*,eap_payload_t**))initiate_peer;
262 this->public.eap_method_interface.process = (status_t(*)(eap_method_t*,eap_payload_t*,eap_payload_t**))process_peer;
263 break;
264 default:
265 free(this);
266 return NULL;
267 }
268 this->public.eap_method_interface.get_type = (eap_type_t(*)(eap_method_t*))get_type;
269 this->public.eap_method_interface.is_mutual = (bool(*)(eap_method_t*))is_mutual;
270 this->public.eap_method_interface.get_msk = (status_t(*)(eap_method_t*,chunk_t*))get_msk;
271 this->public.eap_method_interface.destroy = (void(*)(eap_method_t*))destroy;
272
273 /* private data */
274 this->peer = peer;
275 this->server = server;
276 this->challenge = chunk_empty;
277 this->identifier = random();
278
279 return &this->public;
280 }