summaryrefslogtreecommitdiff
path: root/sys/src/cmd/ssh/ssh.c
diff options
context:
space:
mode:
authorTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
committerTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
commite5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch)
treed8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/cmd/ssh/ssh.c
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/cmd/ssh/ssh.c')
-rwxr-xr-xsys/src/cmd/ssh/ssh.c592
1 files changed, 592 insertions, 0 deletions
diff --git a/sys/src/cmd/ssh/ssh.c b/sys/src/cmd/ssh/ssh.c
new file mode 100755
index 000000000..83a2751e6
--- /dev/null
+++ b/sys/src/cmd/ssh/ssh.c
@@ -0,0 +1,592 @@
+#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);
+}