Return free memory more aggressively.
[radsecproxy.git] / radmsg.c
1 /* Copyright (c) 2007-2009, UNINETT AS */
2 /* See LICENSE for licensing information. */
3
4 #ifdef SYS_SOLARIS9
5 #include <sys/inttypes.h>
6 #else
7 #include <stdint.h>
8 #endif
9 #include <stdlib.h>
10 #include <string.h>
11 #include <arpa/inet.h>
12 #include "list.h"
13 #include "tlv11.h"
14 #include "radmsg.h"
15 #include "debug.h"
16 #include <pthread.h>
17 #include <openssl/hmac.h>
18 #include <openssl/rand.h>
19
20 #define RADLEN(x) ntohs(((uint16_t *)(x))[1])
21
22 void radmsg_free(struct radmsg *msg) {
23     if (msg) {
24         freetlvlist(msg->attrs);
25         free(msg);
26     }
27 }
28
29 struct radmsg *radmsg_init(uint8_t code, uint8_t id, uint8_t *auth) {
30     struct radmsg *msg;
31
32     msg = malloc(sizeof(struct radmsg));
33     if (!msg)
34         return NULL;
35     memset(msg, 0, sizeof(struct radmsg));
36     msg->attrs = list_create();
37     if (!msg->attrs) {
38         free(msg);
39         return NULL;
40     }
41     msg->code = code;
42     msg->id = id;
43     if (auth)
44         memcpy(msg->auth, auth, 16);
45     else if (!RAND_bytes(msg->auth, 16)) {
46         free(msg);
47         return NULL;
48     }
49     return msg;
50 }
51
52 int radmsg_add(struct radmsg *msg, struct tlv *attr) {
53     if (!msg || !msg->attrs)
54         return 1;
55     if (!attr)
56         return 0;
57     return list_push(msg->attrs, attr);
58 }
59
60 /* returns first tlv of the given type */
61 struct tlv *radmsg_gettype(struct radmsg *msg, uint8_t type) {
62     struct list_node *node;
63     struct tlv *tlv;
64
65     if (!msg)
66         return NULL;
67     for (node = list_first(msg->attrs); node; node = list_next(node)) {
68         tlv = (struct tlv *)node->data;
69         if (tlv->t == type)
70             return tlv;
71     }
72     return NULL;
73 }
74
75 int _checkmsgauth(unsigned char *rad, uint8_t *authattr, uint8_t *secret) {
76     static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
77     static unsigned char first = 1;
78     static HMAC_CTX hmacctx;
79     unsigned int md_len;
80     uint8_t auth[16], hash[EVP_MAX_MD_SIZE];
81
82     pthread_mutex_lock(&lock);
83     if (first) {
84         HMAC_CTX_init(&hmacctx);
85         first = 0;
86     }
87
88     memcpy(auth, authattr, 16);
89     memset(authattr, 0, 16);
90     md_len = 0;
91     HMAC_Init_ex(&hmacctx, secret, strlen((char *)secret), EVP_md5(), NULL);
92     HMAC_Update(&hmacctx, rad, RADLEN(rad));
93     HMAC_Final(&hmacctx, hash, &md_len);
94     memcpy(authattr, auth, 16);
95     if (md_len != 16) {
96         debug(DBG_WARN, "message auth computation failed");
97         pthread_mutex_unlock(&lock);
98         return 0;
99     }
100
101     if (memcmp(auth, hash, 16)) {
102         debug(DBG_WARN, "message authenticator, wrong value");
103         pthread_mutex_unlock(&lock);
104         return 0;
105     }
106
107     pthread_mutex_unlock(&lock);
108     return 1;
109 }
110
111 int _validauth(unsigned char *rad, unsigned char *reqauth, unsigned char *sec) {
112     static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
113     static unsigned char first = 1;
114     static EVP_MD_CTX mdctx;
115     unsigned char hash[EVP_MAX_MD_SIZE];
116     unsigned int len;
117     int result;
118
119     pthread_mutex_lock(&lock);
120     if (first) {
121         EVP_MD_CTX_init(&mdctx);
122         first = 0;
123     }
124
125     len = RADLEN(rad);
126
127     result = (EVP_DigestInit_ex(&mdctx, EVP_md5(), NULL) &&
128               EVP_DigestUpdate(&mdctx, rad, 4) &&
129               EVP_DigestUpdate(&mdctx, reqauth, 16) &&
130               (len <= 20 || EVP_DigestUpdate(&mdctx, rad + 20, len - 20)) &&
131               EVP_DigestUpdate(&mdctx, sec, strlen((char *)sec)) &&
132               EVP_DigestFinal_ex(&mdctx, hash, &len) &&
133               len == 16 &&
134               !memcmp(hash, rad + 4, 16));
135     pthread_mutex_unlock(&lock);
136     return result;
137 }
138
139 int _createmessageauth(unsigned char *rad, unsigned char *authattrval, uint8_t *secret) {
140     static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
141     static unsigned char first = 1;
142     static HMAC_CTX hmacctx;
143     unsigned int md_len;
144
145     if (!authattrval)
146         return 1;
147
148     pthread_mutex_lock(&lock);
149     if (first) {
150         HMAC_CTX_init(&hmacctx);
151         first = 0;
152     }
153
154     memset(authattrval, 0, 16);
155     md_len = 0;
156     HMAC_Init_ex(&hmacctx, secret, strlen((char *)secret), EVP_md5(), NULL);
157     HMAC_Update(&hmacctx, rad, RADLEN(rad));
158     HMAC_Final(&hmacctx, authattrval, &md_len);
159     if (md_len != 16) {
160         debug(DBG_WARN, "message auth computation failed");
161         pthread_mutex_unlock(&lock);
162         return 0;
163     }
164     pthread_mutex_unlock(&lock);
165     return 1;
166 }
167
168 int _radsign(unsigned char *rad, unsigned char *sec) {
169     static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
170     static unsigned char first = 1;
171     static EVP_MD_CTX mdctx;
172     unsigned int md_len;
173     int result;
174
175     pthread_mutex_lock(&lock);
176     if (first) {
177         EVP_MD_CTX_init(&mdctx);
178         first = 0;
179     }
180
181     result = (EVP_DigestInit_ex(&mdctx, EVP_md5(), NULL) &&
182               EVP_DigestUpdate(&mdctx, rad, RADLEN(rad)) &&
183               EVP_DigestUpdate(&mdctx, sec, strlen((char *)sec)) &&
184               EVP_DigestFinal_ex(&mdctx, rad + 4, &md_len) &&
185               md_len == 16);
186     pthread_mutex_unlock(&lock);
187     return result;
188 }
189
190 uint8_t *radmsg2buf(struct radmsg *msg, uint8_t *secret) {
191     struct list_node *node;
192     struct tlv *tlv;
193     int size;
194     uint8_t *buf, *p, *msgauth = NULL;
195
196     if (!msg || !msg->attrs)
197         return NULL;
198     size = 20;
199     for (node = list_first(msg->attrs); node; node = list_next(node))
200         size += 2 + ((struct tlv *)node->data)->l;
201     if (size > 65535)
202         return NULL;
203     buf = malloc(size);
204     if (!buf)
205         return NULL;
206
207     p = buf;
208     *p++ = msg->code;
209     *p++ = msg->id;
210     *(uint16_t *)p = htons(size);
211     p += 2;
212     memcpy(p, msg->auth, 16);
213     p += 16;
214
215     for (node = list_first(msg->attrs); node; node = list_next(node)) {
216         tlv = (struct tlv *)node->data;
217         p = tlv2buf(p, tlv);
218         p[-1] += 2;
219         if (tlv->t == RAD_Attr_Message_Authenticator && secret)
220             msgauth = p;
221         p += tlv->l;
222     }
223     if (msgauth && !_createmessageauth(buf, msgauth, secret)) {
224         free(buf);
225         return NULL;
226     }
227     if (secret) {
228         if ((msg->code == RAD_Access_Accept || msg->code == RAD_Access_Reject || msg->code == RAD_Access_Challenge || msg->code == RAD_Accounting_Response || msg->code == RAD_Accounting_Request) && !_radsign(buf, secret)) {
229             free(buf);
230             return NULL;
231         }
232         if (msg->code == RAD_Accounting_Request)
233             memcpy(msg->auth, buf + 4, 16);
234     }
235     return buf;
236 }
237
238 /* if secret set we also validate message authenticator if present */
239 struct radmsg *buf2radmsg(uint8_t *buf, uint8_t *secret, uint8_t *rqauth) {
240     struct radmsg *msg;
241     uint8_t t, l, *v = NULL, *p, auth[16];
242     uint16_t len;
243     struct tlv *attr;
244
245     len = RADLEN(buf);
246     if (len < 20)
247         return NULL;
248
249     if (secret && buf[0] == RAD_Accounting_Request) {
250         memset(auth, 0, 16);
251         if (!_validauth(buf, auth, secret)) {
252             debug(DBG_WARN, "buf2radmsg: Accounting-Request message authentication failed");
253             return NULL;
254         }
255     }
256
257     if (rqauth && !_validauth(buf, rqauth, secret)) {
258         debug(DBG_WARN, "buf2radmsg: Invalid auth, ignoring reply");
259         return NULL;
260     }
261
262     msg = radmsg_init(buf[0], buf[1], (uint8_t *)buf + 4);
263     if (!msg)
264         return NULL;
265
266     p = buf + 20;
267     while (p - buf + 2 <= len) {
268         t = *p++;
269         l = *p++;
270         if (l < 2) {
271             debug(DBG_WARN, "buf2radmsg: invalid attribute length %d", l);
272             radmsg_free(msg);
273             return NULL;
274         }
275         l -= 2;
276         if (l) {
277             if (p - buf + l > len) {
278                 debug(DBG_WARN, "buf2radmsg: attribute length %d exceeds packet length", l + 2);
279                 radmsg_free(msg);
280                 return NULL;
281             }
282             v = p;
283             p += l;
284         }
285
286         if (t == RAD_Attr_Message_Authenticator && secret) {
287             if (rqauth)
288                 memcpy(buf + 4, rqauth, 16);
289             if (l != 16 || !_checkmsgauth(buf, v, secret)) {
290                 debug(DBG_WARN, "buf2radmsg: message authentication failed");
291                 if (rqauth)
292                     memcpy(buf + 4, msg->auth, 16);
293                 radmsg_free(msg);
294                 return NULL;
295             }
296             if (rqauth)
297                 memcpy(buf + 4, msg->auth, 16);
298             debug(DBG_DBG, "buf2radmsg: message auth ok");
299         }
300
301         attr = maketlv(t, l, v);
302         if (!attr || !radmsg_add(msg, attr)) {
303             freetlv(attr);
304             radmsg_free(msg);
305             return NULL;
306         }
307     }
308     return msg;
309 }
310
311 /* Local Variables: */
312 /* c-file-style: "stroustrup" */
313 /* End: */