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;