1 /* Copyright (c) 2007-2009, UNINETT AS */
2 /* See LICENSE for licensing information. */
5 #include <sys/inttypes.h>
11 #include <arpa/inet.h>
17 #include <openssl/hmac.h>
18 #include <openssl/rand.h>
20 #define RADLEN(x) ntohs(((uint16_t *)(x))[1])
22 void radmsg_free(struct radmsg *msg) {
24 freetlvlist(msg->attrs);
29 struct radmsg *radmsg_init(uint8_t code, uint8_t id, uint8_t *auth) {
32 msg = malloc(sizeof(struct radmsg));
35 memset(msg, 0, sizeof(struct radmsg));
36 msg->attrs = list_create();
44 memcpy(msg->auth, auth, 16);
45 else if (!RAND_bytes(msg->auth, 16)) {
52 int radmsg_add(struct radmsg *msg, struct tlv *attr) {
53 if (!msg || !msg->attrs)
57 return list_push(msg->attrs, attr);
60 /** Return a new list with all tlv's in \a msg of type \a type. The
61 * caller is responsible for freeing the list by calling \a
64 radmsg_getalltype(const struct radmsg *msg, uint8_t type)
66 struct list *list = NULL;
67 struct list_node *node = NULL;
69 if (!msg || !msg->attrs)
75 for (node = list_first(msg->attrs); node; node = list_next(node))
76 if (((struct tlv *) node->data)->t == type)
77 if (list_push(list, node->data) != 1) {
84 /* returns first tlv of the given type */
85 struct tlv *radmsg_gettype(struct radmsg *msg, uint8_t type) {
86 struct list_node *node;
91 for (node = list_first(msg->attrs); node; node = list_next(node)) {
92 tlv = (struct tlv *)node->data;
99 /** Copy all attributes of type \a type from \a src to \a dst.
101 * If all attributes were copied successfully, the number of
102 * attributes copied is returned.
104 * If copying failed, a negative number is returned. The number
105 * returned is 0 minus the number of attributes successfully copied
106 * before the failure. */
107 int radmsg_copy_attrs(struct radmsg *dst,
108 const struct radmsg *src,
111 struct list_node *node = NULL;
112 struct list *list = radmsg_getalltype(src, type);
115 for (node = list_first(list); node; node = list_next(node)) {
116 if (radmsg_add(dst, (struct tlv *) node->data) != 1) {
126 int _checkmsgauth(unsigned char *rad, uint8_t *authattr, uint8_t *secret) {
127 static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
128 static unsigned char first = 1;
129 static HMAC_CTX hmacctx;
131 uint8_t auth[16], hash[EVP_MAX_MD_SIZE];
133 pthread_mutex_lock(&lock);
135 HMAC_CTX_init(&hmacctx);
139 memcpy(auth, authattr, 16);
140 memset(authattr, 0, 16);
142 HMAC_Init_ex(&hmacctx, secret, strlen((char *)secret), EVP_md5(), NULL);
143 HMAC_Update(&hmacctx, rad, RADLEN(rad));
144 HMAC_Final(&hmacctx, hash, &md_len);
145 memcpy(authattr, auth, 16);
147 debug(DBG_WARN, "message auth computation failed");
148 pthread_mutex_unlock(&lock);
152 if (memcmp(auth, hash, 16)) {
153 debug(DBG_WARN, "message authenticator, wrong value");
154 pthread_mutex_unlock(&lock);
158 pthread_mutex_unlock(&lock);
162 int _validauth(unsigned char *rad, unsigned char *reqauth, unsigned char *sec) {
163 static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
164 static unsigned char first = 1;
165 static EVP_MD_CTX mdctx;
166 unsigned char hash[EVP_MAX_MD_SIZE];
170 pthread_mutex_lock(&lock);
172 EVP_MD_CTX_init(&mdctx);
178 result = (EVP_DigestInit_ex(&mdctx, EVP_md5(), NULL) &&
179 EVP_DigestUpdate(&mdctx, rad, 4) &&
180 EVP_DigestUpdate(&mdctx, reqauth, 16) &&
181 (len <= 20 || EVP_DigestUpdate(&mdctx, rad + 20, len - 20)) &&
182 EVP_DigestUpdate(&mdctx, sec, strlen((char *)sec)) &&
183 EVP_DigestFinal_ex(&mdctx, hash, &len) &&
185 !memcmp(hash, rad + 4, 16));
186 pthread_mutex_unlock(&lock);
190 int _createmessageauth(unsigned char *rad, unsigned char *authattrval, uint8_t *secret) {
191 static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
192 static unsigned char first = 1;
193 static HMAC_CTX hmacctx;
199 pthread_mutex_lock(&lock);
201 HMAC_CTX_init(&hmacctx);
205 memset(authattrval, 0, 16);
207 HMAC_Init_ex(&hmacctx, secret, strlen((char *)secret), EVP_md5(), NULL);
208 HMAC_Update(&hmacctx, rad, RADLEN(rad));
209 HMAC_Final(&hmacctx, authattrval, &md_len);
211 debug(DBG_WARN, "message auth computation failed");
212 pthread_mutex_unlock(&lock);
215 pthread_mutex_unlock(&lock);
219 int _radsign(unsigned char *rad, unsigned char *sec) {
220 static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
221 static unsigned char first = 1;
222 static EVP_MD_CTX mdctx;
226 pthread_mutex_lock(&lock);
228 EVP_MD_CTX_init(&mdctx);
232 result = (EVP_DigestInit_ex(&mdctx, EVP_md5(), NULL) &&
233 EVP_DigestUpdate(&mdctx, rad, RADLEN(rad)) &&
234 EVP_DigestUpdate(&mdctx, sec, strlen((char *)sec)) &&
235 EVP_DigestFinal_ex(&mdctx, rad + 4, &md_len) &&
237 pthread_mutex_unlock(&lock);
241 uint8_t *radmsg2buf(struct radmsg *msg, uint8_t *secret) {
242 struct list_node *node;
245 uint8_t *buf, *p, *msgauth = NULL;
247 if (!msg || !msg->attrs)
250 for (node = list_first(msg->attrs); node; node = list_next(node))
251 size += 2 + ((struct tlv *)node->data)->l;
261 *(uint16_t *)p = htons(size);
263 memcpy(p, msg->auth, 16);
266 for (node = list_first(msg->attrs); node; node = list_next(node)) {
267 tlv = (struct tlv *)node->data;
270 if (tlv->t == RAD_Attr_Message_Authenticator && secret)
274 if (msgauth && !_createmessageauth(buf, msgauth, secret)) {
279 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)) {
283 if (msg->code == RAD_Accounting_Request)
284 memcpy(msg->auth, buf + 4, 16);
289 /* if secret set we also validate message authenticator if present */
290 struct radmsg *buf2radmsg(uint8_t *buf, uint8_t *secret, uint8_t *rqauth) {
292 uint8_t t, l, *v = NULL, *p, auth[16];
300 if (secret && buf[0] == RAD_Accounting_Request) {
302 if (!_validauth(buf, auth, secret)) {
303 debug(DBG_WARN, "buf2radmsg: Accounting-Request message authentication failed");
308 if (rqauth && !_validauth(buf, rqauth, secret)) {
309 debug(DBG_WARN, "buf2radmsg: Invalid auth, ignoring reply");
313 msg = radmsg_init(buf[0], buf[1], (uint8_t *)buf + 4);
318 while (p - buf + 2 <= len) {
322 debug(DBG_WARN, "buf2radmsg: invalid attribute length %d", l);
328 if (p - buf + l > len) {
329 debug(DBG_WARN, "buf2radmsg: attribute length %d exceeds packet length", l + 2);
337 if (t == RAD_Attr_Message_Authenticator && secret) {
339 memcpy(buf + 4, rqauth, 16);
340 if (l != 16 || !_checkmsgauth(buf, v, secret)) {
341 debug(DBG_WARN, "buf2radmsg: message authentication failed");
343 memcpy(buf + 4, msg->auth, 16);
348 memcpy(buf + 4, msg->auth, 16);
349 debug(DBG_DBG, "buf2radmsg: message auth ok");
352 attr = maketlv(t, l, v);
353 if (!attr || !radmsg_add(msg, attr)) {
362 /* Local Variables: */
363 /* c-file-style: "stroustrup" */