/*
- * rlm_exe.c
+ * rlm_exec.c
*
* Version: $Id$
*
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*
- * Copyright 2002 The FreeRADIUS server project
+ * Copyright 2002,2006 The FreeRADIUS server project
* Copyright 2002 Alan DeKok <aland@ox.org>
*/
-#include "autoconf.h"
-#include "libradius.h"
+#include <freeradius-devel/ident.h>
+RCSID("$Id$")
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "radiusd.h"
-#include "modules.h"
-#include "conffile.h"
-
-static const char rcsid[] = "$Id$";
+#include <freeradius-devel/radiusd.h>
+#include <freeradius-devel/modules.h>
/*
* Define a structure for our module configuration.
*/
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;
/*
* to the strdup'd string into 'config.string'. This gets around
* buffer over-flows.
*/
-static CONF_PARSER module_config[] = {
+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, NULL },
+ 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 } /* end the list */
};
return &request->reply->vps;
}
+#ifdef WITH_PROXY
if (strcmp(string, "proxy-request") == 0) {
if (!request->proxy) return NULL;
return &request->proxy_reply->vps;
}
+#endif
if (strcmp(string, "config") == 0) {
return &request->config_items;
}
+ if (strcmp(string, "none") == 0) {
+ return NULL;
+ }
+
return NULL;
}
/*
* Do xlat of strings.
- */
-static int exec_xlat(void *instance, REQUEST *request,
- char *fmt, char *out, int outlen,
- RADIUS_ESCAPE_STRING func)
+ */
+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) {
/*
* FIXME: Do xlat of program name?
*/
- DEBUG2("rlm_exec (%s): Executing %s", inst->xlat_name, fmt);
+ RDEBUG2("Executing %s", fmt);
result = radius_exec_program(fmt, request, inst->wait,
- out, outlen, *input_pairs, NULL);
- DEBUG2("rlm_exec (%s): result %d", inst->xlat_name, result);
+ 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);
}
free(inst->xlat_name);
}
- /*
- * Free the strings.
- */
- if (inst->program) free(inst->program);
- if (inst->input) free(inst->input);
- if (inst->output) free(inst->output);
-
free(inst);
return 0;
}
static int exec_instantiate(CONF_SECTION *conf, void **instance)
{
rlm_exec_t *inst;
- char *xlat_name;
-
+ const char *xlat_name;
+
/*
* Set up a storage area for instance data
*/
-
+
inst = rad_malloc(sizeof(rlm_exec_t));
+ if (!inst)
+ return -1;
memset(inst, 0, sizeof(rlm_exec_t));
-
+
/*
* If the configuration parameters can't be parsed, then
* fail.
exec_detach(inst);
return -1;
}
-
+
/*
* No input pairs defined. Why are we executing a program?
*/
return -1;
}
+ /*
+ * Get the packet type on which to execute
+ */
+ if (!inst->packet_type) {
+ inst->packet_code = 0;
+ } else {
+ DICT_VALUE *dval;
+
+ dval = dict_valbyname(PW_PACKET_TYPE, 0, 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)
+ if (xlat_name == NULL) {
xlat_name = cf_section_name1(conf);
- if (xlat_name){
+ inst->bare = 1;
+ }
+ if (xlat_name){
inst->xlat_name = strdup(xlat_name);
- xlat_register(xlat_name, exec_xlat, inst);
- }
+ xlat_register(xlat_name, exec_xlat, inst);
+ }
*instance = inst;
-
+
return 0;
}
}
/*
+ * See if we're supposed to execute it now.
+ */
+ if (!((inst->packet_code == 0) ||
+ (request->packet->code == inst->packet_code) ||
+ (request->reply->code == inst->packet_code)
+#ifdef WITH_PROXY
+ || (request->proxy &&
+ (request->proxy->code == inst->packet_code)) ||
+ (request->proxy_reply &&
+ (request->proxy_reply->code == inst->packet_code))
+#endif
+ )) {
+ RDEBUG2("Packet type is not %s. Not executing.",
+ inst->packet_type);
+ return RLM_MODULE_NOOP;
+ }
+
+ /*
* Decide what input/output the program takes.
*/
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;
+ }
+
/*
- * We need a place to store the returned VP's
+ * It points to the attribute list, but the attribute
+ * list is empty.
*/
- if (!output_pairs) {
- radlog(L_ERR, "rlm_exec (%s): Nowhere to place output",
- inst->xlat_name);
- return RLM_MODULE_FAIL;
+ if (!*input_pairs) {
+ RDEBUG2("WARNING! Input pairs are empty. No attributes will be passed to the script");
}
-
+
/*
* This function does it's own xlat of the input program
* to execute.
*/
result = radius_exec_program(inst->program, request,
inst->wait, NULL, 0,
- *input_pairs, &answer);
- if (result != 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;
/*
* Move the answer over to the output pairs.
+ *
+ * If we're not waiting, then there are no output pairs.
*/
- pairmove(output_pairs, &answer);
+ 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;
+}
+
+
+/*
+ * First, look for Exec-Program && Exec-Program-Wait.
+ *
+ * Then, call exec_dispatch.
+ */
+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, 0);
+ if (vp) {
+ exec_wait = 0;
+
+ } else if ((vp = pairfind(request->reply->vps, PW_EXEC_PROGRAM_WAIT, 0)) != 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);
+
+ /*
+ * Always add the value-pairs to the reply.
+ */
+ pairmove(&request->reply->vps, &tmp);
+ pairfree(&tmp);
+
+ if (result < 0) {
+ /*
+ * Error. radius_exec_program() returns -1 on
+ * fork/exec errors.
+ */
+ 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) {
+ /*
+ * Reject. radius_exec_program() returns >0
+ * if the exec'ed program had a non-zero
+ * exit status.
+ */
+ request->reply->code = PW_AUTHENTICATION_REJECT;
+ RDEBUG2("Login incorrect (external check said so)");
+ return RLM_MODULE_REJECT;
+ }
+
return RLM_MODULE_OK;
}
+/*
+ * First, look for Exec-Program && Exec-Program-Wait.
+ *
+ * Then, call exec_dispatch.
+ */
+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;
+
+ /*
+ * The "bare" exec module takes care of handling
+ * Exec-Program and Exec-Program-Wait.
+ */
+ if (!inst->bare) return exec_dispatch(instance, request);
+
+ vp = pairfind(request->reply->vps, PW_EXEC_PROGRAM, 0);
+ if (vp) {
+ exec_wait = 0;
+
+ } else if ((vp = pairfind(request->reply->vps, PW_EXEC_PROGRAM_WAIT, 0)) != 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;
+}
/*
* The module name should be the only globally exported symbol.
* is single-threaded.
*/
module_t rlm_exec = {
+ RLM_MODULE_INIT,
"exec", /* Name */
- RLM_TYPE_THREAD_SAFE, /* type */
- NULL, /* initialization */
+ RLM_TYPE_CHECK_CONFIG_SAFE, /* type */
exec_instantiate, /* instantiation */
+ exec_detach, /* detach */
{
exec_dispatch, /* authentication */
exec_dispatch, /* authorization */
exec_dispatch, /* pre-accounting */
- exec_dispatch, /* accounting */
+ exec_accounting, /* accounting */
NULL, /* check simul */
exec_dispatch, /* pre-proxy */
exec_dispatch, /* post-proxy */
- exec_dispatch /* post-auth */
+ exec_postauth /* post-auth */
+#ifdef WITH_COA
+ , exec_dispatch,
+ exec_dispatch
+#endif
},
- exec_detach, /* detach */
- NULL, /* destroy */
};