#include <freeradius-devel/ident.h>
RCSID("$Id$")
#include <freeradius-devel/radiusd.h>
#include <freeradius-devel/modules.h>
typedef struct rlm_exec_t {
char *xlat_name;
int bare;
int wait;
char *program;
char *input;
char *output;
char *packet_type;
unsigned int packet_code;
int shell_escape;
} rlm_exec_t;
static const CONF_PARSER module_config[] = {
{ "wait", PW_TYPE_BOOLEAN, offsetof(rlm_exec_t,wait), NULL, "yes" },
{ "program", PW_TYPE_STRING_PTR,
offsetof(rlm_exec_t,program), NULL, NULL },
{ "input_pairs", PW_TYPE_STRING_PTR,
offsetof(rlm_exec_t,input), NULL, "request" },
{ "output_pairs", PW_TYPE_STRING_PTR,
offsetof(rlm_exec_t,output), NULL, NULL },
{ "packet_type", PW_TYPE_STRING_PTR,
offsetof(rlm_exec_t,packet_type), NULL, NULL },
{ "shell_escape", PW_TYPE_BOOLEAN, offsetof(rlm_exec_t,shell_escape), NULL, "yes" },
{ NULL, -1, 0, NULL, NULL }
};
static VALUE_PAIR **decode_string(REQUEST *request, const char *string)
{
if (!string) return NULL;
if (strcmp(string, "request") == 0) {
return &request->packet->vps;
}
if (strcmp(string, "reply") == 0) {
if (!request->reply) return NULL;
return &request->reply->vps;
}
if (strcmp(string, "proxy-request") == 0) {
if (!request->proxy) return NULL;
return &request->proxy->vps;
}
if (strcmp(string, "proxy-reply") == 0) {
if (!request->proxy_reply) return NULL;
return &request->proxy_reply->vps;
}
if (strcmp(string, "config") == 0) {
return &request->config_items;
}
if (strcmp(string, "none") == 0) {
return NULL;
}
return NULL;
}
static size_t exec_xlat(void *instance, REQUEST *request,
char *fmt, char *out, size_t outlen,
UNUSED RADIUS_ESCAPE_STRING func)
{
int result;
rlm_exec_t *inst = instance;
VALUE_PAIR **input_pairs;
char *p;
input_pairs = decode_string(request, inst->input);
if (!input_pairs) {
radlog(L_ERR, "rlm_exec (%s): Failed to find input pairs for xlat",
inst->xlat_name);
out[0] = '\0';
return 0;
}
RDEBUG2("Executing %s", fmt);
result = radius_exec_program(fmt, request, inst->wait,
out, outlen, *input_pairs, NULL, inst->shell_escape);
RDEBUG2("result %d", result);
if (result != 0) {
out[0] = '\0';
return 0;
}
for (p = out; *p != '\0'; p++) {
if (*p < ' ') *p = ' ';
}
return strlen(out);
}
static int exec_detach(void *instance)
{
rlm_exec_t *inst = instance;
if (inst->xlat_name) {
xlat_unregister(inst->xlat_name, exec_xlat);
free(inst->xlat_name);
}
free(inst);
return 0;
}
static int exec_instantiate(CONF_SECTION *conf, void **instance)
{
rlm_exec_t *inst;
const char *xlat_name;
inst = rad_malloc(sizeof(rlm_exec_t));
if (!inst)
return -1;
memset(inst, 0, sizeof(rlm_exec_t));
if (cf_section_parse(conf, inst, module_config) < 0) {
radlog(L_ERR, "rlm_exec: Failed parsing the configuration");
exec_detach(inst);
return -1;
}
if (!inst->input) {
radlog(L_ERR, "rlm_exec: Must define input pairs for external program.");
exec_detach(inst);
return -1;
}
if (!inst->wait &&
(inst->output != NULL)) {
radlog(L_ERR, "rlm_exec: Cannot read output pairs if wait=no");
exec_detach(inst);
return -1;
}
if (!inst->packet_type) {
inst->packet_code = 0;
} else {
DICT_VALUE *dval;
dval = dict_valbyname(PW_PACKET_TYPE, inst->packet_type);
if (!dval) {
radlog(L_ERR, "rlm_exec: Unknown packet type %s: See list of VALUEs for Packet-Type in share/dictionary", inst->packet_type);
exec_detach(inst);
return -1;
}
inst->packet_code = dval->value;
}
xlat_name = cf_section_name2(conf);
if (xlat_name == NULL) {
xlat_name = cf_section_name1(conf);
inst->bare = 1;
}
if (xlat_name){
inst->xlat_name = strdup(xlat_name);
xlat_register(xlat_name, exec_xlat, inst);
}
*instance = inst;
return 0;
}
static int exec_dispatch(void *instance, REQUEST *request)
{
int result;
VALUE_PAIR **input_pairs, **output_pairs;
VALUE_PAIR *answer;
rlm_exec_t *inst = (rlm_exec_t *) instance;
if (!inst->program) {
radlog(L_ERR, "rlm_exec (%s): We require a program to execute",
inst->xlat_name);
return RLM_MODULE_FAIL;
}
if (!((inst->packet_code == 0) ||
(request->packet->code == inst->packet_code) ||
(request->reply->code == inst->packet_code) ||
(request->proxy &&
(request->proxy->code == inst->packet_code)) ||
(request->proxy_reply &&
(request->proxy_reply->code == inst->packet_code)))) {
RDEBUG2("Packet type is not %s. Not executing.",
inst->packet_type);
return RLM_MODULE_NOOP;
}
input_pairs = decode_string(request, inst->input);
output_pairs = decode_string(request, inst->output);
if (!input_pairs) {
RDEBUG2("WARNING: Possible parse error in %s",
inst->input);
return RLM_MODULE_NOOP;
}
if (!*input_pairs) {
RDEBUG2("WARNING! Input pairs are empty. No attributes will be passed to the script");
}
result = radius_exec_program(inst->program, request,
inst->wait, NULL, 0,
*input_pairs, &answer, inst->shell_escape);
if (result < 0) {
radlog(L_ERR, "rlm_exec (%s): External script failed",
inst->xlat_name);
return RLM_MODULE_FAIL;
}
if (output_pairs) pairmove(output_pairs, &answer);
pairfree(&answer);
if (result == 0) {
return RLM_MODULE_OK;
}
if (result > RLM_MODULE_NUMCODES) {
return RLM_MODULE_FAIL;
}
return result-1;
}
static int exec_postauth(void *instance, REQUEST *request)
{
int result;
int exec_wait = 0;
VALUE_PAIR *vp, *tmp;
rlm_exec_t *inst = (rlm_exec_t *) instance;
vp = pairfind(request->reply->vps, PW_EXEC_PROGRAM);
if (vp) {
exec_wait = 0;
} else if ((vp = pairfind(request->reply->vps, PW_EXEC_PROGRAM_WAIT)) != NULL) {
exec_wait = 1;
}
if (!vp) {
if (!inst->program) return RLM_MODULE_NOOP;
return exec_dispatch(instance, request);
}
tmp = NULL;
result = radius_exec_program(vp->vp_strvalue, request, exec_wait,
NULL, 0, request->packet->vps, &tmp,
inst->shell_escape);
pairmove(&request->reply->vps, &tmp);
pairfree(&tmp);
if (result < 0) {
tmp = pairmake("Reply-Message", "Access denied (external check failed)", T_OP_SET);
pairadd(&request->reply->vps, tmp);
RDEBUG2("Login incorrect (external check failed)");
request->reply->code = PW_AUTHENTICATION_REJECT;
return RLM_MODULE_REJECT;
}
if (result > 0) {
request->reply->code = PW_AUTHENTICATION_REJECT;
RDEBUG2("Login incorrect (external check said so)");
return RLM_MODULE_REJECT;
}
return RLM_MODULE_OK;
}
static int exec_accounting(void *instance, REQUEST *request)
{
int result;
int exec_wait = 0;
VALUE_PAIR *vp;
rlm_exec_t *inst = (rlm_exec_t *) instance;
if (!inst->bare) return exec_dispatch(instance, request);
vp = pairfind(request->reply->vps, PW_EXEC_PROGRAM);
if (vp) {
exec_wait = 0;
} else if ((vp = pairfind(request->reply->vps, PW_EXEC_PROGRAM_WAIT)) != NULL) {
exec_wait = 1;
}
if (!vp) return RLM_MODULE_NOOP;
result = radius_exec_program(vp->vp_strvalue, request, exec_wait,
NULL, 0, request->packet->vps, NULL,
inst->shell_escape);
if (result != 0) {
return RLM_MODULE_REJECT;
}
return RLM_MODULE_OK;
}
module_t rlm_exec = {
RLM_MODULE_INIT,
"exec",
RLM_TYPE_CHECK_CONFIG_SAFE,
exec_instantiate,
exec_detach,
{
exec_dispatch,
exec_dispatch,
exec_dispatch,
exec_accounting,
NULL,
exec_dispatch,
exec_dispatch,
exec_postauth
#ifdef WITH_COA
, exec_dispatch,
exec_dispatch
#endif
},
};