123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512 |
- /*
- * This file is part of the UCB release of Plan 9. It is subject to the license
- * terms in the LICENSE file found in the top-level directory of this
- * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
- * part of the UCB release of Plan 9, including this file, may be copied,
- * modified, propagated, or distributed except according to the terms contained
- * in the LICENSE file.
- */
- /*
- * ssh server - serve SSH protocol v2
- * /net/ssh does most of the work; we copy bytes back and forth
- */
- #include <u.h>
- #include <libc.h>
- #include <ip.h>
- #include <auth.h>
- #include "ssh2.h"
- char *confine(char *, char *);
- char *get_string(char *, char *);
- void newchannel(int, char *, int);
- void runcmd(int, int, char *, char *, char *, char *);
- int errfd, toppid, sflag, tflag, prevent;
- int debug;
- char *idstring;
- char *netdir; /* /net/ssh/<conn> */
- char *nsfile = nil;
- char *restdir;
- char *shell;
- char *srvpt;
- char *uname;
- void
- usage(void)
- {
- fprint(2, "usage: %s [-i id] [-s shell] [-r dir] [-R dir] [-S srvpt] "
- "[-n ns] [-t] [netdir]\n", argv0);
- exits("usage");
- }
- static int
- getctlfd(void)
- {
- int ctlfd;
- char *name;
- name = smprint("%s/clone", netdir);
- ctlfd = -1;
- if (name)
- ctlfd = open(name, ORDWR);
- if (ctlfd < 0) {
- syslog(0, "ssh", "server can't clone: %s: %r", name);
- exits("open clone");
- }
- free(name);
- return ctlfd;
- }
- static int
- getdatafd(int ctlfd)
- {
- int fd;
- char *name;
- name = smprint("%s/data", netdir);
- fd = -1;
- if (name)
- fd = open(name, OREAD);
- if (fd < 0) {
- syslog(0, "ssh", "can't open %s: %r", name);
- hangup(ctlfd);
- exits("open data");
- }
- free(name);
- return fd;
- }
- static void
- auth(char *buf, int n, int ctlfd)
- {
- int fd;
- fd = open("#¤/capuse", OWRITE);
- if (fd < 0) {
- syslog(0, "ssh", "server can't open capuse: %r");
- hangup(ctlfd);
- exits("capuse");
- }
- if (write(fd, buf, n) != n) {
- syslog(0, "ssh", "server write `%.*s' to capuse failed: %r",
- n, buf);
- hangup(ctlfd);
- exits("capuse");
- }
- close(fd);
- }
- /*
- * mount tunnel if there isn't one visible.
- */
- static void
- mounttunnel(int ctlfd)
- {
- int fd;
- char *p, *np, *q;
- if (access(netdir, AEXIST) >= 0)
- return;
- p = smprint("/srv/%s", srvpt? srvpt: "ssh");
- np = strdup(netdir);
- if (p == nil || np == nil)
- sysfatal("out of memory");
- q = strstr(np, "/ssh");
- if (q != nil)
- *q = '\0';
- fd = open(p, ORDWR);
- if (fd < 0) {
- syslog(0, "ssh", "can't open %s: %r", p);
- hangup(ctlfd);
- exits("open");
- }
- if (mount(fd, -1, np, MBEFORE, "", 'M') < 0) {
- syslog(0, "ssh", "can't mount %s in %s: %r", p, np);
- hangup(ctlfd);
- exits("can't mount");
- }
- free(p);
- free(np);
- }
- static int
- authnewns(int ctlfd, char *buf, int size, int n)
- {
- char *p, *q;
- USED(size);
- if (n <= 0)
- return 0;
- buf[n] = '\0';
- if (strcmp(buf, "n/a") == 0)
- return 0;
- auth(buf, n, ctlfd);
- p = strchr(buf, '@');
- if (p == nil)
- return 0;
- ++p;
- q = strchr(p, '@');
- if (q) {
- *q = '\0';
- uname = strdup(p);
- }
- if (!tflag && newns(p, nsfile) < 0) {
- syslog(0, "ssh", "server: newns(%s,%s) failed: %r", p, nsfile);
- return -1;
- }
- return 0;
- }
- static void
- listenloop(char *listfile, int ctlfd, char *buf, int size)
- {
- int fd, n;
- while ((fd = open(listfile, ORDWR)) >= 0) {
- n = read(fd, buf, size - 1);
- fprint(errfd, "read from listen file returned %d\n", n);
- if (n <= 0) {
- syslog(0, "ssh", "read on listen failed: %r");
- break;
- }
- buf[n >= 0? n: 0] = '\0';
- fprint(errfd, "read %s\n", buf);
- switch (fork()) {
- case 0: /* child */
- close(ctlfd);
- newchannel(fd, netdir, atoi(buf)); /* never returns */
- case -1:
- syslog(0, "ssh", "fork failed: %r");
- hangup(ctlfd);
- exits("fork");
- }
- close(fd);
- }
- if (fd < 0)
- syslog(0, "ssh", "listen failed: %r");
- }
- void
- main(int argc, char *argv[])
- {
- char *listfile;
- int ctlfd, fd, n;
- char buf[Arbpathlen], path[Arbpathlen];
- rfork(RFNOTEG);
- toppid = getpid();
- shell = "/bin/rc -il";
- ARGBEGIN {
- case 'd':
- debug++;
- break;
- case 'i':
- idstring = EARGF(usage());
- break;
- case 'n':
- nsfile = EARGF(usage());
- break;
- case 'R':
- prevent = 1;
- /* fall through */
- case 'r':
- restdir = EARGF(usage());
- break;
- case 's':
- sflag = 1;
- shell = EARGF(usage());
- break;
- case 'S':
- srvpt = EARGF(usage());
- break;
- case 't':
- tflag = 1;
- break;
- default:
- usage();
- break;
- } ARGEND;
- errfd = -1;
- if (debug)
- errfd = 2;
- /* work out network connection's directory */
- if (argc >= 1)
- netdir = argv[0];
- else /* invoked by listen1 */
- netdir = getenv("net");
- if (netdir == nil) {
- syslog(0, "ssh", "server netdir is nil");
- exits("nil netdir");
- }
- syslog(0, "ssh", "server netdir is %s", netdir);
- uname = getenv("user");
- if (uname == nil)
- uname = "none";
- /* extract dfd and cfd from netdir */
- ctlfd = getctlfd();
- fd = getdatafd(ctlfd);
- n = read(fd, buf, sizeof buf - 1);
- if (n < 0) {
- syslog(0, "ssh", "server read error for data file: %r");
- hangup(ctlfd);
- exits("read cap");
- }
- close(fd);
- authnewns(ctlfd, buf, sizeof buf, n);
- /* get connection number in buf */
- n = read(ctlfd, buf, sizeof buf - 1);
- buf[n >= 0? n: 0] = '\0';
- /* tell netssh our id string */
- fd2path(ctlfd, path, sizeof path);
- if (0 && idstring) { /* was for coexistence */
- syslog(0, "ssh", "server conn %s, writing \"id %s\" to %s",
- buf, idstring, path);
- fprint(ctlfd, "id %s", idstring);
- }
- /* announce */
- fprint(ctlfd, "announce session");
- /* construct listen file name */
- listfile = smprint("%s/%s/listen", netdir, buf);
- if (listfile == nil) {
- syslog(0, "ssh", "out of memory");
- exits("out of memory");
- }
- syslog(0, "ssh", "server listen is %s", listfile);
- mounttunnel(ctlfd);
- listenloop(listfile, ctlfd, buf, sizeof buf);
- hangup(ctlfd);
- exits(nil);
- }
- /* an abbreviation. note the assumed variables. */
- #define REPLY(s) if (want_reply) fprint(reqfd, s);
- static void
- forkshell(char *cmd, int reqfd, int datafd, int want_reply)
- {
- switch (fork()) {
- case 0:
- if (sflag)
- snprint(cmd, sizeof cmd, "-s%s", shell);
- else
- cmd[0] = '\0';
- USED(cmd);
- syslog(0, "ssh", "server starting ssh shell for %s", uname);
- /* runcmd doesn't return */
- runcmd(reqfd, datafd, "con", "/bin/ip/telnetd", "-nt", nil);
- case -1:
- REPLY("failure");
- syslog(0, "ssh", "server can't fork: %r");
- exits("fork");
- }
- }
- static void
- forkcmd(char *cmd, char *p, int reqfd, int datafd, int want_reply)
- {
- char *q;
- switch (fork()) {
- case 0:
- if (restdir && chdir(restdir) < 0) {
- syslog(0, "ssh", "can't chdir(%s): %r", restdir);
- exits("can't chdir");
- }
- if (!prevent || (q = getenv("sshsession")) &&
- strcmp(q, "allow") == 0)
- get_string(p+1, cmd);
- else
- confine(p+1, cmd);
- syslog(0, "ssh", "server running `%s' for %s", cmd, uname);
- /* runcmd doesn't return */
- runcmd(reqfd, datafd, "rx", "/bin/rc", "-lc", cmd);
- case -1:
- REPLY("failure");
- syslog(0, "ssh", "server can't fork: %r");
- exits("fork");
- }
- }
- void
- newchannel(int fd, char *conndir, int channum)
- {
- char *p, *reqfile, *datafile;
- int n, reqfd, datafd, want_reply, already_done;
- char buf[Maxpayload], cmd[Bigbufsz];
- close(fd);
- already_done = 0;
- reqfile = smprint("%s/%d/request", conndir, channum);
- if (reqfile == nil)
- sysfatal("out of memory");
- reqfd = open(reqfile, ORDWR);
- if (reqfd < 0) {
- syslog(0, "ssh", "can't open request file %s: %r", reqfile);
- exits("net");
- }
- datafile = smprint("%s/%d/data", conndir, channum);
- if (datafile == nil)
- sysfatal("out of memory");
- datafd = open(datafile, ORDWR);
- if (datafd < 0) {
- syslog(0, "ssh", "can't open data file %s: %r", datafile);
- exits("net");
- }
- while ((n = read(reqfd, buf, sizeof buf - 1)) > 0) {
- fprint(errfd, "read from request file returned %d\n", n);
- for (p = buf; p < buf + n && *p != ' '; ++p)
- ;
- *p++ = '\0';
- want_reply = (*p == 't');
- /* shell, exec, and various flavours of failure */
- if (strcmp(buf, "shell") == 0) {
- if (already_done) {
- REPLY("failure");
- continue;
- }
- forkshell(cmd, reqfd, datafd, want_reply);
- already_done = 1;
- REPLY("success");
- } else if (strcmp(buf, "exec") == 0) {
- if (already_done) {
- REPLY("failure");
- continue;
- }
- forkcmd(cmd, p, reqfd, datafd, want_reply);
- already_done = 1;
- REPLY("success");
- } else if (strcmp(buf, "pty-req") == 0 ||
- strcmp(buf, "window-change") == 0) {
- REPLY("success");
- } else if (strcmp(buf, "x11-req") == 0 ||
- strcmp(buf, "env") == 0 || strcmp(buf, "subsystem") == 0) {
- REPLY("failure");
- } else if (strcmp(buf, "xon-xoff") == 0 ||
- strcmp(buf, "signal") == 0 ||
- strcmp(buf, "exit-status") == 0 ||
- strcmp(buf, "exit-signal") == 0) {
- ;
- } else
- syslog(0, "ssh", "server unknown channel request: %s",
- buf);
- }
- if (n < 0)
- syslog(0, "ssh", "server read failed: %r");
- exits(nil);
- }
- 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[Maxtoks];
- n = nhgetl(q);
- q += 4;
- buf = malloc(n+1);
- if (buf == nil)
- return nil;
- memmove(buf, q, n);
- buf[n] = 0;
- m = tokenize(buf, toks, nelem(toks));
- e = s + n + 1;
- for (i = 0, r = s; i < m; ++i) {
- p = strrchr(toks[i], '/');
- if (p == nil)
- r = seprint(r, e, "%s ", toks[i]);
- else if (p[0] != '\0' && p[1] != '\0')
- r = seprint(r, e, "%s ", p+1);
- else
- r = seprint(r, e, ". ");
- }
- 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|RFENVG);
- switch (cmdpid) {
- case -1:
- syslog(0, "ssh", "fork failed: %r");
- exits("fork");
- case 0:
- if (restdir == nil) {
- p = smprint("/usr/%s", uname);
- if (p && access(p, AREAD) == 0 && chdir(p) < 0) {
- syslog(0, "ssh", "can't chdir(%s): %r", p);
- exits("can't chdir");
- }
- 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);
- syslog(0, "ssh", "cannot exec %s: %r", cmd);
- exits("exec");
- default:
- close(datafd);
- fprint(errfd, "waiting for child %d\n", cmdpid);
- while ((child = waitpid()) != cmdpid && child != -1)
- fprint(errfd, "child %d passed\n", child);
- if (child == -1)
- fprint(errfd, "wait failed: %r\n");
- syslog(0, "ssh", "server closing ssh session for %s", uname);
- fprint(errfd, "closing connection\n");
- fprint(reqfd, "close");
- p = smprint("/proc/%d/notepg", toppid);
- if (p) {
- fd = open(p, OWRITE);
- fprint(fd, "interrupt");
- close(fd);
- }
- exits(nil);
- }
- }
|