summaryrefslogtreecommitdiff
path: root/radmsg.c
diff options
context:
space:
mode:
authorvenaas <venaas>2008-09-11 10:38:51 +0000
committervenaas <venaas@e88ac4ed-0b26-0410-9574-a7f39faa03bf>2008-09-11 10:38:51 +0000
commitfaf8717dcbc9c2e3ed1892402133b6c9663a5e7d (patch)
tree3085f5bd29b9c68d8ae94892b91d78586cd89a95 /radmsg.c
parenta98c9b737c943e062efa1a4fc7b31d539e1a2f11 (diff)
lots of changes to radsrv/reply and use of new radmsg stuff
git-svn-id: https://svn.testnett.uninett.no/radsecproxy/trunk@373 e88ac4ed-0b26-0410-9574-a7f39faa03bf
Diffstat (limited to 'radmsg.c')
-rw-r--r--radmsg.c238
1 files changed, 238 insertions, 0 deletions
diff --git a/radmsg.c b/radmsg.c
new file mode 100644
index 0000000..26f8fbe
--- /dev/null
+++ b/radmsg.c
@@ -0,0 +1,238 @@
+/*
+ * Copyright (C) 2006-2008 Stig Venaas <venaas@uninett.no>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <arpa/inet.h>
+#include "list.h"
+#include "tlv11.h"
+#include "radmsg.h"
+#include "debug.h"
+#include <pthread.h>
+#include <openssl/hmac.h>
+
+#define RADLEN(x) ntohs(((uint16_t *)(x))[1])
+
+void radmsg_free(struct radmsg *msg) {
+ if (msg) {
+ freetlvlist(msg->attrs);
+ free(msg);
+ }
+}
+
+struct radmsg *radmsg_init(uint8_t code, uint8_t id, uint8_t *auth) {
+ struct radmsg *msg;
+
+ msg = malloc(sizeof(struct radmsg));
+ if (!msg)
+ return NULL;
+ msg->attrs = list_create();
+ if (!msg->attrs) {
+ free(msg);
+ return NULL;
+ }
+ msg->code = code;
+ msg->id = id;
+ memcpy(msg->auth, auth, 16);
+ return msg;
+}
+
+int radmsg_add(struct radmsg *msg, struct tlv *attr) {
+ if (!msg || !msg->attrs)
+ return 1;
+ if (!attr)
+ return 0;
+ return list_push(msg->attrs, attr);
+}
+
+/* returns first tlv of the given type */
+struct tlv *radmsg_gettype(struct radmsg *msg, uint8_t type) {
+ struct list_node *node;
+ struct tlv *tlv;
+
+ if (!msg)
+ return NULL;
+ for (node = list_first(msg->attrs); node; node = list_next(node)) {
+ tlv = (struct tlv *)node->data;
+ if (tlv->t == type)
+ return tlv;
+ }
+ return NULL;
+}
+
+uint8_t *radmsg2buf(struct radmsg *msg) {
+ struct list_node *node;
+ struct tlv *tlv;
+ int size;
+ uint8_t *buf, *p;
+
+ if (!msg || !msg->attrs)
+ return NULL;
+ size = 20;
+ for (node = list_first(msg->attrs); node; node = list_next(node))
+ size += 2 + ((struct tlv *)node->data)->l;
+ if (size > 65535)
+ return NULL;
+ buf = malloc(size);
+ if (!buf)
+ return NULL;
+
+ p = buf;
+ *p++ = msg->code;
+ *p++ = msg->id;
+ *(uint16_t *)p = htons(size);
+ p += 2;
+ memcpy(p, msg->auth, 16);
+ p += 16;
+
+ for (node = list_first(msg->attrs); node; node = list_next(node)) {
+ tlv = (struct tlv *)node->data;
+ p = tlv2buf(p, tlv);
+ p[-1] += 2;
+ p += tlv->l;
+ }
+ return buf;
+}
+
+int _checkmsgauth(unsigned char *rad, uint8_t *authattr, uint8_t *secret) {
+ static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
+ static unsigned char first = 1;
+ static HMAC_CTX hmacctx;
+ unsigned int md_len;
+ uint8_t auth[16], hash[EVP_MAX_MD_SIZE];
+
+ pthread_mutex_lock(&lock);
+ if (first) {
+ HMAC_CTX_init(&hmacctx);
+ first = 0;
+ }
+
+ memcpy(auth, authattr, 16);
+ memset(authattr, 0, 16);
+ md_len = 0;
+ HMAC_Init_ex(&hmacctx, secret, strlen((char *)secret), EVP_md5(), NULL);
+ HMAC_Update(&hmacctx, rad, RADLEN(rad));
+ HMAC_Final(&hmacctx, hash, &md_len);
+ memcpy(authattr, auth, 16);
+ if (md_len != 16) {
+ debug(DBG_WARN, "message auth computation failed");
+ pthread_mutex_unlock(&lock);
+ return 0;
+ }
+
+ if (memcmp(auth, hash, 16)) {
+ debug(DBG_WARN, "message authenticator, wrong value");
+ pthread_mutex_unlock(&lock);
+ return 0;
+ }
+
+ pthread_mutex_unlock(&lock);
+ return 1;
+}
+
+int _validauth(unsigned char *rad, unsigned char *reqauth, unsigned char *sec) {
+ static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
+ static unsigned char first = 1;
+ static EVP_MD_CTX mdctx;
+ unsigned char hash[EVP_MAX_MD_SIZE];
+ unsigned int len;
+ int result;
+
+ pthread_mutex_lock(&lock);
+ if (first) {
+ EVP_MD_CTX_init(&mdctx);
+ first = 0;
+ }
+
+ len = RADLEN(rad);
+
+ result = (EVP_DigestInit_ex(&mdctx, EVP_md5(), NULL) &&
+ EVP_DigestUpdate(&mdctx, rad, 4) &&
+ EVP_DigestUpdate(&mdctx, reqauth, 16) &&
+ (len <= 20 || EVP_DigestUpdate(&mdctx, rad + 20, len - 20)) &&
+ EVP_DigestUpdate(&mdctx, sec, strlen((char *)sec)) &&
+ EVP_DigestFinal_ex(&mdctx, hash, &len) &&
+ len == 16 &&
+ !memcmp(hash, rad + 4, 16));
+ pthread_mutex_unlock(&lock);
+ return result;
+}
+
+/* if secret set we also validate message authenticator if present */
+struct radmsg *buf2radmsg(uint8_t *buf, uint8_t *secret, uint8_t *rqauth) {
+ struct radmsg *msg;
+ uint8_t t, l, *v, *p, auth[16];
+ uint16_t len;
+ struct tlv *attr;
+
+ len = RADLEN(buf);
+ if (len < 20)
+ return NULL;
+
+ if (secret && buf[0] == RAD_Accounting_Request) {
+ memset(auth, 0, 16);
+ if (!_validauth(buf, auth, secret)) {
+ debug(DBG_WARN, "buf2radmsg: Accounting-Request message authentication failed");
+ return NULL;
+ }
+ }
+
+ if (rqauth && !_validauth(buf, rqauth, secret)) {
+ debug(DBG_WARN, "buf2radmsg: Invalid auth, ignoring reply");
+ return NULL;
+ }
+
+ msg = radmsg_init(buf[0], buf[1], (uint8_t *)buf + 4);
+ if (!msg)
+ return NULL;
+
+ p = buf + 20;
+ while (p - buf + 2 <= len) {
+ t = *p++;
+ l = *p++;
+ if (l < 2) {
+ debug(DBG_WARN, "buf2radmsg: invalid attribute length %d", l);
+ radmsg_free(msg);
+ return NULL;
+ }
+ l -= 2;
+ if (l) {
+ if (p - buf + l > len) {
+ debug(DBG_WARN, "buf2radmsg: attribute length %d exceeds packet length", l + 2);
+ radmsg_free(msg);
+ return NULL;
+ }
+ v = p;
+ p += l;
+ }
+
+ if (t == RAD_Attr_Message_Authenticator && secret) {
+ if (rqauth)
+ memcpy(buf + 4, rqauth, 16);
+ if (l != 16 || !_checkmsgauth(buf, v, secret)) {
+ debug(DBG_WARN, "buf2radmsg: message authentication failed");
+ if (rqauth)
+ memcpy(buf + 4, msg->auth, 16);
+ radmsg_free(msg);
+ return NULL;
+ }
+ if (rqauth)
+ memcpy(buf + 4, msg->auth, 16);
+ debug(DBG_DBG, "buf2radmsg: message auth ok");
+ }
+
+ attr = maketlv(t, l, v);
+ if (!attr || !radmsg_add(msg, attr)) {
+ freetlv(attr);
+ radmsg_free(msg);
+ return NULL;
+ }
+ }
+ return msg;
+}