devnull = open("/dev/null", O_RDWR);
if (devnull < 0) {
RDEBUG("Failed opening /dev/null: %s\n", strerror(errno));
-
- exit(1);
+
+ /*
+ * Where the status code is interpreted as a module rcode
+ * one is subtracted from it, to allow 0 to equal success
+ *
+ * 2 is RLM_MODULE_FAIL + 1
+ */
+ exit(2);
}
/*
*/
execve(argv[0], argv, envp);
printf("Failed to execute \"%s\": %s", argv[0], strerror(errno)); /* fork output will be captured */
- exit(1);
+
+ /*
+ * Where the status code is interpreted as a module rcode
+ * one is subtracted from it, to allow 0 to equal success
+ *
+ * 2 is RLM_MODULE_FAIL + 1
+ */
+ exit(2);
}
/*
/** Execute a program.
*
- * @param cmd Command to execute. This is parsed into argv[] parts, then each individual argv part
- * is xlat'ed.
* @param[in] request Current request.
+ * @param[in] cmd Command to execute. This is parsed into argv[] parts, then each individual argv part
+ * is xlat'ed.
* @param[in] exec_wait set to 1 if you want to read from or write to child.
+ * @param[in] shell_escape values before passing them as arguments.
* @param[in] user_msg buffer to append plaintext (non valuepair) output.
* @param[in] msg_len length of user_msg buffer.
* @param[in] input_pairs list of value pairs - these will be available in the environment of the child.
* @param[out] output_pairs list of value pairs - child stdout will be parsed and added into this list
* of value pairs.
- * @param shell_escape values before passing them as arguments.
* @return 0 if exec_wait==0, exit code if exec_wait!=0, -1 on error.
*/
-int radius_exec_program(char const *cmd, REQUEST *request,
- int exec_wait,
- char *user_msg, int msg_len,
- VALUE_PAIR *input_pairs,
- VALUE_PAIR **output_pairs,
- int shell_escape)
+int radius_exec_program(REQUEST *request, char const *cmd, bool exec_wait, bool shell_escape,
+ char *user_msg, size_t msg_len,
+ VALUE_PAIR *input_pairs, VALUE_PAIR **output_pairs)
+
{
pid_t pid;
int from_child;
* have called close(from_child) for us
*/
DEBUG("Failed to read from child output");
- return 1;
+ return -1;
}
- answer[done] = 0;
+ answer[done] = '\0';
/*
* Make sure that the writer can't block while writing to
if (userparse(request, answer, &vp) == T_OP_INVALID) {
REDEBUG("Unparsable reply from '%s'", cmd);
+
+ return -1;
} else {
/*
* Tell the caller about the value
child_pid = rad_waitpid(pid, &status);
if (child_pid == 0) {
REDEBUG("Timeout waiting for child");
- return 2;
+
+ return -2;
}
if (child_pid == pid) {
if (WIFEXITED(status)) {
status = WEXITSTATUS(status);
- if (status != 0) {
- REDEBUG("Program returned error code(%d): %s", status, answer);
- return status;
- }
- RDEBUG("Program executed successfully");
- RDEBUG2("Program output is \"%s\"", answer);
- return 0;
+ RDEBUG("Program returned code (%d): %s", status, answer);
+
+ return status;
}
}
REDEBUG("Abnormal child exit: %s", strerror(errno));
#endif /* __MINGW32__ */
- return 1;
+ return -1;
}
{ NULL, -1, 0, NULL, NULL } /* end the list */
};
+static char const special[] = "\\'\"`<>|; \t\r\n()[]?#$^&*=";
+
+/*
+ * Escape special characters
+ */
+static size_t rlm_exec_shell_escape(UNUSED REQUEST *request, char *out, size_t outlen, char const *in,
+ UNUSED void *inst)
+{
+ char *q, *end;
+ char const *p;
+
+ q = out;
+ end = out + outlen;
+ p = in;
+
+ while (*p) {
+ if ((q + 3) >= end) break;
+
+ if (strchr(special, *p) != NULL) {
+ *(q++) = '\\';
+ }
+ *(q++) = *(p++);
+ }
+
+ *q = '\0';
+ return q - out;
+}
+
+/** Process the exit code returned by one of the exec functions
+ *
+ * @param request Current request.
+ * @param answer Output string from exec call.
+ * @param len length of data in answer.
+ * @param status code returned by exec call.
+ * @return One of the RLM_MODULE_* values.
+ */
+static rlm_rcode_t rlm_exec_status2rcode(REQUEST *request, char *answer, size_t len, int status)
+{
+ if (status < 0) {
+ return RLM_MODULE_FAIL;
+ }
+
+ /*
+ * Exec'd programs are meant to return exit statuses that correspond
+ * to the standard RLM_MODULE_* + 1.
+ *
+ * This frees up 0, for success where it'd normally be reject.
+ */
+ if (status == 0) {
+ RDEBUG("Program executed successfully");
+
+ return RLM_MODULE_OK;
+ }
+
+ if (status > RLM_MODULE_NUMCODES) {
+ REDEBUG("Program returned invalid code (greater than max rcode) (%i > %i): %s",
+ status, RLM_MODULE_NUMCODES, answer);
+ goto fail;
+
+ return RLM_MODULE_FAIL;
+ }
+
+ status--; /* Lets hope no one ever re-enumerates RLM_MODULE_* */
+
+ if (status == RLM_MODULE_FAIL) {
+ fail:
+
+ if (len > 0) {
+ char *p = &answer[len - 1];
+
+ /*
+ * Trim off trailing returns
+ */
+ while((p > answer) && ((*p == '\r') || (*p == '\n'))) {
+ *p-- = '\0';
+ }
+
+ module_failure_msg(request, answer);
+ }
+
+ return RLM_MODULE_FAIL;
+ }
+
+ return status;
+}
/*
* Do xlat of strings.
*/
-static size_t exec_xlat(void *instance, REQUEST *request,
- char const *fmt, char *out, size_t outlen)
+static size_t exec_xlat(void *instance, REQUEST *request, char const *fmt, char *out, size_t outlen)
{
int result;
rlm_exec_t *inst = instance;
/*
* FIXME: Do xlat of program name?
*/
- result = radius_exec_program(fmt, request,
- inst->wait, out, outlen,
- input_pairs ? *input_pairs : NULL, NULL, inst->shell_escape);
+ result = radius_exec_program(request, fmt, inst->wait, inst->shell_escape,
+ out, outlen, input_pairs ? *input_pairs : NULL, NULL);
if (result != 0) {
out[0] = '\0';
return 0;
return strlen(out);
}
-static char const special[] = "\\'\"`<>|; \t\r\n()[]?#$^&*=";
-
-/*
- * Escape special characters
- */
-static size_t shell_escape(UNUSED REQUEST *request, char *out, size_t outlen, char const *in, UNUSED void *inst)
-{
- char *q, *end;
- char const *p;
-
- q = out;
- end = out + outlen;
- p = in;
-
- while (*p) {
- if ((q + 3) >= end) break;
-
- if (strchr(special, *p) != NULL) {
- *(q++) = '\\';
- }
- *(q++) = *(p++);
- }
-
- *q = '\0';
- return q - out;
-}
-
-
/*
* Do any per-module initialization that is separate to each
* configured instance of the module. e.g. set up connections
inst->bare = 1;
}
- xlat_register(inst->xlat_name, exec_xlat, shell_escape, inst);
+ xlat_register(inst->xlat_name, exec_xlat, rlm_exec_shell_escape, inst);
/*
* Check whether program actually exists
*/
if (!inst->wait &&
(inst->output != NULL)) {
- cf_log_err_cs(conf, "Cannot read output pairs if "
- "wait=no");
+ cf_log_err_cs(conf, "Cannot read output pairs if wait = no");
return -1;
}
*/
static rlm_rcode_t exec_dispatch(void *instance, REQUEST *request)
{
- rlm_exec_t *inst = (rlm_exec_t *)instance;
- int result;
+ rlm_exec_t *inst = (rlm_exec_t *)instance;
+ rlm_rcode_t rcode;
+ int status;
+
VALUE_PAIR **input_pairs = NULL, **output_pairs = NULL;
VALUE_PAIR *answer = NULL;
char out[1024];
- size_t len;
/*
* We need a program to execute.
#endif
)) {
RDEBUG2("Packet type is not %s. Not executing.", inst->packet_type);
+
return RLM_MODULE_NOOP;
}
* exec program function xlat's it's string value
* into something else.
*/
- result = radius_exec_program(inst->program, request,
- inst->wait, out, sizeof(out),
- input_pairs ? *input_pairs : NULL, &answer, inst->shell_escape);
- /*
- * Write any exec output to module failure message
- */
- if (*out) {
- /* Trim off returns and newlines */
- len = strlen(out);
- if (out[len - 1] == '\n' || out[len - 1] == '\r') {
- out[len - 1] = '\0';
- }
- }
-
- if (result < 0) {
- module_failure_msg(request, out);
-
- return RLM_MODULE_FAIL;
- }
+ status = radius_exec_program(request, inst->program, inst->wait, inst->shell_escape,
+ out, sizeof(out),
+ input_pairs ? *input_pairs : NULL, &answer);
+ rcode = rlm_exec_status2rcode(request, out, strlen(out), status);
/*
* Move the answer over to the output pairs.
if (output_pairs) {
pairmove(request, output_pairs, &answer);
}
-
pairfree(&answer);
-
- if (result == 0) {
- return RLM_MODULE_OK;
- }
- if (result > RLM_MODULE_NUMCODES) {
- module_failure_msg(request, out);
-
- return RLM_MODULE_FAIL;
- }
- return result - 1;
+ return rcode;
}
*/
static rlm_rcode_t mod_post_auth(void *instance, REQUEST *request)
{
- int result;
- int exec_wait = 0;
- VALUE_PAIR *vp, *tmp;
- rlm_exec_t *inst = (rlm_exec_t *) instance;
+ rlm_exec_t *inst = (rlm_exec_t *) instance;
+ rlm_rcode_t rcode;
+ int status;
+
+ char out[1024];
+ bool wait = false;
+ VALUE_PAIR *vp, *tmp;
vp = pairfind(request->reply->vps, PW_EXEC_PROGRAM, 0, TAG_ANY);
if (vp) {
- exec_wait = 0;
-
+ wait = false;
} else if ((vp = pairfind(request->reply->vps, PW_EXEC_PROGRAM_WAIT, 0, TAG_ANY)) != NULL) {
- exec_wait = 1;
+ wait = true;
}
if (!vp) {
- if (!inst->program) return RLM_MODULE_NOOP;
+ if (!inst->program) {
+ return RLM_MODULE_NOOP;
+ }
- return exec_dispatch(instance, request);
+ rcode = exec_dispatch(instance, request);
+ goto finish;
}
tmp = NULL;
- result = radius_exec_program(vp->vp_strvalue, request, exec_wait, NULL, 0, request->packet->vps, &tmp,
- inst->shell_escape);
+ status = radius_exec_program(request, vp->vp_strvalue, wait, inst->shell_escape,
+ out, sizeof(out),
+ request->packet->vps, &tmp);
+ rcode = rlm_exec_status2rcode(request, out, strlen(out), status);
/*
* Always add the value-pairs to the reply.
*/
pairmove(request->reply, &request->reply->vps, &tmp);
pairfree(&tmp);
-
- if (result < 0) {
- REDEBUG("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;
-
- REDEBUG("Login incorrect (external check said so)");
- return RLM_MODULE_REJECT;
+
+ finish:
+ switch (rcode) {
+ case RLM_MODULE_FAIL:
+ case RLM_MODULE_INVALID:
+ case RLM_MODULE_REJECT:
+ request->reply->code = PW_AUTHENTICATION_REJECT;
+ break;
+ default:
+ break;
}
- return RLM_MODULE_OK;
+ return rcode;
}
/*
*/
static rlm_rcode_t mod_accounting(void *instance, REQUEST *request)
{
- int result;
- int exec_wait = 0;
- VALUE_PAIR *vp;
- rlm_exec_t *inst = (rlm_exec_t *) instance;
+ rlm_exec_t *inst = (rlm_exec_t *) instance;
+ int status;
+
+ char out[1024];
+ bool wait = false;
+ VALUE_PAIR *vp;
/*
* The "bare" exec module takes care of handling
vp = pairfind(request->reply->vps, PW_EXEC_PROGRAM, 0, TAG_ANY);
if (vp) {
- exec_wait = 0;
+ wait = true;
} else if ((vp = pairfind(request->reply->vps, PW_EXEC_PROGRAM_WAIT, 0, TAG_ANY)) != NULL) {
- exec_wait = 1;
+ wait = false;
}
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;
+ status = radius_exec_program(request, vp->vp_strvalue, wait, inst->shell_escape,
+ out, sizeof(out),
+ request->packet->vps, NULL);
+ return rlm_exec_status2rcode(request, out, strlen(out), status);
}
/*