#include <mp.h>
#include <libsec.h>
#include <auth.h>
+#include <authsrv.h>
enum {
MSG_DISCONNECT = 1,
MSG_USERAUTH_BANNER,
MSG_USERAUTH_PK_OK = 60,
+ MSG_USERAUTH_INFO_REQUEST = 60,
+ MSG_USERAUTH_INFO_RESPONSE = 61,
MSG_GLOBAL_REQUEST = 80,
MSG_REQUEST_SUCCESS,
MSG_CHANNEL_FAILURE,
};
+
+enum {
+ Overhead = 256, // enougth for MSG_CHANNEL_DATA header
+ MaxPacket = 1<<15,
+ WinPackets = 8, // (1<<15) * 8 = 256K
+};
+
+int MaxPwTries = 3; // retry this often for keyboard-interactive
+
typedef struct
{
u32int seq;
u32int kex;
+ u32int chan;
+
+ int win;
+ int pkt;
+ int eof;
+
Chachastate cs1;
Chachastate cs2;
- char *v;
- char eof;
uchar *r;
uchar *w;
- uchar b[1<<15];
+ uchar b[Overhead + MaxPacket];
+
+ char *v;
+ int pid;
+ Rendez;
} Oneway;
int nsid;
uchar sid[256];
+char thumb[2*SHA2_256dlen+1], *thumbfile;
-int fd, pid1, pid2, intr, raw, debug;
-char *user, *status, *host, *cmd;
+int fd, intr, raw, port, mux, debug;
+char *user, *service, *status, *host, *remote, *cmd;
Oneway recv, send;
+void dispatch(void);
void
shutdown(void)
{
- int pid = getpid();
- if(pid1 && pid1 != pid)
- postnote(PNPROC, pid1, "shutdown");
- if(pid2 && pid2 != pid)
- postnote(PNPROC, pid2, "shutdown");
+ recv.eof = send.eof = 1;
+ if(send.pid > 0)
+ postnote(PNPROC, send.pid, "shutdown");
}
void
catch(void*, char *msg)
{
- if(strstr(msg, "interrupt") != nil){
+ if(strcmp(msg, "interrupt") == 0){
intr = 1;
noted(NCONT);
}
char err[ERRMAX];
int r;
- if(intr)
- return 1;
memset(err, 0, sizeof(err));
errstr(err, sizeof(err));
- r = strstr(err, "interrupt") != nil;
+ r = strcmp(err, "interrupted") == 0;
errstr(err, sizeof(err));
return r;
}
if(n < 0 && wasintr()){
n = 0;
continue;
- }
+ } else if(n == 0)
+ werrstr("eof");
break;
}
}
return recv.r[0];
}
-void
-unexpected(char *info)
-{
- char *s;
- int n, c;
-
- switch(recv.r[0]){
- case MSG_DISCONNECT:
- if(unpack(recv.r, recv.w-recv.r, "_us", &c, &s, &n) < 0)
- break;
- sysfatal("disconnect: (%d) %.*s", c, n, s);
- break;
- case MSG_IGNORE:
- case MSG_GLOBAL_REQUEST:
- return;
- case MSG_DEBUG:
- if(unpack(recv.r, recv.w-recv.r, "__sb", &s, &n, &c) < 0)
- break;
- if(c != 0) fprint(2, "%s: %.*s\n", argv0, n, s);
- return;
- case MSG_USERAUTH_BANNER:
- if(unpack(recv.r, recv.w-recv.r, "_s", &s, &n) < 0)
- break;
- if(raw) write(2, s, n);
- return;
- }
- sysfatal("%s got: %.*H", info, (int)(recv.w - recv.r), recv.r);
-}
-
static char sshrsa[] = "ssh-rsa";
int
return m;
}
-/* libsec */
-extern mpint* pkcs1padbuf(uchar *buf, int len, mpint *modulus, int blocktype);
-extern int asn1encodedigest(DigestState* (*fun)(uchar*, ulong, uchar*, DigestState*),
- uchar *digest, uchar *buf, int len);
-
mpint*
pkcs1digest(uchar *data, int len, RSApub *pub)
{
kex(int gotkexinit)
{
static char kexalgs[] = "curve25519-sha256,curve25519-sha256@libssh.org";
- static char hostkeyalgs[] = "ssh-rsa";
static char cipheralgs[] = "chacha20-poly1305@openssh.com";
static char zipalgs[] = "none";
- static char macalgs[] = "";
+ static char macalgs[] = "hmac-sha1"; /* work around for github.com */
static char langs[] = "";
uchar cookie[16], x[32], yc[32], z[32], k[32+1], h[SHA2_256dlen], *ys, *ks, *sig;
uchar k12[2*ChachaKeylen];
- int nk, nys, nks, nsig;
+ int i, nk, nys, nks, nsig;
DigestState *ds;
mpint *S, *K;
RSApub *pub;
sendpkt("b[ssssssssssbu", MSG_KEXINIT,
cookie, sizeof(cookie),
kexalgs, sizeof(kexalgs)-1,
- hostkeyalgs, sizeof(hostkeyalgs)-1,
+ sshrsa, sizeof(sshrsa)-1,
cipheralgs, sizeof(cipheralgs)-1,
cipheralgs, sizeof(cipheralgs)-1,
macalgs, sizeof(macalgs)-1,
if(!gotkexinit){
Next0: switch(recvpkt()){
default:
- unexpected("KEXINIT");
+ dispatch();
goto Next0;
case MSG_KEXINIT:
break;
for(t=tab; *t != nil; t++){
if(unpack(p, recv.w-p, "s.", &s, &n, &p) < 0)
break;
- fprint(2, "%s: %.*s\n", *t, n, s);
+ fprint(2, "%s: %.*s\n", *t, utfnlen(s, n), s);
}
}
sendpkt("bs", MSG_ECDH_INIT, yc, sizeof(yc));
Next1: switch(recvpkt()){
default:
- unexpected("ECDH_INIT");
+ dispatch();
goto Next1;
+ case MSG_KEXINIT:
+ sysfatal("inception");
case MSG_ECDH_REPLY:
if(unpack(recv.r, recv.w-recv.r, "_sss", &ks, &nks, &ys, &nys, &sig, &nsig) < 0)
sysfatal("bad ECDH_REPLY");
ds = hashstr(yc, 32, ds);
ds = hashstr(ys, 32, ds);
+ if(thumb[0] == 0){
+ Thumbprint *ok;
+
+ sha2_256(ks, nks, h, nil);
+ i = enc64(thumb, sizeof(thumb), h, sizeof(h));
+ while(i > 0 && thumb[i-1] == '=')
+ i--;
+ thumb[i] = '\0';
+
+ if(debug)
+ fprint(2, "host fingerprint: %s\n", thumb);
+
+ ok = initThumbprints(thumbfile, nil, "ssh");
+ if(ok == nil || !okThumbprint(h, sizeof(h), ok)){
+ if(ok != nil) werrstr("unknown host");
+ fprint(2, "%s: %r\n", argv0);
+ fprint(2, "verify hostkey: %s %.*[\n", sshrsa, nks, ks);
+ fprint(2, "add thumbprint after verification:\n");
+ fprint(2, "\techo 'ssh sha256=%s server=%s' >> %q\n", thumb, host, thumbfile);
+ sysfatal("checking hostkey failed: %r");
+ }
+ freeThumbprints(ok);
+ }
+
if((pub = ssh2rsapub(ks, nks)) == nil)
sysfatal("bad server public key");
if((S = ssh2rsasig(sig, nsig)) == nil)
sendpkt("b", MSG_NEWKEYS);
Next2: switch(recvpkt()){
default:
- unexpected("NEWKEYS");
+ dispatch();
goto Next2;
+ case MSG_KEXINIT:
+ sysfatal("inception");
case MSG_NEWKEYS:
break;
}
setupChachastate(&recv.cs2, k12+0*ChachaKeylen, ChachaKeylen, nil, 64/8, 20);
}
+static char *authnext;
+
int
-auth(char *username, char *servicename)
+authok(char *meth)
{
- static char sshuserauth[] = "ssh-userauth";
- static char publickey[] = "publickey";
+ int ok = authnext == nil || strstr(authnext, meth) != nil;
+if(debug)
+ fprint(2, "userauth %s %s\n", meth, ok ? "ok" : "skipped");
+ return ok;
+}
+
+int
+authfailure(char *meth)
+{
+ char *s;
+ int n, partial;
+
+ if(unpack(recv.r, recv.w-recv.r, "_sb", &s, &n, &partial) < 0)
+ sysfatal("bad auth failure response");
+ free(authnext);
+ authnext = smprint("%.*s", utfnlen(s, n), s);
+if(debug)
+ fprint(2, "userauth %s failed: partial=%d, next=%s\n", meth, partial, authnext);
+ return partial != 0 || !authok(meth);
+}
+
+int
+noneauth(void)
+{
+ static char authmeth[] = "none";
+
+ if(!authok(authmeth))
+ return -1;
+
+ sendpkt("bsss", MSG_USERAUTH_REQUEST,
+ user, strlen(user),
+ service, strlen(service),
+ authmeth, sizeof(authmeth)-1);
+
+Next0: switch(recvpkt()){
+ default:
+ dispatch();
+ goto Next0;
+ case MSG_USERAUTH_FAILURE:
+ werrstr("authentication needed");
+ authfailure(authmeth);
+ return -1;
+ case MSG_USERAUTH_SUCCESS:
+ return 0;
+ }
+}
+
+int
+pubkeyauth(void)
+{
+ static char authmeth[] = "publickey";
uchar pk[4096], sig[4096];
int npk, nsig;
AuthRpc *rpc;
RSApub *pub;
- sendpkt("bs", MSG_SERVICE_REQUEST, sshuserauth, sizeof(sshuserauth)-1);
-Next0: switch(recvpkt()){
- default:
- unexpected("SERVICE_REQUEST");
- goto Next0;
- case MSG_SERVICE_ACCEPT:
- break;
- }
+ if(!authok(authmeth))
+ return -1;
if((afd = open("/mnt/factotum/rpc", ORDWR)) < 0)
return -1;
npk = rsapub2ssh(pub, pk, sizeof(pk));
sendpkt("bsssbss", MSG_USERAUTH_REQUEST,
- username, strlen(username),
- servicename, strlen(servicename),
- publickey, sizeof(publickey)-1,
+ user, strlen(user),
+ service, strlen(service),
+ authmeth, sizeof(authmeth)-1,
0,
sshrsa, sizeof(sshrsa)-1,
pk, npk);
Next1: switch(recvpkt()){
default:
- unexpected("USERAUTH_REQUEST");
+ dispatch();
goto Next1;
case MSG_USERAUTH_FAILURE:
+ if(authfailure(authmeth))
+ goto Failed;
continue;
case MSG_USERAUTH_SUCCESS:
case MSG_USERAUTH_PK_OK:
n = pack(send.b, sizeof(send.b), "sbsssbss",
sid, nsid,
MSG_USERAUTH_REQUEST,
- username, strlen(username),
- servicename, strlen(servicename),
- publickey, sizeof(publickey)-1,
+ user, strlen(user),
+ service, strlen(service),
+ authmeth, sizeof(authmeth)-1,
1,
sshrsa, sizeof(sshrsa)-1,
pk, npk);
/* send final userauth request with the signature */
sendpkt("bsssbsss", MSG_USERAUTH_REQUEST,
- username, strlen(username),
- servicename, strlen(servicename),
- publickey, sizeof(publickey)-1,
+ user, strlen(user),
+ service, strlen(service),
+ authmeth, sizeof(authmeth)-1,
1,
sshrsa, sizeof(sshrsa)-1,
pk, npk,
sig, nsig);
Next2: switch(recvpkt()){
default:
- unexpected("USERAUTH_REQUEST");
+ dispatch();
goto Next2;
case MSG_USERAUTH_FAILURE:
+ if(authfailure(authmeth))
+ goto Failed;
continue;
case MSG_USERAUTH_SUCCESS:
break;
close(afd);
return 0;
}
+Failed:
rsapubfree(pub);
auth_freerpc(rpc);
close(afd);
return -1;
}
+int
+passauth(void)
+{
+ static char authmeth[] = "password";
+ UserPasswd *up;
+
+ if(!authok(authmeth))
+ return -1;
+
+ up = auth_getuserpasswd(auth_getkey, "proto=pass service=ssh user=%q server=%q thumb=%q",
+ user, host, thumb);
+ if(up == nil)
+ return -1;
+
+ sendpkt("bsssbs", MSG_USERAUTH_REQUEST,
+ user, strlen(user),
+ service, strlen(service),
+ authmeth, sizeof(authmeth)-1,
+ 0,
+ up->passwd, strlen(up->passwd));
+
+ memset(up->passwd, 0, strlen(up->passwd));
+ free(up);
+
+Next0: switch(recvpkt()){
+ default:
+ dispatch();
+ goto Next0;
+ case MSG_USERAUTH_FAILURE:
+ werrstr("wrong password");
+ authfailure(authmeth);
+ return -1;
+ case MSG_USERAUTH_SUCCESS:
+ return 0;
+ }
+}
+
+int
+kbintauth(void)
+{
+ static char authmeth[] = "keyboard-interactive";
+ int tries;
+
+ char *name, *inst, *s, *a;
+ int fd, i, n, m;
+ int nquest, echo;
+ uchar *ans, *answ;
+ tries = 0;
+
+ if(!authok(authmeth))
+ return -1;
+
+Loop:
+ if(++tries > MaxPwTries)
+ return -1;
+
+ sendpkt("bsssss", MSG_USERAUTH_REQUEST,
+ user, strlen(user),
+ service, strlen(service),
+ authmeth, sizeof(authmeth)-1,
+ "", 0,
+ "", 0);
+
+Next0: switch(recvpkt()){
+ default:
+ dispatch();
+ goto Next0;
+ case MSG_USERAUTH_FAILURE:
+ werrstr("keyboard-interactive failed");
+ if(authfailure(authmeth))
+ return -1;
+ goto Loop;
+ case MSG_USERAUTH_SUCCESS:
+ return 0;
+ case MSG_USERAUTH_INFO_REQUEST:
+ break;
+ }
+Retry:
+ if((fd = open("/dev/cons", OWRITE)) < 0)
+ return -1;
+
+ if(unpack(recv.r, recv.w-recv.r, "_ss.", &name, &n, &inst, &m, &recv.r) < 0)
+ sysfatal("bad info request: name, inst");
+
+ while(n > 0 && strchr("\r\n\t ", name[n-1]) != nil)
+ n--;
+ while(m > 0 && strchr("\r\n\t ", inst[m-1]) != nil)
+ m--;
+
+ if(n > 0)
+ fprint(fd, "%.*s\n", utfnlen(name, n), name);
+ if(m > 0)
+ fprint(fd, "%.*s\n", utfnlen(inst, m), inst);
+
+ /* lang, nprompt */
+ if(unpack(recv.r, recv.w-recv.r, "su.", &s, &n, &nquest, &recv.r) < 0)
+ sysfatal("bad info request: lang, #quest");
+
+ ans = answ = nil;
+ for(i = 0; i < nquest; i++){
+ if(unpack(recv.r, recv.w-recv.r, "sb.", &s, &n, &echo, &recv.r) < 0)
+ sysfatal("bad info request: question [%d]", i);
+
+ while(n > 0 && strchr("\r\n\t :", s[n-1]) != nil)
+ n--;
+ s[n] = '\0';
+
+ if((a = readcons(s, nil, !echo)) == nil)
+ sysfatal("readcons: %r");
+
+ n = answ - ans;
+ m = strlen(a)+4;
+ if((s = realloc(ans, n + m)) == nil)
+ sysfatal("realloc: %r");
+ ans = (uchar*)s;
+ answ = ans+n;
+ answ += pack(answ, m, "s", a, m-4);
+ }
+
+ sendpkt("bu[", MSG_USERAUTH_INFO_RESPONSE, i, ans, answ - ans);
+ free(ans);
+ close(fd);
+
+Next1: switch(recvpkt()){
+ default:
+ dispatch();
+ goto Next1;
+ case MSG_USERAUTH_INFO_REQUEST:
+ goto Retry;
+ case MSG_USERAUTH_FAILURE:
+ werrstr("keyboard-interactive failed");
+ if(authfailure(authmeth))
+ return -1;
+ goto Loop;
+ case MSG_USERAUTH_SUCCESS:
+ return 0;
+ }
+}
+
+void
+dispatch(void)
+{
+ char *s;
+ uchar *p;
+ int n, b, c;
+
+ switch(recv.r[0]){
+ case MSG_IGNORE:
+ return;
+ case MSG_GLOBAL_REQUEST:
+ if(unpack(recv.r, recv.w-recv.r, "_sb", &s, &n, &b) < 0)
+ break;
+ if(debug)
+ fprint(2, "%s: global request: %.*s\n",
+ argv0, utfnlen(s, n), s);
+ if(b != 0)
+ sendpkt("b", MSG_REQUEST_FAILURE);
+ return;
+ case MSG_DISCONNECT:
+ if(unpack(recv.r, recv.w-recv.r, "_us", &c, &s, &n) < 0)
+ break;
+ sysfatal("disconnect: (%d) %.*s", c, utfnlen(s, n), s);
+ return;
+ case MSG_DEBUG:
+ if(unpack(recv.r, recv.w-recv.r, "__sb", &s, &n, &c) < 0)
+ break;
+ if(c != 0 || debug)
+ fprint(2, "%s: %.*s\n", argv0, utfnlen(s, n), s);
+ return;
+ case MSG_USERAUTH_BANNER:
+ if(unpack(recv.r, recv.w-recv.r, "_s", &s, &n) < 0)
+ break;
+ if(raw) write(2, s, n);
+ return;
+ case MSG_KEXINIT:
+ kex(1);
+ return;
+ }
+
+ if(mux){
+ n = recv.w - recv.r;
+ if(write(1, recv.r, n) != n)
+ sysfatal("write out: %r");
+ return;
+ }
+
+ switch(recv.r[0]){
+ case MSG_CHANNEL_DATA:
+ if(unpack(recv.r, recv.w-recv.r, "_us", &c, &s, &n) < 0)
+ break;
+ if(c != recv.chan)
+ break;
+ if(write(1, s, n) != n)
+ sysfatal("write out: %r");
+ Winadjust:
+ recv.win -= n;
+ if(recv.win < recv.pkt){
+ n = WinPackets*recv.pkt;
+ recv.win += n;
+ sendpkt("buu", MSG_CHANNEL_WINDOW_ADJUST, send.chan, n);
+ }
+ return;
+ case MSG_CHANNEL_EXTENDED_DATA:
+ if(unpack(recv.r, recv.w-recv.r, "_uus", &c, &b, &s, &n) < 0)
+ break;
+ if(c != recv.chan)
+ break;
+ if(b == 1) write(2, s, n);
+ goto Winadjust;
+ case MSG_CHANNEL_WINDOW_ADJUST:
+ if(unpack(recv.r, recv.w-recv.r, "_uu", &c, &n) < 0)
+ break;
+ if(c != recv.chan)
+ break;
+ send.win += n;
+ if(send.win >= send.pkt)
+ rwakeup(&send);
+ return;
+ case MSG_CHANNEL_REQUEST:
+ if(unpack(recv.r, recv.w-recv.r, "_usb.", &c, &s, &n, &b, &p) < 0)
+ break;
+ if(c != recv.chan)
+ break;
+ if(n == 11 && memcmp(s, "exit-signal", n) == 0){
+ if(unpack(p, recv.w-p, "s", &s, &n) < 0)
+ break;
+ if(n != 0 && status == nil)
+ status = smprint("%.*s", utfnlen(s, n), s);
+ c = MSG_CHANNEL_SUCCESS;
+ } else if(n == 11 && memcmp(s, "exit-status", n) == 0){
+ if(unpack(p, recv.w-p, "u", &n) < 0)
+ break;
+ if(n != 0 && status == nil)
+ status = smprint("%d", n);
+ c = MSG_CHANNEL_SUCCESS;
+ } else {
+ if(debug)
+ fprint(2, "%s: channel request: %.*s\n",
+ argv0, utfnlen(s, n), s);
+ c = MSG_CHANNEL_FAILURE;
+ }
+ if(b != 0)
+ sendpkt("bu", c, recv.chan);
+ return;
+ case MSG_CHANNEL_EOF:
+ recv.eof = 1;
+ if(!raw) write(1, "", 0);
+ return;
+ case MSG_CHANNEL_CLOSE:
+ shutdown();
+ return;
+ }
+ sysfatal("got: %.*H", (int)(recv.w - recv.r), recv.r);
+}
+
char*
readline(void)
{
int ypixels;
int lines;
int cols;
-} tty = {
- "dumb",
- 0,
- 0,
- 0,
- 0,
-};
+} tty;
+
+void
+getdim(void)
+{
+ char *s;
+
+ if(s = getenv("XPIXELS")){
+ tty.xpixels = atoi(s);
+ free(s);
+ }
+ if(s = getenv("YPIXELS")){
+ tty.ypixels = atoi(s);
+ free(s);
+ }
+ if(s = getenv("LINES")){
+ tty.lines = atoi(s);
+ free(s);
+ }
+ if(s = getenv("COLS")){
+ tty.cols = atoi(s);
+ free(s);
+ }
+}
void
rawon(void)
{
int ctl;
- char *s;
close(0);
if(open("/dev/cons", OREAD) != 0)
if(open("/dev/cons", OWRITE) != 1)
sysfatal("open: %r");
dup(1, 2);
- if((ctl = open("/dev/consctl", OWRITE)) >= 0)
+ if((ctl = open("/dev/consctl", OWRITE)) >= 0){
write(ctl, "rawon", 5);
- if(s = getenv("TERM")){
- tty.term = s;
- if(s = getenv("XPIXELS")){
- tty.xpixels = atoi(s);
- free(s);
- }
- if(s = getenv("YPIXELS")){
- tty.ypixels = atoi(s);
- free(s);
- }
- if(s = getenv("LINES")){
- tty.lines = atoi(s);
- free(s);
- }
- if(s = getenv("COLS")){
- tty.cols = atoi(s);
- free(s);
- }
+ write(ctl, "winchon", 7); /* vt(1): interrupt note on window change */
}
+ getdim();
+}
+
+#pragma varargck type "k" char*
+
+kfmt(Fmt *f)
+{
+ char *s, *p;
+ int n;
+
+ s = va_arg(f->args, char*);
+ n = fmtstrcpy(f, "'");
+ while((p = strchr(s, '\'')) != nil){
+ *p = '\0';
+ n += fmtstrcpy(f, s);
+ *p = '\'';
+ n += fmtstrcpy(f, "'\\''");
+ s = p+1;
+ }
+ n += fmtstrcpy(f, s);
+ n += fmtstrcpy(f, "'");
+ return n;
}
void
usage(void)
{
- fprint(2, "usage: %s [-dR] [-u user] [user@]host [cmd]\n", argv0);
+ fprint(2, "usage: %s [-dR] [-t thumbfile] [-T tries] [-u user] [-h] [user@]host [-W remote!port] [cmd args...]\n", argv0);
exits("usage");
}
void
main(int argc, char *argv[])
{
- static char buf[8*1024];
static QLock sl;
int b, n, c;
char *s;
- uchar *p;
quotefmtinstall();
fmtinstall('B', mpfmt);
fmtinstall('H', encodefmt);
+ fmtinstall('[', encodefmt);
+ fmtinstall('k', kfmt);
- s = getenv("TERM");
- raw = s != nil && strcmp(s, "dumb") != 0;
- free(s);
+ tty.term = getenv("TERM");
+ if(tty.term == nil)
+ tty.term = "";
+ raw = *tty.term != 0;
ARGBEGIN {
case 'd':
debug++;
break;
+ case 'W':
+ remote = EARGF(usage());
+ s = strrchr(remote, '!');
+ if(s == nil)
+ s = strrchr(remote, ':');
+ if(s == nil)
+ usage();
+ *s++ = 0;
+ port = atoi(s);
+ raw = 0;
+ break;
case 'R':
raw = 0;
break;
+ case 'r':
+ raw = 2; /* bloody */
+ break;
case 'u':
user = EARGF(usage());
break;
+ case 'h':
+ host = EARGF(usage());
+ break;
+ case 't':
+ thumbfile = EARGF(usage());
+ break;
+ case 'T':
+ MaxPwTries = strtol(EARGF(usage()), &s, 0);
+ if(*s != 0) usage();
+ break;
+ case 'X':
+ mux = 1;
+ raw = 0;
+ break;
+ default:
+ usage();
} ARGEND;
- if(argc == 0)
- usage();
+ if(host == nil){
+ if(argc == 0)
+ usage();
+ host = *argv++;
+ }
- host = *argv++;
if(user == nil){
s = strchr(host, '@');
if(s != nil){
host = s;
}
}
+
for(cmd = nil; *argv != nil; argv++){
- if(cmd == nil)
+ if(cmd == nil){
cmd = strdup(*argv);
- else {
- s = smprint("%s %q", cmd, *argv);
+ if(raw == 1)
+ raw = 0;
+ }else{
+ s = smprint("%s %k", cmd, *argv);
free(cmd);
cmd = s;
}
}
- if(cmd != nil)
- raw = 0;
+
+ if(remote != nil && cmd != nil)
+ usage();
if((fd = dial(netmkaddr(host, nil, "ssh"), nil, nil, nil)) < 0)
sysfatal("dial: %r");
sysfatal("bad server version: %s", recv.v);
recv.v = strdup(recv.v);
- kex(0);
+ send.l = recv.l = &sl;
if(user == nil)
user = getuser();
- if(auth(user, "ssh-connection") < 0)
+ if(thumbfile == nil)
+ thumbfile = smprint("%s/lib/sshthumbs", getenv("home"));
+
+ kex(0);
+
+ sendpkt("bs", MSG_SERVICE_REQUEST, "ssh-userauth", 12);
+Next0: switch(recvpkt()){
+ default:
+ dispatch();
+ goto Next0;
+ case MSG_SERVICE_ACCEPT:
+ break;
+ }
+
+ service = "ssh-connection";
+ if(noneauth() < 0 && pubkeyauth() < 0 && passauth() < 0 && kbintauth() < 0)
sysfatal("auth: %r");
+ recv.pkt = send.pkt = MaxPacket;
+ recv.win = send.win = WinPackets*recv.pkt;
+ recv.chan = send.win = 0;
+
+ if(mux)
+ goto Mux;
+
/* open hailing frequencies */
- sendpkt("bsuuu", MSG_CHANNEL_OPEN,
- "session", 7,
- 0,
- sizeof(buf),
- sizeof(buf));
+ if(remote != nil){
+ NetConnInfo *nci = getnetconninfo(nil, fd);
+ if(nci == nil)
+ sysfatal("can't get netconninfo: %r");
+ sendpkt("bsuuususu", MSG_CHANNEL_OPEN,
+ "direct-tcpip", 12,
+ recv.chan,
+ recv.win,
+ recv.pkt,
+ remote, strlen(remote),
+ port,
+ nci->laddr, strlen(nci->laddr),
+ atoi(nci->lserv));
+ free(nci);
+ } else {
+ sendpkt("bsuuu", MSG_CHANNEL_OPEN,
+ "session", 7,
+ recv.chan,
+ recv.win,
+ recv.pkt);
+ }
+Next1: switch(recvpkt()){
+ default:
+ dispatch();
+ goto Next1;
+ case MSG_CHANNEL_OPEN_FAILURE:
+ if(unpack(recv.r, recv.w-recv.r, "_uus", &c, &b, &s, &n) < 0)
+ n = strlen(s = "???");
+ sysfatal("channel open failure: (%d) %.*s", b, utfnlen(s, n), s);
+ case MSG_CHANNEL_OPEN_CONFIRMATION:
+ break;
+ }
+
+ if(unpack(recv.r, recv.w-recv.r, "_uuuu", &recv.chan, &send.chan, &send.win, &send.pkt) < 0)
+ sysfatal("bad channel open confirmation");
+ if(send.pkt <= 0 || send.pkt > MaxPacket)
+ send.pkt = MaxPacket;
+
+ if(remote != nil)
+ goto Mux;
- while((send.eof | recv.eof) == 0){
- if((int)(send.kex - send.seq) <= 0 || (int)(recv.kex - recv.seq) <= 0){
- qlock(&sl);
- kex(0);
+ if(raw) {
+ rawon();
+ sendpkt("busbsuuuus", MSG_CHANNEL_REQUEST,
+ send.chan,
+ "pty-req", 7,
+ 0,
+ tty.term, strlen(tty.term),
+ tty.cols,
+ tty.lines,
+ tty.xpixels,
+ tty.ypixels,
+ "", 0);
+ }
+ if(cmd == nil){
+ sendpkt("busb", MSG_CHANNEL_REQUEST,
+ send.chan,
+ "shell", 5,
+ 0);
+ } else if(*cmd == '#') {
+ sendpkt("busbs", MSG_CHANNEL_REQUEST,
+ send.chan,
+ "subsystem", 9,
+ 0,
+ cmd+1, strlen(cmd)-1);
+ } else {
+ sendpkt("busbs", MSG_CHANNEL_REQUEST,
+ send.chan,
+ "exec", 4,
+ 0,
+ cmd, strlen(cmd));
+ }
+
+Mux:
+ notify(catch);
+ atexit(shutdown);
+
+ recv.pid = getpid();
+ n = rfork(RFPROC|RFMEM);
+ if(n < 0)
+ sysfatal("fork: %r");
+
+ /* parent reads and dispatches packets */
+ if(n > 0) {
+ send.pid = n;
+ while(recv.eof == 0){
+ recvpkt();
+ qlock(&sl);
+ dispatch();
+ if((int)(send.kex - send.seq) <= 0 || (int)(recv.kex - recv.seq) <= 0)
+ kex(0);
qunlock(&sl);
}
- switch(recvpkt()){
- default:
- unexpected("CHANNEL");
- continue;
- case MSG_KEXINIT:
- qlock(&sl);
- kex(1);
- qunlock(&sl);
- continue;
- case MSG_CHANNEL_WINDOW_ADJUST:
- continue;
- case MSG_CHANNEL_EXTENDED_DATA:
- if(unpack(recv.r, recv.w-recv.r, "_uus", &c, &b, &s, &n) < 0)
- unexpected("CHANNEL_EXTENDED_DATA");
- if(b == 1) write(2, s, n);
- sendpkt("buu", MSG_CHANNEL_WINDOW_ADJUST, c, n);
- continue;
- case MSG_CHANNEL_DATA:
- if(unpack(recv.r, recv.w-recv.r, "_us", &c, &s, &n) < 0)
- unexpected("CHANNEL_DATA");
- write(1, s, n);
- sendpkt("buu", MSG_CHANNEL_WINDOW_ADJUST, c, n);
- continue;
- case MSG_CHANNEL_EOF:
- recv.eof = 1;
- if(!raw) write(1, "", 0);
- continue;
- case MSG_CHANNEL_OPEN_FAILURE:
- if(unpack(recv.r, recv.w-recv.r, "_uus", &c, &b, &s, &n) < 0)
- unexpected("CHANNEL_OPEN_FAILURE");
- sysfatal("channel open failure: (%d) %.*s", b, n, s);
- break;
- case MSG_CHANNEL_OPEN_CONFIRMATION:
- if(raw) {
- rawon();
- sendpkt("busbsuuuus", MSG_CHANNEL_REQUEST,
- 0,
- "pty-req", 7,
- 0,
- tty.term, strlen(tty.term),
- tty.cols,
- tty.lines,
- tty.xpixels,
- tty.ypixels,
- "", 0);
- }
- if(cmd == nil){
- sendpkt("busb", MSG_CHANNEL_REQUEST,
- 0,
- "shell", 5,
- 0);
- } else {
- sendpkt("busbs", MSG_CHANNEL_REQUEST,
- 0,
- "exec", 4,
- 0,
- cmd, strlen(cmd));
- }
- if(pid2)
- continue;
- pid1 = getpid();
- notify(catch);
- atexit(shutdown);
- n = rfork(RFPROC|RFMEM);
- if(n){
- pid2 = n;
- continue;
- }
- qlock(&sl);
- for(;;){
- qunlock(&sl);
- n = read(0, buf, sizeof(buf));
- qlock(&sl);
- if(n < 0 && wasintr()){
- sendpkt("busbs", MSG_CHANNEL_REQUEST,
- 0,
- "signal", 6,
- 0,
- "INT", 3);
- intr = 0;
- continue;
- }
- if(n <= 0)
- break;
- sendpkt("bus", MSG_CHANNEL_DATA,
- 0,
- buf, n);
- }
- send.eof = 1;
- sendpkt("bu", MSG_CHANNEL_EOF, 0);
- qunlock(&sl);
+ exits(status);
+ }
+
+ /* child reads input and sends packets */
+ qlock(&sl);
+ for(;;){
+ static uchar buf[MaxPacket];
+ qunlock(&sl);
+ n = read(0, buf, send.pkt);
+ qlock(&sl);
+ if(send.eof)
break;
- case MSG_CHANNEL_REQUEST:
- if(unpack(recv.r, recv.w-recv.r, "_usb.", &c, &s, &n, &b, &p) < 0)
- unexpected("CHANNEL_REQUEST");
- if(n == 11 && memcmp(s, "exit-signal", n) == 0){
- if(unpack(p, recv.w-p, "s", &s, &n) < 0)
- continue;
- if(n != 0 && status == nil)
- status = smprint("%.*s", n, s);
- } else if(n == 11 && memcmp(s, "exit-status", n) == 0){
- if(unpack(p, recv.w-p, "u", &n) < 0)
- continue;
- if(n != 0 && status == nil)
- status = smprint("%d", n);
- } else {
- fprint(2, "%s: channel request: %.*s\n", argv0, n, s);
- }
+ if(n < 0 && wasintr())
+ intr = 1;
+ if(intr){
+ if(!raw) break;
+ getdim();
+ sendpkt("busbuuuu", MSG_CHANNEL_REQUEST,
+ send.chan,
+ "window-change", 13,
+ 0,
+ tty.cols,
+ tty.lines,
+ tty.xpixels,
+ tty.ypixels);
+ sendpkt("busbs", MSG_CHANNEL_REQUEST,
+ send.chan,
+ "signal", 6,
+ 0,
+ "INT", 3);
+ intr = 0;
continue;
- case MSG_CHANNEL_CLOSE:
+ }
+ if(n <= 0)
break;
+ if(mux){
+ sendpkt("[", buf, n);
+ continue;
}
- break;
+ send.win -= n;
+ while(send.win < 0)
+ rsleep(&send);
+ sendpkt("bus", MSG_CHANNEL_DATA,
+ send.chan,
+ buf, n);
}
- exits(status);
+ if(send.eof++ == 0 && !mux)
+ sendpkt("bu", raw ? MSG_CHANNEL_CLOSE : MSG_CHANNEL_EOF, send.chan);
+ else if(recv.pid > 0 && mux)
+ postnote(PNPROC, recv.pid, "shutdown");
+ qunlock(&sl);
+
+ exits(nil);
}