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