]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/hostapd/radius.c
Submitted following patch to FSF GCC:
[FreeBSD/FreeBSD.git] / contrib / hostapd / radius.c
1 /*
2  * Host AP (software wireless LAN access point) user space daemon for
3  * Host AP kernel driver / RADIUS client
4  * Copyright (c) 2002-2005, Jouni Malinen <jkmaline@cc.hut.fi>
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 version 2 as
8  * published by the Free Software Foundation.
9  *
10  * Alternatively, this software may be distributed under the terms of BSD
11  * license.
12  *
13  * See README and COPYING for more details.
14  */
15
16 #include <stdlib.h>
17 #include <stdio.h>
18 #include <unistd.h>
19 #include <netinet/in.h>
20 #include <string.h>
21 #include <sys/ioctl.h>
22 #include <signal.h>
23 #include <sys/time.h>
24 #include <sys/socket.h>
25 #include <arpa/inet.h>
26
27
28 #include "common.h"
29 #include "radius.h"
30 #include "md5.h"
31
32
33 struct radius_msg *radius_msg_new(u8 code, u8 identifier)
34 {
35         struct radius_msg *msg;
36
37         msg = (struct radius_msg *) malloc(sizeof(*msg));
38         if (msg == NULL)
39                 return NULL;
40
41         if (radius_msg_initialize(msg, RADIUS_DEFAULT_MSG_SIZE)) {
42                 free(msg);
43                 return NULL;
44         }
45
46         radius_msg_set_hdr(msg, code, identifier);
47
48         return msg;
49 }
50
51
52 int radius_msg_initialize(struct radius_msg *msg, size_t init_len)
53 {
54         if (msg == NULL || init_len < sizeof(struct radius_hdr))
55                 return -1;
56
57         memset(msg, 0, sizeof(*msg));
58         msg->buf = (unsigned char *) malloc(init_len);
59         if (msg->buf == NULL)
60                 return -1;
61         memset(msg->buf, 0, init_len);
62
63         msg->buf_size = init_len;
64         msg->hdr = (struct radius_hdr *) msg->buf;
65         msg->buf_used = sizeof(*msg->hdr);
66
67         msg->attrs = (struct radius_attr_hdr **)
68                 malloc(RADIUS_DEFAULT_ATTR_COUNT * sizeof(*msg->attrs));
69         if (msg->attrs == NULL) {
70                 free(msg->buf);
71                 msg->buf = NULL;
72                 msg->hdr = NULL;
73                 return -1;
74         }
75
76         msg->attr_size = RADIUS_DEFAULT_ATTR_COUNT;
77         msg->attr_used = 0;
78
79         return 0;
80 }
81
82
83 void radius_msg_set_hdr(struct radius_msg *msg, u8 code, u8 identifier)
84 {
85         msg->hdr->code = code;
86         msg->hdr->identifier = identifier;
87 }
88
89
90 void radius_msg_free(struct radius_msg *msg)
91 {
92         if (msg->buf != NULL) {
93                 free(msg->buf);
94                 msg->buf = NULL;
95                 msg->hdr = NULL;
96         }
97         msg->buf_size = msg->buf_used = 0;
98
99         if (msg->attrs != NULL) {
100                 free(msg->attrs);
101                 msg->attrs = NULL;
102         }
103         msg->attr_size = msg->attr_used = 0;
104 }
105
106
107 static const char *radius_code_string(u8 code)
108 {
109         switch (code) {
110         case RADIUS_CODE_ACCESS_REQUEST: return "Access-Request";
111         case RADIUS_CODE_ACCESS_ACCEPT: return "Access-Accept";
112         case RADIUS_CODE_ACCESS_REJECT: return "Access-Reject";
113         case RADIUS_CODE_ACCOUNTING_REQUEST: return "Accounting-Request";
114         case RADIUS_CODE_ACCOUNTING_RESPONSE: return "Accounting-Response";
115         case RADIUS_CODE_ACCESS_CHALLENGE: return "Access-Challenge";
116         case RADIUS_CODE_STATUS_SERVER: return "Status-Server";
117         case RADIUS_CODE_STATUS_CLIENT: return "Status-Client";
118         case RADIUS_CODE_RESERVED: return "Reserved";
119         default: return "?Unknown?";
120         }
121 }
122
123
124 struct radius_attr_type {
125         u8 type;
126         char *name;
127         enum { RADIUS_ATTR_UNDIST, RADIUS_ATTR_TEXT, RADIUS_ATTR_IP,
128                RADIUS_ATTR_HEXDUMP, RADIUS_ATTR_INT32 } data_type;
129 };
130
131 static struct radius_attr_type radius_attrs[] =
132 {
133         { RADIUS_ATTR_USER_NAME, "User-Name", RADIUS_ATTR_TEXT },
134         { RADIUS_ATTR_USER_PASSWORD, "User-Password", RADIUS_ATTR_UNDIST },
135         { RADIUS_ATTR_NAS_IP_ADDRESS, "NAS-IP-Address", RADIUS_ATTR_IP },
136         { RADIUS_ATTR_NAS_PORT, "NAS-Port", RADIUS_ATTR_INT32 },
137         { RADIUS_ATTR_FRAMED_MTU, "Framed-MTU", RADIUS_ATTR_INT32 },
138         { RADIUS_ATTR_STATE, "State", RADIUS_ATTR_UNDIST },
139         { RADIUS_ATTR_CLASS, "Class", RADIUS_ATTR_UNDIST },
140         { RADIUS_ATTR_VENDOR_SPECIFIC, "Vendor-Specific", RADIUS_ATTR_UNDIST },
141         { RADIUS_ATTR_SESSION_TIMEOUT, "Session-Timeout", RADIUS_ATTR_INT32 },
142         { RADIUS_ATTR_IDLE_TIMEOUT, "Idle-Timeout", RADIUS_ATTR_INT32 },
143         { RADIUS_ATTR_TERMINATION_ACTION, "Termination-Action",
144           RADIUS_ATTR_INT32 },
145         { RADIUS_ATTR_CALLED_STATION_ID, "Called-Station-Id",
146           RADIUS_ATTR_TEXT },
147         { RADIUS_ATTR_CALLING_STATION_ID, "Calling-Station-Id",
148           RADIUS_ATTR_TEXT },
149         { RADIUS_ATTR_NAS_IDENTIFIER, "NAS-Identifier", RADIUS_ATTR_TEXT },
150         { RADIUS_ATTR_ACCT_STATUS_TYPE, "Acct-Status-Type",
151           RADIUS_ATTR_INT32 },
152         { RADIUS_ATTR_ACCT_DELAY_TIME, "Acct-Delay-Time", RADIUS_ATTR_INT32 },
153         { RADIUS_ATTR_ACCT_INPUT_OCTETS, "Acct-Input-Octets",
154           RADIUS_ATTR_INT32 },
155         { RADIUS_ATTR_ACCT_OUTPUT_OCTETS, "Acct-Output-Octets",
156           RADIUS_ATTR_INT32 },
157         { RADIUS_ATTR_ACCT_SESSION_ID, "Acct-Session-Id", RADIUS_ATTR_TEXT },
158         { RADIUS_ATTR_ACCT_AUTHENTIC, "Acct-Authentic", RADIUS_ATTR_INT32 },
159         { RADIUS_ATTR_ACCT_SESSION_TIME, "Acct-Session-Time",
160           RADIUS_ATTR_INT32 },
161         { RADIUS_ATTR_ACCT_INPUT_PACKETS, "Acct-Input-Packets",
162           RADIUS_ATTR_INT32 },
163         { RADIUS_ATTR_ACCT_OUTPUT_PACKETS, "Acct-Output-Packets",
164           RADIUS_ATTR_INT32 },
165         { RADIUS_ATTR_ACCT_TERMINATE_CAUSE, "Acct-Terminate-Cause",
166           RADIUS_ATTR_INT32 },
167         { RADIUS_ATTR_ACCT_MULTI_SESSION_ID, "Acct-Multi-Session-Id",
168           RADIUS_ATTR_TEXT },
169         { RADIUS_ATTR_ACCT_LINK_COUNT, "Acct-Link-Count", RADIUS_ATTR_INT32 },
170         { RADIUS_ATTR_ACCT_INPUT_GIGAWORDS, "Acct-Input-Gigawords", 
171           RADIUS_ATTR_INT32 },
172         { RADIUS_ATTR_ACCT_OUTPUT_GIGAWORDS, "Acct-Output-Gigawords",
173           RADIUS_ATTR_INT32 },
174         { RADIUS_ATTR_EVENT_TIMESTAMP, "Event-Timestamp",
175           RADIUS_ATTR_INT32 },
176         { RADIUS_ATTR_NAS_PORT_TYPE, "NAS-Port-Type", RADIUS_ATTR_INT32 },
177         { RADIUS_ATTR_CONNECT_INFO, "Connect-Info", RADIUS_ATTR_TEXT },
178         { RADIUS_ATTR_EAP_MESSAGE, "EAP-Message", RADIUS_ATTR_UNDIST },
179         { RADIUS_ATTR_MESSAGE_AUTHENTICATOR, "Message-Authenticator",
180           RADIUS_ATTR_UNDIST },
181         { RADIUS_ATTR_ACCT_INTERIM_INTERVAL, "Acct-Interim-Interval",
182           RADIUS_ATTR_INT32 }
183
184 };
185 #define RADIUS_ATTRS (sizeof(radius_attrs) / sizeof(radius_attrs[0]))
186
187
188 static struct radius_attr_type *radius_get_attr_type(u8 type)
189 {
190         int i;
191
192         for (i = 0; i < RADIUS_ATTRS; i++) {
193                 if (type == radius_attrs[i].type)
194                         return &radius_attrs[i];
195         }
196
197         return NULL;
198 }
199
200
201 static void radius_msg_dump_attr(struct radius_attr_hdr *hdr)
202 {
203         struct radius_attr_type *attr;
204         int i, len;
205         unsigned char *pos;
206
207         attr = radius_get_attr_type(hdr->type);
208
209         printf("   Attribute %d (%s) length=%d\n",
210                hdr->type, attr ? attr->name : "?Unknown?", hdr->length);
211
212         if (attr == NULL)
213                 return;
214
215         len = hdr->length - sizeof(struct radius_attr_hdr);
216         pos = (unsigned char *) (hdr + 1);
217
218         switch (attr->data_type) {
219         case RADIUS_ATTR_TEXT:
220                 printf("      Value: '");
221                 for (i = 0; i < len; i++)
222                         print_char(pos[i]);
223                 printf("'\n");
224                 break;
225
226         case RADIUS_ATTR_IP:
227                 if (len == 4) {
228                         struct in_addr *addr = (struct in_addr *) pos;
229                         printf("      Value: %s\n", inet_ntoa(*addr));
230                 } else
231                         printf("      Invalid IP address length %d\n", len);
232                 break;
233
234         case RADIUS_ATTR_HEXDUMP:
235         case RADIUS_ATTR_UNDIST:
236                 printf("      Value:");
237                 for (i = 0; i < len; i++)
238                         printf(" %02x", pos[i]);
239                 printf("\n");
240                 break;
241
242         case RADIUS_ATTR_INT32:
243                 if (len == 4) {
244                         u32 *val = (u32 *) pos;
245                         printf("      Value: %d\n", ntohl(*val));
246                 } else
247                         printf("      Invalid INT32 length %d\n", len);
248                 break;
249
250         default:
251                 break;
252         }
253 }
254
255
256 void radius_msg_dump(struct radius_msg *msg)
257 {
258         int i;
259
260         printf("RADIUS message: code=%d (%s) identifier=%d length=%d\n",
261                msg->hdr->code, radius_code_string(msg->hdr->code),
262                msg->hdr->identifier, ntohs(msg->hdr->length));
263
264         for (i = 0; i < msg->attr_used; i++) {
265                 radius_msg_dump_attr(msg->attrs[i]);
266         }
267 }
268
269
270 int radius_msg_finish(struct radius_msg *msg, u8 *secret, size_t secret_len)
271 {
272         if (secret) {
273                 u8 auth[MD5_MAC_LEN];
274                 struct radius_attr_hdr *attr;
275
276                 memset(auth, 0, MD5_MAC_LEN);
277                 attr = radius_msg_add_attr(msg,
278                                            RADIUS_ATTR_MESSAGE_AUTHENTICATOR,
279                                            auth, MD5_MAC_LEN);
280                 if (attr == NULL) {
281                         printf("WARNING: Could not add "
282                                "Message-Authenticator\n");
283                         return -1;
284                 }
285                 msg->hdr->length = htons(msg->buf_used);
286                 hmac_md5(secret, secret_len, msg->buf, msg->buf_used,
287                          (u8 *) (attr + 1));
288         } else
289                 msg->hdr->length = htons(msg->buf_used);
290
291         if (msg->buf_used > 0xffff) {
292                 printf("WARNING: too long RADIUS message (%lu)\n",
293                        (unsigned long) msg->buf_used);
294                 return -1;
295         }
296         return 0;
297 }
298
299
300 int radius_msg_finish_srv(struct radius_msg *msg, const u8 *secret,
301                           size_t secret_len, const u8 *req_authenticator)
302 {
303         u8 auth[MD5_MAC_LEN];
304         struct radius_attr_hdr *attr;
305         MD5_CTX context;
306
307         memset(auth, 0, MD5_MAC_LEN);
308         attr = radius_msg_add_attr(msg, RADIUS_ATTR_MESSAGE_AUTHENTICATOR,
309                                    auth, MD5_MAC_LEN);
310         if (attr == NULL) {
311                 printf("WARNING: Could not add Message-Authenticator\n");
312                 return -1;
313         }
314         msg->hdr->length = htons(msg->buf_used);
315         memcpy(msg->hdr->authenticator, req_authenticator,
316                sizeof(msg->hdr->authenticator));
317         hmac_md5(secret, secret_len, msg->buf, msg->buf_used,
318                  (u8 *) (attr + 1));
319
320         /* ResponseAuth = MD5(Code+ID+Length+RequestAuth+Attributes+Secret) */
321         MD5Init(&context);
322         MD5Update(&context, (u8 *) msg->hdr, 1 + 1 + 2);
323         MD5Update(&context, req_authenticator, MD5_MAC_LEN);
324         MD5Update(&context, (u8 *) (msg->hdr + 1),
325                   msg->buf_used - sizeof(*msg->hdr));
326         MD5Update(&context, secret, secret_len);
327         MD5Final(msg->hdr->authenticator, &context);
328
329         if (msg->buf_used > 0xffff) {
330                 printf("WARNING: too long RADIUS message (%lu)\n",
331                        (unsigned long) msg->buf_used);
332                 return -1;
333         }
334         return 0;
335 }
336
337
338 void radius_msg_finish_acct(struct radius_msg *msg, u8 *secret,
339                             size_t secret_len)
340 {
341         MD5_CTX context;
342
343         msg->hdr->length = htons(msg->buf_used);
344         memset(msg->hdr->authenticator, 0, MD5_MAC_LEN);
345         MD5Init(&context);
346         MD5Update(&context, msg->buf, msg->buf_used);
347         MD5Update(&context, secret, secret_len);
348         MD5Final(msg->hdr->authenticator, &context);
349
350         if (msg->buf_used > 0xffff) {
351                 printf("WARNING: too long RADIUS messages (%lu)\n",
352                        (unsigned long) msg->buf_used);
353         }
354 }
355
356
357 static int radius_msg_add_attr_to_array(struct radius_msg *msg,
358                                         struct radius_attr_hdr *attr)
359 {
360         if (msg->attr_used >= msg->attr_size) {
361                 struct radius_attr_hdr **nattrs;
362                 int nlen = msg->attr_size * 2;
363
364                 nattrs = (struct radius_attr_hdr **)
365                         realloc(msg->attrs, nlen * sizeof(*msg->attrs));
366
367                 if (nattrs == NULL)
368                         return -1;
369
370                 msg->attrs = nattrs;
371                 msg->attr_size = nlen;
372         }
373
374         msg->attrs[msg->attr_used++] = attr;
375
376         return 0;
377 }
378
379
380 struct radius_attr_hdr *radius_msg_add_attr(struct radius_msg *msg, u8 type,
381                                             u8 *data, size_t data_len)
382 {
383         size_t buf_needed;
384         struct radius_attr_hdr *attr;
385
386         if (data_len > RADIUS_MAX_ATTR_LEN) {
387                 printf("radius_msg_add_attr: too long attribute (%lu bytes)\n",
388                        (unsigned long) data_len);
389                 return NULL;
390         }
391
392         buf_needed = msg->buf_used + sizeof(*attr) + data_len;
393
394         if (msg->buf_size < buf_needed) {
395                 /* allocate more space for message buffer */
396                 unsigned char *nbuf;
397                 int nlen = msg->buf_size;
398                 int diff, i;
399
400                 while (nlen < buf_needed)
401                         nlen *= 2;
402                 nbuf = (unsigned char *) realloc(msg->buf, nlen);
403                 if (nbuf == NULL)
404                         return NULL;
405                 diff = nbuf - msg->buf;
406                 msg->buf = nbuf;
407                 msg->hdr = (struct radius_hdr *) msg->buf;
408                 /* adjust attr pointers to match with the new buffer */
409                 for (i = 0; i < msg->attr_used; i++)
410                         msg->attrs[i] = (struct radius_attr_hdr *)
411                                 (((u8 *) msg->attrs[i]) + diff);
412                 memset(msg->buf + msg->buf_size, 0, nlen - msg->buf_size);
413                 msg->buf_size = nlen;
414         }
415
416         attr = (struct radius_attr_hdr *) (msg->buf + msg->buf_used);
417         attr->type = type;
418         attr->length = sizeof(*attr) + data_len;
419         if (data_len > 0)
420                 memcpy(attr + 1, data, data_len);
421
422         msg->buf_used += sizeof(*attr) + data_len;
423
424         if (radius_msg_add_attr_to_array(msg, attr))
425                 return NULL;
426
427         return attr;
428 }
429
430
431 struct radius_msg *radius_msg_parse(const u8 *data, size_t len)
432 {
433         struct radius_msg *msg;
434         struct radius_hdr *hdr;
435         struct radius_attr_hdr *attr;
436         size_t msg_len;
437         unsigned char *pos, *end;
438
439         if (data == NULL || len < sizeof(*hdr))
440                 return NULL;
441
442         hdr = (struct radius_hdr *) data;
443
444         msg_len = ntohs(hdr->length);
445         if (msg_len < sizeof(*hdr) || msg_len > len) {
446                 printf("Invalid RADIUS message length\n");
447                 return NULL;
448         }
449
450         if (msg_len < len) {
451                 printf("Ignored %lu extra bytes after RADIUS message\n",
452                        (unsigned long) len - msg_len);
453         }
454
455         msg = (struct radius_msg *) malloc(sizeof(*msg));
456         if (msg == NULL)
457                 return NULL;
458
459         if (radius_msg_initialize(msg, msg_len)) {
460                 free(msg);
461                 return NULL;
462         }
463
464         memcpy(msg->buf, data, msg_len);
465         msg->buf_size = msg->buf_used = msg_len;
466
467         /* parse attributes */
468         pos = (unsigned char *) (msg->hdr + 1);
469         end = msg->buf + msg->buf_used;
470         while (pos < end) {
471                 if (end - pos < sizeof(*attr))
472                         goto fail;
473
474                 attr = (struct radius_attr_hdr *) pos;
475
476                 if (pos + attr->length > end || attr->length < sizeof(*attr))
477                         goto fail;
478
479                 /* TODO: check that attr->length is suitable for attr->type */
480
481                 if (radius_msg_add_attr_to_array(msg, attr))
482                         goto fail;
483
484                 pos += attr->length;
485         }
486
487         return msg;
488
489  fail:
490         radius_msg_free(msg);
491         free(msg);
492         return NULL;
493 }
494
495
496 int radius_msg_add_eap(struct radius_msg *msg, u8 *data, size_t data_len)
497 {
498         u8 *pos = data;
499         size_t left = data_len;
500
501         while (left > 0) {
502                 int len;
503                 if (left > RADIUS_MAX_ATTR_LEN)
504                         len = RADIUS_MAX_ATTR_LEN;
505                 else
506                         len = left;
507
508                 if (!radius_msg_add_attr(msg, RADIUS_ATTR_EAP_MESSAGE,
509                                          pos, len))
510                         return 0;
511
512                 pos += len;
513                 left -= len;
514         }
515
516         return 1;
517 }
518
519
520 u8 *radius_msg_get_eap(struct radius_msg *msg, size_t *eap_len)
521 {
522         u8 *eap, *pos;
523         size_t len;
524         int i;
525
526         if (msg == NULL)
527                 return NULL;
528
529         len = 0;
530         for (i = 0; i < msg->attr_used; i++) {
531                 if (msg->attrs[i]->type == RADIUS_ATTR_EAP_MESSAGE)
532                         len += msg->attrs[i]->length -
533                                 sizeof(struct radius_attr_hdr);
534         }
535
536         if (len == 0)
537                 return NULL;
538
539         eap = malloc(len);
540         if (eap == NULL)
541                 return NULL;
542
543         pos = eap;
544         for (i = 0; i < msg->attr_used; i++) {
545                 if (msg->attrs[i]->type == RADIUS_ATTR_EAP_MESSAGE) {
546                         struct radius_attr_hdr *attr = msg->attrs[i];
547                         int flen = attr->length - sizeof(*attr);
548                         memcpy(pos, attr + 1, flen);
549                         pos += flen;
550                 }
551         }
552
553         if (eap_len)
554                 *eap_len = len;
555
556         return eap;
557 }
558
559
560 int radius_msg_verify_msg_auth(struct radius_msg *msg, const u8 *secret,
561                                size_t secret_len, const u8 *req_auth)
562 {
563         u8 auth[MD5_MAC_LEN], orig[MD5_MAC_LEN];
564         u8 orig_authenticator[16];
565         struct radius_attr_hdr *attr = NULL;
566         int i;
567
568         for (i = 0; i < msg->attr_used; i++) {
569                 if (msg->attrs[i]->type == RADIUS_ATTR_MESSAGE_AUTHENTICATOR) {
570                         if (attr != NULL) {
571                                 printf("Multiple Message-Authenticator "
572                                        "attributes in RADIUS message\n");
573                                 return 1;
574                         }
575                         attr = msg->attrs[i];
576                 }
577         }
578
579         if (attr == NULL) {
580                 printf("No Message-Authenticator attribute found\n");
581                 return 1;
582         }
583
584         memcpy(orig, attr + 1, MD5_MAC_LEN);
585         memset(attr + 1, 0, MD5_MAC_LEN);
586         if (req_auth) {
587                 memcpy(orig_authenticator, msg->hdr->authenticator,
588                        sizeof(orig_authenticator));
589                 memcpy(msg->hdr->authenticator, req_auth,
590                        sizeof(msg->hdr->authenticator));
591         }
592         hmac_md5(secret, secret_len, msg->buf, msg->buf_used, auth);
593         memcpy(attr + 1, orig, MD5_MAC_LEN);
594         if (req_auth) {
595                 memcpy(msg->hdr->authenticator, orig_authenticator,
596                        sizeof(orig_authenticator));
597         }
598
599         if (memcmp(orig, auth, MD5_MAC_LEN) != 0) {
600                 printf("Invalid Message-Authenticator!\n");
601                 return 1;
602         }
603
604         return 0;
605 }
606
607
608 int radius_msg_verify(struct radius_msg *msg, u8 *secret, size_t secret_len,
609                       struct radius_msg *sent_msg)
610 {
611         MD5_CTX context;
612         u8 hash[MD5_MAC_LEN];
613
614         if (sent_msg == NULL) {
615                 printf("No matching Access-Request message found\n");
616                 return 1;
617         }
618
619         if (radius_msg_verify_msg_auth(msg, secret, secret_len,
620                                        sent_msg->hdr->authenticator)) {
621                 return 1;
622         }
623
624         /* ResponseAuth = MD5(Code+ID+Length+RequestAuth+Attributes+Secret) */
625         MD5Init(&context);
626         MD5Update(&context, (u8 *) msg->hdr, 1 + 1 + 2);
627         MD5Update(&context, sent_msg->hdr->authenticator, MD5_MAC_LEN);
628         MD5Update(&context, (u8 *) (msg->hdr + 1),
629                   msg->buf_used - sizeof(*msg->hdr));
630         MD5Update(&context, secret, secret_len);
631         MD5Final(hash, &context);
632         if (memcmp(hash, msg->hdr->authenticator, MD5_MAC_LEN) != 0) {
633                 printf("Response Authenticator invalid!\n");
634                 return 1;
635         }
636
637         return 0;
638
639 }
640
641
642 int radius_msg_verify_acct(struct radius_msg *msg, u8 *secret,
643                            size_t secret_len, struct radius_msg *sent_msg)
644 {
645         MD5_CTX context;
646         u8 hash[MD5_MAC_LEN];
647
648         MD5Init(&context);
649         MD5Update(&context, msg->buf, 4);
650         MD5Update(&context, sent_msg->hdr->authenticator, MD5_MAC_LEN);
651         if (msg->buf_used > sizeof(struct radius_hdr))
652                 MD5Update(&context, msg->buf + sizeof(struct radius_hdr),
653                           msg->buf_used - sizeof(struct radius_hdr));
654         MD5Update(&context, secret, secret_len);
655         MD5Final(hash, &context);
656         if (memcmp(hash, msg->hdr->authenticator, MD5_MAC_LEN) != 0) {
657                 printf("Response Authenticator invalid!\n");
658                 return 1;
659         }
660
661         return 0;
662 }
663
664
665 int radius_msg_copy_attr(struct radius_msg *dst, struct radius_msg *src,
666                          u8 type)
667 {
668         struct radius_attr_hdr *attr = NULL;
669         int i;
670
671         for (i = 0; i < src->attr_used; i++) {
672                 if (src->attrs[i]->type == type) {
673                         attr = src->attrs[i];
674                         break;
675                 }
676         }
677
678         if (attr == NULL)
679                 return 0;
680
681         if (!radius_msg_add_attr(dst, type, (u8 *) (attr + 1),
682                                  attr->length - sizeof(*attr)))
683                 return -1;
684
685         return 1;
686 }
687
688
689 /* Create Request Authenticator. The value should be unique over the lifetime
690  * of the shared secret between authenticator and authentication server.
691  * Use one-way MD5 hash calculated from current timestamp and some data given
692  * by the caller. */
693 void radius_msg_make_authenticator(struct radius_msg *msg,
694                                    u8 *data, size_t len)
695 {
696         struct timeval tv;
697         MD5_CTX context;
698         long int l;
699
700         gettimeofday(&tv, NULL);
701         l = random();
702         MD5Init(&context);
703         MD5Update(&context, (u8 *) &tv, sizeof(tv));
704         MD5Update(&context, data, len);
705         MD5Update(&context, (u8 *) &l, sizeof(l));
706         MD5Final(msg->hdr->authenticator, &context);
707 }
708
709
710 /* Get Vendor-specific RADIUS Attribute from a parsed RADIUS message.
711  * Returns the Attribute payload and sets alen to indicate the length of the
712  * payload if a vendor attribute with subtype is found, otherwise returns NULL.
713  * The returned payload is allocated with malloc() and caller must free it.
714  */
715 static u8 *radius_msg_get_vendor_attr(struct radius_msg *msg, u32 vendor,
716                                       u8 subtype, size_t *alen)
717 {
718         u8 *data, *pos;
719         int i;
720         size_t len;
721
722         if (msg == NULL)
723                 return NULL;
724
725         for (i = 0; i < msg->attr_used; i++) {
726                 struct radius_attr_hdr *attr = msg->attrs[i];
727                 int left;
728                 u32 vendor_id;
729                 struct radius_attr_vendor *vhdr;
730
731                 if (attr->type != RADIUS_ATTR_VENDOR_SPECIFIC)
732                         continue;
733
734                 left = attr->length - sizeof(*attr);
735                 if (left < 4)
736                         continue;
737
738                 pos = (u8 *) (attr + 1);
739
740                 memcpy(&vendor_id, pos, 4);
741                 pos += 4;
742                 left -= 4;
743
744                 if (ntohl(vendor_id) != vendor)
745                         continue;
746
747                 while (left >= sizeof(*vhdr)) {
748                         vhdr = (struct radius_attr_vendor *) pos;
749                         if (vhdr->vendor_length > left ||
750                             vhdr->vendor_length < sizeof(*vhdr)) {
751                                 left = 0;
752                                 break;
753                         }
754                         if (vhdr->vendor_type != subtype) {
755                                 pos += vhdr->vendor_length;
756                                 left -= vhdr->vendor_length;
757                                 continue;
758                         }
759
760                         len = vhdr->vendor_length - sizeof(*vhdr);
761                         data = malloc(len);
762                         if (data == NULL)
763                                 return NULL;
764                         memcpy(data, pos + sizeof(*vhdr), len);
765                         if (alen)
766                                 *alen = len;
767                         return data;
768                 }
769         }
770
771         return NULL;
772 }
773
774
775 static u8 * decrypt_ms_key(const u8 *key, size_t len,
776                            const u8 *req_authenticator,
777                            const u8 *secret, size_t secret_len, size_t *reslen)
778 {
779         u8 *plain, *ppos, *res;
780         const u8 *pos;
781         size_t left, plen;
782         u8 hash[MD5_MAC_LEN];
783         MD5_CTX context;
784         int i, first = 1;
785
786         /* key: 16-bit salt followed by encrypted key info */
787
788         if (len < 2 + 16)
789                 return NULL;
790
791         pos = key + 2;
792         left = len - 2;
793         if (left % 16) {
794                 printf("Invalid ms key len %lu\n", (unsigned long) left);
795                 return NULL;
796         }
797
798         plen = left;
799         ppos = plain = malloc(plen);
800         if (plain == NULL)
801                 return NULL;
802
803         while (left > 0) {
804                 /* b(1) = MD5(Secret + Request-Authenticator + Salt)
805                  * b(i) = MD5(Secret + c(i - 1)) for i > 1 */
806
807                 MD5Init(&context);
808                 MD5Update(&context, secret, secret_len);
809                 if (first) {
810                         MD5Update(&context, req_authenticator, MD5_MAC_LEN);
811                         MD5Update(&context, key, 2); /* Salt */
812                         first = 0;
813                 } else
814                         MD5Update(&context, pos - MD5_MAC_LEN, MD5_MAC_LEN);
815                 MD5Final(hash, &context);
816
817                 for (i = 0; i < MD5_MAC_LEN; i++)
818                         *ppos++ = *pos++ ^ hash[i];
819                 left -= MD5_MAC_LEN;
820         }
821
822         if (plain[0] > plen - 1) {
823                 printf("Failed to decrypt MPPE key\n");
824                 free(plain);
825                 return NULL;
826         }
827
828         res = malloc(plain[0]);
829         if (res == NULL) {
830                 free(plain);
831                 return NULL;
832         }
833         memcpy(res, plain + 1, plain[0]);
834         if (reslen)
835                 *reslen = plain[0];
836         free(plain);
837         return res;
838 }
839
840
841 static void encrypt_ms_key(const u8 *key, size_t key_len, u16 salt,
842                            const u8 *req_authenticator,
843                            const u8 *secret, size_t secret_len,
844                            u8 *ebuf, size_t *elen)
845 {
846         int i, len, first = 1;
847         u8 hash[MD5_MAC_LEN], saltbuf[2], *pos;
848         MD5_CTX context;
849
850         saltbuf[0] = salt >> 8;
851         saltbuf[1] = salt;
852
853         len = 1 + key_len;
854         if (len & 0x0f) {
855                 len = (len & 0xf0) + 16;
856         }
857         memset(ebuf, 0, len);
858         ebuf[0] = key_len;
859         memcpy(ebuf + 1, key, key_len);
860
861         *elen = len;
862
863         pos = ebuf;
864         while (len > 0) {
865                 /* b(1) = MD5(Secret + Request-Authenticator + Salt)
866                  * b(i) = MD5(Secret + c(i - 1)) for i > 1 */
867                 MD5Init(&context);
868                 MD5Update(&context, secret, secret_len);
869                 if (first) {
870                         MD5Update(&context, req_authenticator, MD5_MAC_LEN);
871                         MD5Update(&context, saltbuf, sizeof(saltbuf));
872                         first = 0;
873                 } else {
874                         MD5Update(&context, pos - MD5_MAC_LEN, MD5_MAC_LEN);
875                 }
876                 MD5Final(hash, &context);
877
878                 for (i = 0; i < MD5_MAC_LEN; i++)
879                         *pos++ ^= hash[i];
880
881                 len -= MD5_MAC_LEN;
882         }
883 }
884
885
886 struct radius_ms_mppe_keys *
887 radius_msg_get_ms_keys(struct radius_msg *msg, struct radius_msg *sent_msg,
888                        u8 *secret, size_t secret_len)
889 {
890         u8 *key;
891         size_t keylen;
892         struct radius_ms_mppe_keys *keys;
893
894         if (msg == NULL || sent_msg == NULL)
895                 return NULL;
896
897         keys = (struct radius_ms_mppe_keys *) malloc(sizeof(*keys));
898         if (keys == NULL)
899                 return NULL;
900
901         memset(keys, 0, sizeof(*keys));
902
903         key = radius_msg_get_vendor_attr(msg, RADIUS_VENDOR_ID_MICROSOFT,
904                                          RADIUS_VENDOR_ATTR_MS_MPPE_SEND_KEY,
905                                          &keylen);
906         if (key) {
907                 keys->send = decrypt_ms_key(key, keylen,
908                                             sent_msg->hdr->authenticator,
909                                             secret, secret_len,
910                                             &keys->send_len);
911                 free(key);
912         }
913
914         key = radius_msg_get_vendor_attr(msg, RADIUS_VENDOR_ID_MICROSOFT,
915                                          RADIUS_VENDOR_ATTR_MS_MPPE_RECV_KEY,
916                                          &keylen);
917         if (key) {
918                 keys->recv = decrypt_ms_key(key, keylen,
919                                             sent_msg->hdr->authenticator,
920                                             secret, secret_len,
921                                             &keys->recv_len);
922                 free(key);
923         }
924
925         return keys;
926 }
927
928
929 struct radius_ms_mppe_keys *
930 radius_msg_get_cisco_keys(struct radius_msg *msg, struct radius_msg *sent_msg,
931                           u8 *secret, size_t secret_len)
932 {
933         u8 *key;
934         size_t keylen;
935         struct radius_ms_mppe_keys *keys;
936
937         if (msg == NULL || sent_msg == NULL)
938                 return NULL;
939
940         keys = (struct radius_ms_mppe_keys *) malloc(sizeof(*keys));
941         if (keys == NULL)
942                 return NULL;
943
944         memset(keys, 0, sizeof(*keys));
945
946         key = radius_msg_get_vendor_attr(msg, RADIUS_VENDOR_ID_CISCO,
947                                          RADIUS_CISCO_AV_PAIR, &keylen);
948         if (key && keylen == 51 && memcmp(key, "leap:session-key=", 17) == 0) {
949                 keys->recv = decrypt_ms_key(key + 17, keylen - 17,
950                                             sent_msg->hdr->authenticator,
951                                             secret, secret_len,
952                                             &keys->recv_len);
953                 free(key);
954         }
955
956         return keys;
957 }
958
959
960 int radius_msg_add_mppe_keys(struct radius_msg *msg,
961                              const u8 *req_authenticator,
962                              const u8 *secret, size_t secret_len,
963                              const u8 *send_key, size_t send_key_len,
964                              const u8 *recv_key, size_t recv_key_len)
965 {
966         struct radius_attr_hdr *attr;
967         u32 vendor_id = htonl(RADIUS_VENDOR_ID_MICROSOFT);
968         u8 *buf;
969         struct radius_attr_vendor *vhdr;
970         u8 *pos;
971         size_t elen;
972         int hlen;
973         u16 salt;
974
975         hlen = sizeof(vendor_id) + sizeof(*vhdr) + 2;
976
977         /* MS-MPPE-Send-Key */
978         buf = malloc(hlen + send_key_len + 16);
979         if (buf == NULL) {
980                 return 0;
981         }
982         pos = buf;
983         memcpy(pos, &vendor_id, sizeof(vendor_id));
984         pos += sizeof(vendor_id);
985         vhdr = (struct radius_attr_vendor *) pos;
986         vhdr->vendor_type = RADIUS_VENDOR_ATTR_MS_MPPE_SEND_KEY;
987         pos = (u8 *) (vhdr + 1);
988         salt = random() | 0x8000;
989         *pos++ = salt >> 8;
990         *pos++ = salt;
991         encrypt_ms_key(send_key, send_key_len, salt, req_authenticator, secret,
992                        secret_len, pos, &elen);
993         vhdr->vendor_length = hlen + elen - sizeof(vendor_id);
994
995         attr = radius_msg_add_attr(msg, RADIUS_ATTR_VENDOR_SPECIFIC,
996                                    buf, hlen + elen);
997         free(buf);
998         if (attr == NULL) {
999                 return 0;
1000         }
1001
1002         /* MS-MPPE-Recv-Key */
1003         buf = malloc(hlen + send_key_len + 16);
1004         if (buf == NULL) {
1005                 return 0;
1006         }
1007         pos = buf;
1008         memcpy(pos, &vendor_id, sizeof(vendor_id));
1009         pos += sizeof(vendor_id);
1010         vhdr = (struct radius_attr_vendor *) pos;
1011         vhdr->vendor_type = RADIUS_VENDOR_ATTR_MS_MPPE_RECV_KEY;
1012         pos = (u8 *) (vhdr + 1);
1013         salt ^= 1;
1014         *pos++ = salt >> 8;
1015         *pos++ = salt;
1016         encrypt_ms_key(recv_key, recv_key_len, salt, req_authenticator, secret,
1017                        secret_len, pos, &elen);
1018         vhdr->vendor_length = hlen + elen - sizeof(vendor_id);
1019
1020         attr = radius_msg_add_attr(msg, RADIUS_ATTR_VENDOR_SPECIFIC,
1021                                    buf, hlen + elen);
1022         free(buf);
1023         if (attr == NULL) {
1024                 return 0;
1025         }
1026
1027         return 1;
1028 }
1029
1030
1031 /* Add User-Password attribute to a RADIUS message and encrypt it as specified
1032  * in RFC 2865, Chap. 5.2 */
1033 struct radius_attr_hdr *
1034 radius_msg_add_attr_user_password(struct radius_msg *msg,
1035                                   u8 *data, size_t data_len,
1036                                   u8 *secret, size_t secret_len)
1037 {
1038         u8 buf[128];
1039         int padlen, i, pos;
1040         MD5_CTX context;
1041         size_t buf_len;
1042         u8 hash[16];
1043
1044         if (data_len > 128)
1045                 return NULL;
1046
1047         memcpy(buf, data, data_len);
1048         buf_len = data_len;
1049
1050         padlen = data_len % 16;
1051         if (padlen) {
1052                 padlen = 16 - padlen;
1053                 memset(buf + data_len, 0, padlen);
1054                 buf_len += padlen;
1055         }
1056
1057         MD5Init(&context);
1058         MD5Update(&context, secret, secret_len);
1059         MD5Update(&context, msg->hdr->authenticator, 16);
1060         MD5Final(hash, &context);
1061
1062         for (i = 0; i < 16; i++)
1063                 buf[i] ^= hash[i];
1064         pos = 16;
1065
1066         while (pos < buf_len) {
1067                 MD5Init(&context);
1068                 MD5Update(&context, secret, secret_len);
1069                 MD5Update(&context, &buf[pos - 16], 16);
1070                 MD5Final(hash, &context);
1071
1072                 for (i = 0; i < 16; i++)
1073                         buf[pos + i] ^= hash[i];
1074
1075                 pos += 16;
1076         }
1077
1078         return radius_msg_add_attr(msg, RADIUS_ATTR_USER_PASSWORD,
1079                                    buf, buf_len);
1080 }
1081
1082
1083 int radius_msg_get_attr(struct radius_msg *msg, u8 type, u8 *buf, size_t len)
1084 {
1085         int i;
1086         struct radius_attr_hdr *attr = NULL;
1087         size_t dlen;
1088
1089         for (i = 0; i < msg->attr_used; i++) {
1090                 if (msg->attrs[i]->type == type) {
1091                         attr = msg->attrs[i];
1092                         break;
1093                 }
1094         }
1095
1096         if (!attr)
1097                 return -1;
1098
1099         dlen = attr->length - sizeof(*attr);
1100         if (buf)
1101                 memcpy(buf, (attr + 1), dlen > len ? len : dlen);
1102         return dlen;
1103 }
1104
1105
1106 int radius_msg_get_attr_ptr(struct radius_msg *msg, u8 type, u8 **buf,
1107                             size_t *len)
1108 {
1109         int i;
1110         struct radius_attr_hdr *attr = NULL;
1111
1112         for (i = 0; i < msg->attr_used; i++) {
1113                 if (msg->attrs[i]->type == type) {
1114                         attr = msg->attrs[i];
1115                         break;
1116                 }
1117         }
1118
1119         if (!attr)
1120                 return -1;
1121
1122         *buf = (u8 *) (attr + 1);
1123         *len = attr->length - sizeof(*attr);
1124         return 0;
1125 }