summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--radsecproxy.c146
-rw-r--r--radsecproxy.h6
2 files changed, 100 insertions, 52 deletions
diff --git a/radsecproxy.c b/radsecproxy.c
index ba342fe..209d084 100644
--- a/radsecproxy.c
+++ b/radsecproxy.c
@@ -92,7 +92,7 @@ extern int optind;
extern char *optarg;
/* minimum required declarations to avoid reordering code */
-void adddynamicrealmserver(struct realm *realm, struct clsrvconf *conf, char *id);
+struct realm *adddynamicrealmserver(struct realm *realm, struct clsrvconf *conf, 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);
@@ -898,7 +898,8 @@ unsigned char *attrget(unsigned char *attrs, int length, uint8_t type) {
}
struct request *newrqref(struct request *rq) {
- rq->refcount++;
+ if (rq)
+ rq->refcount++;
return rq;
}
@@ -1244,19 +1245,30 @@ int msmppdecrypt(uint8_t *text, uint8_t len, uint8_t *shared, uint8_t sharedlen,
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 list_node *entry;
- struct realm *realm, *subrealm = NULL;
+ 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->subrealms_mutex);
- if (realm->subrealms)
+ pthread_mutex_lock(&realm->mutex);
+ if (realm->subrealms) {
subrealm = id2realm(realm->subrealms, id);
- pthread_mutex_unlock(&realm->subrealms_mutex);
- return subrealm ? subrealm : realm;
+ if (subrealm) {
+ pthread_mutex_unlock(&realm->mutex);
+ return subrealm;
+ }
+ }
+ return newrealmref(realm);
}
}
return NULL;
@@ -1264,13 +1276,18 @@ struct realm *id2realm(struct list *realmlist, char *id) {
/* helper function, only used by removeserversubrealms() */
void _internal_removeserversubrealms(struct list *realmlist, struct clsrvconf *srv) {
- struct list_node *entry;
+ struct list_node *entry, *entry2;
struct realm *realm;
-
+
for (entry = list_first(realmlist); entry;) {
- realm = (struct realm *)entry->data;
+ realm = newrealmref((struct realm *)entry->data);
+ pthread_mutex_lock(&realm->mutex);
entry = list_next(entry);
+
if (realm->srvconfs) {
+ for (entry2 = list_first(realm->srvconfs); entry2; entry2 = list_next(entry2))
+ if (entry2->data == srv)
+ freerealm(realm);
list_removedata(realm->srvconfs, srv);
if (!list_first(realm->srvconfs)) {
list_destroy(realm->srvconfs);
@@ -1278,6 +1295,9 @@ void _internal_removeserversubrealms(struct list *realmlist, struct clsrvconf *s
}
}
if (realm->accsrvconfs) {
+ for (entry2 = list_first(realm->accsrvconfs); entry2; entry2 = list_next(entry2))
+ if (entry2->data == srv)
+ freerealm(realm);
list_removedata(realm->accsrvconfs, srv);
if (!list_first(realm->accsrvconfs)) {
list_destroy(realm->accsrvconfs);
@@ -1286,10 +1306,11 @@ void _internal_removeserversubrealms(struct list *realmlist, struct clsrvconf *s
}
/* remove subrealm if no servers */
- if (!realm->srvconfs && !realm->accsrvconfs) {
+ if (!realm->srvconfs && !realm->accsrvconfs)
list_removedata(realmlist, realm);
- freerealm(realm);
- }
+
+ pthread_mutex_unlock(&realm->mutex);
+ freerealm(realm);
}
}
@@ -1299,7 +1320,7 @@ void removeserversubrealms(struct list *realmlist, struct clsrvconf *srv) {
for (entry = list_first(realmlist); entry; entry = list_next(entry)) {
realm = (struct realm *)entry->data;
- pthread_mutex_lock(&realm->subrealms_mutex);
+ pthread_mutex_lock(&realm->mutex);
if (realm->subrealms) {
_internal_removeserversubrealms(realm->subrealms, srv);
if (!list_first(realm->subrealms)) {
@@ -1307,7 +1328,7 @@ void removeserversubrealms(struct list *realmlist, struct clsrvconf *srv) {
realm->subrealms = NULL;
}
}
- pthread_mutex_unlock(&realm->subrealms_mutex);
+ pthread_mutex_unlock(&realm->mutex);
}
}
@@ -1680,12 +1701,15 @@ struct clsrvconf *choosesrvconf(struct list *srvconfs) {
return best ? best : first;
}
+/* returns with lock on realm, protects from server changes while in use by radsrv/sendrq */
struct server *findserver(struct realm **realm, struct tlv *username, uint8_t acc) {
struct clsrvconf *srvconf;
+ struct realm *subrealm;
char *id = (char *)tlv2str(username);
if (!id)
return NULL;
+ /* returns with lock on realm */
*realm = id2realm(realms, id);
if (!*realm) {
free(id);
@@ -1697,8 +1721,15 @@ struct server *findserver(struct realm **realm, struct tlv *username, uint8_t ac
free(id);
return NULL;
}
- if (!acc && !srvconf->servers)
- adddynamicrealmserver(*realm, srvconf, id);
+ if (!acc && !(*realm)->parent && !srvconf->servers) {
+ subrealm = adddynamicrealmserver(*realm, srvconf, id);
+ if (subrealm) {
+ pthread_mutex_lock(&subrealm->mutex);
+ pthread_mutex_unlock(&(*realm)->mutex);
+ freerealm(*realm);
+ *realm = subrealm;
+ }
+ }
free(id);
return srvconf->servers;
}
@@ -1813,6 +1844,7 @@ int radsrv(struct request *rq) {
goto rmclrqexit;
debug(DBG_DBG, "%s with username: %s", radmsgtype2string(msg->code), userascii);
+ /* will return with lock on the realm */
to = findserver(&realm, attr, msg->code == RAD_Accounting_Request);
if (!realm) {
debug(DBG_INFO, "radsrv: ignoring request, don't know where to send it");
@@ -1867,6 +1899,8 @@ int radsrv(struct request *rq) {
free(userascii);
rq->to = to;
sendrq(rq);
+ pthread_mutex_unlock(&realm->mutex);
+ freerealm(realm);
return 1;
rmclrqexit:
@@ -1874,6 +1908,10 @@ int radsrv(struct request *rq) {
exit:
freerq(rq);
free(userascii);
+ if (realm) {
+ pthread_mutex_unlock(&realm->mutex);
+ freerealm(realm);
+ }
return 1;
}
@@ -2483,22 +2521,21 @@ struct list *addsrvconfs(char *value, char **names) {
void freerealm(struct realm *realm) {
if (!realm)
return;
+ debug(DBG_DBG, "freerealm: called with refcount %d", realm->refcount);
+ if (--realm->refcount)
+ return;
+
free(realm->name);
free(realm->message);
regfree(&realm->regex);
- pthread_mutex_destroy(&realm->subrealms_mutex);
- if (realm->subrealms)
- list_destroy(realm->subrealms);
- if (realm->srvconfs) {
- /* emptying list without freeing data */
- while (list_shift(realm->srvconfs));
- list_destroy(realm->srvconfs);
- }
- if (realm->accsrvconfs) {
- /* emptying list without freeing data */
- while (list_shift(realm->accsrvconfs));
- list_destroy(realm->accsrvconfs);
- }
+ pthread_mutex_destroy(&realm->mutex);
+ /* if refcount == 0, all subrealms gone */
+ list_destroy(realm->subrealms);
+ /* if refcount == 0, all srvconfs gone */
+ list_destroy(realm->srvconfs);
+ /* if refcount == 0, all accsrvconfs gone */
+ list_destroy(realm->accsrvconfs);
+ freerealm(realm->parent);
free(realm);
}
@@ -2546,7 +2583,7 @@ struct realm *addrealm(struct list *realmlist, char *value, char **servers, char
}
memset(realm, 0, sizeof(struct realm));
- if (pthread_mutex_init(&realm->subrealms_mutex, NULL)) {
+ if (pthread_mutex_init(&realm->mutex, NULL)) {
debug(DBG_ERR, "mutex init failed");
free(realm);
realm = NULL;
@@ -2584,7 +2621,7 @@ struct realm *addrealm(struct list *realmlist, char *value, char **servers, char
if (!list_push(realmlist, realm)) {
debug(DBG_ERR, "malloc failed");
- pthread_mutex_destroy(&realm->subrealms_mutex);
+ pthread_mutex_destroy(&realm->mutex);
goto errexit;
}
@@ -2592,52 +2629,55 @@ struct realm *addrealm(struct list *realmlist, char *value, char **servers, char
goto exit;
errexit:
+ while (list_shift(realm->srvconfs));
+ while (list_shift(realm->accsrvconfs));
freerealm(realm);
realm = NULL;
-
exit:
free(regex);
if (servers) {
+ if (realm)
+ for (n = 0; servers[n]; n++)
+ newrealmref(realm);
for (n = 0; servers[n]; n++)
free(servers[n]);
free(servers);
}
if (accservers) {
+ if (realm)
+ for (n = 0; accservers[n]; n++)
+ newrealmref(realm);
for (n = 0; accservers[n]; n++)
free(accservers[n]);
free(accservers);
}
- return realm;
+ return newrealmref(realm);
}
-void adddynamicrealmserver(struct realm *realm, struct clsrvconf *conf, char *id) {
+struct realm *adddynamicrealmserver(struct realm *realm, struct clsrvconf *conf, char *id) {
struct clsrvconf *srvconf;
struct realm *newrealm = NULL;
char *realmname, *s;
pthread_t clientth;
if (!conf->dynamiclookupcommand)
- return;
+ return NULL;
/* create dynamic for the realm (string after last @, exit if nothing after @ */
realmname = strrchr(id, '@');
if (!realmname)
- return;
+ return NULL;
realmname++;
if (!*realmname)
- return;
+ return NULL;
for (s = realmname; *s; s++)
if (*s != '.' && *s != '-' && !isalnum((int)*s))
- return;
+ return NULL;
- pthread_mutex_lock(&realm->subrealms_mutex);
- /* exit if we now already got a matching subrealm */
- if (id2realm(realm->subrealms, id))
- goto exit;
srvconf = malloc(sizeof(struct clsrvconf));
if (!srvconf) {
debug(DBG_ERR, "malloc failed");
- goto exit;
+ return NULL;
}
*srvconf = *conf;
if (!addserver(srvconf))
@@ -2650,27 +2690,35 @@ void adddynamicrealmserver(struct realm *realm, struct clsrvconf *conf, char *id
newrealm = addrealm(realm->subrealms, realmname, NULL, NULL, NULL, 0);
if (!newrealm)
goto errexit;
-
+ newrealm->parent = newrealmref(realm);
+
/* add server and accserver to newrealm */
newrealm->srvconfs = list_create();
if (!newrealm->srvconfs || !list_push(newrealm->srvconfs, srvconf)) {
debug(DBG_ERR, "malloc failed");
goto errexit;
}
+ newrealmref(newrealm);
newrealm->accsrvconfs = list_create();
if (!newrealm->accsrvconfs || !list_push(newrealm->accsrvconfs, srvconf)) {
debug(DBG_ERR, "malloc failed");
+ list_shift(realm->srvconfs);
+ freerealm(newrealm);
goto errexit;
}
+ newrealmref(newrealm);
srvconf->servers->dynamiclookuparg = stringcopy(realmname, 0);
-
if (pthread_create(&clientth, NULL, clientwr, (void *)(srvconf->servers))) {
debug(DBG_ERR, "pthread_create failed");
+ list_shift(realm->srvconfs);
+ freerealm(newrealm);
+ list_shift(realm->accsrvconfs);
+ freerealm(newrealm);
goto errexit;
}
pthread_detach(clientth);
- goto exit;
+ return newrealm;
errexit:
if (newrealm) {
@@ -2684,9 +2732,7 @@ void adddynamicrealmserver(struct realm *realm, struct clsrvconf *conf, char *id
freeserver(srvconf->servers, 1);
free(srvconf);
debug(DBG_ERR, "failed to create dynamic server");
-
- exit:
- pthread_mutex_unlock(&realm->subrealms_mutex);
+ return NULL;
}
int dynamicconfig(struct server *server) {
diff --git a/radsecproxy.h b/radsecproxy.h
index 21c02ec..4be2274 100644
--- a/radsecproxy.h
+++ b/radsecproxy.h
@@ -44,7 +44,7 @@ struct options {
struct request {
struct timeval created;
- uint8_t refcount;
+ uint32_t refcount;
uint8_t *buf, *replybuf;
struct radmsg *msg;
struct client *from;
@@ -138,7 +138,9 @@ struct realm {
char *message;
uint8_t accresp;
regex_t regex;
- pthread_mutex_t subrealms_mutex;
+ uint32_t refcount;
+ pthread_mutex_t mutex;
+ struct realm *parent;
struct list *subrealms;
struct list *srvconfs;
struct list *accsrvconfs;