⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 exec.c

📁 新的radius程序
💻 C
字号:
/* * exec.c	Execute external programs. * * Version:	$Id: exec.c,v 1.42.2.1.2.3 2007/02/09 10:27:57 aland Exp $ * *   This program is free software; you can redistribute it and/or modify *   it under the terms of the GNU General Public License as published by *   the Free Software Foundation; either version 2 of the License, or *   (at your option) any later version. * *   This program is distributed in the hope that it will be useful, *   but WITHOUT ANY WARRANTY; without even the implied warranty of *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the *   GNU General Public License for more details. * *   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 * * Copyright 2000  The FreeRADIUS server project * Copyright 2000  Michael J. Hartwick <hartwick@hartwick.com> */static const char rcsid[] = "$Id: exec.c,v 1.42.2.1.2.3 2007/02/09 10:27:57 aland Exp $";#include "autoconf.h"#include <sys/file.h>#include <stdlib.h>#include <string.h>#include <fcntl.h>#include <ctype.h>#include <unistd.h>#include <signal.h>#ifdef HAVE_SYS_WAIT_H#	include <sys/wait.h>#endif#ifndef WEXITSTATUS#	define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)#endif#ifndef WIFEXITED#	define WIFEXITED(stat_val) (((stat_val) & 255) == 0)#endif#include "radiusd.h"#include "rad_assert.h"/* *	Copy a quoted string. */static int rad_copy_string(char *to, const char *from){	int length = 0;	char quote = *from;	do {		if (*from == '\\') {			*(to++) = *(from++);			length++;		}		*(to++) = *(from++);		length++;	} while (*from && (*from != quote));	if (*from != quote) return -1; /* not properly quoted */	*(to++) = quote;	length++;	*to = '\0';	return length;}/* *	Copy a %{} string. */static int rad_copy_variable(char *to, const char *from){	int length = 0;	int sublen;	*(to++) = *(from++);	length++;	while (*from) {		switch (*from) {		case '"':		case '\'':			sublen = rad_copy_string(to, from);			if (sublen < 0) return sublen;			from += sublen;			to += sublen;			break;		case '}':	/* end of variable expansion */			*(to++) = *(from++);			*to = '\0';			length++;			return length; /* proper end of variable */		case '\\':			*(to++) = *(from++);			*(to++) = *(from++);			length += 2;			break;		case '%':	/* start of variable expansion */			if (from[1] == '{') {				*(to++) = *(from++);				length++;								sublen = rad_copy_variable(to, from);				if (sublen < 0) return sublen;				from += sublen;				to += sublen;				length += sublen;			} /* else FIXME: catch %%{ ?*/			/* FALL-THROUGH */			break;		default:			*(to++) = *(from++);			length++;			break;		}	} /* loop over the input string */	/*	 *	We ended the string before a trailing '}'	 */	return -1;}#define MAX_ARGV (256)/* *	Execute a program on successful authentication. *	Return 0 if exec_wait == 0. *	Return the exit code of the called program if exec_wait != 0. *	Return -1 on fork/other errors in the parent process. */int radius_exec_program(const char *cmd, REQUEST *request,			int exec_wait,			char *user_msg, int msg_len,			VALUE_PAIR *input_pairs,			VALUE_PAIR **output_pairs){	VALUE_PAIR *vp;	char mycmd[1024];	char answer[4096];	char argv_buf[4096];	char *argv[MAX_ARGV];	const char *from;	char *p, *to;	int pd[2];	pid_t pid, child_pid;	int argc = -1;	int comma = 0;	int status;	int i;	int n, left, done;	if (user_msg) *user_msg = '\0';	if (output_pairs) *output_pairs = NULL;	if (strlen(cmd) > (sizeof(mycmd) - 1)) {		radlog(L_ERR|L_CONS, "Command line is too long");		return -1;	}	/*	 *	Check for bad escapes.	 */	if (cmd[strlen(cmd) - 1] == '\\') {		radlog(L_ERR|L_CONS, "Command line has final backslash, without a following character");		return -1;	}	strNcpy(mycmd, cmd, sizeof(mycmd));	/*	 *	Split the string into argv's BEFORE doing radius_xlat...	 */	from = cmd;	to = mycmd;	argc = 0;	while (*from) {		int length;		/*		 *	Skip spaces.		 */		if ((*from == ' ') || (*from == '\t')) {			from++;			continue;		}		argv[argc] = to;		argc++;		if (argc >= (MAX_ARGV - 1)) break;		/*		 *	Copy the argv over to our buffer.		 */		while (*from && (*from != ' ') && (*from != '\t')) {			if (to >= mycmd + sizeof(mycmd) - 1) {				return -1; /* ran out of space */			}			switch (*from) {			case '"':			case '\'':				length = rad_copy_string(to, from);				if (length < 0) {					radlog(L_ERR|L_CONS, "Invalid string passed as argument for external program");					return -1;				}				from += length;				to += length;				break;			case '%':				if (from[1] == '{') {					*(to++) = *(from++);					length = rad_copy_variable(to, from);					if (length < 0) {						radlog(L_ERR|L_CONS, "Invalid variable expansion passed as argument for external program");						return -1;					}					from += length;					to += length;				} else { /* FIXME: catch %%{ ? */					*(to++) = *(from++);				}				break;			default:				*(to++) = *(from++);			}		} /* end of string, or found a space */		*(to++) = '\0';	/* terminate the string */	}	/*	 *	We have to have SOMETHING, at least.	 */	if (argc <= 0) {		radlog(L_ERR, "Exec-Program: empty command line.");		return -1;	}	/*	 *	Expand each string, as appropriate.	 */	to = argv_buf;	left = sizeof(argv_buf);	for (i = 0; i < argc; i++) {		int sublen;		/*		 *	Don't touch argv's which won't be translated.		 */		if (strchr(argv[i], '%') == NULL) continue;		sublen = radius_xlat(to, left - 1, argv[i], request, NULL);		if (sublen <= 0) {			/*			 *	Fail to be backwards compatible.			 *			 *	It's yucky, but it won't break anything,			 *	and it won't cause security problems.			 */			sublen = 0;		}		argv[i] = to;		to += sublen;		*(to++) = '\0';		left -= sublen;		left--;		if (left <= 0) {			radlog(L_ERR, "Exec-Program: Ran out of space while expanding arguments.");			return -1;		}	}	argv[argc] = NULL;	/*	 *	Open a pipe for child/parent communication, if	 *	necessary.	 */	if (exec_wait) {		if (pipe(pd) != 0) {			radlog(L_ERR|L_CONS, "Couldn't open pipe: %s",			       strerror(errno));			return -1;		}	} else {		/*		 *	We're not waiting, so we don't look for a		 *	message, or VP's.		 */		user_msg = NULL;		output_pairs = NULL;	}	if (exec_wait) {		pid = rad_fork();	/* remember PID */	} else {		pid = fork();		/* don't wait */	}	if (pid == 0) {#define MAX_ENVP 1024		int devnull;		char *envp[MAX_ENVP];		int envlen;		char buffer[1024];		/*		 *	Child process.		 *		 *	We try to be fail-safe here.  So if ANYTHING		 *	goes wrong, we exit with status 1.		 */		/*		 *	Open STDIN to /dev/null		 */		devnull = open("/dev/null", O_RDWR);		if (devnull < 0) {			radlog(L_ERR|L_CONS, "Failed opening /dev/null: %s\n",			       strerror(errno));			exit(1);		}		dup2(devnull, STDIN_FILENO);		/*		 *	Only massage the pipe handles if the parent		 *	has created them.		 */		if (exec_wait) {			/*			 *	pd[0] is the FD the child will read from,			 *	which we don't want.			 */			if (close(pd[0]) != 0) {				radlog(L_ERR|L_CONS, "Can't close pipe: %s",				       strerror(errno));				exit(1);			}			/*			 *	pd[1] is the FD that the child will write to,			 *	so we make it STDOUT.			 */			if (dup2(pd[1], STDOUT_FILENO) != 1) {				radlog(L_ERR|L_CONS, "Can't dup stdout: %s",				       strerror(errno));				exit(1);			}		} else {	/* no pipe, STDOUT should be /dev/null */			dup2(devnull, STDOUT_FILENO);		}		/*		 *	If we're not debugging, then we can't do		 *	anything with the error messages, so we throw		 *	them away.		 *		 *	If we are debugging, then we want the error		 *	messages to go to the STDERR of the server.		 */		if (debug_flag == 0) {			dup2(devnull, STDERR_FILENO);		}		close(devnull);		/*		 *	The server may have MANY FD's open.  We don't		 *	want to leave dangling FD's for the child process		 *	to play funky games with, so we close them.		 */		closefrom(3);		/*		 *	Set up the environment variables.		 *	We're in the child, and it will exit in 4 lines		 *	anyhow, so memory allocation isn't an issue.		 */		envlen = 0;		for (vp = input_pairs; vp != NULL; vp = vp->next) {			/*			 *	Hmm... maybe we shouldn't pass the			 *	user's password in an environment			 *	variable...			 */			snprintf(buffer, sizeof(buffer), "%s=", vp->name);			for (p = buffer; *p != '='; p++) {				if (*p == '-') {					*p = '_';				} else if (isalpha((int) *p)) {					*p = toupper(*p);				}			}			n = strlen(buffer);			vp_prints_value(buffer+n, sizeof(buffer) - n, vp, 1);			envp[envlen++] = strdup(buffer);			/*			 *	Don't add too many attributes.			 */			if (envlen == (MAX_ENVP - 1)) break;		}		envp[envlen] = NULL;		execve(argv[0], argv, envp);		radlog(L_ERR, "Exec-Program: FAILED to execute %s: %s",		       argv[0], strerror(errno));		exit(1);	}	/*	 *	Parent process.	 */	if (pid < 0) {		radlog(L_ERR|L_CONS, "Couldn't fork %s: %s",		       argv[0], strerror(errno));		return -1;	}	/*	 *	We're not waiting, exit, and ignore any child's	 *	status.	 */	if (!exec_wait) {		return 0;	}	/*	 *	Close the FD to which the child writes it's data.	 *	 *	If we can't close it, then we close pd[0], and return an	 *	error.	 */	if (close(pd[1]) != 0) {		radlog(L_ERR|L_CONS, "Can't close pipe: %s", strerror(errno));		close(pd[0]);		return -1;	}	/*	 *	Read from the pipe until we doesn't get any more or	 *	until the message is full.	 */	done = 0;	left = sizeof(answer) - 1;	while (1) {		status = read(pd[0], answer + done, left);		/*		 *	Nothing more to read: stop.		 */		if (status == 0) {			break;		}		/*		 *	Error: See if we have to continue.		 */		if (status < 0) {			/*			 *	We were interrupted: continue reading.			 */			if (errno == EINTR) {				continue;			}			/*			 *	There was another error.  Most likely			 *	The child process has finished, and			 *	exited.			 */			break;		}		done += status;		left -= status;		if (left <= 0) break;	}	answer[done] = 0;	/*	 *	Make sure that the writer can't block while writing to	 *	a pipe that no one is reading from anymore.	 */	close(pd[0]);	DEBUG2("Exec-Program output: %s", answer);	/*	 *	Parse the output, if any.	 */	if (done) {		n = T_INVALID;		if (output_pairs) {			/*			 *	For backwards compatibility, first check			 *	for plain text (user_msg).			 */			vp = NULL;			n = userparse(answer, &vp);			if (vp) {				pairfree(&vp);			}		}		if (n == T_INVALID) {			radlog(L_DBG, "Exec-Program-Wait: plaintext: %s", answer);			if (user_msg) {				strNcpy(user_msg, answer, msg_len);			}		} else {			/*			 *	HACK: Replace '\n' with ',' so that			 *	userparse() can parse the buffer in			 *	one go (the proper way would be to			 *	fix userparse(), but oh well).			 */			for (p = answer; *p; p++) {				if (*p == '\n') {					*p = comma ? ' ' : ',';					p++;					comma = 0;				}				if (*p == ',') comma++;			}			/*			 *  Replace any trailing comma by a NUL.			 */			if (answer[strlen(answer) - 1] == ',') {				answer[strlen(answer) - 1] = '\0';			}			radlog(L_DBG,"Exec-Program-Wait: value-pairs: %s", answer);			if (userparse(answer, &vp) == T_INVALID) {				radlog(L_ERR, "Exec-Program-Wait: %s: unparsable reply", cmd);			} else {				/*				 *	Tell the caller about the value				 *	pairs.				 */				*output_pairs = vp;			}		} /* else the answer was a set of VP's, not a text message */	} /* else we didn't read anything from the child. */	/*	 *	Call rad_waitpid (should map to waitpid on non-threaded	 *	or single-server systems).	 */	child_pid = rad_waitpid(pid, &status);	if (child_pid == 0) {		radlog(L_DBG, "Exec-Program: Timeout waiting for child");		return 2;	}	if (child_pid == pid) {		if (WIFEXITED(status)) {			status = WEXITSTATUS(status);			radlog(L_DBG, "Exec-Program: returned: %d", status);			return status;		}	}	radlog(L_ERR|L_CONS, "Exec-Program: Abnormal child exit: %s",	       strerror(errno));	return 1;}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -