Newer
Older
* Copyright (C) 2006-2009 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.
*/
/* Code contributions from:
*
* Arne Schwabe <schwabe at uni-paderborn.de>
* Maja Wolniewicz <mgw@umk.pl>
* Simon Leinen <simon.leinen@switch.ch>
/* For UDP there is one server instance consisting of udpserverrd and udpserverth
* rd is responsible for init and launching wr
* For TLS there is a server instance that launches tlsserverrd for each TLS peer
* each tlsserverrd launches tlsserverwr
* For each UDP/TLS peer there is clientrd and clientwr, clientwr is responsible
* for init and launching rd
*
* serverrd will receive a request, processes it and puts it in the requestq of
* the appropriate clientwr
* clientwr monitors its requestq and sends requests
* clientrd looks for responses, processes them and puts them in the replyq of
* the peer the request came from
* serverwr monitors its reply and sends replies
*
* In addition to the main thread, we have:
* If UDP peers are configured, there will be 2 + 2 * #peers UDP threads
* If TLS peers are configured, there will initially be 2 * #peers TLS threads
* For each TLS peer connecting to us there will be 2 more TLS threads
* This is only for connected peers
* Example: With 3 UDP peers and 30 TLS peers, there will be a max of
* Remove expired stuff from clients request list?
* Multiple outgoing connections if not enough IDs? (multiple servers per conf?)
* Useful for TCP accounting? Now we require separate server config for alt port
#if defined(HAVE_MALLOPT)
#include <malloc.h>
#endif
#include <assert.h>
#include <openssl/ssl.h>
#include <openssl/rand.h>
#include <openssl/err.h>
#include <openssl/md5.h>
#include "hostport.h"
static struct options options;
static struct list *realms;
static struct hash *rewriteconfs;
/* minimum required declarations to avoid reordering code */
struct realm *adddynamicrealmserver(struct realm *realm, char *id);
int dynamicconfig(struct server *server);
int confserver_cb(struct gconffile **cf, void *arg, char *block, char *opt, char *val);
void freerealm(struct realm *realm);
void freeclsrvconf(struct clsrvconf *conf);
void freerq(struct request *rq);
void freerqoutdata(struct rqout *rqout);
void rmclientrq(struct request *rq, uint8_t id);
static const struct protodefs *(*protoinits[])(uint8_t) = { udpinit, tlsinit, tcpinit, dtlsinit };
for (i = 0; i < RAD_PROTOCOUNT; i++)
if (protodefs[i] && protodefs[i]->name && !strcasecmp(protodefs[i]->name, name))
return i;
return 255;
/* callbacks for making OpenSSL thread safe */
unsigned long ssl_thread_id() {
void ssl_locking_callback(int mode, int type, const char *file, int line) {
if (mode & CRYPTO_LOCK) {
pthread_mutex_lock(&ssl_locks[type]);
ssl_lock_count[type]++;
} else
pthread_mutex_unlock(&ssl_locks[type]);
}
/* returns 1 if the len first bits are equal, else 0 */
int prefixmatch(void *a1, void *a2, uint8_t len) {
static uint8_t mask[] = { 0, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe };
if (l && memcmp(a1, a2, l))
return 0;
r = len % 8;
if (!r)
return 1;
return (((uint8_t *)a1)[l] & mask[r]) == (((uint8_t *)a2)[l] & mask[r]);
}
/* returns next config with matching address, or NULL */
struct clsrvconf *find_conf(uint8_t type, struct sockaddr *addr, struct list *confs, struct list_node **cur, uint8_t server_p) {
struct list_node *entry;
struct clsrvconf *conf;
for (entry = (cur && *cur ? list_next(*cur) : list_first(confs)); entry; entry = list_next(entry)) {
if (conf->type == type && addressmatches(conf->hostports, addr, server_p)) {
if (cur)
*cur = entry;
return conf;
struct clsrvconf *find_clconf(uint8_t type, struct sockaddr *addr, struct list_node **cur) {
return find_conf(type, addr, clconfs, cur, 0);
}
struct clsrvconf *find_srvconf(uint8_t type, struct sockaddr *addr, struct list_node **cur) {
return find_conf(type, addr, srvconfs, cur, 1);
/* returns next config of given type, or NULL */
struct clsrvconf *find_clconf_type(uint8_t type, struct list_node **cur) {
struct list_node *entry;
struct clsrvconf *conf;
for (entry = (cur && *cur ? list_next(*cur) : list_first(clconfs)); entry; entry = list_next(entry)) {
conf = (struct clsrvconf *)entry->data;
if (conf->type == type) {
if (cur)
*cur = entry;
return conf;
}
struct gqueue *newqueue() {
struct gqueue *q;
q = malloc(sizeof(struct gqueue));
debugx(1, DBG_ERR, "malloc failed");
q->entries = list_create();
if (!q->entries)
debugx(1, DBG_ERR, "malloc failed");
pthread_mutex_init(&q->mutex, NULL);
pthread_cond_init(&q->cond, NULL);
return q;
}
void removequeue(struct gqueue *q) {
pthread_mutex_lock(&q->mutex);
for (entry = list_first(q->entries); entry; entry = list_next(entry))
list_destroy(q->entries);
pthread_cond_destroy(&q->cond);
pthread_mutex_unlock(&q->mutex);
pthread_mutex_destroy(&q->mutex);
void freebios(struct gqueue *q) {
pthread_mutex_lock(&q->mutex);
while ((bio = (BIO *)list_shift(q->entries)))
BIO_free(bio);
pthread_mutex_unlock(&q->mutex);
removequeue(q);
struct client *addclient(struct clsrvconf *conf, uint8_t lock) {
struct client *new = malloc(sizeof(struct client));
if (!new) {
debug(DBG_ERR, "malloc failed");
return NULL;
if (lock)
pthread_mutex_lock(conf->lock);
conf->clients = list_create();
if (!conf->clients) {
if (lock)
pthread_mutex_unlock(conf->lock);
debug(DBG_ERR, "malloc failed");
return NULL;
}
memset(new, 0, sizeof(struct client));
new->conf = conf;
if (conf->pdef->addclient)
conf->pdef->addclient(new);
else
new->replyq = newqueue();
if (lock)
pthread_mutex_unlock(conf->lock);
venaas
committed
void removeclientrqs_sendrq_freeserver_lock(uint8_t wantlock) {
static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
if (wantlock)
pthread_mutex_lock(&lock);
else
pthread_mutex_unlock(&lock);
}
void removeclientrqs(struct client *client) {
struct request *rq;
struct rqout *rqout;
int i;
venaas
committed
removeclientrqs_sendrq_freeserver_lock(1);
for (i = 0; i < MAX_REQUESTS; i++) {
rq = client->rqs[i];
if (!rq)
continue;
venaas
committed
if (rq->to) {
rqout = rq->to->requests + rq->newid;
pthread_mutex_lock(rqout->lock);
if (rqout->rq == rq) /* still pointing to our request */
freerqoutdata(rqout);
pthread_mutex_unlock(rqout->lock);
}
freerq(rq);
}
venaas
committed
removeclientrqs_sendrq_freeserver_lock(0);
}
struct clsrvconf *conf;
conf = client->conf;
if (conf->clients) {
removeclientrqs(client);
removequeue(client->replyq);
list_removedata(conf->clients, client);
free(client->addr);
free(client);
}
}
void removeclient(struct client *client) {
struct clsrvconf *conf;
if (!client)
return;
conf = client->conf;
pthread_mutex_lock(conf->lock);
removelockedclient(client);
pthread_mutex_unlock(conf->lock);
void freeserver(struct server *server, uint8_t destroymutex) {
struct rqout *rqout, *end;
venaas
committed
venaas
committed
venaas
committed
removeclientrqs_sendrq_freeserver_lock(1);
rqout = server->requests;
for (end = rqout + MAX_REQUESTS; rqout < end; rqout++) {
venaas
committed
if (rqout->rq)
rqout->rq->to = NULL;
freerqoutdata(rqout);
pthread_mutex_destroy(rqout->lock);
free(rqout->lock);
}
venaas
committed
free(server->requests);
}
if (server->rbios)
freebios(server->rbios);
Linus Nordberg
committed
if (server->ssl) {
#if defined ENABLE_EXPERIMENTAL_DYNDISC
if (server->sock >= 0)
close(server->sock);
#endif
SSL_free(server->ssl);
}
if (destroymutex) {
pthread_mutex_destroy(&server->lock);
pthread_cond_destroy(&server->newrq_cond);
pthread_mutex_destroy(&server->newrq_mutex);
}
venaas
committed
removeclientrqs_sendrq_freeserver_lock(0);
free(server);
}
int addserver(struct clsrvconf *conf) {
if (conf->servers) {
debug(DBG_ERR, "addserver: currently works with just one server per conf");
return 0;
}
conf->servers = malloc(sizeof(struct server));
if (!conf->servers) {
debug(DBG_ERR, "malloc failed");
return 0;
}
memset(conf->servers, 0, sizeof(struct server));
conf->servers->conf = conf;
if (conf->type == RAD_DTLS)
conf->servers->sock = -1;
if (conf->pdef->addserverextra)
conf->pdef->addserverextra(conf);
conf->servers->requests = calloc(MAX_REQUESTS, sizeof(struct rqout));
if (!conf->servers->requests) {
debug(DBG_ERR, "malloc failed");
goto errexit;
}
for (i = 0; i < MAX_REQUESTS; i++) {
conf->servers->requests[i].lock = malloc(sizeof(pthread_mutex_t));
if (!conf->servers->requests[i].lock) {
debug(DBG_ERR, "malloc failed");
goto errexit;
}
if (pthread_mutex_init(conf->servers->requests[i].lock, NULL)) {
debugerrno(errno, DBG_ERR, "mutex init failed");
free(conf->servers->requests[i].lock);
conf->servers->requests[i].lock = NULL;
goto errexit;
}
}
if (pthread_mutex_init(&conf->servers->lock, NULL)) {
debugerrno(errno, DBG_ERR, "mutex init failed");
conf->servers->newrq = 0;
if (pthread_mutex_init(&conf->servers->newrq_mutex, NULL)) {
debugerrno(errno, DBG_ERR, "mutex init failed");
pthread_mutex_destroy(&conf->servers->lock);
goto errexit;
}
if (pthread_cond_init(&conf->servers->newrq_cond, NULL)) {
debugerrno(errno, DBG_ERR, "mutex init failed");
pthread_mutex_destroy(&conf->servers->newrq_mutex);
pthread_mutex_destroy(&conf->servers->lock);
goto errexit;
}
return 1;
freeserver(conf->servers, 0);
conf->servers = NULL;
return 0;
unsigned char *attrget(unsigned char *attrs, int length, uint8_t type) {
length -= ATTRLEN(attrs);
attrs += ATTRLEN(attrs);
if (rq)
rq->refcount++;
void freerq(struct request *rq) {
if (!rq)
return;
debug(DBG_DBG, "freerq: called with refcount %d", rq->refcount);
if (--rq->refcount)
return;
if (rq->origusername)
free(rq->origusername);
if (rq->buf)
free(rq->buf);
free(rq);
}
void freerqoutdata(struct rqout *rqout) {
if (!rqout)
return;
if (rqout->rq->buf) {
free(rqout->rq->buf);
rqout->rq->buf = NULL;
}
rqout->rq = NULL;
}
rqout->tries = 0;
memset(&rqout->expiry, 0, sizeof(struct timeval));
void sendrq(struct request *rq) {
venaas
committed
struct server *to;
removeclientrqs_sendrq_freeserver_lock(1);
to = rq->to;
if (!to)
goto errexit;
start = to->conf->statusserver ? 1 : 0;
if (start && rq->msg->code == RAD_Status_Server) {
pthread_mutex_lock(to->requests[0].lock);
if (to->requests[0].rq) {
pthread_mutex_unlock(to->requests[0].lock);
debug(DBG_INFO, "sendrq: status server already in queue, dropping request");
i = 0;
} else {
if (!to->nextid)
to->nextid = start;
/* might simplify if only try nextid, might be ok */
for (i = to->nextid; i < MAX_REQUESTS; i++) {
pthread_mutex_lock(to->requests[i].lock);
break;
pthread_mutex_unlock(to->requests[i].lock);
}
}
if (i == MAX_REQUESTS) {
for (i = start; i < to->nextid; i++) {
if (!to->requests[i].rq) {
pthread_mutex_lock(to->requests[i].lock);
if (!to->requests[i].rq)
break;
pthread_mutex_unlock(to->requests[i].lock);
}
}
if (i == to->nextid) {
debug(DBG_INFO, "sendrq: no room in queue, dropping request");
rq->newid = (uint8_t)i;
rq->msg->id = (uint8_t)i;
rq->buf = radmsg2buf(rq->msg, (uint8_t *)to->conf->secret);
if (!rq->buf) {
pthread_mutex_unlock(to->requests[i].lock);
debug(DBG_DBG, "sendrq: inserting packet with id %d in queue for %s", i, to->conf->name);
pthread_mutex_unlock(to->requests[i].lock);
if (i >= start) /* i is not reserved for statusserver */
to->nextid = i + 1;
debug(DBG_DBG, "sendrq: signalling client writer");
pthread_mutex_unlock(&to->newrq_mutex);
venaas
committed
removeclientrqs_sendrq_freeserver_lock(0);
if (rq->from)
rmclientrq(rq, rq->msg->id);
venaas
committed
removeclientrqs_sendrq_freeserver_lock(0);
uint8_t first;
if (!rq->replybuf)
rq->replybuf = radmsg2buf(rq->msg, (uint8_t *)to->conf->secret);
radmsg_free(rq->msg);
rq->msg = NULL;
if (!rq->replybuf) {
freerq(rq);
pthread_mutex_lock(&to->replyq->mutex);
first = list_first(to->replyq->entries) == NULL;
pthread_mutex_unlock(&to->replyq->mutex);
debug(DBG_ERR, "sendreply: malloc failed");
return;
}
debug(DBG_DBG, "signalling server writer");
pthread_cond_signal(&to->replyq->cond);
pthread_mutex_unlock(&to->replyq->mutex);
int pwdcrypt(char encrypt_flag, uint8_t *in, uint8_t len, char *shared, uint8_t sharedlen, uint8_t *auth) {
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], *input;
unsigned int md_len;
pthread_mutex_lock(&lock);
if (first) {
EVP_MD_CTX_init(&mdctx);
first = 0;
}
input = auth;
for (;;) {
if (!EVP_DigestInit_ex(&mdctx, EVP_md5(), NULL) ||
!EVP_DigestUpdate(&mdctx, (uint8_t *)shared, sharedlen) ||
!EVP_DigestUpdate(&mdctx, input, 16) ||
!EVP_DigestFinal_ex(&mdctx, hash, &md_len) ||
md_len != 16) {
pthread_mutex_unlock(&lock);
return 0;
}
for (i = 0; i < 16; i++)
out[offset + i] = hash[i] ^ in[offset + i];
if (encrypt_flag)
input = out + offset;
else
input = in + offset;
int msmppencrypt(uint8_t *text, uint8_t len, uint8_t *shared, uint8_t sharedlen, uint8_t *auth, uint8_t *salt) {
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 md_len;
uint8_t i, offset;
pthread_mutex_lock(&lock);
if (first) {
EVP_MD_CTX_init(&mdctx);
first = 0;
}
#if 0
printfchars(NULL, "msppencrypt auth in", "%02x ", auth, 16);
printfchars(NULL, "msppencrypt salt in", "%02x ", salt, 2);
printfchars(NULL, "msppencrypt in", "%02x ", text, len);
if (!EVP_DigestInit_ex(&mdctx, EVP_md5(), NULL) ||
!EVP_DigestUpdate(&mdctx, shared, sharedlen) ||
!EVP_DigestUpdate(&mdctx, auth, 16) ||
!EVP_DigestUpdate(&mdctx, salt, 2) ||
!EVP_DigestFinal_ex(&mdctx, hash, &md_len)) {
pthread_mutex_unlock(&lock);
return 0;
}
printfchars(NULL, "msppencrypt hash", "%02x ", hash, 16);
for (i = 0; i < 16; i++)
text[i] ^= hash[i];
for (offset = 16; offset < len; offset += 16) {
printf("text + offset - 16 c(%d): ", offset / 16);
printfchars(NULL, NULL, "%02x ", text + offset - 16, 16);
if (!EVP_DigestInit_ex(&mdctx, EVP_md5(), NULL) ||
!EVP_DigestUpdate(&mdctx, shared, sharedlen) ||
!EVP_DigestUpdate(&mdctx, text + offset - 16, 16) ||
!EVP_DigestFinal_ex(&mdctx, hash, &md_len) ||
md_len != 16) {
pthread_mutex_unlock(&lock);
return 0;
}
#if 0
printfchars(NULL, "msppencrypt hash", "%02x ", hash, 16);
for (i = 0; i < 16; i++)
text[offset + i] ^= hash[i];
}
printfchars(NULL, "msppencrypt out", "%02x ", text, len);
#endif
pthread_mutex_unlock(&lock);
return 1;
}
int msmppdecrypt(uint8_t *text, uint8_t len, uint8_t *shared, uint8_t sharedlen, uint8_t *auth, uint8_t *salt) {
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 md_len;
uint8_t i, offset;
char plain[255];
pthread_mutex_lock(&lock);
if (first) {
EVP_MD_CTX_init(&mdctx);
first = 0;
}
#if 0
printfchars(NULL, "msppdecrypt auth in", "%02x ", auth, 16);
printfchars(NULL, "msppdecrypt salt in", "%02x ", salt, 2);
printfchars(NULL, "msppdecrypt in", "%02x ", text, len);
if (!EVP_DigestInit_ex(&mdctx, EVP_md5(), NULL) ||
!EVP_DigestUpdate(&mdctx, shared, sharedlen) ||
!EVP_DigestUpdate(&mdctx, auth, 16) ||
!EVP_DigestUpdate(&mdctx, salt, 2) ||
!EVP_DigestFinal_ex(&mdctx, hash, &md_len)) {
pthread_mutex_unlock(&lock);
return 0;
}
printfchars(NULL, "msppdecrypt hash", "%02x ", hash, 16);
for (i = 0; i < 16; i++)
plain[i] = text[i] ^ hash[i];
for (offset = 16; offset < len; offset += 16) {
printf("text + offset - 16 c(%d): ", offset / 16);
printfchars(NULL, NULL, "%02x ", text + offset - 16, 16);
#endif
if (!EVP_DigestInit_ex(&mdctx, EVP_md5(), NULL) ||
!EVP_DigestUpdate(&mdctx, shared, sharedlen) ||
!EVP_DigestUpdate(&mdctx, text + offset - 16, 16) ||
!EVP_DigestFinal_ex(&mdctx, hash, &md_len) ||
md_len != 16) {
pthread_mutex_unlock(&lock);
return 0;
}
#if 0
printfchars(NULL, "msppdecrypt hash", "%02x ", hash, 16);
for (i = 0; i < 16; i++)
plain[offset + i] = text[offset + i] ^ hash[i];
printfchars(NULL, "msppdecrypt out", "%02x ", text, len);
#endif
pthread_mutex_unlock(&lock);
return 1;
}
struct realm *newrealmref(struct realm *r) {
if (r)
r->refcount++;
return r;
}
/* returns with lock on realm */
struct realm *id2realm(struct list *realmlist, char *id) {
struct realm *realm, *subrealm;
/* need to do locking for subrealms and check subrealm timers */
for (entry = list_first(realmlist); entry; entry = list_next(entry)) {
realm = (struct realm *)entry->data;
if (!regexec(&realm->regex, id, 0, NULL, 0)) {
pthread_mutex_lock(&realm->mutex);
if (realm->subrealms) {
subrealm = id2realm(realm->subrealms, id);
if (subrealm) {
pthread_mutex_unlock(&realm->mutex);
return subrealm;
}
}
return newrealmref(realm);
int hasdynamicserver(struct list *srvconfs) {
struct list_node *entry;
for (entry = list_first(srvconfs); entry; entry = list_next(entry))
Linus Nordberg
committed
#if defined ENABLE_EXPERIMENTAL_DYNDISC
if (((struct clsrvconf *)entry->data)->dynamiclookupcommand
|| ((struct clsrvconf *)entry->data)->servers->in_use)
#else
if (((struct clsrvconf *)entry->data)->dynamiclookupcommand)
#endif
return 1;
return 0;
}
/* helper function, only used by removeserversubrealms() */
void _internal_removeserversubrealms(struct list *realmlist, struct clsrvconf *srv) {
struct list_node *entry, *entry2;
struct list *srvconfs;
for (entry = list_first(realmlist); entry;) {
realm = newrealmref((struct realm *)entry->data);
pthread_mutex_lock(&realm->mutex);
srvconfs = realm->srvconfs;
for (entry2 = list_first(realm->srvconfs); entry2; entry2 = list_next(entry2))
if (entry2->data == srv)
freerealm(realm);
list_removedata(srvconfs, srv);
srvconfs = realm->accsrvconfs;
for (entry2 = list_first(realm->accsrvconfs); entry2; entry2 = list_next(entry2))
if (entry2->data == srv)
freerealm(realm);
list_removedata(srvconfs, srv);
/* remove subrealm if no dynamic servers left */
if (!hasdynamicserver(realm->srvconfs) && !hasdynamicserver(realm->accsrvconfs)) {
while (list_shift(realm->srvconfs))
freerealm(realm);
list_destroy(realm->srvconfs);
realm->srvconfs = NULL;
while (list_shift(realm->accsrvconfs))
freerealm(realm);
list_destroy(realm->accsrvconfs);
realm->accsrvconfs = NULL;
list_removedata(realmlist, realm);
pthread_mutex_unlock(&realm->mutex);
freerealm(realm);
}
}
void removeserversubrealms(struct list *realmlist, struct clsrvconf *srv) {
struct list_node *entry;
struct realm *realm;
for (entry = list_first(realmlist); entry; entry = list_next(entry)) {
realm = (struct realm *)entry->data;
pthread_mutex_lock(&realm->mutex);
if (realm->subrealms) {
_internal_removeserversubrealms(realm->subrealms, srv);
if (!list_first(realm->subrealms)) {
list_destroy(realm->subrealms);
realm->subrealms = NULL;
}
}
pthread_mutex_unlock(&realm->mutex);
int attrvalidate(unsigned char *attrs, int length) {
while (length > 1) {
debug(DBG_INFO, "attrvalidate: invalid attribute length %d", ATTRLEN(attrs));
debug(DBG_INFO, "attrvalidate: attribute length %d exceeds packet length", ATTRLEN(attrs));
debug(DBG_INFO, "attrvalidate: malformed packet? remaining byte after last attribute");
int pwdrecrypt(uint8_t *pwd, uint8_t len, char *oldsecret, char *newsecret, 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)) {
debug(DBG_WARN, "pwdrecrypt: cannot decrypt password");
return 0;
}
#ifdef DEBUG
printfchars(NULL, "pwdrecrypt: password", "%02x ", pwd, len);
if (!pwdcrypt(1, pwd, len, newsecret, strlen(newsecret), 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) {
if (!msmppdecrypt(msmpp + 2, len - 2, (uint8_t *)oldsecret, strlen(oldsecret), 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)) {
debug(DBG_WARN, "msmpprecrypt: failed to encrypt msppe key");
return 0;
}
return 1;
}
int msmppe(unsigned char *attrs, int length, uint8_t type, char *attrtxt, struct request *rq,
char *oldsecret, char *newsecret) {
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))
int findvendorsubattr(uint32_t *attrs, uint32_t vendor, uint32_t subattr) {
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;
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;
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;
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) {
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;
}
}