Code Cleanup
[freeradius.git] / src / modules / rlm_eap / types / rlm_eap_md5 / eap_md5.c
1 /*
2  * eap_md5.c  EAP MD5 functionality.
3  *
4  * Version:     $Id$
5  *
6  *   This program is free software; you can redistribute it and/or modify
7  *   it under the terms of the GNU General Public License as published by
8  *   the Free Software Foundation; either version 2 of the License, or
9  *   (at your option) any later version.
10  *
11  *   This program is distributed in the hope that it will be useful,
12  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *   GNU General Public License for more details.
15  *
16  *   You should have received a copy of the GNU General Public License
17  *   along with this program; if not, write to the Free Software
18  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  * Copyright 2000,2001  The FreeRADIUS server project
21  * Copyright 2001  hereUare Communications, Inc. <raghud@hereuare.com>
22  */
23
24 /*
25  *
26  *  MD5 Packet Format in EAP Type-Data
27  *  --- ------ ------ -- --- --------- 
28  *  0                   1                   2                   3
29  *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
30  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
31  * |  Value-Size   |  Value ...
32  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
33  * |  Name ...
34  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
35  *
36  */
37
38 #include <stdio.h>
39 #include <malloc.h>
40 #include "eap.h"
41
42 #include "eap_md5.h"
43
44 /*
45  *      Allocate a new MD5_PACKET
46  */
47 MD5_PACKET *eapmd5_alloc(void)
48 {
49         MD5_PACKET   *rp;
50
51         if ((rp = malloc(sizeof(MD5_PACKET))) == NULL) {
52                 radlog(L_ERR, "rlm_eap_md5: out of memory");
53                 return NULL;
54         }
55         memset(rp, 0, sizeof(MD5_PACKET));
56         return rp;
57 }
58
59 /*
60  *      Free MD5_PACKET
61  */
62 void eapmd5_free(MD5_PACKET **md5_packet_ptr)
63 {
64         MD5_PACKET *md5_packet;
65
66         if (!md5_packet_ptr) return;
67         md5_packet = *md5_packet_ptr;
68         if (md5_packet == NULL) return;
69
70         if (md5_packet->value) free(md5_packet->value);
71         if (md5_packet->name) free(md5_packet->name);
72
73         free(md5_packet);
74
75         *md5_packet_ptr = NULL;
76 }
77
78 /* 
79  * We expect only RESPONSE for which CHALLENGE, SUCCESS or FAILURE is sent back
80  */ 
81 MD5_PACKET *eapmd5_extract(EAP_DS *eap_ds)
82 {
83         md5_packet_t    *data;
84         MD5_PACKET      *packet;
85         int             name_len;
86
87         if (!eap_ds                                             || 
88                 !eap_ds->response                               || 
89         (eap_ds->response->code != PW_MD5_RESPONSE)     ||
90                 eap_ds->response->type.type != PW_EAP_MD5               ||
91                 !eap_ds->response->type.data                    ||
92                 (eap_ds->response->length < MD5_HEADER_LEN)     ||
93                 (eap_ds->response->type.data[0] <= 0)   ) {
94
95                 radlog(L_ERR, "rlm_eap_md5: corrupted data");
96                 return NULL;
97         }
98
99         packet = eapmd5_alloc();
100         if (!packet) return NULL;
101
102         /*
103          * Code, id & length for MD5 & EAP are same
104          * but md5_length = eap_length - 1(Type = 1 octet)
105          */
106         packet->code = eap_ds->response->code;
107         packet->id = eap_ds->response->id;
108         packet->length = eap_ds->response->length - 1;
109         packet->value_size = 0;
110         packet->value = NULL;
111         packet->name = NULL;
112
113         data = (md5_packet_t *)eap_ds->response->type.data;
114
115         packet->value_size = data->value_size;
116
117         packet->value = malloc(packet->value_size);
118         if (packet->value == NULL) {
119                 radlog(L_ERR, "rlm_eap_md5: out of memory");
120                 eapmd5_free(&packet);
121                 return NULL;
122         }
123         memcpy(packet->value, data->value_name, packet->value_size);
124
125         /*
126          * Name is optional and is present after Value, but we need to check for it
127          */
128         name_len =  packet->length - (packet->value_size + 5);
129         if (name_len) {
130                 packet->name = malloc(name_len+1);
131                 if (!packet->name) {
132                         radlog(L_ERR, "rlm_eap_md5: out of memory");
133                         eapmd5_free(&packet);
134                         return NULL;
135                 }
136                 memset(packet->name, 0, name_len+1);
137                 memcpy(packet->name, data->value_name+packet->value_size, name_len);
138         }
139
140         return packet;
141 }
142
143 /*
144  * Generate a random value
145  * challenge = MD5(random)
146  */
147 int eapmd5_challenge(unsigned char *value, int len)
148 {
149         /*
150          * TODO: Make sure Challenge is always unique,
151          *           no matter how many times it is called.
152          *           random_vector() in lib/radius.c should be used.
153          */
154         librad_md5_calc((u_char *)value, (u_char *)value, len);
155         radlog(L_INFO, "rlm_eap_md5: Issuing Challenge");
156
157         return 1;
158 }
159
160 /* 
161  * verify = MD5(id+password+challenge_sent)
162  */
163 int eapmd5_verify(MD5_PACKET *packet, VALUE_PAIR* password, 
164                 md5_packet_t *challenge)
165 {
166         int             len;
167         char    *ptr;
168         char    string[MAX_STRING_LEN*2];
169         unsigned char output[MAX_STRING_LEN];
170
171         if ((password == NULL) || (challenge == NULL)) {
172                 return 0;
173         }
174
175         len = 0;
176         ptr = string;
177
178         *ptr++ = packet->id;
179         len++;
180         memcpy(ptr, password->strvalue, password->length);
181         ptr += password->length;
182         len += password->length;
183
184         memcpy(ptr, challenge->value_name, challenge->value_size);
185         len += challenge->value_size;
186
187         librad_md5_calc((u_char *)output, (u_char *)string, len);
188
189         if (memcmp(output, packet->value, packet->value_size) != 0) {
190                 return 0;
191         }
192         return 1;
193 }
194
195 /*
196  * Identify whether the response that you got is either the
197  * response to the challenge that we sent or a new one.
198  * If it is a response to the request then issue success/failure
199  * else issue a challenge
200  */
201 MD5_PACKET *eapmd5_process(MD5_PACKET *packet, int id,
202                 VALUE_PAIR *username, VALUE_PAIR* password, md5_packet_t *request)
203 {
204         unsigned char output[MAX_STRING_LEN];
205         MD5_PACKET *reply;
206
207         if (!username || !password || !packet)
208                 return NULL;
209
210         reply = eapmd5_alloc();
211         if (!reply) return NULL;
212         memset(output, 0, MAX_STRING_LEN);
213         reply->id = id;
214         
215         if (request) {
216                 /* verify and issue Success/failure */
217                 if (eapmd5_verify(packet, password, request) == 0) {
218                         radlog(L_INFO, "rlm_eap_md5: Challenge failed");
219                         reply->code = PW_MD5_FAILURE;
220                 }
221                 else {
222                         reply->code = PW_MD5_SUCCESS;
223                 }
224         } else {
225                 /*
226                  * Previous request not found.
227                  * Probably it is timed out.
228                  * So send another challenge.
229                  * TODO: Later Send these challenges for the configurable
230                  *              number of times for each user & stop.
231                  */
232                 eapmd5_challenge(reply->value, MD5_LEN);
233                 reply->code = PW_MD5_CHALLENGE;
234                 radlog(L_INFO, "rlm_eap_md5: Previous request not found");
235                 radlog(L_INFO, "rlm_eap_md5: Issuing Challenge to the user - %s",
236                         (char *)username->strvalue);
237         }
238
239         /* fill reply packet */
240         if (reply->code == PW_MD5_CHALLENGE) {
241                 reply->value_size = packet->value_size;
242                 reply->value = malloc(reply->value_size);
243                 if (reply->value == NULL) {
244                         radlog(L_ERR, "rlm_eap_md5: out of memory");
245                         eapmd5_free(&reply);
246                         return NULL;
247                 }
248                 memcpy(reply->value, output, reply->value_size);
249                 reply->length = packet->length;
250         } else {
251                 reply->length = MD5_HEADER_LEN;
252         }
253         
254         return reply;
255 }
256
257 /*
258  * If an EAP MD5 request needs to be initiated then
259  * create such a packet.
260  */
261 MD5_PACKET *eapmd5_initiate(EAP_DS *eap_ds)
262 {
263         MD5_PACKET      *reply;
264
265         reply = eapmd5_alloc();
266         if (reply == NULL)  {
267                 radlog(L_ERR, "rlm_eap_md5: out of memory");
268                 return NULL;
269         }
270
271         reply->code = PW_MD5_CHALLENGE;
272         reply->length = MD5_HEADER_LEN + 1/*value_size*/ + MD5_LEN;
273         reply->value_size = MD5_LEN;
274
275         reply->value = malloc(reply->value_size);
276         if (reply->value == NULL) {
277                 radlog(L_ERR, "rlm_eap_md5: out of memory");
278                 eapmd5_free(&reply);
279                 return NULL;
280         }
281
282         eapmd5_challenge(reply->value, MD5_LEN);
283
284         return reply;
285 }
286
287 /* 
288  * compose the MD5 reply packet in the EAP reply typedata
289  */
290 int eapmd5_compose(EAP_DS *eap_ds, MD5_PACKET *reply)
291 {
292         uint8_t *ptr;
293         int name_len;
294
295         if (reply->code < 3) {
296
297                 eap_ds->request->type.type = PW_EAP_MD5;
298
299                 eap_ds->request->type.data = malloc(reply->length - MD5_HEADER_LEN);
300                 if (eap_ds->request->type.data == NULL) {
301                         radlog(L_ERR, "rlm_eap_md5: out of memory");
302                         return 0;
303                 }
304                 ptr = eap_ds->request->type.data;
305                 *ptr++ = (uint8_t)(reply->value_size & 0xFF);
306                 memcpy(ptr, reply->value, reply->value_size);
307
308                 /* Just the Challenge length */
309                 eap_ds->request->type.length = reply->value_size + 1;
310
311                 name_len = reply->length - (reply->value_size + 1 + MD5_HEADER_LEN);
312                 if (name_len && reply->name) {
313                         ptr += reply->value_size;
314                         memcpy(ptr, reply->name, name_len);
315                         /* Challenge length + Name length */
316                         eap_ds->request->type.length += name_len;
317                 }
318         } else {
319                 eap_ds->request->type.length = 0;
320                 /* TODO: In future we might add message here wrt rfc1994 */
321         }
322         eap_ds->request->code = reply->code;
323
324         return 1;
325 }