Prototype & Signature corrections
[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 "eap.h"
40
41 #include "eap_md5.h"
42
43 /*
44  *      Allocate a new MD5_PACKET
45  */
46 MD5_PACKET *eapmd5_alloc(void)
47 {
48         MD5_PACKET   *rp;
49
50         if ((rp = malloc(sizeof(MD5_PACKET))) == NULL) {
51                 radlog(L_ERR, "rlm_eap_md5: out of memory");
52                 return NULL;
53         }
54         memset(rp, 0, sizeof(MD5_PACKET));
55         return rp;
56 }
57
58 /*
59  *      Free MD5_PACKET
60  */
61 void eapmd5_free(MD5_PACKET **md5_packet_ptr)
62 {
63         MD5_PACKET *md5_packet;
64
65         if (!md5_packet_ptr) return;
66         md5_packet = *md5_packet_ptr;
67         if (md5_packet == NULL) return;
68
69         if (md5_packet->value) free(md5_packet->value);
70         if (md5_packet->name) free(md5_packet->name);
71
72         free(md5_packet);
73
74         *md5_packet_ptr = NULL;
75 }
76
77 /* 
78  * We expect only RESPONSE for which CHALLENGE, SUCCESS or FAILURE is sent back
79  */ 
80 MD5_PACKET *eapmd5_extract(EAP_DS *eap_ds)
81 {
82         md5_packet_t    *data;
83         MD5_PACKET      *packet;
84         int             name_len;
85
86         if (!eap_ds                                             || 
87                 !eap_ds->response                               || 
88                 (eap_ds->response->code != PW_MD5_RESPONSE)     ||
89                 eap_ds->response->type.type != PW_EAP_MD5               ||
90                 !eap_ds->response->type.data                    ||
91                 (eap_ds->response->length < MD5_HEADER_LEN)     ||
92                 (eap_ds->response->type.data[0] <= 0)   ) {
93                 radlog(L_ERR, "rlm_eap_md5: corrupted data");
94                 return NULL;
95         }
96
97         packet = eapmd5_alloc();
98         if (!packet) return NULL;
99
100         /*
101          * Code, id & length for MD5 & EAP are same
102          * but md5_length = eap_length - 1(Type = 1 octet)
103          */
104         packet->code = eap_ds->response->code;
105         packet->id = eap_ds->response->id;
106         packet->length = eap_ds->response->length - 1;
107         packet->value_size = 0;
108         packet->value = NULL;
109         packet->name = NULL;
110
111         data = (md5_packet_t *)eap_ds->response->type.data;
112
113         packet->value_size = data->value_size;
114
115         packet->value = malloc(packet->value_size);
116         if (packet->value == NULL) {
117                 radlog(L_ERR, "rlm_eap_md5: out of memory");
118                 eapmd5_free(&packet);
119                 return NULL;
120         }
121         memcpy(packet->value, data->value_name, packet->value_size);
122
123         /*
124          * Name is optional and is present after Value, but we need to check for it
125          */
126         name_len =  packet->length - (packet->value_size + 5);
127         if (name_len) {
128                 packet->name = malloc(name_len+1);
129                 if (!packet->name) {
130                         radlog(L_ERR, "rlm_eap_md5: out of memory");
131                         eapmd5_free(&packet);
132                         return NULL;
133                 }
134                 memset(packet->name, 0, name_len+1);
135                 memcpy(packet->name, data->value_name+packet->value_size, name_len);
136         }
137
138         return packet;
139 }
140
141 /*
142  * Identify whether the response that you got is either the
143  * response to the challenge that we sent or a new one.
144  * If it is a response to the request then issue success/failure
145  * else issue a challenge
146  */
147 MD5_PACKET *eapmd5_process(MD5_PACKET *packet, int id,
148                 VALUE_PAIR *username, VALUE_PAIR* password, md5_packet_t *request)
149 {
150         unsigned char output[MAX_STRING_LEN];
151         MD5_PACKET *reply;
152
153         if (!username || !password || !packet)
154                 return NULL;
155
156         reply = eapmd5_alloc();
157         if (!reply) return NULL;
158         memset(output, 0, MAX_STRING_LEN);
159         reply->id = id;
160         
161         if (request) {
162                 /* verify and issue Success/failure */
163                 eapmd5_challenge(packet->id, password->strvalue, password->length,
164                         request->value_name, request->value_size, output);
165
166                 if (memcmp(output, packet->value, packet->value_size) != 0) {
167                         radlog(L_INFO, "rlm_eap_md5: Challenge failed");
168                         reply->code = PW_MD5_FAILURE;
169                 }
170                 else {
171                         reply->code = PW_MD5_SUCCESS;
172                 }
173         } else {
174                 /*
175                  * Issue a challenge, value is a random number.
176                  * If no value then generate some random number
177                  */
178                 eapmd5_challenge(id, password->strvalue, password->length,
179                                 packet->value, packet->value_size, output);
180                 radlog(L_INFO, "rlm_eap_md5: Issuing Challenge to the user - %s",
181                         (char *)username->strvalue);
182                 reply->code = PW_MD5_CHALLENGE;
183         }
184
185         /* fill reply packet */
186         if (reply->code == PW_MD5_CHALLENGE) {
187                 reply->value_size = packet->value_size;
188                 reply->value = malloc(reply->value_size);
189                 if (reply->value == NULL) {
190                         radlog(L_ERR, "rlm_eap_md5: out of memory");
191                         eapmd5_free(&reply);
192                         return NULL;
193                 }
194                 memcpy(reply->value, output, reply->value_size);
195                 reply->length = packet->length;
196         } else {
197                 reply->length = MD5_HEADER_LEN;
198         }
199         
200         return reply;
201 }
202
203 /*
204  * If an EAP MD5 request needs to be initiated then
205  * create such a packet.
206  */
207 MD5_PACKET *eapmd5_initiate(EAP_DS *eap_ds)
208 {
209         MD5_PACKET      *reply;
210
211         reply = eapmd5_alloc();
212         if (reply == NULL)  {
213                 radlog(L_ERR, "rlm_eap_md5: out of memory");
214                 return NULL;
215         }
216
217         reply->code = PW_MD5_CHALLENGE;
218         reply->length = MD5_HEADER_LEN + 1 + MD5_LEN;
219         reply->value_size = MD5_LEN;
220
221         reply->value = malloc(reply->value_size);
222         if (reply->value == NULL) {
223                 radlog(L_ERR, "rlm_eap_md5: out of memory");
224                 eapmd5_free(&reply);
225                 return NULL;
226         }
227
228         /*
229          * generate some random challenge value
230          *
231          * TODO: Make sure Challenge is always unique,
232          *      no matter how many times it is called
233          */
234         librad_md5_calc((uint8_t *)reply->value, (uint8_t *)reply->value, MD5_LEN);
235
236         return reply;
237 }
238
239 /* 
240  * challenge = MD5(id+password+MD5(random))
241  */
242 int eapmd5_challenge(int id,
243                 unsigned char *password, int pass_len, 
244                 unsigned char *challenge, int challenge_len,
245                 unsigned char *output)
246 {
247         int             len;
248         char            *ptr;
249         char            string[MAX_STRING_LEN];
250
251         if ((password == NULL) || (challenge == NULL)) {
252                 return 0;
253         }
254
255         len = 0;
256         ptr = string;
257
258         *ptr++ = id;
259         len++;
260         memcpy(ptr, password, pass_len);
261         ptr += pass_len;
262         len += pass_len;
263
264         memcpy(ptr, challenge, challenge_len);
265         len += challenge_len;
266
267         librad_md5_calc((u_char *)output, (u_char *)string, len);
268
269         return 1;
270 }
271
272 /* 
273  * compose the MD5 reply packet in the EAP reply typedata
274  */
275 int eapmd5_compose(EAP_DS *eap_ds, MD5_PACKET *reply)
276 {
277         uint8_t *ptr;
278         int name_len;
279
280         if (reply->code < 3) {
281
282                 eap_ds->request->type.type = PW_EAP_MD5;
283
284                 eap_ds->request->type.data = malloc(reply->length - 4);
285                 if (eap_ds->request->type.data == NULL) {
286                         radlog(L_ERR, "rlm_eap_md5: out of memory");
287                         return 0;
288                 }
289                 ptr = eap_ds->request->type.data;
290                 *ptr++ = (uint8_t)(reply->value_size & 0xFF);
291                 memcpy(ptr, reply->value, reply->value_size);
292
293                 eap_ds->request->type.length = reply->value_size + 1;
294
295                 name_len = reply->length - (reply->value_size + 5);
296                 if (reply->name  && name_len) {
297                         ptr += reply->value_size;
298                         memcpy(ptr, reply->name, name_len);
299                         eap_ds->request->type.length += name_len;
300                 }
301
302         } else {
303                 eap_ds->request->type.length = 0;
304                 /* TODO: In future we might add message here wrt rfc1994 */
305         }
306         eap_ds->request->code = reply->code;
307
308         return 1;
309 }