lots of changes to radsrv/reply and use of new radmsg stuff
[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 #include <stdint.h>
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
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     msg->attrs = list_create();
36     if (!msg->attrs) {
37         free(msg);
38         return NULL;
39     }   
40     msg->code = code;
41     msg->id = id;
42     memcpy(msg->auth, auth, 16);
43     return msg;
44 }
45
46 int radmsg_add(struct radmsg *msg, struct tlv *attr) {
47     if (!msg || !msg->attrs)
48         return 1;
49     if (!attr)
50         return 0;
51     return list_push(msg->attrs, attr);
52 }
53
54 /* returns first tlv of the given type */
55 struct tlv *radmsg_gettype(struct radmsg *msg, uint8_t type) {
56     struct list_node *node;
57     struct tlv *tlv;
58
59     if (!msg)
60         return NULL;
61     for (node = list_first(msg->attrs); node; node = list_next(node)) {
62         tlv = (struct tlv *)node->data;
63         if (tlv->t == type)
64             return tlv;
65     }
66     return NULL;
67 }
68
69 uint8_t *radmsg2buf(struct radmsg *msg) {
70     struct list_node *node;
71     struct tlv *tlv;
72     int size;
73     uint8_t *buf, *p;
74
75     if (!msg || !msg->attrs)
76         return NULL;
77     size = 20;
78     for (node = list_first(msg->attrs); node; node = list_next(node))
79         size += 2 + ((struct tlv *)node->data)->l;
80     if (size > 65535)
81         return NULL;
82     buf = malloc(size);
83     if (!buf)
84         return NULL;
85     
86     p = buf;
87     *p++ = msg->code;
88     *p++ = msg->id;
89     *(uint16_t *)p = htons(size);
90     p += 2;
91     memcpy(p, msg->auth, 16);
92     p += 16;
93
94     for (node = list_first(msg->attrs); node; node = list_next(node)) {
95         tlv = (struct tlv *)node->data;
96         p = tlv2buf(p, tlv);
97         p[-1] += 2;
98         p += tlv->l;
99     }
100     return buf;
101 }
102
103 int _checkmsgauth(unsigned char *rad, uint8_t *authattr, uint8_t *secret) {
104     static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
105     static unsigned char first = 1;
106     static HMAC_CTX hmacctx;
107     unsigned int md_len;
108     uint8_t auth[16], hash[EVP_MAX_MD_SIZE];
109     
110     pthread_mutex_lock(&lock);
111     if (first) {
112         HMAC_CTX_init(&hmacctx);
113         first = 0;
114     }
115
116     memcpy(auth, authattr, 16);
117     memset(authattr, 0, 16);
118     md_len = 0;
119     HMAC_Init_ex(&hmacctx, secret, strlen((char *)secret), EVP_md5(), NULL);
120     HMAC_Update(&hmacctx, rad, RADLEN(rad));
121     HMAC_Final(&hmacctx, hash, &md_len);
122     memcpy(authattr, auth, 16);
123     if (md_len != 16) {
124         debug(DBG_WARN, "message auth computation failed");
125         pthread_mutex_unlock(&lock);
126         return 0;
127     }
128
129     if (memcmp(auth, hash, 16)) {
130         debug(DBG_WARN, "message authenticator, wrong value");
131         pthread_mutex_unlock(&lock);
132         return 0;
133     }   
134         
135     pthread_mutex_unlock(&lock);
136     return 1;
137 }
138
139 int _validauth(unsigned char *rad, unsigned char *reqauth, unsigned char *sec) {
140     static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
141     static unsigned char first = 1;
142     static EVP_MD_CTX mdctx;
143     unsigned char hash[EVP_MAX_MD_SIZE];
144     unsigned int len;
145     int result;
146     
147     pthread_mutex_lock(&lock);
148     if (first) {
149         EVP_MD_CTX_init(&mdctx);
150         first = 0;
151     }
152
153     len = RADLEN(rad);
154     
155     result = (EVP_DigestInit_ex(&mdctx, EVP_md5(), NULL) &&
156               EVP_DigestUpdate(&mdctx, rad, 4) &&
157               EVP_DigestUpdate(&mdctx, reqauth, 16) &&
158               (len <= 20 || EVP_DigestUpdate(&mdctx, rad + 20, len - 20)) &&
159               EVP_DigestUpdate(&mdctx, sec, strlen((char *)sec)) &&
160               EVP_DigestFinal_ex(&mdctx, hash, &len) &&
161               len == 16 &&
162               !memcmp(hash, rad + 4, 16));
163     pthread_mutex_unlock(&lock);
164     return result;
165 }
166               
167 /* if secret set we also validate message authenticator if present */
168 struct radmsg *buf2radmsg(uint8_t *buf, uint8_t *secret, uint8_t *rqauth) {
169     struct radmsg *msg;
170     uint8_t t, l, *v, *p, auth[16];
171     uint16_t len;
172     struct tlv *attr;
173     
174     len = RADLEN(buf);
175     if (len < 20)
176         return NULL;
177
178     if (secret && buf[0] == RAD_Accounting_Request) {
179         memset(auth, 0, 16);
180         if (!_validauth(buf, auth, secret)) {
181             debug(DBG_WARN, "buf2radmsg: Accounting-Request message authentication failed");
182             return NULL;
183         }
184     }
185
186     if (rqauth && !_validauth(buf, rqauth, secret)) {
187         debug(DBG_WARN, "buf2radmsg: Invalid auth, ignoring reply");
188         return NULL;
189     }
190         
191     msg = radmsg_init(buf[0], buf[1], (uint8_t *)buf + 4);
192     if (!msg)
193         return NULL;
194
195     p = buf + 20;
196     while (p - buf + 2 <= len) {
197         t = *p++;
198         l = *p++;
199         if (l < 2) {
200             debug(DBG_WARN, "buf2radmsg: invalid attribute length %d", l);
201             radmsg_free(msg);
202             return NULL;
203         }
204         l -= 2;
205         if (l) {
206             if (p - buf + l > len) {
207                 debug(DBG_WARN, "buf2radmsg: attribute length %d exceeds packet length", l + 2);
208                 radmsg_free(msg);
209                 return NULL;
210             }
211             v = p;
212             p += l;
213         }
214         
215         if (t == RAD_Attr_Message_Authenticator && secret) {
216             if (rqauth)
217                 memcpy(buf + 4, rqauth, 16);
218             if (l != 16 || !_checkmsgauth(buf, v, secret)) {
219                 debug(DBG_WARN, "buf2radmsg: message authentication failed");
220                 if (rqauth)
221                     memcpy(buf + 4, msg->auth, 16);
222                 radmsg_free(msg);
223                 return NULL;
224             }
225             if (rqauth)
226                 memcpy(buf + 4, msg->auth, 16);
227             debug(DBG_DBG, "buf2radmsg: message auth ok");
228         }
229
230         attr = maketlv(t, l, v);
231         if (!attr || !radmsg_add(msg, attr)) {
232             freetlv(attr);
233             radmsg_free(msg);
234             return NULL;
235         }
236     }
237     return msg;
238 }