diff options
| author | venaas <venaas> | 2008-09-11 10:38:51 +0000 | 
|---|---|---|
| committer | venaas <venaas@e88ac4ed-0b26-0410-9574-a7f39faa03bf> | 2008-09-11 10:38:51 +0000 | 
| commit | faf8717dcbc9c2e3ed1892402133b6c9663a5e7d (patch) | |
| tree | 3085f5bd29b9c68d8ae94892b91d78586cd89a95 /radmsg.c | |
| parent | a98c9b737c943e062efa1a4fc7b31d539e1a2f11 (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.c | 238 | 
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; +} | 
