From 12929e8cf09bf16ad59e04eaa20f31282bd58e30 Mon Sep 17 00:00:00 2001
From: Matt Johnston <matt@ucc.asn.au>
Date: Mon, 15 Sep 2008 14:04:55 +0000
Subject: [PATCH] - Add run_shell_command() function to run a "sh -c" command,
 handling lots of the work that exechild did (and can be shared by client -J
 option)

--HG--
extra : convert_revision : a15dfd8017af8212b3b227f18ce2539dd471f7f6
---
 common-session.c  |  9 +++++++++
 dbutil.c          | 45 +++++++++++++++++++++++++++++++++++++++++--
 dbutil.h          |  1 +
 session.h         |  1 +
 svr-chansession.c | 49 ++++-------------------------------------------
 5 files changed, 58 insertions(+), 47 deletions(-)

diff --git a/common-session.c b/common-session.c
index 30c0a58f..ea8b3f7f 100644
--- a/common-session.c
+++ b/common-session.c
@@ -414,3 +414,12 @@ static long select_timeout() {
 		ret = MIN(opts.keepalive_secs, ret);
 	return ret;
 }
+
+const char* get_user_shell() {
+	/* an empty shell should be interpreted as "/bin/sh" */
+	if (ses.authstate.pw_shell[0] == '\0') {
+		return "/bin/sh";
+	} else {
+		return ses.authstate.pw_shell;
+	}
+}
diff --git a/dbutil.c b/dbutil.c
index d10a6513..2dcae6b6 100644
--- a/dbutil.c
+++ b/dbutil.c
@@ -393,8 +393,7 @@ int connect_remote(const char* remotehost, const char* remoteport,
  * and the pid. exec_fn is the function that will actually execute the child process,
  * it will be run after the child has fork()ed, and is passed exec_data. */
 int spawn_command(void(*exec_fn)(void *user_data), void *exec_data,
-		int *ret_writefd, int *ret_readfd, int *ret_errfd, pid_t *ret_pid)
-{
+		int *ret_writefd, int *ret_readfd, int *ret_errfd, pid_t *ret_pid) {
 	int infds[2];
 	int outfds[2];
 	int errfds[2];
@@ -466,6 +465,48 @@ int spawn_command(void(*exec_fn)(void *user_data), void *exec_data,
 	}
 }
 
+/* Runs a command with "sh -c". Will close FDs (except stdin/stdout/stderr) and
+ * re-enabled SIGPIPE. If cmd is NULL, will run a login shell.
+ */
+void run_shell_command(const char* cmd, unsigned int maxfd, char* usershell) {
+	char * argv[4];
+	char * baseshell = NULL;
+	unsigned int i;
+
+	baseshell = basename(usershell);
+
+	if (cmd != NULL) {
+		argv[0] = baseshell;
+	} else {
+		/* a login shell should be "-bash" for "/bin/bash" etc */
+		int len = strlen(baseshell) + 2; /* 2 for "-" */
+		argv[0] = (char*)m_malloc(len);
+		snprintf(argv[0], len, "-%s", baseshell);
+	}
+
+	if (cmd != NULL) {
+		argv[1] = "-c";
+		argv[2] = cmd;
+		argv[3] = NULL;
+	} else {
+		/* construct a shell of the form "-bash" etc */
+		argv[1] = NULL;
+	}
+
+	/* Re-enable SIGPIPE for the executed process */
+	if (signal(SIGPIPE, SIG_DFL) == SIG_ERR) {
+		dropbear_exit("signal() error");
+	}
+
+	/* close file descriptors except stdin/stdout/stderr
+	 * Need to be sure FDs are closed here to avoid reading files as root */
+	for (i = 3; i <= maxfd; i++) {
+		m_close(i);
+	}
+
+	execv(usershell, argv);
+}
+
 /* Return a string representation of the socket address passed. The return
  * value is allocated with malloc() */
 unsigned char * getaddrstring(struct sockaddr_storage* addr, int withport) {
diff --git a/dbutil.h b/dbutil.h
index ca09b4a3..a70ed46d 100644
--- a/dbutil.h
+++ b/dbutil.h
@@ -51,6 +51,7 @@ int dropbear_listen(const char* address, const char* port,
 		int *socks, unsigned int sockcount, char **errstring, int *maxfd);
 int spawn_command(void(*exec_fn)(void *user_data), void *exec_data,
 		int *writefd, int *readfd, int *errfd, pid_t *pid);
+void run_shell_command(const char* cmd, unsigned int maxfd, char* usershell);
 int connect_remote(const char* remotehost, const char* remoteport,
 		int nonblocking, char ** errstring);
 char* getaddrhostname(struct sockaddr_storage * addr);
diff --git a/session.h b/session.h
index c6d6ef89..60c34220 100644
--- a/session.h
+++ b/session.h
@@ -47,6 +47,7 @@ void common_session_cleanup();
 void session_identification();
 void send_msg_ignore();
 
+const char* get_user_shell();
 
 /* Server */
 void svr_session(int sock, int childpipe, char *remotehost, char *addrstring);
diff --git a/svr-chansession.c b/svr-chansession.c
index 6c99e70f..060a235e 100644
--- a/svr-chansession.c
+++ b/svr-chansession.c
@@ -809,12 +809,8 @@ static void addchildpid(struct ChanSess *chansess, pid_t pid) {
 /* Clean up, drop to user privileges, set up the environment and execute
  * the command/shell. This function does not return. */
 static void execchild(void *user_data) {
-
-	char *argv[4];
-	char * usershell = NULL;
-	char * baseshell = NULL;
-	unsigned int i;
 	struct ChanSess *chansess = user_data;
+	char *usershell = NULL;
 
     /* with uClinux we'll have vfork()ed, so don't want to overwrite the
      * hostkey. can't think of a workaround to clear it */
@@ -827,12 +823,6 @@ static void execchild(void *user_data) {
 	reseedrandom();
 #endif
 
-	/* close file descriptors except stdin/stdout/stderr
-	 * Need to be sure FDs are closed here to avoid reading files as root */
-	for (i = 3; i <= (unsigned int)ses.maxfd; i++) {
-		m_close(i);
-	}
-
 	/* clear environment */
 	/* if we're debugging using valgrind etc, we need to keep the LD_PRELOAD
 	 * etc. This is hazardous, so should only be used for debugging. */
@@ -871,18 +861,11 @@ static void execchild(void *user_data) {
 		}
 	}
 
-	/* an empty shell should be interpreted as "/bin/sh" */
-	if (ses.authstate.pw_shell[0] == '\0') {
-		usershell = "/bin/sh";
-	} else {
-		usershell = ses.authstate.pw_shell;
-	}
-
 	/* set env vars */
 	addnewvar("USER", ses.authstate.pw_name);
 	addnewvar("LOGNAME", ses.authstate.pw_name);
 	addnewvar("HOME", ses.authstate.pw_dir);
-	addnewvar("SHELL", usershell);
+	addnewvar("SHELL", get_user_shell());
 	if (chansess->term != NULL) {
 		addnewvar("TERM", chansess->term);
 	}
@@ -901,32 +884,8 @@ static void execchild(void *user_data) {
 	agentset(chansess);
 #endif
 
-	/* Re-enable SIGPIPE for the executed process */
-	if (signal(SIGPIPE, SIG_DFL) == SIG_ERR) {
-		dropbear_exit("signal() error");
-	}
-
-	baseshell = basename(usershell);
-
-	if (chansess->cmd != NULL) {
-		argv[0] = baseshell;
-	} else {
-		/* a login shell should be "-bash" for "/bin/bash" etc */
-		int len = strlen(baseshell) + 2; /* 2 for "-" */
-		argv[0] = (char*)m_malloc(len);
-		snprintf(argv[0], len, "-%s", baseshell);
-	}
-
-	if (chansess->cmd != NULL) {
-		argv[1] = "-c";
-		argv[2] = chansess->cmd;
-		argv[3] = NULL;
-	} else {
-		/* construct a shell of the form "-bash" etc */
-		argv[1] = NULL;
-	}
-
-	execv(usershell, argv);
+	usershell = m_strdup(get_user_shell());
+	run_shell_command(chansess->cmd, ses.maxfd, usershell);
 
 	/* only reached on error */
 	dropbear_exit("child failed");
-- 
GitLab