radsecproxy-1.6.5.
[libradsec.git] / radmsg.c
1 /*
2  * Copyright (C) 2006-2008 Stig Venaas <venaas@uninett.no>
3  *
4  * Permission to use, copy, modify, and distribute this software for any
5  * purpose with or without fee is hereby granted, provided that the above
6  * copyright notice and this permission notice appear in all copies.
7  */
8
9 #ifdef SYS_SOLARIS9
10 #include <sys/inttypes.h>
11 #else
12 #include <stdint.h>
13 #endif
14 #include <stdlib.h>
15 #include <string.h>
16 #include <arpa/inet.h>
17 #include "list.h"
18 #include "tlv11.h"
19 #include "radmsg.h"
20 #include "debug.h"
21 #include <pthread.h>
22 #include <openssl/hmac.h>
23 #include <openssl/rand.h>
24
25 #define RADLEN(x) ntohs(((uint16_t *)(x))[1])
26
27 void radmsg_free(struct radmsg *msg) {
28     if (msg) {
29         freetlvlist(msg->attrs);
30         free(msg);
31     }
32 }
33
34 struct radmsg *radmsg_init(uint8_t code, uint8_t id, uint8_t *auth) {
35     struct radmsg *msg;
36
37     msg = malloc(sizeof(struct radmsg));
38     if (!msg)
39         return NULL;
40     memset(msg, 0, sizeof(struct radmsg));
41     msg->attrs = list_create();
42     if (!msg->attrs) {
43         free(msg);
44         return NULL;
45     }
46     msg->code = code;
47     msg->id = id;
48     if (auth)
49         memcpy(msg->auth, auth, 16);
50     else if (!RAND_bytes(msg->auth, 16)) {
51         free(msg);
52         return NULL;
53     }
54     return msg;
55 }
56
57 int radmsg_add(struct radmsg *msg, struct tlv *attr) {
58     if (!msg || !msg->attrs)
59         return 1;
60     if (!attr)
61         return 0;
62     return list_push(msg->attrs, attr);
63 }
64
65 /** Return a new list with all tlv's in \a msg of type \a type. The
66  * caller is responsible for freeing the list by calling \a
67  * list_free(). */
68 struct list *
69 radmsg_getalltype(const struct radmsg *msg, uint8_t type)
70 {
71     struct list *list = NULL;
72     struct list_node *node = NULL;
73
74     if (!msg || !msg->attrs)
75         return NULL;
76     list = list_create();
77     if (!list)
78         return NULL;
79
80     for (node = list_first(msg->attrs); node; node = list_next(node))
81         if (((struct tlv *) node->data)->t == type)
82             if (list_push(list, node->data) != 1) {
83                 list_free(list);
84                 return NULL;
85             }
86     return list;
87 }
88
89 /* returns first tlv of the given type */
90 struct tlv *radmsg_gettype(struct radmsg *msg, uint8_t type) {
91     struct list_node *node;
92     struct tlv *tlv;
93
94     if (!msg)
95         return NULL;
96     for (node = list_first(msg->attrs); node; node = list_next(node)) {
97         tlv = (struct tlv *)node->data;
98         if (tlv->t == type)
99             return tlv;
100     }
101     return NULL;
102 }
103
104 /** Copy all attributes of type \a type from \a src to \a dst.
105  *
106  * If all attributes were copied successfully, the number of
107  * attributes copied is returned.
108  *
109  * If copying failed, a negative number is returned. */
110 int radmsg_copy_attrs(struct radmsg *dst,
111                       const struct radmsg *src,
112                       uint8_t type)
113 {
114     struct list_node *node = NULL;
115     struct list *list = radmsg_getalltype(src, type);
116     int n = 0;
117
118     for (node = list_first(list); node; node = list_next(node)) {
119         if (radmsg_add(dst, copytlv((struct tlv *) node->data)) != 1) {
120             n = -1;
121             break;
122         }
123         n++;
124     }
125     list_free(list);
126     return n;
127 }
128
129 int _checkmsgauth(unsigned char *rad, uint8_t *authattr, uint8_t *secret) {
130     static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
131     static unsigned char first = 1;
132     static HMAC_CTX hmacctx;
133     unsigned int md_len;
134     uint8_t auth[16], hash[EVP_MAX_MD_SIZE];
135
136     pthread_mutex_lock(&lock);
137     if (first) {
138         HMAC_CTX_init(&hmacctx);
139         first = 0;
140     }
141
142     memcpy(auth, authattr, 16);
143     memset(authattr, 0, 16);
144     md_len = 0;
145     HMAC_Init_ex(&hmacctx, secret, strlen((char *)secret), EVP_md5(), NULL);
146     HMAC_Update(&hmacctx, rad, RADLEN(rad));
147     HMAC_Final(&hmacctx, hash, &md_len);
148     memcpy(authattr, auth, 16);
149     if (md_len != 16) {
150         debug(DBG_WARN, "message auth computation failed");
151         pthread_mutex_unlock(&lock);
152         return 0;
153     }
154
155     if (memcmp(auth, hash, 16)) {
156         debug(DBG_WARN, "message authenticator, wrong value");
157         pthread_mutex_unlock(&lock);
158         return 0;
159     }
160
161     pthread_mutex_unlock(&lock);
162     return 1;
163 }
164
165 int _validauth(unsigned char *rad, unsigned char *reqauth, unsigned char *sec) {
166     static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
167     static unsigned char first = 1;
168     static EVP_MD_CTX mdctx;
169     unsigned char hash[EVP_MAX_MD_SIZE];
170     unsigned int len;
171     int result;
172
173     pthread_mutex_lock(&lock);
174     if (first) {
175         EVP_MD_CTX_init(&mdctx);
176         first = 0;
177     }
178
179     len = RADLEN(rad);
180
181     result = (EVP_DigestInit_ex(&mdctx, EVP_md5(), NULL) &&
182               EVP_DigestUpdate(&mdctx, rad, 4) &&
183               EVP_DigestUpdate(&mdctx, reqauth, 16) &&
184               (len <= 20 || EVP_DigestUpdate(&mdctx, rad + 20, len - 20)) &&
185               EVP_DigestUpdate(&mdctx, sec, strlen((char *)sec)) &&
186               EVP_DigestFinal_ex(&mdctx, hash, &len) &&
187               len == 16 &&
188               !memcmp(hash, rad + 4, 16));
189     pthread_mutex_unlock(&lock);
190     return result;
191 }
192
193 int _createmessageauth(unsigned char *rad, unsigned char *authattrval, uint8_t *secret) {
194     static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
195     static unsigned char first = 1;
196     static HMAC_CTX hmacctx;
197     unsigned int md_len;
198
199     if (!authattrval)
200         return 1;
201
202     pthread_mutex_lock(&lock);
203     if (first) {
204         HMAC_CTX_init(&hmacctx);
205         first = 0;
206     }
207
208     memset(authattrval, 0, 16);
209     md_len = 0;
210     HMAC_Init_ex(&hmacctx, secret, strlen((char *)secret), EVP_md5(), NULL);
211     HMAC_Update(&hmacctx, rad, RADLEN(rad));
212     HMAC_Final(&hmacctx, authattrval, &md_len);
213     if (md_len != 16) {
214         debug(DBG_WARN, "message auth computation failed");
215         pthread_mutex_unlock(&lock);
216         return 0;
217     }
218     pthread_mutex_unlock(&lock);
219     return 1;
220 }
221
222 int _radsign(unsigned char *rad, unsigned char *sec) {
223     static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
224     static unsigned char first = 1;
225     static EVP_MD_CTX mdctx;
226     unsigned int md_len;
227     int result;
228
229     pthread_mutex_lock(&lock);
230     if (first) {
231         EVP_MD_CTX_init(&mdctx);
232         first = 0;
233     }
234
235     result = (EVP_DigestInit_ex(&mdctx, EVP_md5(), NULL) &&
236               EVP_DigestUpdate(&mdctx, rad, RADLEN(rad)) &&
237               EVP_DigestUpdate(&mdctx, sec, strlen((char *)sec)) &&
238               EVP_DigestFinal_ex(&mdctx, rad + 4, &md_len) &&
239               md_len == 16);
240     pthread_mutex_unlock(&lock);
241     return result;
242 }
243
244 uint8_t *radmsg2buf(struct radmsg *msg, uint8_t *secret) {
245     struct list_node *node;
246     struct tlv *tlv;
247     int size;
248     uint8_t *buf, *p, *msgauth = NULL;
249
250     if (!msg || !msg->attrs)
251         return NULL;
252     size = 20;
253     for (node = list_first(msg->attrs); node; node = list_next(node))
254         size += 2 + ((struct tlv *)node->data)->l;
255     if (size > 65535)
256         return NULL;
257     buf = malloc(size);
258     if (!buf)
259         return NULL;
260
261     p = buf;
262     *p++ = msg->code;
263     *p++ = msg->id;
264     *(uint16_t *)p = htons(size);
265     p += 2;
266     memcpy(p, msg->auth, 16);
267     p += 16;
268
269     for (node = list_first(msg->attrs); node; node = list_next(node)) {
270         tlv = (struct tlv *)node->data;
271         p = tlv2buf(p, tlv);
272         p[-1] += 2;
273         if (tlv->t == RAD_Attr_Message_Authenticator && secret)
274             msgauth = p;
275         p += tlv->l;
276     }
277     if (msgauth && !_createmessageauth(buf, msgauth, secret)) {
278         free(buf);
279         return NULL;
280     }
281     if (secret) {
282         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)) {
283             free(buf);
284             return NULL;
285         }
286         if (msg->code == RAD_Accounting_Request)
287             memcpy(msg->auth, buf + 4, 16);
288     }
289     return buf;
290 }
291
292 /* if secret set we also validate message authenticator if present */
293 struct radmsg *buf2radmsg(uint8_t *buf, uint8_t *secret, uint8_t *rqauth) {
294     struct radmsg *msg;
295     uint8_t t, l, *v = NULL, *p, auth[16];
296     uint16_t len;
297     struct tlv *attr;
298
299     len = RADLEN(buf);
300     if (len < 20)
301         return NULL;
302
303     if (secret && buf[0] == RAD_Accounting_Request) {
304         memset(auth, 0, 16);
305         if (!_validauth(buf, auth, secret)) {
306             debug(DBG_WARN, "buf2radmsg: Accounting-Request message authentication failed");
307             return NULL;
308         }
309     }
310
311     if (rqauth && !_validauth(buf, rqauth, secret)) {
312         debug(DBG_WARN, "buf2radmsg: Invalid auth, ignoring reply");
313         return NULL;
314     }
315
316     msg = radmsg_init(buf[0], buf[1], (uint8_t *)buf + 4);
317     if (!msg)
318         return NULL;
319
320     p = buf + 20;
321     while (p - buf + 2 <= len) {
322         t = *p++;
323         l = *p++;
324         if (l < 2) {
325             debug(DBG_WARN, "buf2radmsg: invalid attribute length %d", l);
326             radmsg_free(msg);
327             return NULL;
328         }
329         l -= 2;
330         if (l) {
331             if (p - buf + l > len) {
332                 debug(DBG_WARN, "buf2radmsg: attribute length %d exceeds packet length", l + 2);
333                 radmsg_free(msg);
334                 return NULL;
335             }
336             v = p;
337             p += l;
338         }
339
340         if (t == RAD_Attr_Message_Authenticator && secret) {
341             if (rqauth)
342                 memcpy(buf + 4, rqauth, 16);
343             if (l != 16 || !_checkmsgauth(buf, v, secret)) {
344                 debug(DBG_WARN, "buf2radmsg: message authentication failed");
345                 if (rqauth)
346                     memcpy(buf + 4, msg->auth, 16);
347                 radmsg_free(msg);
348                 return NULL;
349             }
350             if (rqauth)
351                 memcpy(buf + 4, msg->auth, 16);
352             debug(DBG_DBG, "buf2radmsg: message auth ok");
353         }
354
355         attr = maketlv(t, l, v);
356         if (!attr || !radmsg_add(msg, attr)) {
357             freetlv(attr);
358             radmsg_free(msg);
359             return NULL;
360         }
361     }
362     return msg;
363 }
364
365 /* Local Variables: */
366 /* c-file-style: "stroustrup" */
367 /* End: */