#include "ssh.h" int cooked = 0; /* user wants cooked mode */ int raw = 0; /* console is in raw mode */ int crstrip; int interactive = -1; int usemenu = 1; int isatty(int); int rawhack; int forwardagent = 0; char *buildcmd(int, char**); void fromnet(Conn*); void fromstdin(Conn*); void winchanges(Conn*); static void sendwritemsg(Conn *c, char *buf, int n); Cipher *allcipher[] = { &cipherrc4, &cipherblowfish, &cipher3des, &cipherdes, &ciphernone, &ciphertwiddle, }; Auth *allauth[] = { &authpassword, &authrsa, &authtis, }; char *cipherlist = "blowfish rc4 3des"; char *authlist = "rsa password tis"; Cipher* findcipher(char *name, Cipher **list, int nlist) { int i; for(i=0; iname) == 0) return list[i]; error("unknown cipher %s", name); return nil; } Auth* findauth(char *name, Auth **list, int nlist) { int i; for(i=0; iname) == 0) return list[i]; error("unknown auth %s", name); return nil; } void usage(void) { fprint(2, "usage: ssh [-CiImPpRrw] [-A authlist] [-c cipherlist] [user@]hostname [cmd [args]]\n"); exits("usage"); } void main(int argc, char **argv) { int i, dowinchange, fd, usepty; char *host, *cmd, *user, *p; char *f[16]; Conn c; Msg *m; fmtinstall('B', mpfmt); fmtinstall('H', encodefmt); atexit(atexitkiller); atexitkill(getpid()); dowinchange = 0; usepty = -1; user = nil; ARGBEGIN{ case 'B': /* undocumented, debugging */ doabort = 1; break; case 'D': /* undocumented, debugging */ debuglevel = strtol(EARGF(usage()), nil, 0); break; case 'l': /* deprecated */ case 'u': user = EARGF(usage()); break; case 'a': /* used by Unix scp implementations; we must ignore them. */ case 'x': break; case 'A': authlist = EARGF(usage()); break; case 'C': cooked = 1; break; case 'c': cipherlist = EARGF(usage()); break; case 'f': forwardagent = 1; break; case 'I': interactive = 0; break; case 'i': interactive = 1; break; case 'm': usemenu = 0; break; case 'P': usepty = 0; break; case 'p': usepty = 1; break; case 'R': rawhack = 1; break; case 'r': crstrip = 1; break; case 'w': dowinchange = 1; break; default: usage(); }ARGEND if(argc < 1) usage(); host = argv[0]; cmd = nil; if(argc > 1) cmd = buildcmd(argc-1, argv+1); if((p = strchr(host, '@')) != nil){ *p++ = '\0'; user = host; host = p; } if(user == nil) user = getenv("user"); if(user == nil) sysfatal("cannot find user name"); privatefactotum(); if(interactive==-1) interactive = isatty(0); if((fd = dial(netmkaddr(host, "tcp", "ssh"), nil, nil, nil)) < 0) sysfatal("dialing %s: %r", host); memset(&c, 0, sizeof c); c.interactive = interactive; c.fd[0] = c.fd[1] = fd; c.user = user; c.host = host; setaliases(&c, host); c.nokcipher = getfields(cipherlist, f, nelem(f), 1, ", "); c.okcipher = emalloc(sizeof(Cipher*)*c.nokcipher); for(i=0; i=9 && strcmp(buf+strlen(buf)-9, "/dev/cons")==0) return 1; return 0; } char* buildcmd(int argc, char **argv) { int i, len; char *s, *t; len = argc-1; for(i=0; itype){ default: badmsg(m, 0); case SSH_SMSG_EXITSTATUS: ex = getlong(m); if(ex==0) exits(0); sprint(buf, "%lud", ex); exits(buf); case SSH_MSG_DISCONNECT: s = getstring(m); error("disconnect: %s", s); /* * If we ever add reverse port forwarding, we'll have to * revisit this. It assumes that the agent connections are * the only ones. */ case SSH_SMSG_AGENT_OPEN: if(!forwardagent) error("server tried to use agent forwarding"); handleagentopen(m); break; case SSH_MSG_CHANNEL_INPUT_EOF: if(!forwardagent) error("server tried to use agent forwarding"); handleagentieof(m); break; case SSH_MSG_CHANNEL_OUTPUT_CLOSED: if(!forwardagent) error("server tried to use agent forwarding"); handleagentoclose(m); break; case SSH_MSG_CHANNEL_DATA: if(!forwardagent) error("server tried to use agent forwarding"); handleagentmsg(m); break; case SSH_SMSG_STDOUT_DATA: fd = 1; goto Dataout; case SSH_SMSG_STDERR_DATA: fd = 2; goto Dataout; Dataout: len = getlong(m); s = (char*)getbytes(m, len); if(crstrip){ es = s+len; for(r=w=s; r>> "); for(done = 0; !done; ){ n = read(0, buf, sizeof(buf)-1); if(n <= 0) return -1; buf[n] = 0; switch(buf[0]){ case '!': print(buf); system(c, buf+1); print("!\n"); done = 1; break; case 'i': buf[0] = 0x1c; sendwritemsg(c, buf, 1); done = 1; break; case '.': case 'q': done = 1; break; case 'r': crstrip = 1-crstrip; done = 1; break; default: fprint(2, STDHELP); break; } if(!done) fprint(2, ">>> "); } if(wasraw) rawon(); else rawoff(); return buf[0]; } static void sendwritemsg(Conn *c, char *buf, int n) { Msg *m; if(n==0) m = allocmsg(c, SSH_CMSG_EOF, 0); else{ m = allocmsg(c, SSH_CMSG_STDIN_DATA, 4+n); putlong(m, n); putbytes(m, buf, n); } sendmsg(m); } /* * run a command with the network connection as standard IO */ static void system(Conn *c, char *cmd) { int pid; int p; int pfd[2]; int n; int wasconsctl; char buf[4096]; if(pipe(pfd) < 0){ perror("pipe"); return; } outfd1 = outfd2 = pfd[1]; wasconsctl = consctl; close(consctl); consctl = -1; switch(pid = fork()){ case -1: perror("con"); return; case 0: close(pfd[1]); dup(pfd[0], 0); dup(pfd[0], 1); close(c->fd[0]); /* same as c->fd[1] */ close(pfd[0]); if(*cmd) execl("/bin/rc", "rc", "-c", cmd, 0); else execl("/bin/rc", "rc", 0); perror("con"); exits("exec"); break; default: close(pfd[0]); while((n = read(pfd[1], buf, sizeof(buf))) > 0) sendwritemsg(c, buf, n); p = waitpid(); outfd1 = 1; outfd2 = 2; close(pfd[1]); if(p < 0 || p != pid) return; break; } if(wasconsctl >= 0){ consctl = open("/dev/consctl", OWRITE); if(consctl < 0) error("cannot open consctl"); } } static void cookedcatchint(void*, char *msg) { if(strstr(msg, "interrupt")) noted(NCONT); else if(strstr(msg, "kill")) noted(NDFLT); else noted(NCONT); } static int wasintr(void) { char err[64]; rerrstr(err, sizeof err); return strstr(err, "interrupt") != 0; } void fromstdin(Conn *c) { int n; char buf[1024]; int pid; int eofs; switch(pid = rfork(RFMEM|RFPROC|RFNOWAIT)){ case -1: error("fork: %r"); case 0: break; default: rfork(RFNOTEG); atexitkill(pid); return; } atexit(atexitkiller); if(interactive){ consctl = open("/dev/consctl", OWRITE); if(!cooked) rawon(); }else consctl = -1; notify(cookedcatchint); eofs = 0; for(;;){ n = read(0, buf, sizeof(buf)); if(n < 0){ if(wasintr()){ if(!raw){ buf[0] = 0x7f; n = 1; }else continue; }else break; } if(n == 0){ if(++eofs > 32) break; }else eofs = 0; if(interactive && usemenu && n && memchr(buf, 0x1c, n)) { if(menu(c)=='q'){ sendwritemsg(c, "", 0); exits("quit"); } continue; } if(!raw && n==0){ buf[0] = 0x4; n = 1; } sendwritemsg(c, buf, n); } sendwritemsg(c, "", 0); atexitdont(atexitkiller); exits(nil); } void winchanges(Conn *c) { int nrow, ncol, width, height; int pid; switch(pid = rfork(RFMEM|RFPROC|RFNOWAIT)){ case -1: error("fork: %r"); case 0: break; default: rfork(RFNOTEG); atexitkill(pid); return; } for(;;){ if(readgeom(&nrow, &ncol, &width, &height) < 0) break; sendwindowsize(c, nrow, ncol, width, height); } exits(nil); }