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