Fix libeap/src/utils/common.h to support windows+ipv6.
[mech_eap.git] / libeap / src / utils / radius_utils.c
1 /*
2  * RADIUS tlv construction and parsing utilites
3  * Copyright (c) 2012, Painless Security, LLC
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License version 2 as
7  * published by the Free Software Foundation.
8  *
9  * Alternatively, this software may be distributed under the terms of BSD
10  * license.
11  *
12  * See README and COPYING for more details.
13  */
14
15 #include "includes.h"
16
17 #include "common.h"
18
19 #include "radius/radius.h"
20 #include "radius_utils.h"
21 #include "wpabuf.h"
22
23 struct radius_vendor_attr_struct
24 {
25     struct wpabuf *buf;
26     u8 *len_pos;
27     size_t start;
28 };
29
30 radius_vendor_attr radius_vendor_attr_start(struct wpabuf *buf, u32 vendor)
31 {
32     radius_vendor_attr attr = (radius_vendor_attr )os_zalloc(sizeof(*attr));
33     if (!attr)
34         return attr;
35     attr->buf = buf;
36     attr->start = wpabuf_len(buf);
37     wpabuf_put_u8(buf, 26);
38     attr->len_pos = (u8*)wpabuf_put(buf, 1);
39     /* @TODO: Verify high 8 bits of vendor are 0? */
40     wpabuf_put_be32(buf, vendor);
41     return attr;
42 }
43
44 radius_vendor_attr radius_vendor_attr_add_subtype(radius_vendor_attr attr,
45                                                   u8 type,
46                                                   u8 *data,
47                                                   size_t len)
48 {
49     if (attr == VENDOR_ATTR_INVALID)
50         return attr;
51     if (len + 2 + (wpabuf_len(attr->buf) - attr->start) > 255) {
52         os_free(attr);
53         return VENDOR_ATTR_INVALID;
54     }
55     wpabuf_put_u8(attr->buf, type);
56     wpabuf_put_u8(attr->buf, len + 2);
57     wpabuf_put_data(attr->buf, data, len);
58     return attr;
59 }
60
61 radius_vendor_attr radius_vendor_attr_finish(radius_vendor_attr attr)
62 {
63     /* poke size into correct place and free attr */
64     size_t len;
65     radius_vendor_attr ret = VENDOR_ATTR_INVALID;
66     if (attr == ret)
67         return ret;
68
69     len = wpabuf_len(attr->buf) - attr->start;
70     if (len < 255) {
71         ret = attr;
72         *(attr->len_pos) = (u8 )len;
73     }
74     os_free(attr);
75     return ret;
76 }
77
78 struct radius_parser_struct
79 {
80         u8 *data;
81         size_t len;
82         size_t pos;
83 };
84
85 radius_parser radius_parser_start(void *tlvdata, size_t len)
86 {
87         radius_parser parser = malloc(sizeof(struct radius_parser_struct));
88         if (parser) {
89                 parser->data = (u8 *)tlvdata;
90                 parser->len = len;
91                 parser->pos = 0;
92         }
93         return parser;
94 }
95
96 void radius_parser_finish(radius_parser parser)
97 {
98         free(parser);
99 }
100
101 int radius_parser_parse_tlv(radius_parser parser, u8 *type, u32 *vendor_id,
102                                 void **value, size_t *len)
103 {
104         u8 rawtype, rawlen;
105         if (!parser)
106                 return -1;
107         if (parser->len < parser->pos + 3)
108                 return -1;
109         rawtype = parser->data[parser->pos];
110         rawlen = parser->data[parser->pos+1];
111         if (parser->len < parser->pos + rawlen)
112                 return -1;
113
114         if (rawtype == RADIUS_ATTR_VENDOR_SPECIFIC) {
115                 if (rawlen < 7)
116                         return -1;
117                 *vendor_id = WPA_GET_BE24(&parser->data[parser->pos + 3]);
118                 *value = &parser->data[parser->pos + 6];
119                 *len = rawlen - 6;
120         }
121         else {
122                 if (rawlen < 3)
123                         return -1;
124
125                 *value = &parser->data[parser->pos + 2];
126                 *len = rawlen - 2;
127         }
128         *type = rawtype;
129
130         parser->pos += rawlen;
131         return 0;
132 }
133
134 int radius_parser_parse_vendor_specific(radius_parser parser, u8 *vendor_type,
135                                             void **value, size_t *len)
136 {
137         u8 rawtype, rawlen;
138         if (!parser)
139                 return -1;
140         if (parser->len < parser->pos + 3)
141                 return -1;
142         rawtype = parser->data[parser->pos];
143         rawlen = parser->data[parser->pos+1];
144         if (parser->len < parser->pos + rawlen)
145                 return -1;
146
147         if (rawlen < 3)
148                 return -1;
149
150         *value = &parser->data[parser->pos + 2];
151         *len = rawlen - 2;
152         *vendor_type = rawtype;
153
154         parser->pos += rawlen;
155         return 0;
156 }