Module Name:    src
Committed By:   pooka
Date:           Mon Feb 14 14:56:23 UTC 2011

Modified Files:
        src/lib/librumpclient: rumpclient.c rumpclient.h
        src/lib/librumphijack: hijack.c
        src/lib/librumpuser: sp_common.c

Log Message:
A bunch of changes which essentially make sshd work with a hijacked
rump tcp/ip stack:

* sshd likes to fork and then re-exec itself
  ==> trap execve() and augment the env with the current parameters
      essential to a rump kernel (kernel communication fd, information
      about dup2'd file descriptors)

* sshd likes to play lots of games with pipes, socketpairs and dup{,2}()
  ==> make sure we do not close essential rump client descriptors:
      dup() them to a safe place, except for F_CLOSEM where we
      simply leave them alone.  also, partially solved by the above,
      make sure the process's set of rump kernel descriptors persists
      over exec()

* sshd likes to chdir() before exec
  ==> for unix-style rump_sp(7) sockets save the full path on the
      initial exec and use it afterwards.  thread the path through
      the environment in execve()


To generate a diff of this commit:
cvs rdiff -u -r1.27 -r1.28 src/lib/librumpclient/rumpclient.c
cvs rdiff -u -r1.5 -r1.6 src/lib/librumpclient/rumpclient.h
cvs rdiff -u -r1.38 -r1.39 src/lib/librumphijack/hijack.c
cvs rdiff -u -r1.26 -r1.27 src/lib/librumpuser/sp_common.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/lib/librumpclient/rumpclient.c
diff -u src/lib/librumpclient/rumpclient.c:1.27 src/lib/librumpclient/rumpclient.c:1.28
--- src/lib/librumpclient/rumpclient.c:1.27	Wed Feb  9 14:29:58 2011
+++ src/lib/librumpclient/rumpclient.c	Mon Feb 14 14:56:23 2011
@@ -1,4 +1,4 @@
-/*      $NetBSD: rumpclient.c,v 1.27 2011/02/09 14:29:58 pooka Exp $	*/
+/*      $NetBSD: rumpclient.c,v 1.28 2011/02/14 14:56:23 pooka Exp $	*/
 
 /*
  * Copyright (c) 2010, 2011 Antti Kantee.  All Rights Reserved.
@@ -198,6 +198,10 @@
 					rv = host_kevent(kq, NULL, 0,
 					    kev, __arraycount(kev), NULL);
 
+					if (__predict_false(rv == -1)) {
+						goto cleanup;
+					}
+
 					/*
 					 * XXX: don't know how this can
 					 * happen (timeout cannot expire
@@ -546,15 +550,19 @@
 
 /* dup until we get a "good" fd which does not collide with stdio */
 static int
-dupgood(int myfd)
+dupgood(int myfd, int mustchange)
 {
-	int ofds[3];
+	int ofds[4];
 	int i;
 
-	for (i = 0; myfd <= 2 && myfd != -1; i++) {
+	for (i = 0; (myfd <= 2 || mustchange) && myfd != -1; i++) {
 		assert(i < __arraycount(ofds));
 		ofds[i] = myfd;
 		myfd = host_dup(myfd);
+		if (mustchange) {
+			i--; /* prevent closing old fd */
+			mustchange = 0;
+		}
 	}
 
 	for (i--; i >= 0; i--) {
@@ -611,7 +619,7 @@
 	free(clispc.spc_buf);
 	clispc.spc_off = 0;
 
-	s = dupgood(host_socket(parsetab[ptab_idx].domain, SOCK_STREAM, 0));
+	s = dupgood(host_socket(parsetab[ptab_idx].domain, SOCK_STREAM, 0), 0);
 	if (s == -1)
 		return -1;
 
@@ -666,7 +674,7 @@
 	clispc.spc_reconnecting = 0;
 
 	/* setup kqueue, we want all signals and the fd */
-	if ((kq = dupgood(host_kqueue())) == -1) {
+	if ((kq = dupgood(host_kqueue(), 0)) == -1) {
 		error = errno;
 		if (noisy)
 			fprintf(stderr, "rump_sp: cannot setup kqueue");
@@ -742,9 +750,11 @@
 #undef	FINDSYM
 #undef	FINDSY2
 
-	if ((p = getenv("RUMP_SERVER")) == NULL) {
-		errno = ENOENT;
-		return -1;
+	if ((p = getenv("RUMP__PARSEDSERVER")) == NULL) {
+		if ((p = getenv("RUMP_SERVER")) == NULL) {
+			errno = ENOENT;
+			return -1;
+		}
 	}
 
 	if ((error = parseurl(p, &serv_sa, &ptab_idx, 0)) != 0) {
@@ -754,6 +764,13 @@
 
 	if (doinit() == -1)
 		return -1;
+
+	if ((p = getenv("RUMPCLIENT__EXECFD")) != NULL) {
+		sscanf(p, "%d,%d", &clispc.spc_fd, &kq);
+		unsetenv("RUMPCLIENT__EXECFD");
+		return 0;
+	}
+
 	if (doconnect(true) == -1)
 		return -1;
 
@@ -839,3 +856,126 @@
 
 	retrytimo = timeout;
 }
+
+int
+rumpclient__closenotify(int *fdp, enum rumpclient_closevariant variant)
+{
+	int fd = *fdp;
+	int untilfd, rv;
+	int newfd;
+
+	switch (variant) {
+	case RUMPCLIENT_CLOSE_FCLOSEM:
+		untilfd = MAX(clispc.spc_fd, kq);
+		for (; fd <= untilfd; fd++) {
+			if (fd == clispc.spc_fd || fd == kq)
+				continue;
+			rv = host_close(fd);
+			if (rv == -1)
+				return -1;
+		}
+		*fdp = fd;
+		break;
+
+	case RUMPCLIENT_CLOSE_CLOSE:
+	case RUMPCLIENT_CLOSE_DUP2:
+		if (fd == clispc.spc_fd) {
+			struct kevent kev[2];
+
+			newfd = dupgood(clispc.spc_fd, 1);
+			if (newfd == -1)
+				return -1;
+			/*
+			 * now, we have a new socket number, so change
+			 * the file descriptor that kqueue is
+			 * monitoring.  remove old and add new.
+			 */
+			EV_SET(&kev[0], clispc.spc_fd,
+			    EVFILT_READ, EV_DELETE, 0, 0, 0);
+			EV_SET(&kev[1], newfd,
+			    EVFILT_READ, EV_ADD|EV_ENABLE, 0, 0, 0);
+			if (host_kevent(kq, kev, 2, NULL, 0, NULL) == -1) {
+				int sverrno = errno;
+				host_close(newfd);
+				errno = sverrno;
+				return -1;
+			}
+			clispc.spc_fd = newfd;
+		}
+		if (fd == kq) {
+			newfd = dupgood(kq, 1);
+			if (newfd == -1)
+				return -1;
+			kq = newfd;
+		}
+		break;
+	}
+
+	return 0;
+}
+
+/*
+ * Process is about to exec.  Save info about our existing connection
+ * in the env.  rumpclient will check for this info in init().
+ * This is mostly for the benefit of rumphijack, but regular applications
+ * may use it as well.
+ */
+int
+rumpclient__exec_augmentenv(char *const oenv1[], char *const oenv2[],
+	char ***newenvp)
+{
+	char buf[4096];
+	char **newenv;
+	char *envstr, *envstr2;
+	size_t nelem1, nelem2;
+
+	snprintf(buf, sizeof(buf), "RUMPCLIENT__EXECFD=%d,%d",
+	    clispc.spc_fd, kq);
+	envstr = malloc(strlen(buf)+1);
+	if (envstr == NULL) {
+		return ENOMEM;
+	}
+	strcpy(envstr, buf);
+
+	/* do we have a fully parsed url we want to forward in the env? */
+	if (*parsedurl != '\0') {
+		snprintf(buf, sizeof(buf),
+		    "RUMP__PARSEDSERVER=%s", parsedurl);
+		envstr2 = malloc(strlen(buf)+1);
+		if (envstr2 == NULL) {
+			free(envstr);
+			return ENOMEM;
+		}
+		strcpy(envstr2, buf);
+	} else {
+		envstr2 = NULL;
+	}
+
+	nelem1 = 0;
+	if (oenv1) {
+		for (; oenv1[nelem1]; nelem1++)
+			continue;
+	}
+	nelem2 = 0;
+	if (oenv2) {
+		for (; oenv2[nelem2]; nelem2++)
+			continue;
+	}
+
+	newenv = malloc(sizeof(*newenv) * nelem1+nelem2+3);
+	if (newenv == NULL) {
+		free(envstr2);
+		free(envstr);
+		return ENOMEM;
+	}
+	memcpy(&newenv[0], oenv1, sizeof(*oenv1) * nelem1);
+	memcpy(&newenv[nelem1], oenv2, sizeof(*oenv2) * nelem2);
+
+	newenv[nelem1+nelem2] = envstr;
+	newenv[nelem1+nelem2+1] = envstr2;
+	newenv[nelem1+nelem2+2] = NULL;
+
+	*newenvp = newenv;
+
+	return 0;
+}

Index: src/lib/librumpclient/rumpclient.h
diff -u src/lib/librumpclient/rumpclient.h:1.5 src/lib/librumpclient/rumpclient.h:1.6
--- src/lib/librumpclient/rumpclient.h:1.5	Mon Feb  7 14:49:32 2011
+++ src/lib/librumpclient/rumpclient.h	Mon Feb 14 14:56:23 2011
@@ -1,4 +1,4 @@
-/*	$NetBSD: rumpclient.h,v 1.5 2011/02/07 14:49:32 pooka Exp $	*/
+/*	$NetBSD: rumpclient.h,v 1.6 2011/02/14 14:56:23 pooka Exp $	*/
 
 /*-
  * Copyright (c) 2010 Antti Kantee.  All Rights Reserved.
@@ -44,6 +44,14 @@
 #define RUMPCLIENT_RETRYCONN_DIE ((time_t)-3)
 void rumpclient_setconnretry(time_t);
 
+enum rumpclient_closevariant {
+	RUMPCLIENT_CLOSE_CLOSE,
+	RUMPCLIENT_CLOSE_DUP2,
+	RUMPCLIENT_CLOSE_FCLOSEM
+};
+int rumpclient__closenotify(int *, enum rumpclient_closevariant);
+int rumpclient__exec_augmentenv(char *const[], char *const[], char ***);
+
 __END_DECLS
 
 #endif /* _RUMP_RUMPCLIENT_H_ */

Index: src/lib/librumphijack/hijack.c
diff -u src/lib/librumphijack/hijack.c:1.38 src/lib/librumphijack/hijack.c:1.39
--- src/lib/librumphijack/hijack.c:1.38	Sat Feb 12 10:25:46 2011
+++ src/lib/librumphijack/hijack.c	Mon Feb 14 14:56:23 2011
@@ -1,4 +1,4 @@
-/*      $NetBSD: hijack.c,v 1.38 2011/02/12 10:25:46 pooka Exp $	*/
+/*      $NetBSD: hijack.c,v 1.39 2011/02/14 14:56:23 pooka Exp $	*/
 
 /*-
  * Copyright (c) 2011 Antti Kantee.  All Rights Reserved.
@@ -26,7 +26,7 @@
  */
 
 #include <sys/cdefs.h>
-__RCSID("$NetBSD: hijack.c,v 1.38 2011/02/12 10:25:46 pooka Exp $");
+__RCSID("$NetBSD: hijack.c,v 1.39 2011/02/14 14:56:23 pooka Exp $");
 
 #define __ssp_weak_name(fun) _hijack_ ## fun
 
@@ -140,9 +140,10 @@
 
 pid_t	(*host_fork)(void);
 int	(*host_daemon)(int, int);
+int	(*host_execve)(const char *, char *const[], char *const[]);
 
 static unsigned dup2mask;
-#define ISDUP2D(fd) (1<<(fd) & dup2mask)
+#define ISDUP2D(fd) ((fd < 32) && (1<<(fd) & dup2mask))
 
 //#define DEBUGJACK
 #ifdef DEBUGJACK
@@ -217,6 +218,7 @@
 	rumpclient_dlsym = hijackdlsym;
 	host_fork = dlsym(RTLD_NEXT, "fork");
 	host_daemon = dlsym(RTLD_NEXT, "daemon");
+	host_execve = dlsym(RTLD_NEXT, "execve");
 
 	/*
 	 * In theory cannot print anything during lookups because
@@ -273,6 +275,10 @@
 			rumpclient_setconnretry(timeout);
 		}
 	}
+
+	if (getenv_r("RUMPHIJACK__DUP2MASK", buf, sizeof(buf)) == 0) {
+		dup2mask = atoi(buf);
+	}
 }
 
 /* XXX: need runtime selection.  low for now due to FD_SETSIZE */
@@ -397,6 +403,10 @@
 		op_fcntl = GETSYSCALL(rump, FCNTL);
 	} else {
 		op_fcntl = GETSYSCALL(host, FCNTL);
+		if (cmd == F_CLOSEM)
+			if (rumpclient__closenotify(&fd,
+			    RUMPCLIENT_CLOSE_FCLOSEM) == -1)
+				return -1;
 	}
 
 	va_start(ap, cmd);
@@ -405,6 +415,33 @@
 	return rv;
 }
 
+int
+close(int fd)
+{
+	int (*op_close)(int);
+	int rv;
+
+	DPRINTF(("close -> %d\n", fd));
+	if (fd_isrump(fd)) {
+		int undup2 = 0;
+
+		if (ISDUP2D(fd))
+			undup2 = 1;
+		fd = fd_host2rump(fd);
+		op_close = GETSYSCALL(rump, CLOSE);
+		rv = op_close(fd);
+		if (rv == 0 && undup2)
+			dup2mask &= ~(1 << fd);
+	} else {
+		if (rumpclient__closenotify(&fd, RUMPCLIENT_CLOSE_CLOSE) == -1)
+			return -1;
+		op_close = GETSYSCALL(host, CLOSE);
+		rv = op_close(fd);
+	}
+
+	return rv;
+}
+
 /*
  * write cannot issue a standard debug printf due to recursion
  */
@@ -447,6 +484,8 @@
 			dup2mask |= 1<<newd;
 	} else {
 		host_dup2 = syscalls[DUALCALL_DUP2].bs_host;
+		if (rumpclient__closenotify(&newd, RUMPCLIENT_CLOSE_DUP2) == -1)
+			return -1;
 		rv = host_dup2(oldd, newd);
 	}
 
@@ -458,17 +497,21 @@
 {
 	int (*op_dup)(int);
 	int newd;
+	int isrump;
 
 	DPRINTF(("dup -> %d\n", oldd));
 	if (fd_isrump(oldd)) {
 		op_dup = GETSYSCALL(rump, DUP);
+		oldd = fd_host2rump(oldd);
+		isrump = 1;
 	} else {
 		op_dup = GETSYSCALL(host, DUP);
+		isrump = 0;
 	}
 
 	newd = op_dup(oldd);
 
-	if (fd_isrump(oldd))
+	if (isrump)
 		newd = fd_rump2host(newd);
 	DPRINTF(("dup <- %d\n", newd));
 
@@ -524,6 +567,35 @@
 	return 0;
 }
 
+int
+execve(const char *path, char *const argv[], char *const oenvp[])
+{
+	char buf[128];
+	char **env;
+	char *dup2maskenv[2];
+	char *dup2str;
+	int rv;
+
+	snprintf(buf, sizeof(buf), "RUMPHIJACK__DUP2MASK=%d", dup2mask);
+	dup2str = malloc(strlen(buf)+1);
+	if (dup2str == NULL)
+		return ENOMEM;
+	strcpy(dup2str, buf);
+	dup2maskenv[0] = dup2str;
+	dup2maskenv[1] = NULL;
+
+	rv = rumpclient__exec_augmentenv(oenvp, dup2maskenv, &env);
+	if (rv)
+		return rv;
+
+	rv = host_execve(path, argv, env);
+	if (rv != 0) {
+		free(dup2str);
+		free(env); /* XXX missing some strings within env */
+	}
+	return rv;
+}
+
 /*
  * select is done by calling poll.
  */
@@ -760,6 +832,7 @@
 				pfd_host[i].fd = fds[i].fd;
 				pfd_host[i].events = fds[i].events;
 			}
+			pfd_rump[i].revents = pfd_host[i].revents = 0;
 			fds[i].revents = 0;
 		}
 
@@ -991,8 +1064,3 @@
 	(int fd, const struct iovec *iov, int iovcnt),			\
 	(int, const struct iovec *, int),				\
 	(fd, iov, iovcnt))
-
-FDCALL(int, close, DUALCALL_CLOSE,	 				\
-	(int fd),							\
-	(int),								\
-	(fd))

Index: src/lib/librumpuser/sp_common.c
diff -u src/lib/librumpuser/sp_common.c:1.26 src/lib/librumpuser/sp_common.c:1.27
--- src/lib/librumpuser/sp_common.c:1.26	Mon Jan 24 17:47:52 2011
+++ src/lib/librumpuser/sp_common.c	Mon Feb 14 14:56:23 2011
@@ -1,4 +1,4 @@
-/*      $NetBSD: sp_common.c,v 1.26 2011/01/24 17:47:52 pooka Exp $	*/
+/*      $NetBSD: sp_common.c,v 1.27 2011/02/14 14:56:23 pooka Exp $	*/
 
 /*
  * Copyright (c) 2010, 2011 Antti Kantee.  All Rights Reserved.
@@ -560,12 +560,15 @@
 	return 0;
 }
 
+static char parsedurl[256];
+
 /*ARGSUSED*/
 static int
 unix_parse(const char *addr, struct sockaddr **sa, int allow_wildcard)
 {
 	struct sockaddr_un sun;
 	size_t slen;
+	int savepath = 0;
 
 	if (strlen(addr) > sizeof(sun.sun_path))
 		return ENAMETOOLONG;
@@ -590,12 +593,18 @@
 				return ENAMETOOLONG;
 			strlcpy(sun.sun_path, mywd, sizeof(sun.sun_path));
 			strlcat(sun.sun_path, "/", sizeof(sun.sun_path));
+			savepath = 1;
 		}
 	}
 	strlcat(sun.sun_path, addr, sizeof(sun.sun_path));
 	sun.sun_len = SUN_LEN(&sun);
 	slen = sun.sun_len+1; /* get the 0 too */
 
+	if (savepath && *parsedurl == '\0') {
+		snprintf(parsedurl, sizeof(parsedurl),
+		    "unix://%s", sun.sun_path);
+	}
+
 	*sa = malloc(slen);
 	if (*sa == NULL)
 		return errno;

Reply via email to