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