123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592 |
- #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; i<nlist; i++)
- if(strcmp(name, list[i]->name) == 0)
- return list[i];
- error("unknown cipher %s", name);
- return nil;
- }
- Auth*
- findauth(char *name, Auth **list, int nlist)
- {
- int i;
- for(i=0; i<nlist; i++)
- if(strcmp(name, list[i]->name) == 0)
- return list[i];
- error("unknown auth %s", name);
- return nil;
- }
- void
- usage(void)
- {
- fprint(2, "usage: ssh [-CiImPpRr] [-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;
- if(getenv("LINES"))
- dowinchange = 1;
- 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;
- 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<c.nokcipher; i++)
- c.okcipher[i] = findcipher(f[i], allcipher, nelem(allcipher));
- c.nokauth = getfields(authlist, f, nelem(f), 1, ", ");
- c.okauth = emalloc(sizeof(Auth*)*c.nokauth);
- for(i=0; i<c.nokauth; i++)
- c.okauth[i] = findauth(f[i], allauth, nelem(allauth));
- sshclienthandshake(&c);
- if(forwardagent){
- if(startagent(&c) < 0)
- forwardagent = 0;
- }
- if(usepty == -1)
- usepty = cmd==nil;
- if(usepty)
- requestpty(&c);
- if(cmd){
- m = allocmsg(&c, SSH_CMSG_EXEC_CMD, 4+strlen(cmd));
- putstring(m, cmd);
- }else
- m = allocmsg(&c, SSH_CMSG_EXEC_SHELL, 0);
- sendmsg(m);
- fromstdin(&c);
- rfork(RFNOTEG); /* only fromstdin gets notes */
- if(dowinchange)
- winchanges(&c);
- fromnet(&c);
- exits(0);
- }
- int
- isatty(int fd)
- {
- char buf[64];
- buf[0] = '\0';
- fd2path(fd, buf, sizeof buf);
- if(strlen(buf)>=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; i<argc; i++)
- len += strlen(argv[i]);
- s = emalloc(len+1);
- t = s;
- for(i=0; i<argc; i++){
- if(i)
- *t++ = ' ';
- strcpy(t, argv[i]);
- t += strlen(t);
- }
- return s;
- }
- void
- fromnet(Conn *c)
- {
- int fd, len;
- char *s, *es, *r, *w;
- ulong ex;
- char buf[64];
- Msg *m;
- for(;;){
- m = recvmsg(c, -1);
- if(m == nil)
- break;
- switch(m->type){
- 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<es; r++)
- if(*r != '\r')
- *w++ = *r;
- len = w-s;
- }
- write(fd, s, len);
- break;
- }
- free(m);
- }
- }
- /*
- * Lifted from telnet.c, con.c
- */
- static int consctl = -1;
- static int outfd1=1, outfd2=2; /* changed during system */
- static void system(Conn*, char*);
- /*
- * turn keyboard raw mode on
- */
- static void
- rawon(void)
- {
- if(raw)
- return;
- if(cooked)
- return;
- if(consctl < 0)
- consctl = open("/dev/consctl", OWRITE);
- if(consctl < 0)
- return;
- if(write(consctl, "rawon", 5) != 5)
- return;
- raw = 1;
- }
- /*
- * turn keyboard raw mode off
- */
- static void
- rawoff(void)
- {
- if(raw == 0)
- return;
- if(consctl < 0)
- return;
- if(write(consctl, "rawoff", 6) != 6)
- return;
- close(consctl);
- consctl = -1;
- raw = 0;
- }
- /*
- * control menu
- */
- #define STDHELP "\t(q)uit, (i)nterrupt, toggle printing (r)eturns, (.)continue, (!cmd)\n"
- static int
- menu(Conn *c)
- {
- char buf[1024];
- long n;
- int done;
- int wasraw;
- wasraw = raw;
- if(wasraw)
- rawoff();
- buf[0] = '?';
- fprint(2, ">>> ");
- 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, nil);
- else
- execl("/bin/rc", "rc", nil);
- 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:
- atexitkill(pid);
- return;
- }
- atexit(atexitkiller);
- if(interactive)
- rawon();
- 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(!c->interactive || ++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:
- atexitkill(pid);
- return;
- }
- for(;;){
- if(readgeom(&nrow, &ncol, &width, &height) < 0)
- break;
- sendwindowsize(c, nrow, ncol, width, height);
- }
- exits(nil);
- }
|