Enable building #WITHOUT_PROXY
[freeradius.git] / src / modules / rlm_exec / rlm_exec.c
index e100add..bf62521 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * 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;
 
 /*
@@ -54,14 +51,17 @@ typedef struct 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 */
 };
 
@@ -88,6 +88,7 @@ static VALUE_PAIR **decode_string(REQUEST *request, const char *string)
                return &request->reply->vps;
        }
 
+#ifdef WITH_PROXY
        if (strcmp(string, "proxy-request") == 0) {
                if (!request->proxy) return NULL;
 
@@ -99,25 +100,31 @@ static VALUE_PAIR **decode_string(REQUEST *request, const char *string)
 
                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) {
@@ -130,15 +137,19 @@ static int exec_xlat(void *instance, REQUEST *request,
        /*
         *      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);
 }
 
@@ -155,13 +166,6 @@ static int exec_detach(void *instance)
                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;
 }
@@ -180,15 +184,17 @@ static int exec_detach(void *instance)
 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.
@@ -198,7 +204,7 @@ static int exec_instantiate(CONF_SECTION *conf, void **instance)
                exec_detach(inst);
                return -1;
        }
-       
+
        /*
         *      No input pairs defined.  Why are we executing a program?
         */
@@ -219,16 +225,35 @@ static int exec_instantiate(CONF_SECTION *conf, void **instance)
                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;
 }
 
@@ -253,20 +278,43 @@ static int exec_dispatch(void *instance, REQUEST *request)
        }
 
        /*
+        *      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.
@@ -280,8 +328,8 @@ static int exec_dispatch(void *instance, REQUEST *request)
         */
        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;
@@ -289,14 +337,122 @@ static int exec_dispatch(void *instance, REQUEST *request)
 
        /*
         *      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.
@@ -308,20 +464,23 @@ static int exec_dispatch(void *instance, REQUEST *request)
  *     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 */
 };