be used yet, but is getting closer.
detail
unix
}
+
+# Session database, used for checking Simultaneous-Use. The radutmp module
+# handles this
+session {
+ radutmp
+}
#define RLM_COMPONENT_AUTZ 1
#define RLM_COMPONENT_PREACCT 2
#define RLM_COMPONENT_ACCT 3
-#define RLM_COMPONENT_COUNT 4 /* How many components are there */
+#define RLM_COMPONENT_SESS 4
+#define RLM_COMPONENT_COUNT 5 /* How many components are there */
typedef struct module_t {
const char *name;
int (*authenticate)(void *instance, REQUEST *request);
int (*preaccounting)(void *instance, REQUEST *request);
int (*accounting)(void *instance, REQUEST *request);
+ int (*checksimul)(void *instance, REQUEST *request);
int (*detach)(void *instance);
int (*destroy)(void);
} module_t;
int module_authenticate(int type, REQUEST *request);
int module_preacct(REQUEST *request);
int module_accounting(REQUEST *request);
+int module_checksimul(REQUEST *request, int maxsimul);
#endif /* RADIUS_MODULES_H */
int proxy_try_count;
time_t proxy_next_try;
+ int simul_max;
+ int simul_count;
+ int simul_mpp; /* WEIRD: 1 is false, 2 is true */
+
int finished;
struct auth_req *prev;
struct auth_req *next;
int radutmp_zap(uint32_t nas, int port, char *user, time_t t);
int radutmp_checksimul(char *name, VALUE_PAIR *, int maxsimul);
+/* session.c */
+int rad_check_ts(uint32_t nasaddr, int port, const char *user,
+ const char *sessionid);
+int session_zap(uint32_t nasaddr, int port, const char *user,
+ const char *sessionid, uint32_t cliaddr,
+ char proto, time_t t);
+
/* radiusd.c */
void debug_pair(FILE *, VALUE_PAIR *);
int log_err (char *);
radutmp.o: radutmp.c $(INCLUDES)
$(CC) $(CFLAGS) -c radutmp.c
+session.o: session.c $(INCLUDES)
+ $(CC) $(CFLAGS) -c session.c
+
proxy.o: proxy.c $(INCLUDES)
$(CC) $(CFLAGS) -c proxy.c
static indexed_config_module_t *authenticate = NULL;
static config_module_t *preacct = NULL;
static config_module_t *accounting = NULL;
+static config_module_t *session = NULL;
static void config_list_free(config_module_t **cf)
{
config_list_free(&authorize);
config_list_free(&preacct);
config_list_free(&accounting);
+ config_list_free(&session);
instance_list_free(&module_instance_list);
}
add_to_list(&accounting, this);
break;
+ case RLM_COMPONENT_SESS:
+ if (!this->entry->module->checksimul) {
+ radlog(L_ERR|L_CONS,
+ "%s[%d] Module %s does not contain "
+ "a 'checksimul' entry\n",
+ filename, modreflineno,
+ this->entry->module->name);
+ exit(1);
+ }
+ add_to_list(&session, this);
+ break;
default:
radlog(L_ERR|L_CONS, "%s[%d] Unknown component %d.\n",
filename, modreflineno, comp);
case RLM_COMPONENT_AUTZ: control="authorize"; break;
case RLM_COMPONENT_PREACCT: control="preacct"; break;
case RLM_COMPONENT_ACCT: control="accounting"; break;
+ case RLM_COMPONENT_SESS: control="session"; break;
default: control="unknown";
}
}
/*
+ * See if a user is already logged in.
+ *
+ * Returns: 0 == OK, 1 == double logins, 2 == multilink attempt
+ */
+int module_checksimul(REQUEST *request, int maxsimul)
+{
+ config_module_t *this;
+ int rcode;
+
+ if(!session)
+ return 0;
+
+ if(!request->username)
+ return 0;
+
+ request->simul_count = 0;
+ request->simul_max = maxsimul;
+ request->simul_mpp = 1;
+
+ this = session;
+ rcode = RLM_MODULE_FAIL;
+
+ while (this && (rcode == RLM_MODULE_FAIL)) {
+ DEBUG2(" checksimul: %s", this->instance->entry->module->name);
+ rcode = (this->instance->entry->module->checksimul)
+ (this->instance->insthandle, request);
+ this = this->next;
+ }
+
+ if(rcode != RLM_MODULE_OK) {
+ /* FIXME: Good spot for a *rate-limited* warning to the log */
+ return 0;
+ }
+
+ return (request->simul_count < maxsimul) ? 0 : request->simul_mpp;
+}
+
+/*
* Module malloc() call, which does stuff if the malloc fails.
*
* This call ALWAYS succeeds!
/*
* Check one terminal server to see if a user is logged in.
*/
-static int rad_check_ts(uint32_t nasaddr, int portnum, const char *user,
- const char *session_id)
+int rad_check_ts(uint32_t nasaddr, int portnum, const char *user,
+ const char *session_id)
{
int pid, st, e;
int n;
--- /dev/null
+#include "autoconf.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <signal.h>
+#include <errno.h>
+#include <sys/wait.h>
+
+#include "radiusd.h"
+#include "modules.h"
+
+/* End a session by faking a Stop packet to all accounting modules */
+int session_zap(uint32_t nasaddr, int port, const char *user,
+ const char *sessionid, uint32_t cliaddr, char proto, time_t t)
+{
+ static unsigned char id=0;
+
+ REQUEST stopreq;
+ RADIUS_PACKET stoppkt;
+ VALUE_PAIR *vp, *userpair;
+ int modret;
+ int ret;
+
+ memset(&stoppkt, 0, sizeof stoppkt);
+ stoppkt.data=0;
+ stoppkt.sockfd=-1;
+ stoppkt.code=PW_ACCOUNTING_REQUEST;
+ stoppkt.id=id++;
+ stoppkt.timestamp=t?t:time(0);
+ stoppkt.vps=0;
+
+ /* Hold your breath */
+#define PAIR(n,v,t,e) do { \
+ if(!(vp=paircreate(n, t))) { \
+ radlog(L_ERR|L_CONS, "no memory"); \
+ pairfree(stoppkt.vps); \
+ return 0; \
+ } \
+ vp->e=v; \
+ pairadd(&stoppkt.vps, vp); \
+ } while(0)
+#define INTPAIR(n,v) PAIR(n,v,PW_TYPE_INTEGER,lvalue)
+#define IPPAIR(n,v) PAIR(n,v,PW_TYPE_IPADDR,lvalue)
+#define STRINGPAIR(n,v) do { \
+ if(!(vp=paircreate(n, PW_TYPE_STRING))) { \
+ radlog(L_ERR|L_CONS, "no memory"); \
+ pairfree(stoppkt.vps); \
+ return 0; \
+ } \
+ strNcpy((char *)vp->strvalue, v, sizeof vp->strvalue); \
+ vp->length=strlen(v); \
+ pairadd(&stoppkt.vps, vp); \
+ } while(0)
+
+ INTPAIR(PW_ACCT_STATUS_TYPE, PW_STATUS_STOP);
+ IPPAIR(PW_NAS_IP_ADDRESS, nasaddr);
+ INTPAIR(PW_ACCT_DELAY_TIME, 0);
+ STRINGPAIR(PW_USER_NAME, user);
+ userpair=vp;
+ INTPAIR(PW_NAS_PORT_ID, port);
+ STRINGPAIR(PW_ACCT_SESSION_ID, sessionid);
+ if(proto=='P') {
+ INTPAIR(PW_SERVICE_TYPE, PW_FRAMED_USER);
+ INTPAIR(PW_FRAMED_PROTOCOL, PW_PPP);
+ } else if(proto=='S') {
+ INTPAIR(PW_SERVICE_TYPE, PW_FRAMED_USER);
+ INTPAIR(PW_FRAMED_PROTOCOL, PW_SLIP);
+ } else {
+ INTPAIR(PW_SERVICE_TYPE, PW_LOGIN_USER); /* A guess, really */
+ }
+ if(cliaddr)
+ IPPAIR(PW_FRAMED_IP_ADDRESS, cliaddr);
+ INTPAIR(PW_ACCT_SESSION_TIME, 0);
+ INTPAIR(PW_ACCT_INPUT_OCTETS, 0);
+ INTPAIR(PW_ACCT_OUTPUT_OCTETS, 0);
+ INTPAIR(PW_ACCT_INPUT_PACKETS, 0);
+ INTPAIR(PW_ACCT_OUTPUT_PACKETS, 0);
+
+ memset(&stopreq, 0, sizeof stopreq);
+ stopreq.packet=&stoppkt;
+ stopreq.proxy=0;
+ stopreq.reply=0;
+ stopreq.config_items=0;
+ stopreq.username=userpair;
+ stopreq.child_pid=-1;
+ stopreq.timestamp=stoppkt.timestamp;
+ stopreq.next=0;
+
+ /* FIXME: Need to run preaccouting, so replication can work */
+ modret = module_accounting(&stopreq);
+ if(modret==RLM_MODULE_NOOP ||
+ modret==RLM_MODULE_OK ||
+ modret==RLM_MODULE_UPDATED ||
+ modret==RLM_MODULE_HANDLED)
+ ret=1;
+ else
+ ret=0;
+
+ pairfree(stoppkt.vps);
+ return ret;
+}
+
+
+/*
+ * Timeout handler (10 secs)
+ */
+static volatile int got_alrm;
+static void alrm_handler(int s)
+{
+ (void)s;
+ got_alrm = 1;
+}
+
+/*
+ * Check one terminal server to see if a user is logged in.
+ */
+int rad_check_ts(uint32_t nasaddr, int portnum, const char *user,
+ const char *session_id)
+{
+ int pid, st, e;
+ int n;
+ NAS *nas;
+ char address[16];
+ char port[8];
+ void (*handler)(int);
+
+ /*
+ * Find NAS type.
+ */
+ if ((nas = nas_find(nasaddr)) == NULL) {
+ radlog(L_ERR, "Accounting: unknown NAS");
+ return -1;
+ }
+
+ /*
+ * Fork.
+ */
+ handler = signal(SIGCHLD, SIG_DFL);
+ if ((pid = fork()) < 0) {
+ radlog(L_ERR, "Accounting: fork: %s", strerror(errno));
+ signal(SIGCHLD, handler);
+ return -1;
+ }
+
+ if (pid > 0) {
+ /*
+ * Parent - Wait for checkrad to terminate.
+ * We timeout in 10 seconds.
+ */
+ got_alrm = 0;
+ signal(SIGALRM, alrm_handler);
+ alarm(10);
+ while((e = waitpid(pid, &st, 0)) != pid)
+ if (e < 0 && (errno != EINTR || got_alrm))
+ break;
+ alarm(0);
+ signal(SIGCHLD, handler);
+ if (got_alrm) {
+ kill(pid, SIGTERM);
+ sleep(1);
+ kill(pid, SIGKILL);
+ radlog(L_ERR, "Check-TS: timeout waiting for checkrad");
+ return 2;
+ }
+ if (e < 0) {
+ radlog(L_ERR, "Check-TS: unknown error in waitpid()");
+ return 2;
+ }
+ return WEXITSTATUS(st);
+ }
+
+ /*
+ * Child - exec checklogin with the right parameters.
+ */
+ for (n = 32; n >= 3; n--)
+ close(n);
+
+ ip_ntoa(address, nasaddr);
+ sprintf(port, "%d", portnum);
+
+#ifdef __EMX__
+ /* OS/2 can't directly execute scripts then we call the command
+ processor to execute checkrad
+ */
+ execl(getenv("COMSPEC"), "", "/C","checkrad",nas->nastype, address, port,
+ user, session_id, NULL);
+#else
+ execl(CHECKRAD, "checkrad",nas->nastype, address, port,
+ user, session_id, NULL);
+#endif
+ radlog(L_ERR, "Check-TS: exec %s: %s", CHECKRAD, strerror(errno));
+
+ /*
+ * Exit - 2 means "some error occured".
+ */
+ exit(2);
+}
NULL, /* authentication */
NULL, /* preaccounting */
unique_accounting, /* accounting */
+ NULL, /* checksimul */
unique_detach, /* detach */
NULL, /* destroy */
};
NULL, /* authentication */
NULL, /* preaccounting */
detail_accounting, /* accounting */
+ NULL, /* checksimul */
detail_detach, /* detach */
NULL /* destroy */
};
NULL, /* authentication */
NULL, /* preaccounting */
NULL, /* accounting */
+ NULL, /* checksimul */
NULL, /* detach */
NULL /* destroy */
};
return RLM_MODULE_OK;
}
+/*
+ * See if a user is already logged in. Sets request->simul_count to the
+ * current session count for this user and sets request->simul_mpp to 2
+ * if it looks like a multilink attempt based on the requested IP
+ * address, otherwise leaves request->simul_mpp alone.
+ *
+ * Check twice. If on the first pass the user exceeds his
+ * max. number of logins, do a second pass and validate all
+ * logins by querying the terminal server (using eg. SNMP).
+ */
+static int radius_checksimul(void *instance, REQUEST *request)
+{
+ instance = instance;
+
+ request->simul_count=0;
+
+ return RLM_MODULE_OK;
+}
+
static int radius_detach(void *instance)
{
free(((struct example_config_t *)instance)->string);
radius_authenticate, /* authentication */
radius_preacct, /* preaccounting */
radius_accounting, /* accounting */
+ radius_checksimul, /* checksimul */
radius_detach, /* detach */
NULL, /* destroy */
};
file_authenticate, /* authentication */
file_preacct, /* preaccounting */
NULL, /* accounting */
+ NULL, /* checksimul */
file_detach, /* detach */
NULL /* destroy */
};
rlm_ldap_authenticate, /* authentication */
NULL, /* preaccounting */
NULL, /* accounting */
+ NULL, /* checksimul */
rlm_ldap_detach, /* detach */
NULL, /* destroy */
};
pam_auth, /* authenticate */
NULL, /* pre-accounting */
NULL, /* accounting */
+ NULL, /* checksimul */
NULL, /* detach */
NULL, /* destroy */
};
NULL, /* authentication */
preprocess_preaccounting, /* pre-accounting */
NULL, /* accounting */
+ NULL, /* checksimul */
NULL, /* detach */
preprocess_destroy, /* destroy */
};
NULL, /* authentication */
realm_preacct, /* preaccounting */
NULL, /* accounting */
+ NULL, /* checksimul */
NULL, /* detach */
NULL, /* destroy */
};
rlm_sql_authenticate, /* authentication */
NULL, /* preaccounting */
rlm_sql_accounting, /* accounting */
+ NULL, /* checksimul */
NULL, /* detach */
rlm_sql_destroy, /* destroy */
};
unix_authenticate, /* authentication */
NULL, /* preaccounting */
unix_accounting, /* accounting */
+ NULL, /* checksimul */
unix_detach, /* detach */
unix_destroy, /* destroy */
};