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