WIP: Fix the Proxy-State issue.
[libradsec.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 /** Return a new list with all tlv's in \a msg of type \a type. The
61  * caller is responsible for freeing the list by calling \a
62  * list_free(). */
63 struct list *
64 radmsg_getalltype(const struct radmsg *msg, uint8_t type)
65 {
66     struct list *list = NULL;
67     struct list_node *node = NULL;
68
69     if (!msg || !msg->attrs)
70         return NULL;
71     list = list_create();
72     if (!list)
73         return NULL;
74
75     for (node = list_first(msg->attrs); node; node = list_next(node))
76         if (((struct tlv *) node->data)->t == type)
77             if (list_push(list, node->data) != 1) {
78                 list_free(list);
79                 return NULL;
80             }
81     return list;
82 }
83
84 /* returns first tlv of the given type */
85 struct tlv *radmsg_gettype(struct radmsg *msg, uint8_t type) {
86     struct list_node *node;
87     struct tlv *tlv;
88
89     if (!msg)
90         return NULL;
91     for (node = list_first(msg->attrs); node; node = list_next(node)) {
92         tlv = (struct tlv *)node->data;
93         if (tlv->t == type)
94             return tlv;
95     }
96     return NULL;
97 }
98
99 /** Copy all attributes of type \a type from \a src to \a dst.
100  *
101  * If all attributes were copied successfully, the number of
102  * attributes copied is returned.
103  *
104  * If copying failed, a negative number is returned. The number
105  * returned is 0 minus the number of attributes successfully copied
106  * before the failure. */
107 int radmsg_copy_attrs(struct radmsg *dst,
108                       const struct radmsg *src,
109                       uint8_t type)
110 {
111     struct list_node *node = NULL;
112     struct list *list = radmsg_getalltype(src, type);
113     int n = 0;
114
115     for (node = list_first(list); node; node = list_next(node)) {
116         if (radmsg_add(dst, (struct tlv *) node->data) != 1) {
117             n = -n;
118             break;
119         }
120         n++;
121     }
122     list_free(list);
123     return n;
124 }
125
126 int _checkmsgauth(unsigned char *rad, uint8_t *authattr, uint8_t *secret) {
127     static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
128     static unsigned char first = 1;
129     static HMAC_CTX hmacctx;
130     unsigned int md_len;
131     uint8_t auth[16], hash[EVP_MAX_MD_SIZE];
132
133     pthread_mutex_lock(&lock);
134     if (first) {
135         HMAC_CTX_init(&hmacctx);
136         first = 0;
137     }
138
139     memcpy(auth, authattr, 16);
140     memset(authattr, 0, 16);
141     md_len = 0;
142     HMAC_Init_ex(&hmacctx, secret, strlen((char *)secret), EVP_md5(), NULL);
143     HMAC_Update(&hmacctx, rad, RADLEN(rad));
144     HMAC_Final(&hmacctx, hash, &md_len);
145     memcpy(authattr, auth, 16);
146     if (md_len != 16) {
147         debug(DBG_WARN, "message auth computation failed");
148         pthread_mutex_unlock(&lock);
149         return 0;
150     }
151
152     if (memcmp(auth, hash, 16)) {
153         debug(DBG_WARN, "message authenticator, wrong value");
154         pthread_mutex_unlock(&lock);
155         return 0;
156     }
157
158     pthread_mutex_unlock(&lock);
159     return 1;
160 }
161
162 int _validauth(unsigned char *rad, unsigned char *reqauth, unsigned char *sec) {
163     static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
164     static unsigned char first = 1;
165     static EVP_MD_CTX mdctx;
166     unsigned char hash[EVP_MAX_MD_SIZE];
167     unsigned int len;
168     int result;
169
170     pthread_mutex_lock(&lock);
171     if (first) {
172         EVP_MD_CTX_init(&mdctx);
173         first = 0;
174     }
175
176     len = RADLEN(rad);
177
178     result = (EVP_DigestInit_ex(&mdctx, EVP_md5(), NULL) &&
179               EVP_DigestUpdate(&mdctx, rad, 4) &&
180               EVP_DigestUpdate(&mdctx, reqauth, 16) &&
181               (len <= 20 || EVP_DigestUpdate(&mdctx, rad + 20, len - 20)) &&
182               EVP_DigestUpdate(&mdctx, sec, strlen((char *)sec)) &&
183               EVP_DigestFinal_ex(&mdctx, hash, &len) &&
184               len == 16 &&
185               !memcmp(hash, rad + 4, 16));
186     pthread_mutex_unlock(&lock);
187     return result;
188 }
189
190 int _createmessageauth(unsigned char *rad, unsigned char *authattrval, uint8_t *secret) {
191     static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
192     static unsigned char first = 1;
193     static HMAC_CTX hmacctx;
194     unsigned int md_len;
195
196     if (!authattrval)
197         return 1;
198
199     pthread_mutex_lock(&lock);
200     if (first) {
201         HMAC_CTX_init(&hmacctx);
202         first = 0;
203     }
204
205     memset(authattrval, 0, 16);
206     md_len = 0;
207     HMAC_Init_ex(&hmacctx, secret, strlen((char *)secret), EVP_md5(), NULL);
208     HMAC_Update(&hmacctx, rad, RADLEN(rad));
209     HMAC_Final(&hmacctx, authattrval, &md_len);
210     if (md_len != 16) {
211         debug(DBG_WARN, "message auth computation failed");
212         pthread_mutex_unlock(&lock);
213         return 0;
214     }
215     pthread_mutex_unlock(&lock);
216     return 1;
217 }
218
219 int _radsign(unsigned char *rad, unsigned char *sec) {
220     static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
221     static unsigned char first = 1;
222     static EVP_MD_CTX mdctx;
223     unsigned int md_len;
224     int result;
225
226     pthread_mutex_lock(&lock);
227     if (first) {
228         EVP_MD_CTX_init(&mdctx);
229         first = 0;
230     }
231
232     result = (EVP_DigestInit_ex(&mdctx, EVP_md5(), NULL) &&
233               EVP_DigestUpdate(&mdctx, rad, RADLEN(rad)) &&
234               EVP_DigestUpdate(&mdctx, sec, strlen((char *)sec)) &&
235               EVP_DigestFinal_ex(&mdctx, rad + 4, &md_len) &&
236               md_len == 16);
237     pthread_mutex_unlock(&lock);
238     return result;
239 }
240
241 uint8_t *radmsg2buf(struct radmsg *msg, uint8_t *secret) {
242     struct list_node *node;
243     struct tlv *tlv;
244     int size;
245     uint8_t *buf, *p, *msgauth = NULL;
246
247     if (!msg || !msg->attrs)
248         return NULL;
249     size = 20;
250     for (node = list_first(msg->attrs); node; node = list_next(node))
251         size += 2 + ((struct tlv *)node->data)->l;
252     if (size > 65535)
253         return NULL;
254     buf = malloc(size);
255     if (!buf)
256         return NULL;
257
258     p = buf;
259     *p++ = msg->code;
260     *p++ = msg->id;
261     *(uint16_t *)p = htons(size);
262     p += 2;
263     memcpy(p, msg->auth, 16);
264     p += 16;
265
266     for (node = list_first(msg->attrs); node; node = list_next(node)) {
267         tlv = (struct tlv *)node->data;
268         p = tlv2buf(p, tlv);
269         p[-1] += 2;
270         if (tlv->t == RAD_Attr_Message_Authenticator && secret)
271             msgauth = p;
272         p += tlv->l;
273     }
274     if (msgauth && !_createmessageauth(buf, msgauth, secret)) {
275         free(buf);
276         return NULL;
277     }
278     if (secret) {
279         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)) {
280             free(buf);
281             return NULL;
282         }
283         if (msg->code == RAD_Accounting_Request)
284             memcpy(msg->auth, buf + 4, 16);
285     }
286     return buf;
287 }
288
289 /* if secret set we also validate message authenticator if present */
290 struct radmsg *buf2radmsg(uint8_t *buf, uint8_t *secret, uint8_t *rqauth) {
291     struct radmsg *msg;
292     uint8_t t, l, *v = NULL, *p, auth[16];
293     uint16_t len;
294     struct tlv *attr;
295
296     len = RADLEN(buf);
297     if (len < 20)
298         return NULL;
299
300     if (secret && buf[0] == RAD_Accounting_Request) {
301         memset(auth, 0, 16);
302         if (!_validauth(buf, auth, secret)) {
303             debug(DBG_WARN, "buf2radmsg: Accounting-Request message authentication failed");
304             return NULL;
305         }
306     }
307
308     if (rqauth && !_validauth(buf, rqauth, secret)) {
309         debug(DBG_WARN, "buf2radmsg: Invalid auth, ignoring reply");
310         return NULL;
311     }
312
313     msg = radmsg_init(buf[0], buf[1], (uint8_t *)buf + 4);
314     if (!msg)
315         return NULL;
316
317     p = buf + 20;
318     while (p - buf + 2 <= len) {
319         t = *p++;
320         l = *p++;
321         if (l < 2) {
322             debug(DBG_WARN, "buf2radmsg: invalid attribute length %d", l);
323             radmsg_free(msg);
324             return NULL;
325         }
326         l -= 2;
327         if (l) {
328             if (p - buf + l > len) {
329                 debug(DBG_WARN, "buf2radmsg: attribute length %d exceeds packet length", l + 2);
330                 radmsg_free(msg);
331                 return NULL;
332             }
333             v = p;
334             p += l;
335         }
336
337         if (t == RAD_Attr_Message_Authenticator && secret) {
338             if (rqauth)
339                 memcpy(buf + 4, rqauth, 16);
340             if (l != 16 || !_checkmsgauth(buf, v, secret)) {
341                 debug(DBG_WARN, "buf2radmsg: message authentication failed");
342                 if (rqauth)
343                     memcpy(buf + 4, msg->auth, 16);
344                 radmsg_free(msg);
345                 return NULL;
346             }
347             if (rqauth)
348                 memcpy(buf + 4, msg->auth, 16);
349             debug(DBG_DBG, "buf2radmsg: message auth ok");
350         }
351
352         attr = maketlv(t, l, v);
353         if (!attr || !radmsg_add(msg, attr)) {
354             freetlv(attr);
355             radmsg_free(msg);
356             return NULL;
357         }
358     }
359     return msg;
360 }
361
362 /* Local Variables: */
363 /* c-file-style: "stroustrup" */
364 /* End: */