heads up: tcpwrappers support going away

Damien Miller djm at mindrot.org
Thu Apr 24 08:58:02 EST 2014


On Wed, 23 Apr 2014, Damien Miller wrote:

> A simple way out of this would be adding "Match exec" support to sshd_config
> like ssh_config got in the last couple of releases. Anyone want to do this?

like this:

Index: servconf.c
===================================================================
RCS file: /cvs/src/usr.bin/ssh/servconf.c,v
retrieving revision 1.249
diff -u -p -r1.249 servconf.c
--- servconf.c	29 Jan 2014 06:18:35 -0000	1.249
+++ servconf.c	23 Apr 2014 22:56:54 -0000
@@ -50,6 +50,7 @@
 #include "packet.h"
 #include "hostfile.h"
 #include "auth.h"
+#include "misc.h"
 
 static void add_listen_addr(ServerOptions *, char *, int);
 static void add_one_listen_addr(ServerOptions *, char *, int);
@@ -604,9 +605,10 @@ out:
 static int
 match_cfg_line(char **condition, int line, struct connection_info *ci)
 {
-	int result = 1, attributes = 0, port;
-	char *arg, *attrib, *cp = *condition;
+	int r, result = 1, attributes = 0, port;
+	char *arg, *attrib, *cmd, *cp = *condition;
 	size_t len;
+	char thishost[NI_MAXHOST], shorthost[NI_MAXHOST], portstr[NI_MAXSERV];
 
 	if (ci == NULL)
 		debug3("checking syntax for 'Match %s'", cp);
@@ -717,6 +719,45 @@ match_cfg_line(char **condition, int lin
 				    ci->laddress, port, line);
 			else
 				result = 0;
+		} else if (strcasecmp(attrib, "exec") == 0) {
+			if (ci == NULL) {
+				result = 0;
+				continue;
+			}
+			if (gethostname(thishost, sizeof(thishost)) == -1)
+				fatal("gethostname: %s", strerror(errno));
+			strlcpy(shorthost, thishost, sizeof(shorthost));
+			shorthost[strcspn(thishost, ".")] = '\0';
+			snprintf(portstr, sizeof(portstr), "%d", ci->lport);
+
+			cmd = percent_expand(arg,
+			    "L", shorthost,
+			    "l", thishost,
+			    "h", ci->host,
+			    "P", portstr,
+			    "u", ci->user,
+			    "a", ci->address,
+			    "A", ci->laddress,
+			    (char *)NULL);
+			if (result != 1) {
+				/* skip execution if prior predicate failed */
+				debug("config line %d: skipped exec \"%.100s\"",
+				    line, cmd);
+			} else {
+				r = execute_in_shell(cmd, 0);
+				if (r == -1) {
+					fatal("config line %d: match exec "
+					    "'%.100s' error", line, cmd);
+				} else if (r == 0) {
+					debug("config line %d: matched "
+					    "'exec \"%.100s\"'", line, cmd);
+				} else {
+					debug("config line %d: no match "
+					    "'exec \"%.100s\"'", line, cmd);
+					result = 0;
+				}
+			}
+			free(cmd);
 		} else {
 			error("Unsupported Match attribute %s", attrib);
 			return -1;
Index: readconf.c
===================================================================
RCS file: /cvs/src/usr.bin/ssh/readconf.c,v
retrieving revision 1.219
diff -u -p -r1.219 readconf.c
--- readconf.c	23 Apr 2014 12:42:34 -0000	1.219
+++ readconf.c	23 Apr 2014 22:56:55 -0000
@@ -15,7 +15,6 @@
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <sys/socket.h>
-#include <sys/wait.h>
 
 #include <netinet/in.h>
 #include <netinet/in_systm.h>
@@ -27,7 +26,6 @@
 #include <netdb.h>
 #include <paths.h>
 #include <pwd.h>
-#include <signal.h>
 #include <stdio.h>
 #include <string.h>
 #include <unistd.h>
@@ -46,7 +44,6 @@
 #include "buffer.h"
 #include "kex.h"
 #include "mac.h"
-#include "uidswap.h"
 
 /* Format of the configuration file:
 
@@ -376,80 +373,6 @@ default_ssh_port(void)
 }
 
 /*
- * Execute a command in a shell.
- * Return its exit status or -1 on abnormal exit.
- */
-static int
-execute_in_shell(const char *cmd)
-{
-	char *shell, *command_string;
-	pid_t pid;
-	int devnull, status;
-	extern uid_t original_real_uid;
-
-	if ((shell = getenv("SHELL")) == NULL)
-		shell = _PATH_BSHELL;
-
-	/*
-	 * Use "exec" to avoid "sh -c" processes on some platforms
-	 * (e.g. Solaris)
-	 */
-	xasprintf(&command_string, "exec %s", cmd);
-
-	/* Need this to redirect subprocess stdin/out */
-	if ((devnull = open(_PATH_DEVNULL, O_RDWR)) == -1)
-		fatal("open(/dev/null): %s", strerror(errno));
-
-	debug("Executing command: '%.500s'", cmd);
-
-	/* Fork and execute the command. */
-	if ((pid = fork()) == 0) {
-		char *argv[4];
-
-		/* Child.  Permanently give up superuser privileges. */
-		permanently_drop_suid(original_real_uid);
-
-		/* Redirect child stdin and stdout. Leave stderr */
-		if (dup2(devnull, STDIN_FILENO) == -1)
-			fatal("dup2: %s", strerror(errno));
-		if (dup2(devnull, STDOUT_FILENO) == -1)
-			fatal("dup2: %s", strerror(errno));
-		if (devnull > STDERR_FILENO)
-			close(devnull);
-		closefrom(STDERR_FILENO + 1);
-
-		argv[0] = shell;
-		argv[1] = "-c";
-		argv[2] = command_string;
-		argv[3] = NULL;
-
-		execv(argv[0], argv);
-		error("Unable to execute '%.100s': %s", cmd, strerror(errno));
-		/* Die with signal to make this error apparent to parent. */
-		signal(SIGTERM, SIG_DFL);
-		kill(getpid(), SIGTERM);
-		_exit(1);
-	}
-	/* Parent. */
-	if (pid < 0)
-		fatal("%s: fork: %.100s", __func__, strerror(errno));
-
-	close(devnull);
-	free(command_string);
-
-	while (waitpid(pid, &status, 0) == -1) {
-		if (errno != EINTR && errno != EAGAIN)
-			fatal("%s: waitpid: %s", __func__, strerror(errno));
-	}
-	if (!WIFEXITED(status)) {
-		error("command '%.100s' exited abnormally", cmd);
-		return -1;
-	} 
-	debug3("command returned status %d", WEXITSTATUS(status));
-	return WEXITSTATUS(status);
-}
-
-/*
  * Parse and execute a Match directive.
  */
 static int
@@ -461,6 +384,7 @@ match_cfg_line(Options *options, char **
 	int r, port, result = 1, attributes = 0;
 	size_t len;
 	char thishost[NI_MAXHOST], shorthost[NI_MAXHOST], portstr[NI_MAXSERV];
+	extern uid_t original_real_uid;
 
 	/*
 	 * Configuration is likely to be incomplete at this point so we
@@ -544,7 +468,7 @@ match_cfg_line(Options *options, char **
 				debug("%.200s line %d: skipped exec \"%.100s\"",
 				    filename, linenum, cmd);
 			} else {
-				r = execute_in_shell(cmd);
+				r = execute_in_shell(cmd, original_real_uid);
 				if (r == -1) {
 					fatal("%.200s line %d: match exec "
 					    "'%.100s' error", filename,
Index: misc.c
===================================================================
RCS file: /cvs/src/usr.bin/ssh/misc.c,v
retrieving revision 1.93
diff -u -p -r1.93 misc.c
--- misc.c	20 Apr 2014 02:30:25 -0000	1.93
+++ misc.c	23 Apr 2014 22:56:55 -0000
@@ -28,6 +28,7 @@
 #include <sys/ioctl.h>
 #include <sys/socket.h>
 #include <sys/param.h>
+#include <sys/wait.h>
 
 #include <net/if.h>
 #include <netinet/in.h>
@@ -41,6 +42,7 @@
 #include <netdb.h>
 #include <paths.h>
 #include <pwd.h>
+#include <signal.h>
 #include <stdarg.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -1019,3 +1021,80 @@ lowercase(char *s)
 	for (; *s; s++)
 		*s = tolower((u_char)*s);
 }
+
+/*
+ * Execute a command in a shell.
+ * Return its exit status or -1 on abnormal exit.
+ */
+int
+execute_in_shell(const char *cmd, uid_t drop_uid)
+{
+	char *shell, *command_string;
+	pid_t pid;
+	int devnull, status;
+
+	if ((shell = getenv("SHELL")) == NULL)
+		shell = _PATH_BSHELL;
+
+	/*
+	 * Use "exec" to avoid "sh -c" processes on some platforms
+	 * (e.g. Solaris)
+	 */
+	xasprintf(&command_string, "exec %s", cmd);
+
+	/* Need this to redirect subprocess stdin/out */
+	if ((devnull = open(_PATH_DEVNULL, O_RDWR)) == -1)
+		fatal("open(/dev/null): %s", strerror(errno));
+
+	debug("Executing command: '%.500s'", cmd);
+
+	/* Fork and execute the command. */
+	if ((pid = fork()) == 0) {
+		char *argv[4];
+
+		/* Child.  Permanently give up superuser privileges. */
+		if (drop_uid != 0 &&
+		    setresuid(drop_uid, drop_uid, drop_uid) != 0)
+			fatal("%s: setresuid %lu: %s", __func__,
+			    (u_long)drop_uid, strerror(errno));
+
+		/* Redirect child stdin and stdout. Leave stderr */
+		if (dup2(devnull, STDIN_FILENO) == -1)
+			fatal("dup2: %s", strerror(errno));
+		if (dup2(devnull, STDOUT_FILENO) == -1)
+			fatal("dup2: %s", strerror(errno));
+		if (devnull > STDERR_FILENO)
+			close(devnull);
+		closefrom(STDERR_FILENO + 1);
+
+		argv[0] = shell;
+		argv[1] = "-c";
+		argv[2] = command_string;
+		argv[3] = NULL;
+
+		execv(argv[0], argv);
+		error("Unable to execute '%.100s': %s", cmd, strerror(errno));
+		/* Die with signal to make this error apparent to parent. */
+		signal(SIGTERM, SIG_DFL);
+		kill(getpid(), SIGTERM);
+		_exit(1);
+	}
+	/* Parent. */
+	if (pid < 0)
+		fatal("%s: fork: %.100s", __func__, strerror(errno));
+
+	close(devnull);
+	free(command_string);
+
+	while (waitpid(pid, &status, 0) == -1) {
+		if (errno != EINTR && errno != EAGAIN)
+			fatal("%s: waitpid: %s", __func__, strerror(errno));
+	}
+	if (!WIFEXITED(status)) {
+		error("command '%.100s' exited abnormally", cmd);
+		return -1;
+	} 
+	debug3("command returned status %d", WEXITSTATUS(status));
+	return WEXITSTATUS(status);
+}
+
Index: misc.h
===================================================================
RCS file: /cvs/src/usr.bin/ssh/misc.h,v
retrieving revision 1.52
diff -u -p -r1.52 misc.h
--- misc.h	20 Apr 2014 02:30:25 -0000	1.52
+++ misc.h	23 Apr 2014 22:56:55 -0000
@@ -37,6 +37,7 @@ void	 ms_subtract_diff(struct timeval *,
 void	 ms_to_timeval(struct timeval *, int);
 time_t	 monotime(void);
 void	 lowercase(char *s);
+int	 execute_in_shell(const char *, uid_t);
 
 struct passwd *pwcopy(struct passwd *);
 const char *ssh_gai_strerror(int);


More information about the openssh-unix-dev mailing list