Use existing temporary variable conn.
[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 /* returns first tlv of the given type */
66 struct tlv *radmsg_gettype(struct radmsg *msg, uint8_t type) {
67     struct list_node *node;
68     struct tlv *tlv;
69
70     if (!msg)
71         return NULL;
72     for (node = list_first(msg->attrs); node; node = list_next(node)) {
73         tlv = (struct tlv *)node->data;
74         if (tlv->t == type)
75             return tlv;
76     }
77     return NULL;
78 }
79
80 int _checkmsgauth(unsigned char *rad, uint8_t *authattr, uint8_t *secret) {
81     static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
82     static unsigned char first = 1;
83     static HMAC_CTX hmacctx;
84     unsigned int md_len;
85     uint8_t auth[16], hash[EVP_MAX_MD_SIZE];
86
87     pthread_mutex_lock(&lock);
88     if (first) {
89         HMAC_CTX_init(&hmacctx);
90         first = 0;
91     }
92
93     memcpy(auth, authattr, 16);
94     memset(authattr, 0, 16);
95     md_len = 0;
96     HMAC_Init_ex(&hmacctx, secret, strlen((char *)secret), EVP_md5(), NULL);
97     HMAC_Update(&hmacctx, rad, RADLEN(rad));
98     HMAC_Final(&hmacctx, hash, &md_len);
99     memcpy(authattr, auth, 16);
100     if (md_len != 16) {
101         debug(DBG_WARN, "message auth computation failed");
102         pthread_mutex_unlock(&lock);
103         return 0;
104     }
105
106     if (memcmp(auth, hash, 16)) {
107         debug(DBG_WARN, "message authenticator, wrong value");
108         pthread_mutex_unlock(&lock);
109         return 0;
110     }
111
112     pthread_mutex_unlock(&lock);
113     return 1;
114 }
115
116 int _validauth(unsigned char *rad, unsigned char *reqauth, unsigned char *sec) {
117     static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
118     static unsigned char first = 1;
119     static EVP_MD_CTX mdctx;
120     unsigned char hash[EVP_MAX_MD_SIZE];
121     unsigned int len;
122     int result;
123
124     pthread_mutex_lock(&lock);
125     if (first) {
126         EVP_MD_CTX_init(&mdctx);
127         first = 0;
128     }
129
130     len = RADLEN(rad);
131
132     result = (EVP_DigestInit_ex(&mdctx, EVP_md5(), NULL) &&
133               EVP_DigestUpdate(&mdctx, rad, 4) &&
134               EVP_DigestUpdate(&mdctx, reqauth, 16) &&
135               (len <= 20 || EVP_DigestUpdate(&mdctx, rad + 20, len - 20)) &&
136               EVP_DigestUpdate(&mdctx, sec, strlen((char *)sec)) &&
137               EVP_DigestFinal_ex(&mdctx, hash, &len) &&
138               len == 16 &&
139               !memcmp(hash, rad + 4, 16));
140     pthread_mutex_unlock(&lock);
141     return result;
142 }
143
144 int _createmessageauth(unsigned char *rad, unsigned char *authattrval, uint8_t *secret) {
145     static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
146     static unsigned char first = 1;
147     static HMAC_CTX hmacctx;
148     unsigned int md_len;
149
150     if (!authattrval)
151         return 1;
152
153     pthread_mutex_lock(&lock);
154     if (first) {
155         HMAC_CTX_init(&hmacctx);
156         first = 0;
157     }
158
159     memset(authattrval, 0, 16);
160     md_len = 0;
161     HMAC_Init_ex(&hmacctx, secret, strlen((char *)secret), EVP_md5(), NULL);
162     HMAC_Update(&hmacctx, rad, RADLEN(rad));
163     HMAC_Final(&hmacctx, authattrval, &md_len);
164     if (md_len != 16) {
165         debug(DBG_WARN, "message auth computation failed");
166         pthread_mutex_unlock(&lock);
167         return 0;
168     }
169     pthread_mutex_unlock(&lock);
170     return 1;
171 }
172
173 int _radsign(unsigned char *rad, unsigned char *sec) {
174     static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
175     static unsigned char first = 1;
176     static EVP_MD_CTX mdctx;
177     unsigned int md_len;
178     int result;
179
180     pthread_mutex_lock(&lock);
181     if (first) {
182         EVP_MD_CTX_init(&mdctx);
183         first = 0;
184     }
185
186     result = (EVP_DigestInit_ex(&mdctx, EVP_md5(), NULL) &&
187               EVP_DigestUpdate(&mdctx, rad, RADLEN(rad)) &&
188               EVP_DigestUpdate(&mdctx, sec, strlen((char *)sec)) &&
189               EVP_DigestFinal_ex(&mdctx, rad + 4, &md_len) &&
190               md_len == 16);
191     pthread_mutex_unlock(&lock);
192     return result;
193 }
194
195 uint8_t *radmsg2buf(struct radmsg *msg, uint8_t *secret) {
196     struct list_node *node;
197     struct tlv *tlv;
198     int size;
199     uint8_t *buf, *p, *msgauth = NULL;
200
201     if (!msg || !msg->attrs)
202         return NULL;
203     size = 20;
204     for (node = list_first(msg->attrs); node; node = list_next(node))
205         size += 2 + ((struct tlv *)node->data)->l;
206     if (size > 65535)
207         return NULL;
208     buf = malloc(size);
209     if (!buf)
210         return NULL;
211
212     p = buf;
213     *p++ = msg->code;
214     *p++ = msg->id;
215     *(uint16_t *)p = htons(size);
216     p += 2;
217     memcpy(p, msg->auth, 16);
218     p += 16;
219
220     for (node = list_first(msg->attrs); node; node = list_next(node)) {
221         tlv = (struct tlv *)node->data;
222         p = tlv2buf(p, tlv);
223         p[-1] += 2;
224         if (tlv->t == RAD_Attr_Message_Authenticator && secret)
225             msgauth = p;
226         p += tlv->l;
227     }
228     if (msgauth && !_createmessageauth(buf, msgauth, secret)) {
229         free(buf);
230         return NULL;
231     }
232     if (secret) {
233         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)) {
234             free(buf);
235             return NULL;
236         }
237         if (msg->code == RAD_Accounting_Request)
238             memcpy(msg->auth, buf + 4, 16);
239     }
240     return buf;
241 }
242
243 /* if secret set we also validate message authenticator if present */
244 struct radmsg *buf2radmsg(uint8_t *buf, uint8_t *secret, uint8_t *rqauth) {
245     struct radmsg *msg;
246     uint8_t t, l, *v = NULL, *p, auth[16];
247     uint16_t len;
248     struct tlv *attr;
249
250     len = RADLEN(buf);
251     if (len < 20)
252         return NULL;
253
254     if (secret && buf[0] == RAD_Accounting_Request) {
255         memset(auth, 0, 16);
256         if (!_validauth(buf, auth, secret)) {
257             debug(DBG_WARN, "buf2radmsg: Accounting-Request message authentication failed");
258             return NULL;
259         }
260     }
261
262     if (rqauth && !_validauth(buf, rqauth, secret)) {
263         debug(DBG_WARN, "buf2radmsg: Invalid auth, ignoring reply");
264         return NULL;
265     }
266
267     msg = radmsg_init(buf[0], buf[1], (uint8_t *)buf + 4);
268     if (!msg)
269         return NULL;
270
271     p = buf + 20;
272     while (p - buf + 2 <= len) {
273         t = *p++;
274         l = *p++;
275         if (l < 2) {
276             debug(DBG_WARN, "buf2radmsg: invalid attribute length %d", l);
277             radmsg_free(msg);
278             return NULL;
279         }
280         l -= 2;
281         if (l) {
282             if (p - buf + l > len) {
283                 debug(DBG_WARN, "buf2radmsg: attribute length %d exceeds packet length", l + 2);
284                 radmsg_free(msg);
285                 return NULL;
286             }
287             v = p;
288             p += l;
289         }
290
291         if (t == RAD_Attr_Message_Authenticator && secret) {
292             if (rqauth)
293                 memcpy(buf + 4, rqauth, 16);
294             if (l != 16 || !_checkmsgauth(buf, v, secret)) {
295                 debug(DBG_WARN, "buf2radmsg: message authentication failed");
296                 if (rqauth)
297                     memcpy(buf + 4, msg->auth, 16);
298                 radmsg_free(msg);
299                 return NULL;
300             }
301             if (rqauth)
302                 memcpy(buf + 4, msg->auth, 16);
303             debug(DBG_DBG, "buf2radmsg: message auth ok");
304         }
305
306         attr = maketlv(t, l, v);
307         if (!attr || !radmsg_add(msg, attr)) {
308             freetlv(attr);
309             radmsg_free(msg);
310             return NULL;
311         }
312     }
313     return msg;
314 }
315
316 /* Local Variables: */
317 /* c-file-style: "stroustrup" */
318 /* End: */