Enable building #WITHOUT_PROXY
[freeradius.git] / src / modules / rlm_exec / rlm_exec.c
index bb25a7a..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;
-       int     packet_code;
+       unsigned int    packet_code;
        int     shell_escape;
 } rlm_exec_t;
 
@@ -57,7 +51,7 @@ 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 },
@@ -94,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;
 
@@ -105,6 +100,7 @@ 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;
@@ -121,13 +117,14 @@ static VALUE_PAIR **decode_string(REQUEST *request, const char *string)
 /*
  *     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) {
@@ -140,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, inst->shell_escape);
-       DEBUG2("rlm_exec (%s): result %d", inst->xlat_name, result);
+       RDEBUG2("result %d", result);
        if (result != 0) {
                out[0] = '\0';
                return 0;
        }
 
+       for (p = out; *p != '\0'; p++) {
+               if (*p < ' ') *p = ' ';
+       }
+
        return strlen(out);
 }
 
@@ -165,14 +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);
-       if (inst->packet_type) free(inst->packet_type);
-
        free(inst);
        return 0;
 }
@@ -191,7 +184,7 @@ 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
@@ -233,15 +226,6 @@ static int exec_instantiate(CONF_SECTION *conf, void **instance)
        }
 
        /*
-        *      Sanity check the config.  If we're told to wait,
-        *      then the output pairs should be defined.
-        */
-       if (inst->wait &&
-           (inst->output == NULL)) {
-               radlog(L_INFO, "rlm_exec: Wait=yes but no output defined. Did you mean output=none?");
-       }
-
-       /*
         *      Get the packet type on which to execute
         */
        if (!inst->packet_type) {
@@ -249,7 +233,7 @@ static int exec_instantiate(CONF_SECTION *conf, void **instance)
        } else {
                DICT_VALUE      *dval;
 
-               dval = dict_valbyname(PW_PACKET_TYPE, inst->packet_type);
+               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);
@@ -259,8 +243,10 @@ static int exec_instantiate(CONF_SECTION *conf, void **instance)
        }
 
        xlat_name = cf_section_name2(conf);
-       if (xlat_name == NULL)
+       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);
@@ -296,13 +282,16 @@ static int exec_dispatch(void *instance, REQUEST *request)
         */
        if (!((inst->packet_code == 0) ||
              (request->packet->code == inst->packet_code) ||
-             (request->reply->code == inst->packet_code) ||
-             (request->proxy &&
+             (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)))) {
-               DEBUG2("  rlm_exec (%s): Packet type is not %s.  Not executing.",
-                      inst->xlat_name, inst->packet_type);
+              (request->proxy_reply->code == inst->packet_code))
+#endif
+                   )) {
+               RDEBUG2("Packet type is not %s.  Not executing.",
+                      inst->packet_type);
                return RLM_MODULE_NOOP;
        }
 
@@ -312,12 +301,18 @@ static int exec_dispatch(void *instance, REQUEST *request)
        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;
+       }
+
        /*
         *      It points to the attribute list, but the attribute
         *      list is empty.
         */
-       if (input_pairs && !*input_pairs) {
-               DEBUG2("rlm_exec (%s): WARNING! Input pairs are empty.  No attributes will be passed to the script", inst->xlat_name);
+       if (!*input_pairs) {
+               RDEBUG2("WARNING! Input pairs are empty.  No attributes will be passed to the script");
        }
 
        /*
@@ -360,6 +355,106 @@ static int exec_dispatch(void *instance, REQUEST *request)
 
 
 /*
+ *     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.
  *     That is, everything else should be 'static'.
  *
@@ -369,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 */
 };