2 * Copyright (C) 2006-2008 Stig Venaas <venaas@uninett.no>
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.
10 #include <sys/inttypes.h>
16 #include <arpa/inet.h>
22 #include <openssl/hmac.h>
23 #include <openssl/rand.h>
25 #define RADLEN(x) ntohs(((uint16_t *)(x))[1])
27 void radmsg_free(struct radmsg *msg) {
29 freetlvlist(msg->attrs);
34 struct radmsg *radmsg_init(uint8_t code, uint8_t id, uint8_t *auth) {
37 msg = malloc(sizeof(struct radmsg));
40 memset(msg, 0, sizeof(struct radmsg));
41 msg->attrs = list_create();
49 memcpy(msg->auth, auth, 16);
50 else if (!RAND_bytes(msg->auth, 16)) {
57 int radmsg_add(struct radmsg *msg, struct tlv *attr) {
58 if (!msg || !msg->attrs)
62 return list_push(msg->attrs, attr);
65 /** Return a new list with all tlv's in \a msg of type \a type. The
66 * caller is responsible for freeing the list by calling \a
69 radmsg_getalltype(const struct radmsg *msg, uint8_t type)
71 struct list *list = NULL;
72 struct list_node *node = NULL;
74 if (!msg || !msg->attrs)
80 for (node = list_first(msg->attrs); node; node = list_next(node))
81 if (((struct tlv *) node->data)->t == type)
82 if (list_push(list, node->data) != 1) {
89 /* returns first tlv of the given type */
90 struct tlv *radmsg_gettype(struct radmsg *msg, uint8_t type) {
91 struct list_node *node;
96 for (node = list_first(msg->attrs); node; node = list_next(node)) {
97 tlv = (struct tlv *)node->data;
104 /** Copy all attributes of type \a type from \a src to \a dst.
106 * If all attributes were copied successfully, the number of
107 * attributes copied is returned.
109 * If copying failed, a negative number is returned. The number
110 * returned is 0 minus the number of attributes successfully copied
111 * before the failure. */
112 int radmsg_copy_attrs(struct radmsg *dst,
113 const struct radmsg *src,
116 struct list_node *node = NULL;
117 struct list *list = radmsg_getalltype(src, type);
120 for (node = list_first(list); node; node = list_next(node)) {
121 if (radmsg_add(dst, copytlv((struct tlv *) node->data)) != 1) {
131 int _checkmsgauth(unsigned char *rad, uint8_t *authattr, uint8_t *secret) {
132 static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
133 static unsigned char first = 1;
134 static HMAC_CTX hmacctx;
136 uint8_t auth[16], hash[EVP_MAX_MD_SIZE];
138 pthread_mutex_lock(&lock);
140 HMAC_CTX_init(&hmacctx);
144 memcpy(auth, authattr, 16);
145 memset(authattr, 0, 16);
147 HMAC_Init_ex(&hmacctx, secret, strlen((char *)secret), EVP_md5(), NULL);
148 HMAC_Update(&hmacctx, rad, RADLEN(rad));
149 HMAC_Final(&hmacctx, hash, &md_len);
150 memcpy(authattr, auth, 16);
152 debug(DBG_WARN, "message auth computation failed");
153 pthread_mutex_unlock(&lock);
157 if (memcmp(auth, hash, 16)) {
158 debug(DBG_WARN, "message authenticator, wrong value");
159 pthread_mutex_unlock(&lock);
163 pthread_mutex_unlock(&lock);
167 int _validauth(unsigned char *rad, unsigned char *reqauth, unsigned char *sec) {
168 static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
169 static unsigned char first = 1;
170 static EVP_MD_CTX mdctx;
171 unsigned char hash[EVP_MAX_MD_SIZE];
175 pthread_mutex_lock(&lock);
177 EVP_MD_CTX_init(&mdctx);
183 result = (EVP_DigestInit_ex(&mdctx, EVP_md5(), NULL) &&
184 EVP_DigestUpdate(&mdctx, rad, 4) &&
185 EVP_DigestUpdate(&mdctx, reqauth, 16) &&
186 (len <= 20 || EVP_DigestUpdate(&mdctx, rad + 20, len - 20)) &&
187 EVP_DigestUpdate(&mdctx, sec, strlen((char *)sec)) &&
188 EVP_DigestFinal_ex(&mdctx, hash, &len) &&
190 !memcmp(hash, rad + 4, 16));
191 pthread_mutex_unlock(&lock);
195 int _createmessageauth(unsigned char *rad, unsigned char *authattrval, uint8_t *secret) {
196 static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
197 static unsigned char first = 1;
198 static HMAC_CTX hmacctx;
204 pthread_mutex_lock(&lock);
206 HMAC_CTX_init(&hmacctx);
210 memset(authattrval, 0, 16);
212 HMAC_Init_ex(&hmacctx, secret, strlen((char *)secret), EVP_md5(), NULL);
213 HMAC_Update(&hmacctx, rad, RADLEN(rad));
214 HMAC_Final(&hmacctx, authattrval, &md_len);
216 debug(DBG_WARN, "message auth computation failed");
217 pthread_mutex_unlock(&lock);
220 pthread_mutex_unlock(&lock);
224 int _radsign(unsigned char *rad, unsigned char *sec) {
225 static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
226 static unsigned char first = 1;
227 static EVP_MD_CTX mdctx;
231 pthread_mutex_lock(&lock);
233 EVP_MD_CTX_init(&mdctx);
237 result = (EVP_DigestInit_ex(&mdctx, EVP_md5(), NULL) &&
238 EVP_DigestUpdate(&mdctx, rad, RADLEN(rad)) &&
239 EVP_DigestUpdate(&mdctx, sec, strlen((char *)sec)) &&
240 EVP_DigestFinal_ex(&mdctx, rad + 4, &md_len) &&
242 pthread_mutex_unlock(&lock);
246 uint8_t *radmsg2buf(struct radmsg *msg, uint8_t *secret) {
247 struct list_node *node;
250 uint8_t *buf, *p, *msgauth = NULL;
252 if (!msg || !msg->attrs)
255 for (node = list_first(msg->attrs); node; node = list_next(node))
256 size += 2 + ((struct tlv *)node->data)->l;
266 *(uint16_t *)p = htons(size);
268 memcpy(p, msg->auth, 16);
271 for (node = list_first(msg->attrs); node; node = list_next(node)) {
272 tlv = (struct tlv *)node->data;
275 if (tlv->t == RAD_Attr_Message_Authenticator && secret)
279 if (msgauth && !_createmessageauth(buf, msgauth, secret)) {
284 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)) {
288 if (msg->code == RAD_Accounting_Request)
289 memcpy(msg->auth, buf + 4, 16);
294 /* if secret set we also validate message authenticator if present */
295 struct radmsg *buf2radmsg(uint8_t *buf, uint8_t *secret, uint8_t *rqauth) {
297 uint8_t t, l, *v = NULL, *p, auth[16];
305 if (secret && buf[0] == RAD_Accounting_Request) {
307 if (!_validauth(buf, auth, secret)) {
308 debug(DBG_WARN, "buf2radmsg: Accounting-Request message authentication failed");
313 if (rqauth && !_validauth(buf, rqauth, secret)) {
314 debug(DBG_WARN, "buf2radmsg: Invalid auth, ignoring reply");
318 msg = radmsg_init(buf[0], buf[1], (uint8_t *)buf + 4);
323 while (p - buf + 2 <= len) {
327 debug(DBG_WARN, "buf2radmsg: invalid attribute length %d", l);
333 if (p - buf + l > len) {
334 debug(DBG_WARN, "buf2radmsg: attribute length %d exceeds packet length", l + 2);
342 if (t == RAD_Attr_Message_Authenticator && secret) {
344 memcpy(buf + 4, rqauth, 16);
345 if (l != 16 || !_checkmsgauth(buf, v, secret)) {
346 debug(DBG_WARN, "buf2radmsg: message authentication failed");
348 memcpy(buf + 4, msg->auth, 16);
353 memcpy(buf + 4, msg->auth, 16);
354 debug(DBG_DBG, "buf2radmsg: message auth ok");
357 attr = maketlv(t, l, v);
358 if (!attr || !radmsg_add(msg, attr)) {
367 /* Local Variables: */
368 /* c-file-style: "stroustrup" */