diff --git a/.gitignore b/.gitignore index cc0ae36..fa95497 100644 --- a/.gitignore +++ b/.gitignore @@ -22,6 +22,10 @@ TAGS radsecproxy radsecproxy-conf radsecproxy-hash +build-aux/* tests/t_fticks +tests/t_rewrite +tests/t_rewrite_config +tests/t_resizeattr tests/*.log tests/*.trs diff --git a/ChangeLog b/ChangeLog index a989481..910354a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ changes since 1.7.2 New features: + - Rewrite: supplement attribute (add attribute if not present) (#19) + - Rewrite: modify vendor attribute + - Rewrite whitelist mode - Autodetect status-server capability of servers - Minimalistic status-server @@ -11,6 +14,9 @@ changes since 1.7.2 - Fix compile issues on bsd - Fix server selection when udp were unreachable for long periods + Bug fixes: + - Handle %00 in config correctly (#31) + 2018-09-03 1.7.2 Misc: - Always copy proxy-state attributes in own responses diff --git a/Makefile.am b/Makefile.am index 6b802cb..7c2f5fc 100644 --- a/Makefile.am +++ b/Makefile.am @@ -22,6 +22,7 @@ librsp_a_SOURCES = \ list.c list.h \ radmsg.c radmsg.h \ radsecproxy.c radsecproxy.h \ + rewrite.c rewrite.h \ tcp.c tcp.h \ tls.c tls.h \ tlscommon.c tlscommon.h \ diff --git a/configure.ac b/configure.ac index a36a054..7c1ebe2 100644 --- a/configure.ac +++ b/configure.ac @@ -4,10 +4,12 @@ dnl See LICENSE for licensing information. AC_INIT(radsecproxy, 1.7.2, https://radsecproxy.github.io) AC_CANONICAL_TARGET +AC_CONFIG_AUX_DIR([build-aux]) AM_INIT_AUTOMAKE AC_PROG_CC AC_PROG_RANLIB AC_CHECK_FUNCS([mallopt]) +AC_REQUIRE_AUX_FILE([tap-driver.sh]) udp=yes AC_ARG_ENABLE(udp, diff --git a/gconfig.c b/gconfig.c index 6a5d26b..81fe63e 100644 --- a/gconfig.c +++ b/gconfig.c @@ -366,16 +366,20 @@ uint8_t hexdigit2int(char d) { return 0; } -void unhex(char *s) { +int unhex(char *s, uint8_t process_null) { + int len = 0; char *t; for (t = s; *t; s++) { - if (*t == '%' && isxdigit((int)t[1]) && isxdigit((int)t[2])) { - *s = 16 * hexdigit2int(t[1]) + hexdigit2int(t[2]); - t += 3; - } else - *s = *t++; + if (*t == '%' && isxdigit((int)t[1]) && isxdigit((int)t[2]) && + (process_null || !(t[1]=='0' && t[2]=='0'))) { + *s = 16 * hexdigit2int(t[1]) + hexdigit2int(t[2]); + t += 3; + } else + *s = *t++; + len++; } *s = '\0'; + return len; } typedef int (*t_fptr)(struct gconffile **, void *, char *, char *, char *); @@ -466,7 +470,7 @@ int getgenericconfig(struct gconffile **cf, char *block, ...) { debug(DBG_ERR, "configuration error, option %s already set to %s", opt, *str); goto errexit; } - unhex(val); + unhex(val,0); *str = val; break; case CONF_MSTR: @@ -479,7 +483,7 @@ int getgenericconfig(struct gconffile **cf, char *block, ...) { debug(DBG_ERR, "malloc failed"); goto errexit; } - unhex(val); + unhex(val,0); newmstr[n] = val; newmstr[n + 1] = NULL; *mstr = newmstr; diff --git a/gconfig.h b/gconfig.h index 3cb34b3..a5276f1 100644 --- a/gconfig.h +++ b/gconfig.h @@ -26,6 +26,7 @@ int popgconf(struct gconffile **cf); void freegconfmstr(char **mstr); void freegconf(struct gconffile **cf); struct gconffile *openconfigfile(const char *file); +int unhex(char *s, uint8_t process_null); /* Local Variables: */ /* c-file-style: "stroustrup" */ diff --git a/hash.c b/hash.c index 19d6c18..051c7e1 100644 --- a/hash.c +++ b/hash.c @@ -3,7 +3,6 @@ #include #include -#include #include "list.h" #include "hash.h" diff --git a/hash.h b/hash.h index d22a88c..ccb2917 100644 --- a/hash.h +++ b/hash.h @@ -4,6 +4,7 @@ #ifndef SYS_SOLARIS9 #include #endif +#include struct hash { struct list *hashlist; diff --git a/list.h b/list.h index 38169c7..f015b9d 100644 --- a/list.h +++ b/list.h @@ -1,6 +1,9 @@ /* Copyright (c) 2007,2009, UNINETT AS */ /* See LICENSE for licensing information. */ +#ifndef _LIST_H +#define _LIST_H + #ifdef SYS_SOLARIS9 #include #else @@ -44,6 +47,8 @@ struct list_node *list_next(struct list_node *node); /* returns number of nodes */ uint32_t list_count(struct list *list); +#endif /*_LIST_H*/ + /* Local Variables: */ /* c-file-style: "stroustrup" */ /* End: */ diff --git a/radmsg.c b/radmsg.c index 00c13c9..6828f0d 100644 --- a/radmsg.c +++ b/radmsg.c @@ -10,7 +10,6 @@ #include #include #include "list.h" -#include "tlv11.h" #include "radmsg.h" #include "debug.h" #include @@ -52,7 +51,7 @@ struct radmsg *radmsg_init(uint8_t code, uint8_t id, uint8_t *auth) { int radmsg_add(struct radmsg *msg, struct tlv *attr) { if (!msg || !msg->attrs) return 1; - if (!attr) + if (!attr || attr->l > RAD_Max_Attr_Value_Length) return 0; return list_push(msg->attrs, attr); } @@ -121,7 +120,7 @@ int radmsg_copy_attrs(struct radmsg *dst, return n; } -int _checkmsgauth(unsigned char *rad, uint8_t *authattr, uint8_t *secret) { +int _checkmsgauth(unsigned char *rad, uint8_t *authattr, uint8_t *secret, int secret_len) { int result = 0; /* Fail. */ static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; struct hmac_md5_ctx hmacctx; @@ -133,7 +132,7 @@ int _checkmsgauth(unsigned char *rad, uint8_t *authattr, uint8_t *secret) { memcpy(auth, authattr, 16); memset(authattr, 0, 16); - hmac_md5_set_key(&hmacctx, strlen((char *) secret), secret); + hmac_md5_set_key(&hmacctx, secret_len, secret); hmac_md5_update(&hmacctx, RADLEN(rad), rad); hmac_md5_digest(&hmacctx, sizeof(hash), hash); @@ -150,7 +149,7 @@ int _checkmsgauth(unsigned char *rad, uint8_t *authattr, uint8_t *secret) { return result; } -int _validauth(unsigned char *rad, unsigned char *reqauth, unsigned char *sec) { +int _validauth(unsigned char *rad, unsigned char *reqauth, unsigned char *sec, int sec_len) { static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; struct md5_ctx mdctx; unsigned char hash[MD5_DIGEST_SIZE]; @@ -164,7 +163,7 @@ int _validauth(unsigned char *rad, unsigned char *reqauth, unsigned char *sec) { md5_update(&mdctx, 16, reqauth); if (len > 20) md5_update(&mdctx, len - 20, rad + 20); - md5_update(&mdctx, strlen((char *) sec), sec); + md5_update(&mdctx, sec_len, sec); md5_digest(&mdctx, sizeof(hash), hash); result = !memcmp(hash, rad + 4, 16); @@ -173,7 +172,7 @@ int _validauth(unsigned char *rad, unsigned char *reqauth, unsigned char *sec) { return result; } -int _createmessageauth(unsigned char *rad, unsigned char *authattrval, uint8_t *secret) { +int _createmessageauth(unsigned char *rad, unsigned char *authattrval, uint8_t *secret, int secret_len) { static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; struct hmac_md5_ctx hmacctx; @@ -183,7 +182,7 @@ int _createmessageauth(unsigned char *rad, unsigned char *authattrval, uint8_t * pthread_mutex_lock(&lock); memset(authattrval, 0, 16); - hmac_md5_set_key(&hmacctx, strlen((char *) secret), secret); + hmac_md5_set_key(&hmacctx, secret_len, secret); hmac_md5_update(&hmacctx, RADLEN(rad), rad); hmac_md5_digest(&hmacctx, MD5_DIGEST_SIZE, authattrval); @@ -191,7 +190,7 @@ int _createmessageauth(unsigned char *rad, unsigned char *authattrval, uint8_t * return 1; } -int _radsign(unsigned char *rad, unsigned char *sec) { +int _radsign(unsigned char *rad, unsigned char *sec, int sec_len) { static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; struct md5_ctx mdctx; @@ -199,14 +198,26 @@ int _radsign(unsigned char *rad, unsigned char *sec) { md5_init(&mdctx); md5_update(&mdctx, RADLEN(rad), rad); - md5_update(&mdctx, strlen((char *) sec), sec); + md5_update(&mdctx, sec_len, sec); md5_digest(&mdctx, MD5_DIGEST_SIZE, rad + 4); pthread_mutex_unlock(&lock); return 1; } -uint8_t *radmsg2buf(struct radmsg *msg, uint8_t *secret) { +uint8_t *tlv2buf(uint8_t *p, const struct tlv *tlv) { + p[0] = tlv->t; + p[1] = tlv->l+2; + if (tlv->l) { + if (tlv->v) + memcpy(p+2, tlv->v, tlv->l); + else + memset(p+2, 0, tlv->l); + } + return p; +} + +uint8_t *radmsg2buf(struct radmsg *msg, uint8_t *secret, int secret_len) { struct list_node *node; struct tlv *tlv; int size; @@ -234,17 +245,16 @@ uint8_t *radmsg2buf(struct radmsg *msg, uint8_t *secret) { for (node = list_first(msg->attrs); node; node = list_next(node)) { tlv = (struct tlv *)node->data; p = tlv2buf(p, tlv); - p[-1] += 2; - if (tlv->t == RAD_Attr_Message_Authenticator && secret) - msgauth = p; - p += tlv->l; + if (tlv->t == RAD_Attr_Message_Authenticator && secret) + msgauth = ATTRVAL(p); + p += tlv->l + 2; } - if (msgauth && !_createmessageauth(buf, msgauth, secret)) { + if (msgauth && !_createmessageauth(buf, msgauth, secret, secret_len)) { free(buf); return NULL; } if (secret) { - 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)) { + 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, secret_len)) { free(buf); return NULL; } @@ -255,7 +265,7 @@ uint8_t *radmsg2buf(struct radmsg *msg, uint8_t *secret) { } /* if secret set we also validate message authenticator if present */ -struct radmsg *buf2radmsg(uint8_t *buf, uint8_t *secret, uint8_t *rqauth) { +struct radmsg *buf2radmsg(uint8_t *buf, uint8_t *secret, int secret_len, uint8_t *rqauth) { struct radmsg *msg; uint8_t t, l, *v = NULL, *p, auth[16]; uint16_t len; @@ -267,13 +277,13 @@ struct radmsg *buf2radmsg(uint8_t *buf, uint8_t *secret, uint8_t *rqauth) { if (secret && buf[0] == RAD_Accounting_Request) { memset(auth, 0, 16); - if (!_validauth(buf, auth, secret)) { + if (!_validauth(buf, auth, secret, secret_len)) { debug(DBG_WARN, "buf2radmsg: Accounting-Request message authentication failed"); return NULL; } } - if (rqauth && secret && !_validauth(buf, rqauth, secret)) { + if (rqauth && secret && !_validauth(buf, rqauth, secret, secret_len)) { debug(DBG_WARN, "buf2radmsg: Invalid auth, ignoring reply"); return NULL; } @@ -305,7 +315,7 @@ struct radmsg *buf2radmsg(uint8_t *buf, uint8_t *secret, uint8_t *rqauth) { if (t == RAD_Attr_Message_Authenticator && secret) { if (rqauth) memcpy(buf + 4, rqauth, 16); - if (l != 16 || !_checkmsgauth(buf, v, secret)) { + if (l != 16 || !_checkmsgauth(buf, v, secret, secret_len)) { debug(DBG_WARN, "buf2radmsg: message authentication failed"); if (rqauth) memcpy(buf + 4, msg->auth, 16); @@ -327,6 +337,83 @@ struct radmsg *buf2radmsg(uint8_t *buf, uint8_t *secret, uint8_t *rqauth) { return msg; } +/* should accept both names and numeric values, only numeric right now */ +uint8_t attrname2val(char *attrname) { + int val = 0; + + val = atoi(attrname); + return val > 0 && val < 256 ? val : 0; +} + +/* ATTRNAME is on the form vendor[:type]. + If only vendor is found, TYPE is set to 256 and 1 is returned. + If type is >= 256, 1 is returned. + Otherwise, 0 is returned. +*/ +/* should accept both names and numeric values, only numeric right now */ +int vattrname2val(char *attrname, uint32_t *vendor, uint32_t *type) { + char *s; + + *vendor = atoi(attrname); + s = strchr(attrname, ':'); + if (!s) { /* Only vendor was found. */ + *type = 256; + return 1; + } + *type = atoi(s + 1); + return *type < 256; +} + +int attrvalidate(unsigned char *attrs, int length) { + while (length > 1) { + if (ATTRLEN(attrs) < 2) { + debug(DBG_INFO, "attrvalidate: invalid attribute length %d", ATTRLEN(attrs)); + return 0; + } + length -= ATTRLEN(attrs); + if (length < 0) { + debug(DBG_INFO, "attrvalidate: attribute length %d exceeds packet length", ATTRLEN(attrs)); + return 0; + } + attrs += ATTRLEN(attrs); + } + if (length) + debug(DBG_INFO, "attrvalidate: malformed packet? remaining byte after last attribute"); + return 1; +} + +/** Create vendor specific tlv with ATTR. ATTR is consumed (freed) if + * all is well with the new tlv, i.e. if the function returns + * !NULL. */ +struct tlv *makevendortlv(uint32_t vendor, struct tlv *attr){ + struct tlv *newtlv = NULL; + uint8_t l, *v; + + if (!attr || attr->l > (RAD_Max_Attr_Value_Length - 6)) + return NULL; + l = attr->l + 2 + 4; + v = malloc(l); + if (v) { + vendor = htonl(vendor & 0x00ffffff); /* MSB=0 according to RFC 2865. */ + memcpy(v, &vendor, 4); + tlv2buf(v + 4, attr); + newtlv = maketlv(RAD_Attr_Vendor_Specific, l, v); + free(v); + if (newtlv) + freetlv(attr); + } + return newtlv; +} + +int resizeattr(struct tlv *attr, uint8_t newlen) { + if (newlen > RAD_Max_Attr_Value_Length) + return 0; + + if (resizetlv(attr, newlen)) + return 1; + return 0; +} + /* Local Variables: */ /* c-file-style: "stroustrup" */ /* End: */ diff --git a/radmsg.h b/radmsg.h index 2db35a4..8999bc0 100644 --- a/radmsg.h +++ b/radmsg.h @@ -2,6 +2,13 @@ /* Copyright (c) 2015, NORDUnet A/S */ /* See LICENSE for licensing information. */ +#ifndef _RADMSG_H +#define _RADMSG_H + +#include "tlv11.h" + +#define RAD_Max_Attr_Value_Length 253 + #define RAD_Access_Request 1 #define RAD_Access_Accept 2 #define RAD_Access_Reject 3 @@ -29,9 +36,14 @@ struct radmsg { uint8_t code; uint8_t id; uint8_t auth[20]; - struct list *attrs; + struct list *attrs; /*struct tlv*/ }; +#define ATTRTYPE(x) ((x)[0]) +#define ATTRLEN(x) ((x)[1]) +#define ATTRVAL(x) ((x) + 2) +#define ATTRVALLEN(x) ((x)[1] - 2) + void radmsg_free(struct radmsg *); struct radmsg *radmsg_init(uint8_t, uint8_t, uint8_t *); int radmsg_add(struct radmsg *, struct tlv *); @@ -40,8 +52,16 @@ struct list *radmsg_getalltype(const struct radmsg *msg, uint8_t type); int radmsg_copy_attrs(struct radmsg *dst, const struct radmsg *src, uint8_t type); -uint8_t *radmsg2buf(struct radmsg *msg, uint8_t *); -struct radmsg *buf2radmsg(uint8_t *, uint8_t *, uint8_t *); +uint8_t *tlv2buf(uint8_t *p, const struct tlv *tlv); +uint8_t *radmsg2buf(struct radmsg *msg, uint8_t *, int); +struct radmsg *buf2radmsg(uint8_t *, uint8_t *, int, uint8_t *); +uint8_t attrname2val(char *attrname); +int vattrname2val(char *attrname, uint32_t *vendor, uint32_t *type); +int attrvalidate(unsigned char *attrs, int length); +struct tlv *makevendortlv(uint32_t vendor, struct tlv *attr); +int resizeattr(struct tlv *attr, uint8_t newlen); + +#endif /*_RADMSG_H*/ /* Local Variables: */ /* c-file-style: "stroustrup" */ diff --git a/radsecproxy.c b/radsecproxy.c index bb02fab..fc98825 100644 --- a/radsecproxy.c +++ b/radsecproxy.c @@ -75,7 +75,6 @@ static struct options options; static struct list *clconfs, *srvconfs; static struct list *realms; -static struct hash *rewriteconfs; #ifdef __CYGWIN__ extern int __declspec(dllimport) optind; @@ -447,7 +446,7 @@ int _internal_sendrq(struct server *to, uint8_t id, struct request *rq) { if (!to->requests[id].rq) { rq->newid = id; rq->msg->id = id; - rq->buf = radmsg2buf(rq->msg, (uint8_t *)to->conf->secret); + rq->buf = radmsg2buf(rq->msg, to->conf->secret, to->conf->secret_len); if (!rq->buf) { pthread_mutex_unlock(to->requests[id].lock); debug(DBG_ERR, "sendrq: radmsg2buf failed"); @@ -526,7 +525,7 @@ void sendreply(struct request *rq) { struct client *to = rq->from; if (!rq->replybuf) - rq->replybuf = radmsg2buf(rq->msg, (uint8_t *)to->conf->secret); + rq->replybuf = radmsg2buf(rq->msg, to->conf->secret, to->conf->secret_len); radmsg_free(rq->msg); rq->msg = NULL; if (!rq->replybuf) { @@ -552,7 +551,7 @@ void sendreply(struct request *rq) { pthread_mutex_unlock(&to->replyq->mutex); } -static int pwdcrypt(char encrypt_flag, uint8_t *in, uint8_t len, char *shared, uint8_t sharedlen, uint8_t *auth) { +static int pwdcrypt(char encrypt_flag, uint8_t *in, uint8_t len, uint8_t *shared, uint8_t sharedlen, uint8_t *auth) { static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; struct md5_ctx mdctx; unsigned char hash[MD5_DIGEST_SIZE], *input; @@ -563,7 +562,7 @@ static int pwdcrypt(char encrypt_flag, uint8_t *in, uint8_t len, char *shared, u md5_init(&mdctx); input = auth; for (;;) { - md5_update(&mdctx, sharedlen, (uint8_t *) shared); + md5_update(&mdctx, sharedlen, shared); md5_update(&mdctx, 16, input); md5_digest(&mdctx, sizeof(hash), hash); for (i = 0; i < 16; i++) @@ -788,52 +787,34 @@ void removeserversubrealms(struct list *realmlist, struct clsrvconf *srv) { } } -int attrvalidate(unsigned char *attrs, int length) { - while (length > 1) { - if (ATTRLEN(attrs) < 2) { - debug(DBG_INFO, "attrvalidate: invalid attribute length %d", ATTRLEN(attrs)); - return 0; - } - length -= ATTRLEN(attrs); - if (length < 0) { - debug(DBG_INFO, "attrvalidate: attribute length %d exceeds packet length", ATTRLEN(attrs)); - return 0; - } - attrs += ATTRLEN(attrs); - } - if (length) - debug(DBG_INFO, "attrvalidate: malformed packet? remaining byte after last attribute"); - return 1; -} - -int pwdrecrypt(uint8_t *pwd, uint8_t len, char *oldsecret, char *newsecret, uint8_t *oldauth, uint8_t *newauth) { +int pwdrecrypt(uint8_t *pwd, uint8_t len, uint8_t *oldsecret, int oldsecret_len, uint8_t *newsecret, int newsecret_len, uint8_t *oldauth, uint8_t *newauth) { if (len < 16 || len > 128 || len % 16) { debug(DBG_WARN, "pwdrecrypt: invalid password length"); return 0; } - if (!pwdcrypt(0, pwd, len, oldsecret, strlen(oldsecret), oldauth)) { + if (!pwdcrypt(0, pwd, len, oldsecret, oldsecret_len, oldauth)) { debug(DBG_WARN, "pwdrecrypt: cannot decrypt password"); return 0; } #ifdef DEBUG printfchars(NULL, "pwdrecrypt: password", "%02x ", pwd, len); #endif - if (!pwdcrypt(1, pwd, len, newsecret, strlen(newsecret), newauth)) { + if (!pwdcrypt(1, pwd, len, newsecret, newsecret_len, newauth)) { debug(DBG_WARN, "pwdrecrypt: cannot encrypt password"); return 0; } return 1; } -int msmpprecrypt(uint8_t *msmpp, uint8_t len, char *oldsecret, char *newsecret, uint8_t *oldauth, uint8_t *newauth) { +int msmpprecrypt(uint8_t *msmpp, uint8_t len, uint8_t *oldsecret, int oldsecret_len, uint8_t *newsecret, int newsecret_len, uint8_t *oldauth, uint8_t *newauth) { if (len < 18) return 0; - if (!msmppdecrypt(msmpp + 2, len - 2, (uint8_t *)oldsecret, strlen(oldsecret), oldauth, msmpp)) { + if (!msmppdecrypt(msmpp + 2, len - 2, oldsecret, oldsecret_len, oldauth, msmpp)) { debug(DBG_WARN, "msmpprecrypt: failed to decrypt msppe key"); return 0; } - if (!msmppencrypt(msmpp + 2, len - 2, (uint8_t *)newsecret, strlen(newsecret), newauth, msmpp)) { + if (!msmppencrypt(msmpp + 2, len - 2, newsecret, newsecret_len, newauth, msmpp)) { debug(DBG_WARN, "msmpprecrypt: failed to encrypt msppe key"); return 0; } @@ -841,202 +822,17 @@ int msmpprecrypt(uint8_t *msmpp, uint8_t len, char *oldsecret, char *newsecret, } int msmppe(unsigned char *attrs, int length, uint8_t type, char *attrtxt, struct request *rq, - char *oldsecret, char *newsecret) { + uint8_t *oldsecret, int oldsecret_len, uint8_t *newsecret, int newsecret_len) { unsigned char *attr; for (attr = attrs; (attr = attrget(attr, length - (attr - attrs), type)); attr += ATTRLEN(attr)) { debug(DBG_DBG, "msmppe: Got %s", attrtxt); - if (!msmpprecrypt(ATTRVAL(attr), ATTRVALLEN(attr), oldsecret, newsecret, rq->buf + 4, rq->rqauth)) - return 0; - } - return 1; -} - -int findvendorsubattr(uint32_t *attrs, uint32_t vendor, uint32_t subattr) { - if (!attrs) - return 0; - - for (; attrs[0]; attrs += 2) - if (attrs[0] == vendor && attrs[1] == subattr) - return 1; - return 0; -} - -/* 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 || attr->l <= 4) - return 0; - - memcpy(&vendor, attr->v, 4); - vendor = ntohl(vendor); - while (*removevendorattrs && *removevendorattrs != vendor) - removevendorattrs += 2; - if (!*removevendorattrs) - return 0; - - if (findvendorsubattr(removevendorattrs, vendor, 256)) - return 1; /* remove entire vendor attribute */ - - sublen = attr->l - 4; - subattrs = attr->v + 4; - - if (!attrvalidate(subattrs, sublen)) { - debug(DBG_INFO, "dovendorrewrite: vendor attribute validation failed, no rewrite"); - return 0; - } - - while (sublen > 1) { - alen = ATTRLEN(subattrs); - sublen -= alen; - if (findvendorsubattr(removevendorattrs, vendor, ATTRTYPE(subattrs))) { - memmove(subattrs, subattrs + alen, sublen); - attr->l -= alen; - } else - subattrs += alen; - } - return 0; -} - -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); - } - } -} - -int dorewriteadd(struct radmsg *msg, struct list *addattrs) { - struct list_node *n; - struct tlv *a; - - for (n = list_first(addattrs); n; n = list_next(n)) { - a = copytlv((struct tlv *)n->data); - if (!a) - return 0; - if (!radmsg_add(msg, a)) { - freetlv(a); - return 0; - } - } - return 1; -} - -int resizeattr(struct tlv *attr, uint8_t newlen) { - uint8_t *newv; - - if (newlen != attr->l) { - newv = realloc(attr->v, newlen); - if (!newv) + if (!msmpprecrypt(ATTRVAL(attr), ATTRVALLEN(attr), oldsecret, oldsecret_len, newsecret, newsecret_len, rq->buf + 4, rq->rqauth)) return 0; - attr->v = newv; - attr->l = newlen; } return 1; } -int dorewritemodattr(struct tlv *attr, struct modattr *modattr) { - size_t nmatch = 10, reslen = 0, start = 0; - regmatch_t pmatch[10], *pfield; - int i; - char *in, *out; - - in = stringcopy((char *)attr->v, attr->l); - if (!in) - return 0; - - if (regexec(modattr->regex, in, nmatch, pmatch, 0)) { - free(in); - return 1; - } - - out = modattr->replacement; - - 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) { - reslen += i - start + pfield->rm_eo - pfield->rm_so; - start = i + 2; - } - i++; - } - } - reslen += i - start; - if (reslen > 253) { - debug(DBG_INFO, "rewritten attribute length would be %d, max possible is 253, discarding message", reslen); - free(in); - return 0; - } - - if (!resizeattr(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(attr->v + reslen, out + start, i - start); - reslen += i - start; - memcpy(attr->v + reslen, in + pfield->rm_so, pfield->rm_eo - pfield->rm_so); - reslen += pfield->rm_eo - pfield->rm_so; - start = i + 2; - } - i++; - } - } - free(in); - - memcpy(attr->v + reslen, out + start, i - start); - return 1; -} - -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(struct radmsg *msg, struct rewrite *rewrite) { - int rv = 1; /* Success. */ - - if (rewrite) { - if (rewrite->removeattrs || rewrite->removevendorattrs) - dorewriterm(msg, rewrite->removeattrs, rewrite->removevendorattrs); - if (rewrite->modattrs) - if (!dorewritemod(msg, rewrite->modattrs)) - rv = 0; - if (rewrite->addattrs) - if (!dorewriteadd(msg, rewrite->addattrs)) - rv = 0; - } - return rv; -} - int rewriteusername(struct request *rq, struct tlv *attr) { char *orig = (char *)tlv2str(attr); if (!dorewritemodattr(attr, rq->from->conf->rewriteusername)) { @@ -1050,49 +846,6 @@ int rewriteusername(struct request *rq, struct tlv *attr) { return 1; } -/** Create vendor specific tlv with ATTR. ATTR is consumed (freed) if - * all is well with the new tlv, i.e. if the function returns - * !NULL. */ -static struct tlv * -makevendortlv(uint32_t vendor, struct tlv *attr) -{ - struct tlv *newtlv = NULL; - uint8_t l, *v; - - if (!attr) - return NULL; - l = attr->l + 6; - v = malloc(l); - if (v) { - vendor = htonl(vendor & 0x00ffffff); /* MSB=0 according to RFC 2865. */ - memcpy(v, &vendor, 4); - tlv2buf(v + 4, attr); - v[5] += 2; /* Vendor length increased for type and length fields. */ - newtlv = maketlv(RAD_Attr_Vendor_Specific, l, v); - free(v); - if (newtlv) - freetlv(attr); - } - return newtlv; -} - -/** Ad vendor attribute with VENDOR + ATTR and push it on MSG. ATTR - * is consumed. */ -int addvendorattr(struct radmsg *msg, uint32_t vendor, struct tlv *attr) { - struct tlv *vattr; - - vattr = makevendortlv(vendor, attr); - if (!vattr) { - freetlv(attr); - return 0; - } - if (!radmsg_add(msg, vattr)) { - freetlv(vattr); - return 0; - } - return 1; -} - void addttlattr(struct radmsg *msg, uint32_t *attrtype, uint8_t addttl) { uint8_t ttl[4]; struct tlv *attr; @@ -1475,7 +1228,7 @@ int radsrv(struct request *rq) { int ttlres; char tmp[INET6_ADDRSTRLEN]; - msg = buf2radmsg(rq->buf, (uint8_t *)from->conf->secret, NULL); + msg = buf2radmsg(rq->buf, from->conf->secret, from->conf->secret_len, NULL); free(rq->buf); rq->buf = NULL; @@ -1595,14 +1348,14 @@ int radsrv(struct request *rq) { attr = radmsg_gettype(msg, RAD_Attr_User_Password); if (attr) { debug(DBG_DBG, "radsrv: found userpwdattr with value length %d", attr->l); - if (!pwdrecrypt(attr->v, attr->l, from->conf->secret, to->conf->secret, rq->rqauth, msg->auth)) + if (!pwdrecrypt(attr->v, attr->l, from->conf->secret, from->conf->secret_len, to->conf->secret, to->conf->secret_len, rq->rqauth, msg->auth)) goto rmclrqexit; } attr = radmsg_gettype(msg, RAD_Attr_Tunnel_Password); if (attr) { debug(DBG_DBG, "radsrv: found tunnelpwdattr with value length %d", attr->l); - if (!pwdrecrypt(attr->v, attr->l, from->conf->secret, to->conf->secret, rq->rqauth, msg->auth)) + if (!pwdrecrypt(attr->v, attr->l, from->conf->secret, from->conf->secret_len, to->conf->secret, to->conf->secret_len, rq->rqauth, msg->auth)) goto rmclrqexit; } @@ -1653,7 +1406,7 @@ void replyh(struct server *server, unsigned char *buf) { goto errunlock; } - msg = buf2radmsg(buf, (uint8_t *)server->conf->secret, rqout->rq->msg->auth); + msg = buf2radmsg(buf, server->conf->secret, server->conf->secret_len, rqout->rq->msg->auth); #ifdef DEBUG printfchars(NULL, "origauth/buf+4", "%02x ", buf + 4, 16); #endif @@ -1709,9 +1462,9 @@ void replyh(struct server *server, unsigned char *buf) { subattrs = attr->v + 4; if (!attrvalidate(subattrs, sublen) || !msmppe(subattrs, sublen, RAD_VS_ATTR_MS_MPPE_Send_Key, "MS MPPE Send Key", - rqout->rq, server->conf->secret, from->conf->secret) || + rqout->rq, server->conf->secret, server->conf->secret_len, from->conf->secret, from->conf->secret_len) || !msmppe(subattrs, sublen, RAD_VS_ATTR_MS_MPPE_Recv_Key, "MS MPPE Recv Key", - rqout->rq, server->conf->secret, from->conf->secret)) + rqout->rq, server->conf->secret, server->conf->secret_len, from->conf->secret, from->conf->secret_len)) break; } if (node) { @@ -2384,250 +2137,6 @@ int dynamicconfig(struct server *server) { return 0; } -/* should accept both names and numeric values, only numeric right now */ -uint8_t attrname2val(char *attrname) { - int val = 0; - - val = atoi(attrname); - return val > 0 && val < 256 ? val : 0; -} - -/* ATTRNAME is on the form vendor[:type]. - If only vendor is found, TYPE is set to 256 and 1 is returned. - If type is >= 256, 1 is returned. - Otherwise, 0 is returned. -*/ -/* should accept both names and numeric values, only numeric right now */ -int vattrname2val(char *attrname, uint32_t *vendor, uint32_t *type) { - char *s; - - *vendor = atoi(attrname); - s = strchr(attrname, ':'); - if (!s) { /* Only vendor was found. */ - *type = 256; - return 1; - } - *type = atoi(s + 1); - return *type < 256; -} - -/** Extract attributes from string NAMEVAL, create a struct tlv and - * return the tlv. If VENDOR_FLAG, NAMEVAL is on the form - * "::" and otherwise it's ":". Return - * NULL if fields are missing or if conversion fails. - * - * FIXME: Should accept both names and numeric values, only numeric - * right now */ -struct tlv *extractattr(char *nameval, char vendor_flag) { - int len, name = 0; - int vendor = 0; /* Vendor 0 is reserved, see RFC 1700. */ - char *s, *s2; - struct tlv *a; - - s = strchr(nameval, ':'); - if (!s) - return NULL; - name = atoi(nameval); - - if (vendor_flag) { - s2 = strchr(s + 1, ':'); - if (!s2) - return NULL; - vendor = name; - name = atoi(s + 1); - s = s2; - } - len = strlen(s + 1); - if (len > 253) - return NULL; - - if (name < 1 || name > 255) - return NULL; - a = malloc(sizeof(struct tlv)); - if (!a) - return NULL; - - a->v = (uint8_t *)stringcopy(s + 1, 0); - if (!a->v) { - free(a); - return NULL; - } - a->t = name; - a->l = len; - - if (vendor_flag) - a = makevendortlv(vendor, a); - - return a; -} - -/* should accept both names and numeric values, only numeric right now */ -struct modattr *extractmodattr(char *nameval) { - int name = 0; - char *s, *t; - struct modattr *m; - - if (!strncasecmp(nameval, "User-Name:/", 11)) { - s = nameval + 11; - name = 1; - } else { - s = strchr(nameval, ':'); - name = atoi(nameval); - if (!s || name < 1 || name > 255 || s[1] != '/') - return NULL; - s += 2; - } - /* regexp, remove optional trailing / if present */ - if (s[strlen(s) - 1] == '/') - s[strlen(s) - 1] = '\0'; - - for (t = strchr(s, '/'); t; t = strchr(t+1, '/')) - if (t == s || t[-1] != '\\') - break; - if (!t) - return NULL; - *t = '\0'; - t++; - - m = malloc(sizeof(struct modattr)); - if (!m) { - debug(DBG_ERR, "malloc failed"); - return NULL; - } - m->t = name; - - m->replacement = stringcopy(t, 0); - if (!m->replacement) { - free(m); - debug(DBG_ERR, "malloc failed"); - return NULL; - } - - m->regex = malloc(sizeof(regex_t)); - if (!m->regex) { - free(m->replacement); - free(m); - debug(DBG_ERR, "malloc failed"); - return NULL; - } - - if (regcomp(m->regex, s, REG_ICASE | REG_EXTENDED)) { - free(m->regex); - free(m->replacement); - free(m); - debug(DBG_ERR, "failed to compile regular expression %s", s); - return NULL; - } - - return m; -} - -struct rewrite *getrewrite(char *alt1, char *alt2) { - struct rewrite *r; - - if (alt1) - if ((r = hash_read(rewriteconfs, alt1, strlen(alt1)))) - return r; - if (alt2) - if ((r = hash_read(rewriteconfs, alt2, strlen(alt2)))) - return r; - return NULL; -} - -void addrewrite(char *value, char **rmattrs, char **rmvattrs, char **addattrs, char **addvattrs, char **modattrs) -{ - struct rewrite *rewrite = NULL; - int i, n; - uint8_t *rma = NULL; - uint32_t *p, *rmva = NULL; - struct list *adda = NULL, *moda = NULL; - struct tlv *a; - struct modattr *m; - - if (rmattrs) { - for (n = 0; rmattrs[n]; n++); - rma = calloc(n + 1, sizeof(uint8_t)); - if (!rma) - debugx(1, DBG_ERR, "malloc failed"); - - for (i = 0; i < n; i++) - if (!(rma[i] = attrname2val(rmattrs[i]))) - debugx(1, DBG_ERR, "addrewrite: removing invalid attribute %s", rmattrs[i]); - freegconfmstr(rmattrs); - rma[i] = 0; - } - - if (rmvattrs) { - for (n = 0; rmvattrs[n]; n++); - rmva = calloc(2 * n + 1, sizeof(uint32_t)); - if (!rmva) - debugx(1, DBG_ERR, "malloc failed"); - - for (p = rmva, i = 0; i < n; i++, p += 2) - if (!vattrname2val(rmvattrs[i], p, p + 1)) - debugx(1, DBG_ERR, "addrewrite: removing invalid vendor attribute %s", rmvattrs[i]); - freegconfmstr(rmvattrs); - *p = 0; - } - - if (addattrs) { - adda = list_create(); - if (!adda) - debugx(1, DBG_ERR, "malloc failed"); - for (i = 0; addattrs[i]; i++) { - a = extractattr(addattrs[i], 0); - if (!a) - debugx(1, DBG_ERR, "addrewrite: adding invalid attribute %s", addattrs[i]); - if (!list_push(adda, a)) - debugx(1, DBG_ERR, "malloc failed"); - } - freegconfmstr(addattrs); - } - - if (addvattrs) { - if (!adda) - adda = list_create(); - if (!adda) - debugx(1, DBG_ERR, "malloc failed"); - for (i = 0; addvattrs[i]; i++) { - a = extractattr(addvattrs[i], 1); - if (!a) - debugx(1, DBG_ERR, "addrewrite: adding invalid vendor attribute %s", addvattrs[i]); - if (!list_push(adda, a)) - debugx(1, DBG_ERR, "malloc failed"); - } - freegconfmstr(addvattrs); - } - - if (modattrs) { - moda = list_create(); - if (!moda) - debugx(1, DBG_ERR, "malloc failed"); - for (i = 0; modattrs[i]; i++) { - m = extractmodattr(modattrs[i]); - if (!m) - debugx(1, DBG_ERR, "addrewrite: modifying invalid attribute %s", modattrs[i]); - if (!list_push(moda, m)) - debugx(1, DBG_ERR, "malloc failed"); - } - freegconfmstr(modattrs); - } - - if (rma || rmva || adda || moda) { - rewrite = malloc(sizeof(struct rewrite)); - if (!rewrite) - debugx(1, DBG_ERR, "malloc failed"); - rewrite->removeattrs = rma; - rewrite->removevendorattrs = rmva; - rewrite->addattrs = adda; - rewrite->modattrs = moda; - } - - if (!hash_insert(rewriteconfs, value, strlen(value), rewrite)) - debugx(1, DBG_ERR, "malloc failed"); - debug(DBG_DBG, "addrewrite: added rewrite block %s", value); -} - int setttlattr(struct options *opts, char *defaultattr) { char *ttlattr = opts->ttlattr ? opts->ttlattr : defaultattr; @@ -2645,6 +2154,7 @@ void freeclsrvconf(struct clsrvconf *conf) { if (conf->hostsrc) freegconfmstr(conf->hostsrc); free(conf->portsrc); + free(conf->confsecret); free(conf->secret); free(conf->tls); free(conf->matchcertattr); @@ -2738,7 +2248,7 @@ int mergesrvconf(struct clsrvconf *dst, struct clsrvconf *src) { if (!mergeconfstring(&dst->name, &src->name) || !mergeconfmstring(&dst->hostsrc, &src->hostsrc) || !mergeconfstring(&dst->portsrc, &src->portsrc) || - !mergeconfstring(&dst->secret, &src->secret) || + !mergeconfstring(&dst->confsecret, &src->confsecret) || !mergeconfstring(&dst->tls, &src->tls) || !mergeconfstring(&dst->matchcertattr, &src->matchcertattr) || !mergeconfstring(&dst->confrewritein, &src->confrewritein) || @@ -2800,7 +2310,7 @@ int confclient_cb(struct gconffile **cf, void *arg, char *block, char *opt, char "host", CONF_MSTR, &conf->hostsrc, "IPv4Only", CONF_BLN, &ipv4only, "IPv6Only", CONF_BLN, &ipv6only, - "secret", CONF_STR, &conf->secret, + "secret", CONF_STR, &conf->confsecret, #if defined(RADPROT_TLS) || defined(RADPROT_DTLS) "tls", CONF_STR, &conf->tls, "matchcertificateattribute", CONF_STR, &conf->matchcertattr, @@ -2889,13 +2399,15 @@ int confclient_cb(struct gconffile **cf, void *arg, char *block, char *opt, char !resolvehostports(conf->hostports, conf->hostaf, conf->pdef->socktype)) debugx(1, DBG_ERR, "%s: resolve failed, exiting", __func__); - if (!conf->secret) { + if (!conf->confsecret) { if (!conf->pdef->secretdefault) debugx(1, DBG_ERR, "error in block %s, secret must be specified for transport type %s", block, conf->pdef->name); - conf->secret = stringcopy(conf->pdef->secretdefault, 0); - if (!conf->secret) + conf->confsecret = stringcopy(conf->pdef->secretdefault, 0); + if (!conf->confsecret) debugx(1, DBG_ERR, "malloc failed"); } + conf->secret = (unsigned char *)stringcopy(conf->confsecret, 0); + conf->secret_len = unhex((char *)conf->secret, 1); if (conf->tlsconf) { for (entry = list_first(clconfs); entry; entry = list_next(entry)) { @@ -2999,7 +2511,7 @@ int confserver_cb(struct gconffile **cf, void *arg, char *block, char *opt, char "IPv4Only", CONF_BLN, &ipv4only, "IPv6Only", CONF_BLN, &ipv6only, "port", CONF_STR, &conf->portsrc, - "secret", CONF_STR, &conf->secret, + "secret", CONF_STR, &conf->confsecret, #if defined(RADPROT_TLS) || defined(RADPROT_DTLS) "tls", CONF_STR, &conf->tls, "MatchCertificateAttribute", CONF_STR, &conf->matchcertattr, @@ -3116,17 +2628,19 @@ int confserver_cb(struct gconffile **cf, void *arg, char *block, char *opt, char goto errexit; } - if (!conf->secret) { + if (!conf->confsecret) { if (!conf->pdef->secretdefault) { debug(DBG_ERR, "error in block %s, secret must be specified for transport type %s", block, conf->pdef->name); goto errexit; } - conf->secret = stringcopy(conf->pdef->secretdefault, 0); + conf->confsecret = stringcopy(conf->pdef->secretdefault, 0); if (!conf->secret) { debug(DBG_ERR, "malloc failed"); goto errexit; } } + conf->secret = (unsigned char *)stringcopy(conf->confsecret,0); + conf->secret_len = unhex((char *)conf->secret,1); if (resconf) return 1; @@ -3146,6 +2660,39 @@ int confserver_cb(struct gconffile **cf, void *arg, char *block, char *opt, char return 0; } +int confrewrite_cb(struct gconffile **cf, void *arg, char *block, char *opt, char *val) { + uint8_t whitelist_mode = 0; + char **rmattrs = NULL, **rmvattrs = NULL; + char **wlattrs = NULL, **wlvattrs = NULL; + char **addattrs = NULL, **addvattrs = NULL; + char **modattrs = NULL, **modvattrs = NULL; + char **supattrs = NULL, **supvattrs = NULL; + + debug(DBG_DBG, "confrewrite_cb called for %s", block); + + if (!getgenericconfig(cf, block, + "whitelistMode", CONF_BLN, &whitelist_mode, + "removeAttribute", CONF_MSTR, &rmattrs, + "removeVendorAttribute", CONF_MSTR, &rmvattrs, + "whitelistAttribute", CONF_MSTR, &wlattrs, + "whitelistVendorAttribute", CONF_MSTR, &wlvattrs, + "addAttribute", CONF_MSTR, &addattrs, + "addVendorAttribute", CONF_MSTR, &addvattrs, + "modifyAttribute", CONF_MSTR, &modattrs, + "modifyVendorAttribute", CONF_MSTR, &modvattrs, + "supplementAttribute", CONF_MSTR, &supattrs, + "supplementVendorAttribute", CONF_MSTR, &supvattrs, + NULL)) + debugx(1, DBG_ERR, "configuration error"); + addrewrite(val, whitelist_mode, whitelist_mode? wlattrs : rmattrs, whitelist_mode? wlvattrs : rmvattrs, + addattrs, addvattrs, modattrs, modvattrs, supattrs, supvattrs); + + freegconfmstr(whitelist_mode? rmattrs : wlattrs); + freegconfmstr(whitelist_mode? rmvattrs : wlvattrs); + + return 1; +} + int confrealm_cb(struct gconffile **cf, void *arg, char *block, char *opt, char *val) { char **servers = NULL, **accservers = NULL, *msg = NULL; uint8_t accresp = 0; @@ -3165,26 +2712,6 @@ int confrealm_cb(struct gconffile **cf, void *arg, char *block, char *opt, char return 1; } -int confrewrite_cb(struct gconffile **cf, void *arg, char *block, char *opt, char *val) { - char **rmattrs = NULL, **rmvattrs = NULL; - char **addattrs = NULL, **addvattrs = NULL; - char **modattrs = NULL; - - debug(DBG_DBG, "confrewrite_cb called for %s", block); - - if (!getgenericconfig(cf, block, - "removeAttribute", CONF_MSTR, &rmattrs, - "removeVendorAttribute", CONF_MSTR, &rmvattrs, - "addAttribute", CONF_MSTR, &addattrs, - "addVendorAttribute", CONF_MSTR, &addvattrs, - "modifyAttribute", CONF_MSTR, &modattrs, - NULL - )) - debugx(1, DBG_ERR, "configuration error"); - addrewrite(val, rmattrs, rmvattrs, addattrs, addvattrs, modattrs); - return 1; -} - int setprotoopts(uint8_t type, char **listenargs, char *sourcearg) { struct commonprotoopts *protoopts; @@ -3228,10 +2755,6 @@ void getmainconfig(const char *configfile) { if (!realms) debugx(1, DBG_ERR, "malloc failed"); - rewriteconfs = hash_create(); - if (!rewriteconfs) - debugx(1, DBG_ERR, "malloc failed"); - if (!getgenericconfig( &cfs, NULL, #ifdef RADPROT_UDP diff --git a/radsecproxy.conf.5 b/radsecproxy.conf.5 index 706e9b6..10584f2 100644 --- a/radsecproxy.conf.5 +++ b/radsecproxy.conf.5 @@ -61,11 +61,13 @@ blocktype name { .fi .RE -Option value characters can also be written in hex. This is done by writing the -character % followed by two hexadecimal digits. If a % is used without two -following hexadecimal digits, the % and the following characters are used as -written. If you want to write a % and not use this decoding, you may of course -write % in hex; i.e., %25. +Option value characters can also be written in hex for options requiring a +string type value. This is done by writing the character % followed by two +hexadecimal digits. If a % is used without two following hexadecimal digits, the +% and the following characters are used as written. If you want to write a % and +not use this decoding, you may of course write % in hex; i.e., %25. As %00 would +terminate a string, this value is not converted in most cases, except when used +with rewrite statements or secrets. Some options allow or require the use of regular expressions, denoted as \fIregex\fR. The POSIX extended RE system is used, see @@ -806,6 +808,17 @@ use another, then you would be fine only defining two rewrite blocks named used for rewrite on input. No rewriting is done on output unless explicitly specified using the \fBRewriteOut\fR option. +The rewrite actions are performed in this sequence: +.RS +1. RemoveAttribute (or WhitelistAttribute) +.br +2. ModifyAttribute +.br +3. SupplementAttribute +.br +4. AddAttribute +.RE + All options can be specified multiple times. The allowed options in a rewrite block are: @@ -813,15 +826,47 @@ block are: .RS Add an \fIattribute\fR to the radius message and set it to \fIvalue\fR. The \fIattribute\fR must be specified using the numerical attribute id. The -\fIvalue\fR can either be numerical, a string, or a hex value. See the -\fBCONFIGURATION SYNTAX\fR section for details. +\fIvalue\fR can either be numerical, a string, or a hex value. If the value +starts with a number, it is interpreted as a 32bit unsigned integer. Use the ' +character at the start of the value to force string interpretation. When using +hex value, it is recommended to also lead with ' to avoid unintended numeric +interpretation. See the \fBCONFIGURATION SYNTAX\fR section for further details. .RE .BI "AddVendorAttribute " vendor \fR: subattribute \fR: value .RS Add a vendor attribute to the radius message, specified by \fIvendor\fR and \fIsubattribute\fR. Both \fIvendor\fR and \fIsubattribute\fR must be specified -as numerical values. The format of \fIvalue\fR is the same as for \fBaddAttibute\fR above. +as numerical values. The format of \fIvalue\fR is the same as for \fBaddAttribute\fR above. +.RE + +.BI "SupplementAttribute " attribute \fR: value +.RS +Add an \fIattribute\fR to the radius mesage and set it to \fIvalue\fR, only if +the attribute is not yet present on the message. The format of \fIvalue\fR is +the same as for \fBaddAttribute\fR above. +.RE + +.BI "SupplementVendorAttribute " vendor \fR: subattribute \fR: value +.RS +Add a vendor attribute to the radius message only if the \fIsubattribute\fR of +this \fIvendor\fR is not yet present on the message. The format of is the same +as for \fBaddVendorAttribute\fR above. +.RE + +.BI "ModifyAttribute " attribute \fR:/ regex \fR/ replace \fR/ +.RS +Modify the given \fIattribute\fR using the \fIregex\fR \fIreplace\fR pattern. As +above, \fIattribute\fR must be specified by a numerical value. Example usage: + +modifyAttribute 1:/^(.*)@local$/\e1@example.com/ +.RE + +.BI "ModifyVendorAttribute " vendor \fR: subattribute \fR:/ regex \fR/ replace \fR/ +.RS +Modify the given \fIsubattribute\fR of given \fIvendor\fR using the \fIregex\fR +\fIreplace\fR pattern. Other than the added vendor, the same syntax as for +\fBModifyAttribute\fR applies. .RE .BI "RemoveAttribute " attribute @@ -836,13 +881,30 @@ Remove all vendor attributes that match the given \fIvendor\fR and the given vendor id are removed. .RE -.BI "ModifyAttribute " attribute \fR:/ regex \fR/ replace \fR/ +.BR "WhitelistMode (" on | off ) .RS -Modify the given \fIattribute\fR using the \fIregex\fR \fIreplace\fR pattern. As -above, \fIattribute\fR must be specified by a numerical value. Example usage: +Enable whitelist mode. All attributes except those configured with +\fBWhitelistAttrbiute\fR or \fBWhitelistVendorAttribute\fR will be removed. +While whitelist mode is active, \fBRemoveAttribute\fR and +\fBRemoveVendorAttribute\fR statements are ignored. +.RE -modifyAttribute 1:/^(.*)@local$/\e1@example.com/ +.BI "WhitelistAttribute " attribute +.RS +Do not remove attributes with the given id when \fBWhitelistMode\fR is on. +Ignored otherwise. +.RE + +.BI "WhitelistVendorAttribute " vendor [\fR: subattribute ] +.RS +Do not remove vendor attributes that match the given \fIvendor\fR and +\fIsubattribute\fR when \fBWhitelistMode\fR is on. Ignored otherwise. + +If the \fIsubattribute\fR is omitted, the complete vendor attribute is +whitelisted. Otherwise only the specified subattribute is kept but all other +subattributes are removed. .RE + .SH "SEE ALSO" \fBradsecproxy\fR(1), .URL https://tools.ietf.org/html/rfc6614 " Transport Layer Security (TLS) Encryption for RADIUS " diff --git a/radsecproxy.h b/radsecproxy.h index 9b32705..f660080 100644 --- a/radsecproxy.h +++ b/radsecproxy.h @@ -7,9 +7,9 @@ #include #include #include "list.h" -#include "tlv11.h" #include "radmsg.h" #include "gconfig.h" +#include "rewrite.h" #define DEBUG_LEVEL 2 @@ -143,7 +143,9 @@ struct clsrvconf { int hostaf; char *portsrc; struct list *hostports; - char *secret; + char *confsecret; + uint8_t *secret; + int secret_len; char *tls; char *matchcertattr; regex_t *certcnregex; @@ -219,19 +221,6 @@ struct realm { struct list *accsrvconfs; }; -struct modattr { - uint8_t t; - char *replacement; - regex_t *regex; -}; - -struct rewrite { - uint8_t *removeattrs; - uint32_t *removevendorattrs; - struct list *addattrs; - struct list *modattrs; -}; - struct protodefs { char *name; char *secretdefault; @@ -256,11 +245,6 @@ struct protodefs { #define RADLEN(x) ntohs(((uint16_t *)(x))[1]) -#define ATTRTYPE(x) ((x)[0]) -#define ATTRLEN(x) ((x)[1]) -#define ATTRVAL(x) ((x) + 2) -#define ATTRVALLEN(x) ((x)[1] - 2) - struct clsrvconf *find_clconf(uint8_t type, struct sockaddr *addr, struct list_node **cur); struct clsrvconf *find_srvconf(uint8_t type, struct sockaddr *addr, struct list_node **cur); struct clsrvconf *find_clconf_type(uint8_t type, struct list_node **cur); @@ -273,7 +257,7 @@ void freerq(struct request *rq); int radsrv(struct request *rq); void replyh(struct server *server, unsigned char *buf); struct addrinfo *resolve_hostport_addrinfo(uint8_t type, char *hostport); -uint8_t *radattr2ascii(struct tlv *attr); +uint8_t *radattr2ascii(struct tlv *attr); /* TODO: mv this to radmsg? */ pthread_attr_t pthread_attr; /* Local Variables: */ diff --git a/rewrite.c b/rewrite.c new file mode 100644 index 0000000..b383103 --- /dev/null +++ b/rewrite.c @@ -0,0 +1,616 @@ +/* Copyright (c) 2019, SWITCH */ +/* See LICENSE for licensing information. */ + +#include +#include +#include +#include +#include "debug.h" +#include "gconfig.h" +#include "hash.h" +#include "list.h" +#include "radmsg.h" +#include "rewrite.h" +#include "util.h" + +static struct hash *rewriteconfs; + +/** Extract attributes from string NAMEVAL, create a struct tlv and + * return the tlv. If VENDOR_FLAG, NAMEVAL is on the form + * "::" and otherwise it's ":". Return + * NULL if fields are missing or if conversion fails. + * + * FIXME: Should accept both names and numeric values, only numeric + * right now */ +struct tlv *extractattr(char *nameval, char vendor_flag) { + int len, name = 0; + int vendor = 0; /* Vendor 0 is reserved, see RFC 1700. */ + uint32_t ival=0; + char *s, *s2; + struct tlv *a; + + s = strchr(nameval, ':'); + if (!s) + return NULL; + name = atoi(nameval); + + if (vendor_flag) { + s2 = strchr(s + 1, ':'); + if (!s2) + return NULL; + vendor = name; + name = atoi(s + 1); + s = s2; + } + + s++; + if (isdigit(*s)) { + ival = atoi(s); + ival = htonl(ival); + len = 4; + s = (char *)&ival; + } else { + if (*s == '\'') + s++; + + len = unhex(s,1); + if (len > 253) + return NULL; + } + + if (name < 1 || name > 255) + return NULL; + a = malloc(sizeof(struct tlv)); + if (!a) + return NULL; + + a->v = (uint8_t *)stringcopy(s, len); + if (!a->v) { + free(a); + return NULL; + } + a->t = name; + a->l = len; + + if (vendor_flag) + a = makevendortlv(vendor, a); + + return a; +} + +/* should accept both names and numeric values, only numeric right now */ +struct modattr *extractmodattr(char *nameval) { + int name = 0; + char *s, *t; + struct modattr *m; + + if (!strncasecmp(nameval, "User-Name:/", 11)) { + s = nameval + 11; + name = 1; + } else { + s = strchr(nameval, ':'); + name = atoi(nameval); + if (!s || name < 1 || name > 255 || s[1] != '/') + return NULL; + s += 2; + } + /* regexp, remove optional trailing / if present */ + if (s[strlen(s) - 1] == '/') + s[strlen(s) - 1] = '\0'; + + for (t = strchr(s, '/'); t; t = strchr(t+1, '/')) + if (t == s || t[-1] != '\\') + break; + if (!t) + return NULL; + *t = '\0'; + t++; + + m = malloc(sizeof(struct modattr)); + if (!m) { + debug(DBG_ERR, "malloc failed"); + return NULL; + } + m->t = name; + + m->replacement = stringcopy(t, 0); + if (!m->replacement) { + free(m); + debug(DBG_ERR, "malloc failed"); + return NULL; + } + + m->regex = malloc(sizeof(regex_t)); + if (!m->regex) { + free(m->replacement); + free(m); + debug(DBG_ERR, "malloc failed"); + return NULL; + } + + if (regcomp(m->regex, s, REG_ICASE | REG_EXTENDED)) { + free(m->regex); + free(m->replacement); + free(m); + debug(DBG_ERR, "failed to compile regular expression %s", s); + return NULL; + } + + return m; +} + +struct modattr *extractmodvattr(char *nameval) { + uint32_t vendor; + char *s; + struct modattr *modvattr; + + s = strchr(nameval, ':'); + vendor = atoi(nameval); + if (!s || !vendor || !strchr(s,':')) + return NULL; + modvattr = extractmodattr(s+1); + if (modvattr) + modvattr ->vendor = vendor; + return modvattr; +} + +void addrewrite(char *value, uint8_t whitelist_mode, char **rmattrs, char **rmvattrs, char **addattrs, + char **addvattrs, char **modattrs, char **modvattrs, char **supattrs, char** supvattrs) +{ + struct rewrite *rewrite = NULL; + int i, n; + uint8_t *rma = NULL; + uint32_t *p, *rmva = NULL; + struct list *adda = NULL, *moda = NULL, *modva = NULL, *supa = NULL; + struct tlv *a; + struct modattr *m; + + if (rmattrs) { + for (n = 0; rmattrs[n]; n++); + rma = calloc(n + 1, sizeof(uint8_t)); + if (!rma) + debugx(1, DBG_ERR, "malloc failed"); + + for (i = 0; i < n; i++) + if (!(rma[i] = attrname2val(rmattrs[i]))) + debugx(1, DBG_ERR, "addrewrite: removing invalid attribute %s", rmattrs[i]); + freegconfmstr(rmattrs); + rma[i] = 0; + } + + if (rmvattrs) { + for (n = 0; rmvattrs[n]; n++); + rmva = calloc(2 * n + 1, sizeof(uint32_t)); + if (!rmva) + debugx(1, DBG_ERR, "malloc failed"); + + for (p = rmva, i = 0; i < n; i++, p += 2) + if (!vattrname2val(rmvattrs[i], p, p + 1)) + debugx(1, DBG_ERR, "addrewrite: removing invalid vendor attribute %s", rmvattrs[i]); + freegconfmstr(rmvattrs); + *p = 0; + } + + if (addattrs) { + adda = list_create(); + if (!adda) + debugx(1, DBG_ERR, "malloc failed"); + for (i = 0; addattrs[i]; i++) { + a = extractattr(addattrs[i], 0); + if (!a) + debugx(1, DBG_ERR, "addrewrite: adding invalid attribute %s", addattrs[i]); + if (!list_push(adda, a)) + debugx(1, DBG_ERR, "malloc failed"); + } + freegconfmstr(addattrs); + } + + if (addvattrs) { + if (!adda) + adda = list_create(); + if (!adda) + debugx(1, DBG_ERR, "malloc failed"); + for (i = 0; addvattrs[i]; i++) { + a = extractattr(addvattrs[i], 1); + if (!a) + debugx(1, DBG_ERR, "addrewrite: adding invalid vendor attribute %s", addvattrs[i]); + if (!list_push(adda, a)) + debugx(1, DBG_ERR, "malloc failed"); + } + freegconfmstr(addvattrs); + } + + if (modattrs) { + moda = list_create(); + if (!moda) + debugx(1, DBG_ERR, "malloc failed"); + for (i = 0; modattrs[i]; i++) { + m = extractmodattr(modattrs[i]); + if (!m) + debugx(1, DBG_ERR, "addrewrite: modifying invalid attribute %s", modattrs[i]); + if (!list_push(moda, m)) + debugx(1, DBG_ERR, "malloc failed"); + } + freegconfmstr(modattrs); + } + + if (modvattrs) { + modva = list_create(); + if (!modva) + debugx(1, DBG_ERR, "malloc failed"); + for (i = 0; modvattrs[i]; i++) { + m = extractmodvattr(modvattrs[i]); + if (!m) + debugx(1, DBG_ERR, "addrewrite: modifying invalid vendor attribute %s", modvattrs[i]); + if (!list_push(modva, m)) + debugx(1, DBG_ERR, "malloc failed"); + } + freegconfmstr(modvattrs); + } + + if (supattrs) { + supa = list_create(); + if (!supa) + debugx(1, DBG_ERR, "malloc failed"); + for (i = 0; supattrs[i]; i++) { + a = extractattr(supattrs[i], 0); + if (!a) + debugx(1, DBG_ERR, "addrewrite: adding invalid attribute %s", supattrs[i]); + if (!list_push(supa, a)) + debugx(1, DBG_ERR, "malloc failed"); + } + freegconfmstr(supattrs); + } + + if (supvattrs) { + if (!supa) + supa = list_create(); + if (!supa) + debugx(1, DBG_ERR, "malloc failed"); + for (i = 0; supvattrs[i]; i++) { + a = extractattr(supvattrs[i], 1); + if (!a) + debugx(1, DBG_ERR, "addrewrite: adding invalid vendor attribute %s", supvattrs[i]); + if (!list_push(supa, a)) + debugx(1, DBG_ERR, "malloc failed"); + } + freegconfmstr(supvattrs); + } + + if (rma || rmva || adda || moda || supa) { + rewrite = malloc(sizeof(struct rewrite)); + if (!rewrite) + debugx(1, DBG_ERR, "malloc failed"); + rewrite->whitelist_mode = whitelist_mode; + rewrite->removeattrs = rma; + rewrite->removevendorattrs = rmva; + rewrite->addattrs = adda; + rewrite->modattrs = moda; + rewrite->modvattrs = modva; + rewrite->supattrs = supa; + } + + if (!rewriteconfs) + rewriteconfs = hash_create(); + if (!hash_insert(rewriteconfs, value, strlen(value), rewrite)) + debugx(1, DBG_ERR, "malloc failed"); + debug(DBG_DBG, "addrewrite: added rewrite block %s", value); +} + +struct rewrite *getrewrite(char *alt1, char *alt2) { + struct rewrite *r; + + if (alt1) + if ((r = hash_read(rewriteconfs, alt1, strlen(alt1)))) + return r; + if (alt2) + if ((r = hash_read(rewriteconfs, alt2, strlen(alt2)))) + return r; + return NULL; +} + +int findvendorsubattr(uint32_t *attrs, uint32_t vendor, uint32_t subattr) { + if (!attrs) + return 0; + + for (; attrs[0]; attrs += 2) + if (attrs[0] == vendor && attrs[1] == subattr) + return 1; + return 0; +} + +/* returns 1 if entire element is to be removed, else 0 */ +int dovendorrewriterm(struct tlv *attr, uint32_t *removevendorattrs, int inverted) { + uint8_t alen, sublen; + uint32_t vendor; + uint8_t *subattrs; + + if (!removevendorattrs || attr->l <= 4) + return 0; + + memcpy(&vendor, attr->v, 4); + vendor = ntohl(vendor); + while (*removevendorattrs && *removevendorattrs != vendor) + removevendorattrs += 2; + if (!*removevendorattrs) + return 0; + + if (findvendorsubattr(removevendorattrs, vendor, 256)) + return 1; /* remove entire vendor attribute */ + + sublen = attr->l - 4; + subattrs = attr->v + 4; + + if (!attrvalidate(subattrs, sublen)) { + debug(DBG_INFO, "dovendorrewrite: vendor attribute validation failed, no rewrite"); + return 0; + } + + while (sublen > 1) { + alen = ATTRLEN(subattrs); + sublen -= alen; + if (!!findvendorsubattr(removevendorattrs, vendor, ATTRTYPE(subattrs)) != !!inverted) { + memmove(subattrs, subattrs + alen, sublen); + attr->l -= alen; + } else + subattrs += alen; + } + if ((attr->l <= 4) != !!inverted) + return 1; + return 0; +} + +/*if inverted is true, remove all attributes except those listed */ +void dorewriterm(struct radmsg *msg, uint8_t *rmattrs, uint32_t *rmvattrs, int inverted) { + 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, inverted))) != !!inverted) { + list_removedata(msg->attrs, attr); + freetlv(attr); + n = p ? list_next(p) : list_first(msg->attrs); + } else { + p = n; + n = list_next(n); + } + } +} + +int dorewritemodattr(struct tlv *attr, struct modattr *modattr) { + size_t nmatch = 10, reslen = 0, start = 0; + regmatch_t pmatch[10], *pfield; + int i; + char *in, *out; + + in = stringcopy((char *)attr->v, attr->l); + if (!in) + return 0; + + if (regexec(modattr->regex, in, nmatch, pmatch, 0)) { + free(in); + return 1; + } + + out = modattr->replacement; + + 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) { + reslen += i - start + pfield->rm_eo - pfield->rm_so; + start = i + 2; + } + i++; + } + } + reslen += i - start; + if (!resizeattr(attr, reslen)) { + debug(DBG_INFO, "rewritten attribute to length %d failed, discarding message", 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(attr->v + reslen, out + start, i - start); + reslen += i - start; + memcpy(attr->v + reslen, in + pfield->rm_so, pfield->rm_eo - pfield->rm_so); + reslen += pfield->rm_eo - pfield->rm_so; + start = i + 2; + } + i++; + } + } + free(in); + + memcpy(attr->v + reslen, out + start, i - start); + return 1; +} + +int replacesubtlv(struct tlv *vendortlv, uint8_t *p, struct tlv *newtlv) { + int size_diff; + uint8_t rem_size, *next_attr; + + size_diff = newtlv->l - ATTRLEN(p); + next_attr = p+ATTRLEN(p); + rem_size = (vendortlv->v + vendortlv->l) - next_attr; + + if (size_diff < 0) + memmove(next_attr + size_diff, next_attr, rem_size); + if (!resizeattr(vendortlv, vendortlv->l+size_diff)) + return 0; + if (size_diff > 0) + memmove(next_attr + size_diff, next_attr, rem_size); + + tlv2buf(p, newtlv); + return 1; +} + +int dorewritemodvattr(struct tlv *vendortlv, struct modattr *modvattr) { + struct tlv *tmpattr; + int offset; + + if (vendortlv->l <= 4 || !attrvalidate(vendortlv->v+4, vendortlv->l-4)) + return 0; + for (offset = 4; offset < vendortlv->l; offset += ATTRLEN(vendortlv->v+offset)) { + if (ATTRTYPE(vendortlv->v+offset) == modvattr->t) { + tmpattr = maketlv(ATTRTYPE(vendortlv->v+offset), ATTRVALLEN(vendortlv->v+offset), ATTRVAL(vendortlv->v+offset)); + if (dorewritemodattr(tmpattr, modvattr)) { + int size_diff = tmpattr->l - ATTRVALLEN(vendortlv->v+offset); + int rem_size = vendortlv->l - offset - ATTRLEN(vendortlv->v+offset); + uint8_t *next; + + if (size_diff > 0) + if (!resizeattr(vendortlv, vendortlv->l+size_diff)) { + freetlv(tmpattr); + return 0; + } + next = vendortlv->v + offset + ATTRLEN(vendortlv->v+offset); + memmove(next + size_diff, next, rem_size); + if (size_diff < 0) + if (!resizeattr(vendortlv, vendortlv->l+size_diff)) { + freetlv(tmpattr); + return 0; + } + + tlv2buf(vendortlv->v+offset, tmpattr); + } else { + freetlv(tmpattr); + return 0; + } + freetlv(tmpattr); + } + } + return 1; +} + +int dorewritemod(struct radmsg *msg, struct list *modattrs, struct list *modvattrs) { + struct list_node *n, *m; + uint32_t vendor; + + for (n = list_first(msg->attrs); n; n = list_next(n)) { + struct tlv *attr = (struct tlv *)n->data; + if (attr->t == RAD_Attr_Vendor_Specific) { + memcpy(&vendor, attr->v, 4); + vendor = ntohl(vendor); + for (m = list_first(modvattrs); m; m = list_next(m)) { + if (vendor == ((struct modattr *)m->data)->vendor && + !dorewritemodvattr(attr, (struct modattr*)m->data)) + return 0; + } + } else { + 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 dorewriteadd(struct radmsg *msg, struct list *addattrs) { + struct list_node *n; + struct tlv *a; + + for (n = list_first(addattrs); n; n = list_next(n)) { + a = copytlv((struct tlv *)n->data); + if (!a) + return 0; + if (!radmsg_add(msg, a)) { + freetlv(a); + return 0; + } + } + return 1; +} + +int dorewritesup(struct radmsg *msg, struct list *supattrs) { + struct list_node *n, *p; + struct tlv *attr, *supattr; + uint8_t exist, *vendortype, *v;; + + for (n = list_first(supattrs); n; n = list_next(n)) { + supattr = (struct tlv *)n->data; + exist = 0; + for(p = list_first(msg->attrs); p; p = list_next(p)) { + attr = (struct tlv *)p->data; + if (attr->t == supattr->t && attr->t != RAD_Attr_Vendor_Specific) { + exist = 1; + break; + } else if (supattr->t == RAD_Attr_Vendor_Specific && attr->t == RAD_Attr_Vendor_Specific && + memcmp (supattr->v, attr->v, 4)==0) { + if (!attrvalidate(attr->v+4, attr->l-4)) { + debug(DBG_INFO, "dorewritesup: vendor attribute validation failed, no rewrite"); + return 0; + } + vendortype = (uint8_t *)supattr->v+4; + for (v=attr->v+4; v < attr->v + attr->l; v += *(v+1)){ + if (*v == *vendortype) { + exist = 1; + break; + } + } + if (exist) break; + } + } + if (!exist) { + supattr = copytlv(supattr); + if (!supattr) + return 0; + if (!radmsg_add(msg, supattr)) { + freetlv(supattr); + return 0; + } + } + } + return 1; +} + +int dorewrite(struct radmsg *msg, struct rewrite *rewrite) { + int rv = 1; /* Success. */ + + if (rewrite) { + if (rewrite->removeattrs || rewrite->removevendorattrs) + dorewriterm(msg, rewrite->removeattrs, rewrite->removevendorattrs, rewrite->whitelist_mode); + if (rewrite->modattrs || rewrite->modvattrs) + if (!dorewritemod(msg, rewrite->modattrs, rewrite->modvattrs)) + rv = 0; + if (rewrite->supattrs) + if (!dorewritesup(msg, rewrite->supattrs)) + rv = 0; + if (rewrite->addattrs) + if (!dorewriteadd(msg, rewrite->addattrs)) + rv = 0; + } + return rv; +} + +/** Ad vendor attribute with VENDOR + ATTR and push it on MSG. ATTR + * is consumed. */ +int addvendorattr(struct radmsg *msg, uint32_t vendor, struct tlv *attr) { + struct tlv *vattr; + + vattr = makevendortlv(vendor, attr); + if (!vattr) { + freetlv(attr); + return 0; + } + if (!radmsg_add(msg, vattr)) { + freetlv(vattr); + return 0; + } + return 1; +} + +/* Local Variables: */ +/* c-file-style: "stroustrup" */ +/* End: */ diff --git a/rewrite.h b/rewrite.h new file mode 100644 index 0000000..3356a00 --- /dev/null +++ b/rewrite.h @@ -0,0 +1,41 @@ +/* Copyright (c) 2019, SWITCH */ +/* See LICENSE for licensing information. */ + +#ifndef _REWRITE_H +#define _REWRITE_H + +#include +#include "list.h" +#include "radmsg.h" + +struct modattr { + uint8_t t; + uint32_t vendor; + char *replacement; + regex_t *regex; +}; + +struct rewrite { + uint8_t whitelist_mode; + uint8_t *removeattrs; /*NULL terminated*/ + uint32_t *removevendorattrs; /*NULL terminated*/ + struct list *addattrs; /*struct tlv*/ + struct list *modattrs; /*struct modattr*/ + struct list *modvattrs; /*struct modattr*/ + struct list *supattrs; /*struct tlv*/ +}; + +void addrewrite(char *value, uint8_t whitelist_mode, char **rmattrs, char **rmvattrs, char **addattrs, + char **addvattrs, char **modattrs, char **modvattrs, char **supattrs, char** supvattrs); +int dorewrite(struct radmsg *msg, struct rewrite *rewrite); +struct modattr *extractmodattr(char *nameval); +struct rewrite *getrewrite(char *alt1, char *alt2); + +int dorewritemodattr(struct tlv *attr, struct modattr *modattr); +int addvendorattr(struct radmsg *msg, uint32_t vendor, struct tlv *attr); + +#endif /*_REWRITE_H*/ + +/* Local Variables: */ +/* c-file-style: "stroustrup" */ +/* End: */ diff --git a/tests/Makefile.am b/tests/Makefile.am index 50f18ca..a2dbce9 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -1,6 +1,10 @@ AUTOMAKE_OPTIONS = foreign -check_PROGRAMS = t_fticks +#LOG_DRIVER = ./tap-driver.sh +LOG_DRIVER = env AM_TAP_AWK='$(AWK)' $(SHELL) \ + $(top_srcdir)/build-aux/tap-driver.sh + +check_PROGRAMS = t_fticks t_rewrite t_resizeattr t_rewrite_config AM_CFLAGS = -g -Wall -Werror @SSL_CFLAGS@ @TARGET_CFLAGS@ LDADD = $(top_builddir)/librsp.a @SSL_LIBS@ LDFLAGS = @SSL_LDFLAGS@ @TARGET_LDFLAGS@ diff --git a/tests/t_fticks.c b/tests/t_fticks.c index b1d852a..2f47459 100644 --- a/tests/t_fticks.c +++ b/tests/t_fticks.c @@ -37,18 +37,29 @@ _check_hash(const char *mac, const char *key, const char *hash, const char*hmac) int main (int argc, char *argv[]) { - if (_check_hash(MAC1, KEY1, HASH1, HMAC1) != 0) - return 1; - /* Again, for good measure. (Or rather to make sure there's no - state left.) */ - if (_check_hash(MAC1, KEY1, HASH1, HMAC1) != 0) - return 1; - if (_check_hash(MAC1_UC, KEY1, HASH1, HMAC1) != 0) - return 1; - if (_check_hash(MAC1_APPENDED, KEY1, HASH1, HMAC1) != 0) - return 1; - if (_check_hash(MAC1_WEIRD, KEY1, HASH1, HMAC1) != 0) - return 1; + int testcount = 5; + printf("1..%d\n", testcount); + testcount = 1; + + if (_check_hash(MAC1, KEY1, HASH1, HMAC1) != 0) + printf("not "); + printf("ok %d - basic hash\n", testcount++); + + /* Again, for good measure. (Or rather to make sure there's no + state left.) */ + if (_check_hash(MAC1, KEY1, HASH1, HMAC1) != 0) + printf("not "); + printf("ok %d - hash stateless\n", testcount++); + + if (_check_hash(MAC1_UC, KEY1, HASH1, HMAC1) != 0) + printf("not "); + printf("ok %d - hash uppercase\n", testcount++); + if (_check_hash(MAC1_APPENDED, KEY1, HASH1, HMAC1) != 0) + printf("not "); + printf("ok %d - hash appended\n", testcount++); + if (_check_hash(MAC1_WEIRD, KEY1, HASH1, HMAC1) != 0) + printf("not "); + printf("ok %d - hash weird\n", testcount++); return 0; } diff --git a/tests/t_resizeattr.c b/tests/t_resizeattr.c new file mode 100644 index 0000000..db20d7c --- /dev/null +++ b/tests/t_resizeattr.c @@ -0,0 +1,60 @@ +/* Copyright (C) 2019, SWITCH */ +/* See LICENSE for licensing information. */ + +#include +#include +#include +#include "../rewrite.h" + +int test_resize(int start_size, int target_size, uint8_t shouldfail) { + + uint8_t *value = malloc(start_size); + struct tlv *attr; + int result = 1; + + memset(value, 42, start_size); + attr = maketlv(1,start_size,value); + + if (!resizeattr(attr, target_size)) + result = shouldfail; + else if (shouldfail) + result = 0; + else if (attr->l != target_size) + result = 0; + else if (memcmp(attr->v, value, target_size <= start_size ? target_size : start_size)) + result = 0; + + freetlv(attr); + free(value); + return result; +} + +int main (int argc, char *argv[]) +{ + int testcount = 4; + + printf("1..%d\n", testcount); + testcount = 1; + + /* test resizeattr normal */ + if (!test_resize(4, 8, 0)) + printf("not "); + printf("ok %d - resizeattr\n", testcount++); + + /* test resizeattr to 0 */ + if (!test_resize(4, 0, 0)) + printf ("not "); + printf("ok %d - resizeattr to zero\n", testcount++); + + /* test resizeattr to max size */ + if (!test_resize(128, 253, 0)) + printf ("not "); + printf("ok %d - resizeattr to max size\n", testcount++); + + /* test resizeattr to oversize */ + if (!test_resize(128, 254, 1)) + printf ("not "); + printf("ok %d - resizeattr to oversize\n", testcount++); + + return 0; +} diff --git a/tests/t_rewrite.c b/tests/t_rewrite.c new file mode 100644 index 0000000..6793be8 --- /dev/null +++ b/tests/t_rewrite.c @@ -0,0 +1,654 @@ +/* Copyright (C) 2019, SWITCH */ +/* See LICENSE for licensing information. */ + +#include +#include +#include +#include "../rewrite.h" +#include "../radmsg.h" +#include "../debug.h" + +/*origattrs and expectedattrs as struct tlv*/ +/*return 0 if expected; 1 otherwise or error*/ +static int +_check_rewrite(struct list *origattrs, struct rewrite *rewrite, struct list *expectedattrs, int shouldfail) { + struct radmsg msg; + struct list_node *n,*m; + + msg.attrs = origattrs; + + if(dorewrite(&msg, rewrite) == shouldfail) { + if (shouldfail) + printf("dorewrite expected to fail, but it didn't\n"); + else + printf("dorewrite failed\n"); + return 1; + } + + if(list_count(expectedattrs) != list_count(msg.attrs)) { + printf("bad attribute list length! expected %d, was %d\n", list_count(expectedattrs), list_count(msg.attrs)); + return 1; + } + m=list_first(origattrs); + for(n=list_first(expectedattrs); n; n=list_next(n)) { + if (!eqtlv((struct tlv *)n->data, (struct tlv *)m->data)) { + printf("attribute list not as expected\n"); + return 1; + } + m=list_next(m); + } + return 0; +} + +void _list_clear(struct list *list) { + void *data; + while ( (data = list_shift(list)) ) + free(data); +} + +void _tlv_list_clear(struct list *list) { + struct tlv *tlv; + while ( (tlv = (struct tlv *)list_shift(list)) ) + freetlv(tlv); +} + +void _reset_rewrite(struct rewrite *rewrite) { + rewrite->whitelist_mode = 0; + rewrite->removeattrs = NULL; + rewrite->removevendorattrs = NULL; + _tlv_list_clear(rewrite->addattrs); + _list_clear(rewrite->modattrs); + _list_clear(rewrite->modvattrs); + _tlv_list_clear(rewrite->supattrs); +} + +int +main (int argc, char *argv[]) +{ + int testcount = 25; + struct list *origattrs, *expectedattrs; + struct rewrite rewrite; + char *username = "user@realm"; + + debug_init("t_rewrite"); + + origattrs=list_create(); + expectedattrs=list_create(); + + rewrite.whitelist_mode=0; + rewrite.removeattrs = NULL; + rewrite.removevendorattrs = NULL; + rewrite.addattrs = list_create(); + rewrite.modattrs = list_create(); + rewrite.modvattrs = list_create(); + rewrite.supattrs = list_create(); + + printf("1..%d\n", testcount); + testcount = 1; + + /* test empty rewrite */ + { + list_push(origattrs, maketlv(RAD_Attr_User_Name, strlen(username), username)); + list_push(expectedattrs, maketlv(RAD_Attr_User_Name, strlen(username), username)); + if (_check_rewrite(origattrs, &rewrite, expectedattrs, 0)) + printf("not "); + printf("ok %d - empty rewrite\n", testcount++); + _tlv_list_clear(origattrs); + _tlv_list_clear(expectedattrs); + _reset_rewrite(&rewrite); + } + + /* test removeattr */ + { + uint8_t removeattrs[] = {1,2,0}; + + rewrite.removeattrs = removeattrs; + list_push(origattrs, maketlv(1, strlen(username), username)); + list_push(origattrs, maketlv(3, strlen(username), username)); + + list_push(expectedattrs, maketlv(3, strlen(username), username)); + + if (_check_rewrite(origattrs, &rewrite, expectedattrs, 0)) + printf("not "); + printf("ok %d - removeattrs\n", testcount++); + _tlv_list_clear(origattrs); + _tlv_list_clear(expectedattrs); + _reset_rewrite(&rewrite); + } + + /* test removevendorattrs full: remove a vendor attribute completely*/ + { + uint32_t removevendorattrs[] = {42,256,0}; + uint8_t value = 42; + + rewrite.removevendorattrs = removevendorattrs; + list_push(origattrs, maketlv(1, strlen(username), username)); + list_push(origattrs, makevendortlv(42, maketlv(1, 1, &value))); + list_push(origattrs, makevendortlv(43, maketlv(1, 1, &value))); + + list_push(expectedattrs, maketlv(1, strlen(username), username)); + list_push(expectedattrs, makevendortlv(43, maketlv(1, 1, &value))); + + if (_check_rewrite(origattrs, &rewrite, expectedattrs, 0)) + printf("not "); + printf("ok %d - removevendorattrs full\n", testcount++); + _tlv_list_clear(origattrs); + _tlv_list_clear(expectedattrs); + _reset_rewrite(&rewrite); + } + + /* test removevendorattrs last element: remove vendor attribute if last subattribute removed*/ + { + uint32_t removevendorattrs[] = {42,2,0}; /*,45,12}; remove vendor 42, type 2; vendor 43 all, vendor 45 type 12} */ + uint8_t value = 42; + + rewrite.removevendorattrs = removevendorattrs; + list_push(origattrs, makevendortlv(42, maketlv(1, 1, &value))); + list_push(origattrs, makevendortlv(42, maketlv(2, 1, &value))); + list_push(origattrs, makevendortlv(43, maketlv(2, 1, &value))); + + list_push(expectedattrs, makevendortlv(42, maketlv(1, 1, &value))); + list_push(expectedattrs, makevendortlv(43, maketlv(2, 1, &value))); + + if (_check_rewrite(origattrs, &rewrite, expectedattrs, 0)) + printf("not "); + printf("ok %d - removevendorattrs last element\n", testcount++); + _tlv_list_clear(origattrs); + _tlv_list_clear(expectedattrs); + _reset_rewrite(&rewrite); + } + + /* test removevendorattrs non-rfc: dont remove if format doesn't follow rfc recommendation*/ + { + uint32_t removevendorattrs[] = {42,1,0}; + uint8_t vendor_nonrfc[] = {0, 0, 0, 45, 1, 0x12, 0x23}; + + rewrite.removevendorattrs = removevendorattrs; + list_push(origattrs, maketlv(26, 7, vendor_nonrfc)); + + list_push(expectedattrs, maketlv(26, 7, vendor_nonrfc)); + + if (_check_rewrite(origattrs, &rewrite, expectedattrs, 0)) + printf("not "); + printf("ok %d - removevendorattrs non-rfc\n", testcount++); + _tlv_list_clear(origattrs); + _tlv_list_clear(expectedattrs); + _reset_rewrite(&rewrite); + } + + /* test removevendorattrs partial attribute */ + { + uint32_t removevendorattrs[] = {42,2,0}; + uint8_t vendor_long1_in[] = {0,0,0,42,2,3,0,1,3,0}; + uint8_t vendor_long1_out[] = {0,0,0,42,1,3,0}; + uint8_t vendor_long2_in[] = {0,0,0,42,1,3,0,2,3,0}; + uint8_t vendor_long2_out[] = {0,0,0,42,1,3,0}; + uint8_t vendor_long3_in[] = {0,0,0,42,1,3,0,2,3,0,3,3,0}; + uint8_t vendor_long3_out[] = {0,0,0,42,1,3,0,3,3,0}; + + rewrite.removevendorattrs = removevendorattrs; + list_push(origattrs, maketlv(26, sizeof(vendor_long1_in), vendor_long1_in)); + list_push(origattrs, maketlv(26, sizeof(vendor_long2_in), vendor_long2_in)); + list_push(origattrs, maketlv(26, sizeof(vendor_long3_in), vendor_long3_in)); + + list_push(expectedattrs, maketlv(26, sizeof(vendor_long1_out), vendor_long1_out)); + list_push(expectedattrs, maketlv(26, sizeof(vendor_long2_out), vendor_long2_out)); + list_push(expectedattrs, maketlv(26, sizeof(vendor_long3_out), vendor_long3_out)); + + if (_check_rewrite(origattrs, &rewrite, expectedattrs, 0)) + printf("not "); + printf("ok %d - removevendorattrs sub-attribute\n", testcount++); + _tlv_list_clear(origattrs); + _tlv_list_clear(expectedattrs); + _reset_rewrite(&rewrite); + } + + /* test simple add */ + { + char *value = "hello world"; + + list_push(rewrite.addattrs, maketlv(1, strlen(value), value)); + list_push(expectedattrs, maketlv(1,strlen(value), value)); + + if (_check_rewrite(origattrs, &rewrite, expectedattrs, 0)) + printf("not "); + printf("ok %d - addattribute simple\n", testcount++); + + _tlv_list_clear(origattrs); + _tlv_list_clear(expectedattrs); + _reset_rewrite(&rewrite); + } + + /* test add with existing attributes*/ + { + char *value = "hello world"; + uint8_t value2 = 42; + + list_push(rewrite.addattrs, maketlv(1, strlen(value), value)); + list_push(origattrs, maketlv(2, strlen(value), value)); + list_push(origattrs, maketlv(1, 1, &value2)); + + list_push(expectedattrs, maketlv(2,strlen(value), value)); + list_push(expectedattrs, maketlv(1,1, &value2)); + list_push(expectedattrs, maketlv(1,strlen(value), value)); + + if (_check_rewrite(origattrs, &rewrite, expectedattrs, 0)) + printf("not "); + printf("ok %d - addattribute with existing attributes\n", testcount++); + + _tlv_list_clear(origattrs); + _tlv_list_clear(expectedattrs); + _reset_rewrite(&rewrite); + } + + /* test add null*/ + { + list_push(rewrite.addattrs, maketlv(1, 0, NULL)); + list_push(expectedattrs, maketlv(1,0, NULL)); + + if (_check_rewrite(origattrs, &rewrite, expectedattrs, 0)) + printf("not "); + printf("ok %d - addattribute null\n", testcount++); + + _tlv_list_clear(origattrs); + _tlv_list_clear(expectedattrs); + _reset_rewrite(&rewrite); + } + + /* test add too big*/ + { + uint8_t *value = malloc(254); + memset(value, 0, 254); + + list_push(rewrite.addattrs, maketlv(1, 254, value)); + + if (_check_rewrite(origattrs, &rewrite, expectedattrs, 1)) + printf("not "); + printf("ok %d - addattribute too big\n", testcount++); + + free(value); + _tlv_list_clear(origattrs); + _tlv_list_clear(expectedattrs); + _reset_rewrite(&rewrite); + } + + /* test supplement non-existing*/ + { + char *value = "hello world"; + + list_push(rewrite.supattrs, maketlv(1, strlen(value), value)); + list_push(expectedattrs, maketlv(1,strlen(value), value)); + + if (_check_rewrite(origattrs, &rewrite, expectedattrs, 0)) + printf("not "); + printf("ok %d - suppattrs non existing\n", testcount++); + + _tlv_list_clear(origattrs); + _tlv_list_clear(expectedattrs); + _reset_rewrite(&rewrite); + } + + /* test supplement existing*/ + { + char *value = "hello world"; + char *value2 = "hello radsec"; + + list_push(rewrite.supattrs, maketlv(1, strlen(value2), value2)); + list_push(origattrs, maketlv(1,strlen(value), value)); + list_push(expectedattrs, maketlv(1,strlen(value), value)); + + if (_check_rewrite(origattrs, &rewrite, expectedattrs, 0)) + printf("not "); + printf("ok %d - suppattrs existing\n", testcount++); + + _tlv_list_clear(origattrs); + _tlv_list_clear(expectedattrs); + _reset_rewrite(&rewrite); + } + + /* test supplement vendor*/ + { + uint8_t value = 42; + uint8_t vendor_long1_in[] = {0,0,0,42,2,3,0,1,3,0}; + + list_push(rewrite.supattrs, makevendortlv(42, maketlv(1, 1, &value))); + list_push(rewrite.supattrs, makevendortlv(42, maketlv(3, 1, &value))); + list_push(origattrs, maketlv(26, sizeof(vendor_long1_in), vendor_long1_in)); + list_push(expectedattrs, maketlv(26, sizeof(vendor_long1_in), vendor_long1_in)); + list_push(expectedattrs, makevendortlv(42, maketlv(3, 1, &value))); + + if (_check_rewrite(origattrs, &rewrite, expectedattrs, 0)) + printf("not "); + printf("ok %d - suppattrs vendor\n", testcount++); + + _tlv_list_clear(origattrs); + _tlv_list_clear(expectedattrs); + _reset_rewrite(&rewrite); + } + + /* test modify no match*/ + { + char *value = "hello world"; + char *value2 = "foo bar"; + struct modattr *mod = malloc(sizeof(struct modattr)); + regex_t regex; + + mod->t = 1; + mod->regex = ®ex; + mod->replacement = value2; + regcomp(mod->regex, "hello bar", REG_ICASE | REG_EXTENDED); + + list_push(rewrite.modattrs, mod); + list_push(origattrs, maketlv(1,strlen(value), value)); + list_push(expectedattrs, maketlv(1,strlen(value), value)); + + if (_check_rewrite(origattrs, &rewrite, expectedattrs, 0)) + printf("not "); + printf("ok %d - modify attribute no match\n", testcount++); + + regfree(®ex); + _tlv_list_clear(origattrs); + _tlv_list_clear(expectedattrs); + _reset_rewrite(&rewrite); + } + + /* test modify match full replace*/ + { + char *value = "hello world"; + char *value2 = "foo bar"; + struct modattr *mod = malloc(sizeof(struct modattr)); + regex_t regex; + + mod->t = 1; + mod->regex = ®ex; + mod->replacement = value2; + regcomp(mod->regex, "hello world", REG_ICASE | REG_EXTENDED); + + list_push(rewrite.modattrs, mod); + list_push(origattrs, maketlv(1,strlen(value), value)); + list_push(expectedattrs, maketlv(1,strlen(value2), value2)); + + if (_check_rewrite(origattrs, &rewrite, expectedattrs, 0)) + printf("not "); + printf("ok %d - modify attribute match full replace\n", testcount++); + + regfree(®ex); + _tlv_list_clear(origattrs); + _tlv_list_clear(expectedattrs); + _reset_rewrite(&rewrite); + } + + /* test modify match partial replace*/ + { + char *value = "hello world"; + char *value2 = "hello foo"; + struct modattr *mod = malloc(sizeof(struct modattr)); + regex_t regex; + + mod->t = 1; + mod->regex = ®ex; + mod->replacement = "\\1 foo"; + regcomp(mod->regex, "(hello) world", REG_ICASE | REG_EXTENDED); + + list_push(rewrite.modattrs, mod); + list_push(origattrs, maketlv(1,strlen(value), value)); + list_push(expectedattrs, maketlv(1,strlen(value2), value2)); + + if (_check_rewrite(origattrs, &rewrite, expectedattrs, 0)) + printf("not "); + printf("ok %d - modify attribute match full replace\n", testcount++); + + regfree(®ex); + _tlv_list_clear(origattrs); + _tlv_list_clear(expectedattrs); + _reset_rewrite(&rewrite); + } + + /* test modify max length*/ + { + char *value = "hello radsecproxy..."; /*make this 20 chars long 8*/ + char value2[254]; + int i; + struct modattr *mod = malloc(sizeof(struct modattr)); + regex_t regex; + + for (i=0; i<253; i+=20){ + memcpy(value2+i, value, 20); + } + memcpy(value2+i-20, "and another13\0", 14); + + mod->t = 1; + mod->regex = ®ex; + mod->replacement = "\\1\\1\\1\\1\\1\\1\\1\\1\\1\\1\\1\\1and another13"; + regcomp(mod->regex, "(.*)", REG_ICASE | REG_EXTENDED); + + list_push(rewrite.modattrs, mod); + list_push(origattrs, maketlv(1,strlen(value), value)); + list_push(expectedattrs, maketlv(1,strlen(value2), value2)); + + if (_check_rewrite(origattrs, &rewrite, expectedattrs, 0)) + printf("not "); + printf("ok %d - modify attribute max length\n", testcount++); + + regfree(®ex); + _tlv_list_clear(origattrs); + _tlv_list_clear(expectedattrs); + _reset_rewrite(&rewrite); + } + + /* test modify too long*/ + { + char *value = "hello radsecproxy..."; /*make this 20 chars long 8*/ + struct modattr *mod = malloc(sizeof(struct modattr)); + regex_t regex; + mod->t = 1; + mod->regex = ®ex; + mod->replacement = "\\1\\1\\1\\1\\1\\1\\1\\1\\1\\1\\1\\1and another14!"; + regcomp(mod->regex, "(.*)", REG_ICASE | REG_EXTENDED); + + list_push(rewrite.modattrs, mod); + list_push(origattrs, maketlv(1,strlen(value), value)); + list_push(expectedattrs, maketlv(1,strlen(value), value)); + + if (_check_rewrite(origattrs, &rewrite, expectedattrs, 1)) + printf("not "); + printf("ok %d - modify attribute too long\n", testcount++); + + regfree(®ex); + _tlv_list_clear(origattrs); + _tlv_list_clear(expectedattrs); + _reset_rewrite(&rewrite); + } + + /* test modify regex replace*/ + { + char *value = "hello"; + char *value2 = "hellohellohellohellohellohellohellohellohello"; + struct modattr *mod = malloc(sizeof(struct modattr)); + regex_t regex; + mod->t = 1; + mod->regex = ®ex; + mod->replacement = "\\1\\2\\3\\4\\5\\6\\7\\8\\9"; + regcomp(mod->regex, "(((((((((hello)))))))))", REG_ICASE | REG_EXTENDED); + + list_push(rewrite.modattrs, mod); + list_push(origattrs, maketlv(1,strlen(value), value)); + list_push(expectedattrs, maketlv(1,strlen(value2), value2)); + + if (_check_rewrite(origattrs, &rewrite, expectedattrs, 0)) + printf("not "); + printf("ok %d - modify attribute regex replace\n", testcount++); + + regfree(®ex); + _tlv_list_clear(origattrs); + _tlv_list_clear(expectedattrs); + _reset_rewrite(&rewrite); + } + + /* test modify vendor*/ + { + struct modattr *mod = malloc(sizeof(struct modattr)); + regex_t regex; + uint8_t vendorattrin[] = {0,0,0,42,1,3,'b',1,3,'a',2,3,0,1,3,'a'}; + uint8_t vendorattrout[] = {0,0,0,42,1,3,'b',1,4,'b','b',2,3,0,1,4,'b','b'}; + + mod->t = 1; + mod->vendor = 42; + mod->regex = ®ex; + mod->replacement = "bb"; + regcomp(mod->regex, "a", REG_ICASE | REG_EXTENDED); + + list_push(rewrite.modvattrs, mod); + list_push(origattrs, maketlv(RAD_Attr_Vendor_Specific,sizeof(vendorattrin), vendorattrin)); + list_push(expectedattrs, maketlv(RAD_Attr_Vendor_Specific,sizeof(vendorattrout), vendorattrout)); + + if (_check_rewrite(origattrs, &rewrite, expectedattrs, 0)) + printf("not "); + printf("ok %d - modify vendor\n", testcount++); + + regfree(®ex); + _tlv_list_clear(origattrs); + _tlv_list_clear(expectedattrs); + _reset_rewrite(&rewrite); + } + + /* test modify vendor too long (total vendor attribute too long) */ + { + struct modattr *mod = malloc(sizeof(struct modattr)); + regex_t regex; + uint8_t vendorattrin[RAD_Max_Attr_Value_Length]; + + memset(vendorattrin, 0, RAD_Max_Attr_Value_Length); + vendorattrin[3] = 42; + vendorattrin[4] = 1; + vendorattrin[5] = 3; + vendorattrin[6] = 'a'; + vendorattrin[7] = 2; + vendorattrin[8] = RAD_Max_Attr_Value_Length - 7; + + mod->t = 1; + mod->vendor = 42; + mod->regex = ®ex; + mod->replacement = "bb"; + regcomp(mod->regex, "a", REG_ICASE | REG_EXTENDED); + + list_push(rewrite.modvattrs, mod); + list_push(origattrs, maketlv(RAD_Attr_Vendor_Specific,sizeof(vendorattrin), vendorattrin)); + + if (_check_rewrite(origattrs, &rewrite, origattrs, 1)) + printf("not "); + printf("ok %d - modify vendor too long\n", testcount++); + + regfree(®ex); + _tlv_list_clear(origattrs); + _tlv_list_clear(expectedattrs); + _reset_rewrite(&rewrite); + } + + /* test whitelist rewrite */ + { + uint8_t whitelistattrs[] = {1,0}; + rewrite.whitelist_mode=1; + rewrite.removeattrs = whitelistattrs; + + list_push(origattrs, maketlv(1, strlen(username), username)); + list_push(origattrs, maketlv(3, strlen(username), username)); + list_push(origattrs, makevendortlv(42, maketlv(1, strlen(username), username))); + + list_push(expectedattrs, maketlv(1, strlen(username), username)); + + if (_check_rewrite(origattrs, &rewrite, expectedattrs, 0)) + printf("not "); + printf("ok %d - whitelistattrs\n", testcount++); + _tlv_list_clear(origattrs); + _tlv_list_clear(expectedattrs); + _reset_rewrite(&rewrite); + + } + + /* test whitelist vendor rewrite */ + { + uint32_t whitelistvendorattrs[] = {42,256,0}; + uint8_t value = 42; + uint8_t vendor_nonrfc_in[] = {0,0,0,42,1,2,3,4}; + + rewrite.whitelist_mode=1; + rewrite.removevendorattrs = whitelistvendorattrs; + list_push(origattrs, maketlv(1, strlen(username), username)); + list_push(origattrs, makevendortlv(42, maketlv(1, 1, &value))); + list_push(origattrs, makevendortlv(43, maketlv(1, 1, &value))); + list_push(origattrs, maketlv(26, sizeof(vendor_nonrfc_in), vendor_nonrfc_in)); + + list_push(expectedattrs, makevendortlv(42, maketlv(1, 1, &value))); + list_push(expectedattrs, maketlv(26, sizeof(vendor_nonrfc_in), vendor_nonrfc_in)); + + if (_check_rewrite(origattrs, &rewrite, expectedattrs, 0)) + printf("not "); + printf("ok %d - whitelistvendorattrs\n", testcount++); + _tlv_list_clear(origattrs); + _tlv_list_clear(expectedattrs); + _reset_rewrite(&rewrite); + } + + /* test whitelist vendor rewrite subattribute*/ + { + uint32_t whitelistvendorattrs[] = {42,1,0}; + uint8_t value = 42; + uint8_t vendor_long1_in[] = {0,0,0,42,2,3,0,1,3,0}; + uint8_t vendor_long1_out[] = {0,0,0,42,1,3,0}; + uint8_t vendor_nonrfc_in[] = {0,0,0,42,1,2,3,4}; + + rewrite.whitelist_mode=1; + rewrite.removevendorattrs = whitelistvendorattrs; + list_push(origattrs, makevendortlv(42, maketlv(1, 1, &value))); + list_push(origattrs, makevendortlv(43, maketlv(1, 1, &value))); + list_push(origattrs, makevendortlv(42, maketlv(2, 1, &value))); + list_push(origattrs, maketlv(26, sizeof(vendor_long1_in), vendor_long1_in)); + list_push(origattrs, maketlv(26, sizeof(vendor_nonrfc_in), vendor_nonrfc_in)); + + list_push(expectedattrs, makevendortlv(42, maketlv(1, 1, &value))); + list_push(expectedattrs, maketlv(26, sizeof(vendor_long1_out), vendor_long1_out)); + + if (_check_rewrite(origattrs, &rewrite, expectedattrs, 0)) + printf("not "); + printf("ok %d - whitelistvendorattrs\n", testcount++); + _tlv_list_clear(origattrs); + _tlv_list_clear(expectedattrs); + _reset_rewrite(&rewrite); + } + + /* test whitelist vendor rewrite combined*/ + { + uint32_t whitelistvendorattrs[] = {42,1,0}; + uint8_t whitelistattrs[] = {1,0}; + uint8_t value = 42; + + rewrite.whitelist_mode=1; + rewrite.removeattrs = whitelistattrs; + rewrite.removevendorattrs = whitelistvendorattrs; + list_push(origattrs, maketlv(1, strlen(username), username)); + list_push(origattrs, maketlv(3, strlen(username), username)); + list_push(origattrs, makevendortlv(42, maketlv(1, 1, &value))); + list_push(origattrs, makevendortlv(43, maketlv(1, 1, &value))); + list_push(origattrs, makevendortlv(43, maketlv(2, 1, &value))); + + list_push(expectedattrs, maketlv(1, strlen(username), username)); + list_push(expectedattrs, makevendortlv(42, maketlv(1, 1, &value))); + + if (_check_rewrite(origattrs, &rewrite, expectedattrs, 0)) + printf("not "); + printf("ok %d - whitelistvendorattrs\n", testcount++); + _tlv_list_clear(origattrs); + _tlv_list_clear(expectedattrs); + _reset_rewrite(&rewrite); + } + + list_destroy(origattrs); + list_destroy(expectedattrs); + list_destroy(rewrite.addattrs); + list_destroy(rewrite.modattrs); + list_destroy(rewrite.modvattrs); + list_destroy(rewrite.supattrs); + + return 0; +} diff --git a/tests/t_rewrite_config.c b/tests/t_rewrite_config.c new file mode 100644 index 0000000..97ae7fc --- /dev/null +++ b/tests/t_rewrite_config.c @@ -0,0 +1,54 @@ +/* Copyright (C) 2019, SWITCH */ +/* See LICENSE for licensing information. */ + +#include +#include +#include +#include "../rewrite.h" +#include "../radmsg.h" +#include "../debug.h" +#include "../util.h" + +int +main (int argc, char *argv[]) +{ + struct rewrite *result; + char *rewritename = "rewrite"; + char **addattrs; + int numtests = 1, i; + struct tlv *tlv, *expected; + uint8_t expectedvalue[] = {'1',0,0,'1','A'}; + + printf("1..%d\n", numtests); + numtests = 1; + + addattrs = malloc(2); + addattrs[0] = stringcopy("1:'1%00%001%41", 0); + addattrs[1] = NULL; + + expected = maketlv(1,5,expectedvalue); + + addrewrite(rewritename, 0, NULL, NULL, addattrs, + NULL, NULL, NULL, NULL, NULL); + + result = getrewrite(rewritename, NULL); + + if (result->addattrs->first) { + tlv = (struct tlv *)result->addattrs->first->data; + if (!eqtlv(tlv, expected)) { + printf ("tlv value was: 0x"); + for (i = 0; i < tlv->l; i++) { + printf ("%x", *((tlv->v)+i)); + } + printf ("\n"); + printf ("not "); + } + printf("ok %d - rewrite config\n", numtests++); + } else { + printf("not ok %d - rewrite ocnfig\n", numtests++); + } + + freetlv(expected); + + return 0; +} diff --git a/tlv11.c b/tlv11.c index cb9a3da..d570b39 100644 --- a/tlv11.c +++ b/tlv11.c @@ -105,16 +105,16 @@ uint8_t *tlv2str(struct tlv *tlv) { return s; } -uint8_t *tlv2buf(uint8_t *p, const struct tlv *tlv) { - *p++ = tlv->t; - *p++ = tlv->l; - if (tlv->l) { - if (tlv->v) - memcpy(p, tlv->v, tlv->l); - else - memset(p, 0, tlv->l); +struct tlv *resizetlv(struct tlv *tlv, uint8_t newlen) { + uint8_t *newv; + if (newlen != tlv->l) { + newv = realloc(tlv->v, newlen); + if (newlen && !newv) + return NULL; + tlv->v = newv; + tlv->l = newlen; } - return p; + return tlv; } /* Local Variables: */ diff --git a/tlv11.h b/tlv11.h index 87909c0..84db3d7 100644 --- a/tlv11.h +++ b/tlv11.h @@ -16,7 +16,7 @@ struct list *copytlvlist(struct list *); void freetlvlist(struct list *); void rmtlv(struct list *, uint8_t); uint8_t *tlv2str(struct tlv *tlv); -uint8_t *tlv2buf(uint8_t *, const struct tlv *tlv); +struct tlv *resizetlv(struct tlv *, uint8_t); /* Local Variables: */ /* c-file-style: "stroustrup" */