Commit faf8717d authored by venaas's avatar venaas Committed by venaas

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
parent a98c9b73
CFLAGS = -g -Wall -pedantic -pthread
LDFLAGS = -lssl
OBJ = util.o debug.o list.o hash.o gconfig.o udp.o tcp.o tls.o dtls.o radsecproxy.o
OBJ = util.o debug.o list.o hash.o gconfig.o tlv11.o radmsg.o udp.o tcp.o tls.o dtls.o radsecproxy.o
all: radsecproxy
......
......@@ -6,6 +6,8 @@ radsecproxy_SOURCES = radsecproxy.c \
debug.c \
list.c \
hash.c \
tlv11.c \
radmsg.c \
udp.c \
tcp.c \
tls.c \
......@@ -16,6 +18,8 @@ radsecproxy_SOURCES = radsecproxy.c \
util.h \
list.h \
hash.h \
tlv11.h \
radmsg.h \
udp.h \
tcp.h \
tls.h \
......
/*
* 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;
}
/*
* 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.
*/
#define RAD_Access_Request 1
#define RAD_Access_Accept 2
#define RAD_Access_Reject 3
#define RAD_Accounting_Request 4
#define RAD_Accounting_Response 5
#define RAD_Access_Challenge 11
#define RAD_Status_Server 12
#define RAD_Status_Client 13
#define RAD_Attr_User_Name 1
#define RAD_Attr_User_Password 2
#define RAD_Attr_Reply_Message 18
#define RAD_Attr_Vendor_Specific 26
#define RAD_Attr_Calling_Station_Id 31
#define RAD_Attr_Tunnel_Password 69
#define RAD_Attr_Message_Authenticator 80
#define RAD_VS_ATTR_MS_MPPE_Send_Key 16
#define RAD_VS_ATTR_MS_MPPE_Recv_Key 17
struct radmsg {
uint8_t code;
uint8_t id;
uint8_t auth[20];
struct list *attrs;
};
void radmsg_free(struct radmsg *);
struct radmsg *radmsg_init(uint8_t, uint8_t, uint8_t *);
int radmsg_add(struct radmsg *, struct tlv *);
struct tlv *radmsg_gettype(struct radmsg *, uint8_t);
uint8_t *radmsg2buf(struct radmsg *msg);
struct radmsg *buf2radmsg(uint8_t *, uint8_t *, uint8_t *);
......@@ -62,6 +62,8 @@
#include "debug.h"
#include "list.h"
#include "hash.h"
#include "tlv11.h"
#include "radmsg.h"
#include "util.h"
#include "gconfig.h"
#include "radsecproxy.h"
......@@ -900,70 +902,6 @@ int radsign(unsigned char *rad, unsigned char *sec) {
return result;
}
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;
}
int checkmessageauth(unsigned char *rad, uint8_t *authattr, char *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(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 createmessageauth(unsigned char *rad, unsigned char *authattrval, char *secret) {
static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
static unsigned char first = 1;
......@@ -1458,136 +1396,100 @@ int findvendorsubattr(uint32_t *attrs, uint32_t vendor, uint8_t subattr) {
return 0;
}
int dovendorrewriterm(uint8_t *attrs, uint16_t length, uint32_t *removevendorattrs) {
uint8_t alen, sublen, rmlen = 0;
uint32_t vendor = *(uint32_t *)ATTRVAL(attrs);
/* returns 1 if entire element is to be removed, else 0 */
int dovendorrewriterm(struct tlv *attr, uint32_t *removevendorattrs) {
uint8_t alen, sublen;
uint32_t vendor;
uint8_t *subattrs;
if (!removevendorattrs)
return 0;
memcpy(&vendor, attr->v, 4);
vendor = ntohl(vendor);
while (*removevendorattrs && *removevendorattrs != vendor)
removevendorattrs += 2;
if (!*removevendorattrs)
return 0;
alen = ATTRLEN(attrs);
if (findvendorsubattr(removevendorattrs, vendor, -1)) {
/* remove entire vendor attribute */
memmove(attrs, attrs + alen, length - alen);
return alen;
}
if (findvendorsubattr(removevendorattrs, vendor, -1))
return 1; /* remove entire vendor attribute */
sublen = alen - 4;
subattrs = ATTRVAL(attrs) + 4;
sublen = attr->l - 4;
subattrs = attr->v + 4;
if (!attrvalidate(subattrs, sublen)) {
debug(DBG_WARN, "dovendorrewrite: vendor attribute validation failed, no rewrite");
return 0;
}
length -= 6;
while (sublen > 1) {
alen = ATTRLEN(subattrs);
sublen -= alen;
length -= alen;
if (findvendorsubattr(removevendorattrs, vendor, ATTRTYPE(subattrs))) {
memmove(subattrs, subattrs + alen, length);
rmlen += alen;
memmove(subattrs, subattrs + alen, sublen);
attr->l -= alen;
} else
subattrs += alen;
}
ATTRLEN(attrs) -= rmlen;
return rmlen;
return 0;
}
void dorewriterm(uint8_t *buf, uint8_t *rmattrs, uint32_t *rmvattrs) {
uint8_t *attrs, alen;
uint16_t len, rmlen = 0;
len = RADLEN(buf) - 20;
attrs = buf + 20;
while (len > 1) {
alen = ATTRLEN(attrs);
len -= alen;
if (rmattrs && strchr((char *)rmattrs, ATTRTYPE(attrs))) {
memmove(attrs, attrs + alen, len);
rmlen += alen;
} else if (ATTRTYPE(attrs) == RAD_Attr_Vendor_Specific && rmvattrs)
rmlen += dovendorrewriterm(attrs, len, rmvattrs);
else
attrs += alen;
void dorewriterm(struct radmsg *msg, uint8_t *rmattrs, uint32_t *rmvattrs) {
struct list_node *n, *p;
struct tlv *attr;
p = NULL;
n = list_first(msg->attrs);
while (n) {
attr = (struct tlv *)n->data;
if ((rmattrs && strchr((char *)rmattrs, attr->t)) ||
(rmvattrs && attr->t == RAD_Attr_Vendor_Specific && dovendorrewriterm(attr, rmvattrs))) {
list_removedata(msg->attrs, attr);
freetlv(attr);
n = p ? list_next(p) : list_first(msg->attrs);
} else
p = n;
n = list_next(n);
}
if (rmlen)
((uint16_t *)buf)[1] = htons(RADLEN(buf) - rmlen);
}
int dorewriteadd(uint8_t **buf, struct list *addattrs) {
int dorewriteadd(struct radmsg *msg, struct list *addattrs) {
struct list_node *n;
struct attribute *a;
uint16_t i, addlen = 0;
uint8_t *newbuf;
struct tlv *a;
for (n = list_first(addattrs); n; n = list_next(n))
addlen += 2 + ((struct attribute *)n->data)->l;
if (!addlen)
return 1;
newbuf = realloc(*buf, RADLEN(*buf) + addlen);
if (!newbuf)
return 0;
i = RADLEN(newbuf);
for (n = list_first(addattrs); n; n = list_next(n)) {
a = (struct attribute *)n->data;
newbuf[i++] = a->t;
newbuf[i++] = a->l + 2;
memcpy(newbuf + i, a->v, a->l);
i += a->l;
}
((uint16_t *)newbuf)[1] = htons(RADLEN(newbuf) + addlen);
*buf = newbuf;
a = copytlv((struct tlv *)n->data);
if (!a)
return 0;
if (!radmsg_add(msg, a)) {
freetlv(a);
return 0;
}
}
return 1;
}
/* returns a pointer to the resized attribute value */
uint8_t *resizeattr(uint8_t **buf, uint8_t **attr, uint8_t newvallen) {
uint8_t vallen;
uint16_t len;
unsigned char *new;
vallen = ATTRVALLEN(*attr);
if (vallen == newvallen)
return *attr + 2;
int resizeattr2(struct tlv *attr, uint8_t newlen) {
uint8_t *newv;
len = RADLEN(*buf) + newvallen - vallen;
if (newvallen > vallen) {
new = realloc(*buf, len);
if (!new) {
debug(DBG_ERR, "resizeattr: malloc failed");
return NULL;
}
if (new != *buf) {
*attr += new - *buf;
*buf = new;
}
if (newlen != attr->l) {
newv = realloc(attr->v, newlen);
if (!newv)
return 0;
attr->v = newv;
attr->l = newlen;
}
memmove(*attr + 2 + newvallen, *attr + 2 + vallen, len - (*attr - *buf + newvallen));
(*attr)[1] = newvallen + 2;
((uint16_t *)*buf)[1] = htons(len);
return *attr + 2;
return 1;
}
int dorewritemodattr(uint8_t **buf, uint8_t **attr, struct modattr *modattr) {
int dorewritemodattr(struct tlv *attr, struct modattr *modattr) {
size_t nmatch = 10, reslen = 0, start = 0;
regmatch_t pmatch[10], *pfield;
int i;
unsigned char *result;
char *in, *out;
in = stringcopy((char *)ATTRVAL(*attr), ATTRVALLEN(*attr));
in = stringcopy((char *)attr->v, attr->l);
if (!in)
return 0;
......@@ -1614,21 +1516,21 @@ int dorewritemodattr(uint8_t **buf, uint8_t **attr, struct modattr *modattr) {
free(in);
return 0;
}
result = resizeattr(buf, attr, reslen);
if (!result) {
if (!resizeattr2(attr, reslen)) {
free(in);
return 0;
}
start = 0;
reslen = 0;
for (i = start; out[i]; i++) {
if (out[i] == '\\' && out[i + 1] >= '1' && out[i + 1] <= '9') {
pfield = &pmatch[out[i + 1] - '0'];
if (pfield->rm_so >= 0) {
memcpy(result + reslen, out + start, i - start);
memcpy(attr->v + reslen, out + start, i - start);
reslen += i - start;
memcpy(result + reslen, in + pfield->rm_so, pfield->rm_eo - pfield->rm_so);
memcpy(attr->v + reslen, in + pfield->rm_so, pfield->rm_eo - pfield->rm_so);
reslen += pfield->rm_eo - pfield->rm_so;
start = i + 2;
}
......@@ -1636,51 +1538,43 @@ int dorewritemodattr(uint8_t **buf, uint8_t **attr, struct modattr *modattr) {
}
}
memcpy(result + reslen, out + start, i - start);
memcpy(attr->v + reslen, out + start, i - start);
return 1;
}
int dorewritemod(uint8_t **buf, struct list *modattrs) {
uint8_t *attr;
uint16_t len = 0;
struct list_node *n;
attr = *buf + 20;
while (RADLEN(*buf) - 22 >= len) {
for (n = list_first(modattrs); n; n = list_next(n))
if (ATTRTYPE(attr) == ((struct modattr *)n->data)->t)
if (!dorewritemodattr(buf, &attr, (struct modattr *)n->data))
return 0;
len += ATTRLEN(attr);
attr += ATTRLEN(attr);
}
int dorewritemod(struct radmsg *msg, struct list *modattrs) {
struct list_node *n, *m;
for (n = list_first(msg->attrs); n; n = list_next(n))
for (m = list_first(modattrs); m; m = list_next(m))
if (((struct tlv *)n->data)->t == ((struct modattr *)m->data)->t &&
!dorewritemodattr((struct tlv *)n->data, (struct modattr *)m->data))
return 0;
return 1;
}
int dorewrite(uint8_t **buf, struct rewrite *rewrite) {
int dorewrite(struct radmsg *msg, struct rewrite *rewrite) {
if (!rewrite)
return 1;
if (rewrite->removeattrs || rewrite->removevendorattrs)
dorewriterm(*buf, rewrite->removeattrs, rewrite->removevendorattrs);
if (rewrite->addattrs && !dorewriteadd(buf, rewrite->addattrs))
dorewriterm(msg, rewrite->removeattrs, rewrite->removevendorattrs);
if (rewrite->addattrs && !dorewriteadd(msg, rewrite->addattrs))
return 0;
if (rewrite->modattrs && !dorewritemod(buf, rewrite->modattrs))
if (rewrite->modattrs && !dorewritemod(msg, rewrite->modattrs))
return 0;
return 1;
}
int rewriteusername(struct request *rq, uint8_t *attr, char *in) {
if (!dorewritemodattr(&rq->buf, &attr, rq->from->conf->rewriteusername))
return 0;
if (strlen(in) == ATTRVALLEN(attr) && !memcmp(in, ATTRVAL(attr), ATTRVALLEN(attr)))
return 1;
rq->origusername = stringcopy(in, 0);
if (!rq->origusername)
int rewriteusername(struct request *rq, struct tlv *attr) {
char *orig = (char *)tlv2str(attr);
if (!dorewritemodattr(attr, rq->from->conf->rewriteusername)) {
free(orig);
return 0;
memcpy(in, ATTRVAL(attr), ATTRVALLEN(attr));
in[ATTRVALLEN(attr)] = '\0';
}
if (strlen(orig) != attr->l || memcmp(orig, attr->v, attr->l))
rq->origusername = (char *)orig;
else
free(orig);
return 1;
}
......@@ -1734,19 +1628,52 @@ char *radattr2ascii(char *ascii, size_t len, unsigned char *attr) {
return ascii;
}
void acclog(unsigned char *attrs, int length, char *host) {
unsigned char *attr;
char username[760];
uint8_t *radattr2ascii2(struct tlv *attr) {
int i, l;
uint8_t *a, *d;
if (!attr)
return NULL;
l = attr->l;
for (i = 0; i < attr->l; i++)
if (attr->v[i] < 32 || attr->v[i] > 126)
l += 2;
if (l == attr->l)
return (uint8_t *)stringcopy((char *)attr->v, attr->l);
a = malloc(l + 1);
if (!a)
return NULL;
d = a;
for (i = 0; i < attr->l; i++)
if (attr->v[i] < 32 || attr->v[i] > 126) {
*d++ = '%';
char2hex((char *)d, attr->v[i]);
d += 2;
} else
*d++ = attr->v[i];
*d = '\0';
return a;
}
void acclog(struct radmsg *msg, char *host) {
struct tlv *attr;
uint8_t *username;
attr = attrget(attrs, length, RAD_Attr_User_Name);
attr = radmsg_gettype(msg, RAD_Attr_User_Name);
if (!attr) {