#include #include #include #include void newchannel(int, char *, int); char *get_string(char *, char *); char *confine(char *, char *); void runcmd(int, int, char *, char *, char *, char *); int errfd, slfd, toppid, sflag, tflag, prevent; char *shell; char *restdir; char *srvpt; char *nsfile = nil; char *uname; void usage(void) { fprint(2, "usage: sshsession [-s shell] [-r restdir] [-R restdir] [-S srvpt] [-n namespace] [-t]\n"); exits("usage"); } main(int argc, char *argv[]) { char *netdir, *filnam, *p, *q; int ctlfd, fd, n; char buf[128]; rfork(RFNOTEG); toppid = getpid(); errfd = create("/tmp/ssh.err", OWRITE, 0664); slfd = open("/dev/syslog", OWRITE); shell = "/bin/rc -il"; ARGBEGIN { case 'n': nsfile = EARGF(usage()); break; case 'R': prevent = 1; case 'r': restdir = EARGF(usage()); break; case 's': sflag = 1; shell = EARGF(usage()); break; case 't': tflag = 1; break; case 'S': srvpt = EARGF(usage()); break; default: usage(); break; } ARGEND; uname = getenv("user"); if (uname == nil) uname = "none"; netdir = getenv("net"); fprint(errfd, "net is %s\n", netdir); filnam = smprint("%s/clone", netdir); ctlfd = open(filnam, ORDWR); if (ctlfd < 0) { fprint(errfd, "could not clone: %s: %r\n", filnam); exits(nil); } free(filnam); filnam = smprint("%s/data", netdir); fd = open(filnam, OREAD); if (fd < 0) { fprint(errfd, "Couldn't open data: %r\n"); fprint(ctlfd, "hangup"); exits(nil); } n = read(fd, buf, 128); close(fd); free(filnam); if (n < 0) { fprint(errfd, "Read error for cap: %r\n"); fprint(ctlfd, "hangup"); exits(nil); } else if (n > 0) { buf[n] = '\0'; if (strcmp(buf, "n/a") != 0) { fd = open("#ยค/capuse", OWRITE); if (fd < 0) { fprint(errfd, "Couldn't open capuse: %r\n"); fprint(ctlfd, "hangup"); exits(nil); } if (write(fd, buf, n) < 0) { fprint(errfd, "Write to capuse failed: %r\n"); fprint(ctlfd, "hangup"); exits(nil); } close(fd); p = strchr(buf, '@'); if (p) { ++p; q = strchr(p, '@'); if (q) { *q = '\0'; uname = strdup(p); } if (!tflag) { if (newns(p, nsfile) < 0) fprint(errfd, "newns failed: %r\n"); } } } } n = read(ctlfd, buf, 128); buf[n] = '\0'; fprint(ctlfd, "announce session"); filnam = smprint("%s/%s/listen", netdir, buf); fprint(errfd, "listen is %s\n", filnam); if (access(netdir, AEXIST) < 0) { p = smprint("/srv/%s", srvpt ? srvpt : "sshtun"); fd = open(p, ORDWR); if (fd < 0) { fprint(errfd, "srv open failed; %r\n"); fprint(ctlfd, "hangup"); exits(nil); } mount(fd, -1, "/net", MBEFORE, ""); } while (1) { fd = open(filnam, ORDWR); if (fd < 0) { fprint(errfd, "listen failed: %r\n"); fprint(ctlfd, "hangup"); exits(nil); } n = read(fd, buf, 128); fprint(errfd, "read from listen file returned %d\n", n); if (n <= 0) { fprint(errfd, "read on listen failed: %r\n"); fprint(ctlfd, "hangup"); exits(nil); } buf[n] = '\0'; fprint(errfd, "read %s\n", buf); switch (fork()) { case 0: close(ctlfd); newchannel(fd, netdir, atoi(buf)); break; case -1: fprint(errfd, "fork failed: %r\n"); fprint(ctlfd, "hangup"); exits(nil); break; default: close(fd); break; } } } void newchannel(int fd, char *conndir, int channum) { char *p, *q, *reqfile, *datafile; int n, reqfd, datafd, motdfd, want_reply, already_done; char buf[32768], buf2[10240], cmd[1024]; close(fd); already_done = 0; reqfile = smprint("%s/%d/request", conndir, channum); reqfd = open(reqfile, ORDWR); if (reqfd < 0) { fprint(errfd, "Couldn't open request file: %r\n"); exits(nil); } datafile = smprint("%s/%d/data", conndir, channum); datafd = open(datafile, ORDWR); if (datafd < 0) { fprint(errfd, "Couldn't open data file: %r\n"); exits(nil); } while (1) { n = read(reqfd, buf, 32768); fprint(errfd, "read from request file returned %d\n", n); if (n == 0) { exits(nil); } else if (n < 0) { fprint(errfd, "Read failed: %r\n"); exits(nil); } for (p = buf; p < buf + n && *p != ' '; ++p) ; *p = '\0'; ++p; want_reply = (*p == 't'); if (strcmp(buf, "pty-req") == 0) { if (want_reply) fprint(reqfd, "success"); } else if (strcmp(buf, "x11-req") == 0) { if (want_reply) fprint(reqfd, "failure"); } else if (strcmp(buf, "env") == 0) { if (want_reply) fprint(reqfd, "failure"); } else if (strcmp(buf, "shell") == 0) { if (already_done) { if (want_reply) fprint(reqfd, "failure"); continue; } switch (fork()) { case 0: if (sflag) snprint(cmd, 1024, "-s%s", shell); else snprint(cmd, 1024, ""); if (slfd > 0) fprint(slfd, "starting ssh shell for %s\n", uname); else syslog(1, "ssh", "starting ssh shell for %s", uname); motdfd = open("/sys/lib/motd", OREAD); if (motdfd >= 0) { while ((n = read(motdfd, buf, 8192)) > 0) { p = buf2; for (q = buf; q < buf+n; ++q) { if (*q == '\n') *p++ = '\r'; *p++ = *q; } write(datafd, buf2, p-buf2); } close(motdfd); } // runcmd(reqfd, datafd, "con", "/bin/conssim", cmd, nil); runcmd(reqfd, datafd, "con", "/bin/ip/telnetd", "-nt", nil); exits(nil); case -1: if (want_reply) fprint(reqfd, "failure"); fprint(2, "Cannot fork: %r\n"); exits(nil); break; default: already_done = 1; if (want_reply) fprint(reqfd, "success"); break; } } else if (strcmp(buf, "exec") == 0) { if (already_done) { if (want_reply) fprint(reqfd, "failure"); continue; } switch (fork()) { case 0: if (restdir) chdir(restdir); if (!prevent || (q = getenv("sshsession")) && strcmp(q, "allow") == 0) get_string(p+1, cmd); else confine(p+1, cmd); if (slfd > 0) fprint(slfd, "running %s for %s\n", cmd, uname); else syslog(1, "ssh", "running %s for %s", cmd, uname); runcmd(reqfd, datafd, "rx", "/bin/rc", "-lc", cmd); exits(nil); case -1: if (want_reply) fprint(reqfd, "failure"); fprint(errfd, "Cannot fork: %r\n"); exits(nil); break; default: already_done = 1; if (want_reply) fprint(reqfd, "success"); break; } } else if (strcmp(buf, "subsystem") == 0) { if (want_reply) fprint(reqfd, "failure"); } else if (strcmp(buf, "window-change") == 0) { if (want_reply) fprint(reqfd, "success"); } else if (strcmp(buf, "xon-xoff") == 0) { } else if (strcmp(buf, "signal") == 0) { } else if (strcmp(buf, "exit-status") == 0) { } else if (strcmp(buf, "exit-signal") == 0) { } else fprint(errfd, "Unknown channel request: %s\n", buf); } } char * get_string(char *q, char *s) { int n; n = nhgetl(q); q += 4; memmove(s, q, n); s[n] = '\0'; q += n; return q; } char * confine(char *q, char *s) { int i, n, m; char *p, *e, *r, *buf, *toks[32]; n = nhgetl(q); q += 4; buf = malloc(n+1); memmove(buf, q, n); buf[n] = 0; m = tokenize(buf, toks, 32); e = s + n + 1; for (i = 0, r = s; i < m; ++i) { p = strrchr(toks[i], '/'); if (p) { if (*(p+1)) r = seprint(r, e, "%s ", p+1); else r = seprint(r, e, ". "); } else r = seprint(r, e, "%s ", toks[i]); } free(buf); q += n; return q; } void runcmd(int reqfd, int datafd, char *svc, char *cmd, char *arg1, char *arg2) { char *p; int fd, cmdpid, child; cmdpid = rfork(RFPROC|RFMEM|RFNOTEG|RFFDG); switch (cmdpid) { case -1: fprint(errfd, "fork failed: %r\n"); break; case 0: if (restdir == nil) { p = smprint("/usr/%s", uname); if (access(p, AREAD) == 0) chdir(p); free(p); } p = strrchr(cmd, '/'); if (p) ++p; else p = cmd; dup(datafd, 0); dup(datafd, 1); dup(datafd, 2); close(datafd); putenv("service", svc); fprint(errfd, "starting %s\n", cmd); execl(cmd, p, arg1, arg2, nil); fprint(errfd, "cannot exec %s: %r\n", cmd); break; default: close(datafd); while (1) { fprint(errfd, "waiting for child %d\n", cmdpid); child = waitpid(); fprint(errfd, "child %d passed\n", child); if (child == cmdpid || child == -1) break; } if (child == -1) fprint(errfd, "wait failed: %r\n"); if (slfd > 0) fprint(slfd, "closing ssh session for %s\n", uname); else syslog(1, "ssh", "closing ssh session for %s", uname); fprint(errfd, "closing connection\n"); write(reqfd, "close", 5); p = smprint("/proc/%d/notepg", toppid); fd = open(p, OWRITE); write(fd, "interrupt", 3); close(fd); break; } exits(nil); }