/*
 *	First, look for Exec-Program && Exec-Program-Wait.
 *
 *	Then, call exec_dispatch.
 */
static rlm_rcode_t 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, TAG_ANY);
	if (vp) {
		exec_wait = 0;

	} else if ((vp = pairfind(request->reply->vps, PW_EXEC_PROGRAM_WAIT, 0, TAG_ANY)) != 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) {
		RDEBUG2("%s", module_failure_msg(request, "rlm_exec (%s): "
						 "Login incorrect (external "
						 "check failed)",
						 inst->xlat_name));

		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("%s", module_failure_msg(request, "rlm_exec (%s): "
						 "Login incorrect (external "
						 "check said so)",
						 inst->xlat_name));
		return RLM_MODULE_REJECT;
	}

	return RLM_MODULE_OK;
}
Example #2
0
/** 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, "%s", answer);
		}

		return RLM_MODULE_FAIL;
	}

	return status;
}
/*
 *  Dispatch an exec method
 */
static rlm_rcode_t exec_dispatch(void *instance, REQUEST *request)
{
	rlm_exec_t *inst = (rlm_exec_t *) instance;
	int result;
	VALUE_PAIR	**input_pairs, **output_pairs;
	VALUE_PAIR	*answer = NULL;
	char		msg[1024];
	size_t		len;

	/*
	 *	We need a program to execute.
	 */
	if (!inst->program) {
		radlog(L_ERR, "rlm_exec (%s): We require a program to execute",
		       inst->xlat_name);
		return RLM_MODULE_FAIL;
	}

	/*
	 *	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;
	}

	/*
	 *	It points to the attribute list, but the attribute
	 *	list is empty.
	 */
	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.
	 *
	 *	FIXME: if inst->program starts with %{, then
	 *	do an xlat ourselves.  This will allow us to do
	 *	program = %{Exec-Program}, which this module
	 *	xlat's into it's string value, and then the
	 *	exec program function xlat's it's string value
	 *	into something else.
	 */
	result = radius_exec_program(inst->program, request,
				     inst->wait, msg, sizeof(msg),
				     *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.
	 */
	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;
	}
	
	/*
	 *	Write any exec output to module failure message
	 */
	if (*msg) {
		/* Trim off returns and newlines */
		len = strlen(msg);
		if (msg[len - 1] == '\n' || msg[len - 1] == '\r') {
			msg[len - 1] = '\0';
		}
		
		module_failure_msg(request, "rlm_exec (%s): %s",
				   inst->xlat_name, msg);
	}
	
	return result-1;
}