/* * 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. */ /* * /net/ssh */#include <u.h>#include <libc.h>#include <fcall.h>#include <thread.h>#include <9p.h>#include <mp.h>#include <auth.h>#include <authsrv.h>#include <libsec.h>#include <ip.h>#include "netssh.h" extern int nokeyverify; void stclunk(Fid *);void stend(Srv *);void stflush(Req *);void stopen(Req *);void stread(Req *);void stwrite(Req *); Srv netsshsrv = { .open = stopen, .read = stread, .write = stwrite, .flush = stflush, .destroyfid = stclunk, .end = stend,}; Cipher *cryptos[] = { &cipheraes128, &cipheraes192, &cipheraes256,// &cipherblowfish, &cipher3des, &cipherrc4,}; Kex *kexes[] = { &dh1sha1, &dh14sha1,}; PKA *pkas[3]; char *macnames[] = { "hmac-sha1",}; char *st_names[] = {[Empty] "Empty",[Allocated] "Allocated",[Initting] "Initting",[Listening] "Listening",[Opening] "Opening",[Negotiating] "Negotiating",[Authing] "Authing",[Established] "Established",[Eof] "Eof",[Closing] "Closing",[Closed] "Closed",}; int debug;int kflag;char *mntpt = "/net";char uid[32];Conn *connections[MAXCONN];File *rootfile, *clonefile, *ctlfile, *keysfile;Ioproc *io9p;MBox keymbox;QLock availlck;Rendez availrend; SSHChan *alloc_chan(Conn *);Conn *alloc_conn(void);int auth_req(Packet *, Conn *);int client_auth(Conn *, Ioproc *);int dohandshake(Conn *, char *);char *factlookup(int, int, char *[]);void filedup(Req *, File *);void readdata(void *);void reader(void *);void readreqrem(void *);void send_kexinit(Conn *);void server(char *, char *);void shutdown(Conn *);void stlisconn(void *);void stlischan(void *);int validatekex(Conn *, Packet *);int validatekexc(Packet *);int validatekexs(Packet *);void writectlproc(void *);void writedataproc(void *);void writereqremproc(void *); static int deferredinit(Conn *c); static voidsshlogint(Conn *c, char *file, char *p){ char *role, *id; if (c == nil) role = ""; else if (c->role == Server) role = "server "; else role = "client "; if (c == nil) id = strdup(""); else if (c->user || c->remote) id = smprint("user %s@%s id %d ", c->user, c->remote, c->id); else id = smprint("id %d ", c->id); syslog(0, file, "%s: %s%s%s", argv0, role, id, p); free(id);} voidsshlog(Conn *c, char *fmt, ...){ va_list args; char *p; /* do this first in case fmt contains "%r" */ va_start(args, fmt); p = vsmprint(fmt, args); va_end(args); sshlogint(c, "ssh", p); sshlogint(c, "sshdebug", p); /* log in both places */ free(p);} voidsshdebug(Conn *c, char *fmt, ...){ va_list args; char *p; if (!debug) return; /* do this first in case fmt contains "%r" */ va_start(args, fmt); p = vsmprint(fmt, args); va_end(args); sshlogint(c, "sshdebug", p); free(p);} voidusage(void){ fprint(2, "usage: %s [-dkv] [-m mntpt] [-s srvpt]\n", argv0); exits("usage");} voidthreadmain(int argc, char *argv[]){ char *p, *srvpt = nil; quotefmtinstall(); threadsetname("main"); nokeyverify = 1; /* temporary until verification is fixed */ ARGBEGIN { case '9': chatty9p = 1; break; case 'd': debug++; break; case 'k': kflag = 1; break; case 'm': mntpt = EARGF(usage()); break; case 's': srvpt = EARGF(usage()); break; case 'v': nokeyverify = 1; break; case 'V': nokeyverify = 0; break; default: usage(); break; } ARGEND; p = getenv("nosshkeyverify"); if (p && p[0] != '\0') nokeyverify = 1; free(p); if (readfile("/dev/user", uid, sizeof uid) <= 0) strcpy(uid, "none"); keymbox.mchan = chancreate(4, 0); availrend.l = &availlck; dh_init(pkas); /* become a daemon */ if (rfork(RFNOTEG) < 0) fprint(2, "%s: rfork(NOTEG) failed: %r\n", argv0); server(mntpt, srvpt); threadexits(nil);} intreadio(Ioproc *io, int fd, void *buf, int n){ if (io) return ioread(io, fd, buf, n); else return read(fd, buf, n);} intwriteio(Ioproc *io, int fd, void *buf, int n){ if (io) return iowrite(io, fd, buf, n); else return write(fd, buf, n);} intread9pmsg(int fd, void *abuf, uint n){ int m, len; uchar *buf; if (io9p == nil) io9p = ioproc(); buf = abuf; /* read count */ m = ioreadn(io9p, fd, buf, BIT32SZ); if(m != BIT32SZ){ if(m < 0) return -1; return 0; } len = GBIT32(buf); if(len <= BIT32SZ || len > n){ werrstr("bad length in 9P2000 message header"); return -1; } len -= BIT32SZ; m = ioreadn(io9p, fd, buf+BIT32SZ, len); if(m < len) return 0; return BIT32SZ+m;} voidstend(Srv *){ closeioproc(io9p); threadkillgrp(threadgetgrp());} voidserver(char *mntpt, char *srvpt){ Dir d; char *p; int fd; netsshsrv.tree = alloctree(uid, uid, 0777, nil); rootfile = createfile(netsshsrv.tree->root, "ssh", uid, 0555|DMDIR, (void*)Qroot); clonefile = createfile(rootfile, "clone", uid, 0666, (void*)Qclone); ctlfile = createfile(rootfile, "ctl", uid, 0666, (void*)Qctl); keysfile = createfile(rootfile, "keys", uid, 0600, (void *)Qreqrem); /* * needs to be MBEFORE in case there are previous, now defunct, * netssh processes mounted in mntpt. */ threadpostmountsrv(&netsshsrv, srvpt, mntpt, MBEFORE); p = esmprint("%s/cs", mntpt); fd = open(p, OWRITE); free(p); if (fd >= 0) { fprint(fd, "add ssh"); close(fd); } if (srvpt) { nulldir(&d); d.mode = 0666; p = esmprint("/srv/%s", srvpt); dirwstat(p, &d); free(p); } sshdebug(nil, "server started for %s", getuser());} static voidrespexit(Conn *c, Req *r, void *freeme, char *msg){ if (msg) sshdebug(c, "%s", msg); r->aux = 0; respond(r, msg); free(freeme); threadexits(nil); /* maybe use msg here */} voidstopen(Req *r){ int lev, xconn, fd; uvlong qidpath; char *p; char buf[32]; Conn *c; SSHChan *sc; qidpath = (uvlong)r->fid->file->aux; lev = qidpath >> Levshift; switch ((ulong)(qidpath & Qtypemask)) { default: respond(r, nil); break; case Qlisten: r->aux = (void *)threadcreate((lev == Connection? stlisconn: stlischan), r, Defstk); break; case Qclone: switch (lev) { case Top: /* should use dial(2) instead of diddling /net/tcp */ p = esmprint("%s/tcp/clone", mntpt); fd = open(p, ORDWR); if (fd < 0) { sshdebug(nil, "stopen: open %s failed: %r", p); free(p); responderror(r); return; } free(p); c = alloc_conn(); if (c == nil) { close(fd); respond(r, "no more connections"); return; } c->ctlfd = fd; c->poisoned = 0; filedup(r, c->ctlfile); sshlog(c, "new connection on fd %d", fd); break; case Connection: xconn = (qidpath >> Connshift) & Connmask; c = connections[xconn]; if (c == nil) { respond(r, "bad connection"); return; } sc = alloc_chan(c); if (sc == nil) { respond(r, "no more channels"); return; } filedup(r, sc->ctl); break; default: snprint(buf, sizeof buf, "bad level %d", lev); readstr(r, buf); break; } respond(r, nil); break; }} static voidlisterrexit(Req *r, Ioproc *io, Conn *cl){ r->aux = 0; responderror(r); closeioproc(io); shutdown(cl); threadexits(nil);} voidstlisconn(void *a){ int xconn, fd, n; uvlong qidpath; char *msg; char buf[Numbsz], path[NETPATHLEN]; Conn *c, *cl; Ioproc *io; Req *r; threadsetname("stlisconn"); r = a; qidpath = (uvlong)r->fid->file->aux; xconn = (qidpath >> Connshift) & Connmask; cl = connections[xconn]; if (cl == nil) { sshlog(cl, "bad connection"); respond(r, "bad connection"); threadexits("bad connection"); } if (cl->poisoned) { sshdebug(cl, "stlisconn conn %d poisoned", xconn); r->aux = 0; respond(r, "top level listen conn poisoned"); threadexits("top level listen conn poisoned"); } if (cl->ctlfd < 0) { sshdebug(cl, "stlisconn conn %d ctlfd < 0; poisoned", xconn); r->aux = 0; respond(r, "top level listen with closed fd"); shutdown(cl); cl->poisoned = 1; /* no more use until ctlfd is set */ threadexits("top level listen with closed fd"); } io = ioproc(); /* read xconn's tcp conn's ctl file */ seek(cl->ctlfd, 0, 0); n = ioread(io, cl->ctlfd, buf, sizeof buf - 1); if (n == 0) { sshlog(cl, "stlisconn read eof on fd %d", cl->ctlfd); listerrexit(r, io, cl); } else if (n < 0) { sshlog(cl, "stlisconn read failed on fd %d: %r", cl->ctlfd); listerrexit(r, io, cl); } buf[n] = '\0'; cl->state = Listening; /* should use dial(2) instead of diddling /net/tcp */ snprint(path, sizeof path, "%s/tcp/%s/listen", mntpt, buf); for(;;) { fd = ioopen(io, path, ORDWR); if (fd < 0) listerrexit(r, io, cl); c = alloc_conn(); if (c) break; n = ioread(io, fd, buf, sizeof buf - 1); if (n <= 0) listerrexit(r, io, cl); buf[n] = '\0'; msg = smprint("reject %s no available connections", buf); iowrite(io, fd, msg, strlen(msg)); free(msg); close(fd); /* surely ioclose? */ } c->ctlfd = fd; if (c->ctlfd < 0) { sshlog(cl, "stlisconn c->ctlfd < 0 for conn %d", xconn); threadexitsall("stlisconn c->ctlfd < 0"); } c->poisoned = 0; c->stifle = 1; /* defer server; was for coexistence */ filedup(r, c->ctlfile); sshdebug(c, "responding to listen open"); r->aux = 0; respond(r, nil); closeioproc(io); threadexits(nil);} voidstlischan(void *a){ Req *r;Lines 1–500 of 3229. Load more
/* * 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. */ /* * /net/ssh */ #include <u.h> #include <libc.h> #include <fcall.h> #include <thread.h> #include <9p.h> #include <mp.h> #include <auth.h> #include <authsrv.h> #include <libsec.h> #include <ip.h> #include "netssh.h" extern int nokeyverify; void stclunk(Fid *); void stend(Srv *); void stflush(Req *); void stopen(Req *); void stread(Req *); void stwrite(Req *); Srv netsshsrv = { .open = stopen, .read = stread, .write = stwrite, .flush = stflush, .destroyfid = stclunk, .end = stend, }; Cipher *cryptos[] = { &cipheraes128, &cipheraes192, &cipheraes256, // &cipherblowfish, &cipher3des, &cipherrc4, }; Kex *kexes[] = { &dh1sha1, &dh14sha1, }; PKA *pkas[3]; char *macnames[] = { "hmac-sha1", }; char *st_names[] = { [Empty] "Empty", [Allocated] "Allocated", [Initting] "Initting", [Listening] "Listening", [Opening] "Opening", [Negotiating] "Negotiating", [Authing] "Authing", [Established] "Established", [Eof] "Eof", [Closing] "Closing", [Closed] "Closed", }; int debug; int kflag; char *mntpt = "/net"; char uid[32]; Conn *connections[MAXCONN]; File *rootfile, *clonefile, *ctlfile, *keysfile; Ioproc *io9p; MBox keymbox; QLock availlck; Rendez availrend; SSHChan *alloc_chan(Conn *); Conn *alloc_conn(void); int auth_req(Packet *, Conn *); int client_auth(Conn *, Ioproc *); int dohandshake(Conn *, char *); char *factlookup(int, int, char *[]); void filedup(Req *, File *); void readdata(void *); void reader(void *); void readreqrem(void *); void send_kexinit(Conn *); void server(char *, char *); void shutdown(Conn *); void stlisconn(void *); void stlischan(void *); int validatekex(Conn *, Packet *); int validatekexc(Packet *); int validatekexs(Packet *); void writectlproc(void *); void writedataproc(void *); void writereqremproc(void *); static int deferredinit(Conn *c); static void sshlogint(Conn *c, char *file, char *p) { char *role, *id; if (c == nil) role = ""; else if (c->role == Server) role = "server "; else role = "client "; if (c == nil) id = strdup(""); else if (c->user || c->remote) id = smprint("user %s@%s id %d ", c->user, c->remote, c->id); else id = smprint("id %d ", c->id); syslog(0, file, "%s: %s%s%s", argv0, role, id, p); free(id); } void sshlog(Conn *c, char *fmt, ...) { va_list args; char *p; /* do this first in case fmt contains "%r" */ va_start(args, fmt); p = vsmprint(fmt, args); va_end(args); sshlogint(c, "ssh", p); sshlogint(c, "sshdebug", p); /* log in both places */ free(p); } void sshdebug(Conn *c, char *fmt, ...) { va_list args; char *p; if (!debug) return; /* do this first in case fmt contains "%r" */ va_start(args, fmt); p = vsmprint(fmt, args); va_end(args); sshlogint(c, "sshdebug", p); free(p); } void usage(void) { fprint(2, "usage: %s [-dkv] [-m mntpt] [-s srvpt]\n", argv0); exits("usage"); } void threadmain(int argc, char *argv[]) { char *p, *srvpt = nil; quotefmtinstall(); threadsetname("main"); nokeyverify = 1; /* temporary until verification is fixed */ ARGBEGIN { case '9': chatty9p = 1; break; case 'd': debug++; break; case 'k': kflag = 1; break; case 'm': mntpt = EARGF(usage()); break; case 's': srvpt = EARGF(usage()); break; case 'v': nokeyverify = 1; break; case 'V': nokeyverify = 0; break; default: usage(); break; } ARGEND; p = getenv("nosshkeyverify"); if (p && p[0] != '\0') nokeyverify = 1; free(p); if (readfile("/dev/user", uid, sizeof uid) <= 0) strcpy(uid, "none"); keymbox.mchan = chancreate(4, 0); availrend.l = &availlck; dh_init(pkas); /* become a daemon */ if (rfork(RFNOTEG) < 0) fprint(2, "%s: rfork(NOTEG) failed: %r\n", argv0); server(mntpt, srvpt); threadexits(nil); } int readio(Ioproc *io, int fd, void *buf, int n) { if (io) return ioread(io, fd, buf, n); else return read(fd, buf, n); } int writeio(Ioproc *io, int fd, void *buf, int n) { if (io) return iowrite(io, fd, buf, n); else return write(fd, buf, n); } int read9pmsg(int fd, void *abuf, uint n) { int m, len; uchar *buf; if (io9p == nil) io9p = ioproc(); buf = abuf; /* read count */ m = ioreadn(io9p, fd, buf, BIT32SZ); if(m != BIT32SZ){ if(m < 0) return -1; return 0; } len = GBIT32(buf); if(len <= BIT32SZ || len > n){ werrstr("bad length in 9P2000 message header"); return -1; } len -= BIT32SZ; m = ioreadn(io9p, fd, buf+BIT32SZ, len); if(m < len) return 0; return BIT32SZ+m; } void stend(Srv *) { closeioproc(io9p); threadkillgrp(threadgetgrp()); } void server(char *mntpt, char *srvpt) { Dir d; char *p; int fd; netsshsrv.tree = alloctree(uid, uid, 0777, nil); rootfile = createfile(netsshsrv.tree->root, "ssh", uid, 0555|DMDIR, (void*)Qroot); clonefile = createfile(rootfile, "clone", uid, 0666, (void*)Qclone); ctlfile = createfile(rootfile, "ctl", uid, 0666, (void*)Qctl); keysfile = createfile(rootfile, "keys", uid, 0600, (void *)Qreqrem); /* * needs to be MBEFORE in case there are previous, now defunct, * netssh processes mounted in mntpt. */ threadpostmountsrv(&netsshsrv, srvpt, mntpt, MBEFORE); p = esmprint("%s/cs", mntpt); fd = open(p, OWRITE); free(p); if (fd >= 0) { fprint(fd, "add ssh"); close(fd); } if (srvpt) { nulldir(&d); d.mode = 0666; p = esmprint("/srv/%s", srvpt); dirwstat(p, &d); free(p); } sshdebug(nil, "server started for %s", getuser()); } static void respexit(Conn *c, Req *r, void *freeme, char *msg) { if (msg) sshdebug(c, "%s", msg); r->aux = 0; respond(r, msg); free(freeme); threadexits(nil); /* maybe use msg here */ } void stopen(Req *r) { int lev, xconn, fd; uvlong qidpath; char *p; char buf[32]; Conn *c; SSHChan *sc; qidpath = (uvlong)r->fid->file->aux; lev = qidpath >> Levshift; switch ((ulong)(qidpath & Qtypemask)) { default: respond(r, nil); break; case Qlisten: r->aux = (void *)threadcreate((lev == Connection? stlisconn: stlischan), r, Defstk); break; case Qclone: switch (lev) { case Top: /* should use dial(2) instead of diddling /net/tcp */ p = esmprint("%s/tcp/clone", mntpt); fd = open(p, ORDWR); if (fd < 0) { sshdebug(nil, "stopen: open %s failed: %r", p); free(p); responderror(r); return; } free(p); c = alloc_conn(); if (c == nil) { close(fd); respond(r, "no more connections"); return; } c->ctlfd = fd; c->poisoned = 0; filedup(r, c->ctlfile); sshlog(c, "new connection on fd %d", fd); break; case Connection: xconn = (qidpath >> Connshift) & Connmask; c = connections[xconn]; if (c == nil) { respond(r, "bad connection"); return; } sc = alloc_chan(c); if (sc == nil) { respond(r, "no more channels"); return; } filedup(r, sc->ctl); break; default: snprint(buf, sizeof buf, "bad level %d", lev); readstr(r, buf); break; } respond(r, nil); break; } } static void listerrexit(Req *r, Ioproc *io, Conn *cl) { r->aux = 0; responderror(r); closeioproc(io); shutdown(cl); threadexits(nil); } void stlisconn(void *a) { int xconn, fd, n; uvlong qidpath; char *msg; char buf[Numbsz], path[NETPATHLEN]; Conn *c, *cl; Ioproc *io; Req *r; threadsetname("stlisconn"); r = a; qidpath = (uvlong)r->fid->file->aux; xconn = (qidpath >> Connshift) & Connmask; cl = connections[xconn]; if (cl == nil) { sshlog(cl, "bad connection"); respond(r, "bad connection"); threadexits("bad connection"); } if (cl->poisoned) { sshdebug(cl, "stlisconn conn %d poisoned", xconn); r->aux = 0; respond(r, "top level listen conn poisoned"); threadexits("top level listen conn poisoned"); } if (cl->ctlfd < 0) { sshdebug(cl, "stlisconn conn %d ctlfd < 0; poisoned", xconn); r->aux = 0; respond(r, "top level listen with closed fd"); shutdown(cl); cl->poisoned = 1; /* no more use until ctlfd is set */ threadexits("top level listen with closed fd"); } io = ioproc(); /* read xconn's tcp conn's ctl file */ seek(cl->ctlfd, 0, 0); n = ioread(io, cl->ctlfd, buf, sizeof buf - 1); if (n == 0) { sshlog(cl, "stlisconn read eof on fd %d", cl->ctlfd); listerrexit(r, io, cl); } else if (n < 0) { sshlog(cl, "stlisconn read failed on fd %d: %r", cl->ctlfd); listerrexit(r, io, cl); } buf[n] = '\0'; cl->state = Listening; /* should use dial(2) instead of diddling /net/tcp */ snprint(path, sizeof path, "%s/tcp/%s/listen", mntpt, buf); for(;;) { fd = ioopen(io, path, ORDWR); if (fd < 0) listerrexit(r, io, cl); c = alloc_conn(); if (c) break; n = ioread(io, fd, buf, sizeof buf - 1); if (n <= 0) listerrexit(r, io, cl); buf[n] = '\0'; msg = smprint("reject %s no available connections", buf); iowrite(io, fd, msg, strlen(msg)); free(msg); close(fd); /* surely ioclose? */ } c->ctlfd = fd; if (c->ctlfd < 0) { sshlog(cl, "stlisconn c->ctlfd < 0 for conn %d", xconn); threadexitsall("stlisconn c->ctlfd < 0"); } c->poisoned = 0; c->stifle = 1; /* defer server; was for coexistence */ filedup(r, c->ctlfile); sshdebug(c, "responding to listen open"); r->aux = 0; respond(r, nil); closeioproc(io); threadexits(nil); } void stlischan(void *a) { Req *r;