1*515d8088SDavid du Colombier /*
2*515d8088SDavid du Colombier * ssh - remote login via SSH v2
3*515d8088SDavid du Colombier * /net/ssh does most of the work; we copy bytes back and forth
4*515d8088SDavid du Colombier */
5*515d8088SDavid du Colombier #include <u.h>
6*515d8088SDavid du Colombier #include <libc.h>
7*515d8088SDavid du Colombier #include <auth.h>
8*515d8088SDavid du Colombier #include "ssh2.h"
9*515d8088SDavid du Colombier
10*515d8088SDavid du Colombier int doauth(int, char *);
11*515d8088SDavid du Colombier int isatty(int);
12*515d8088SDavid du Colombier
13*515d8088SDavid du Colombier char *user, *remote;
14*515d8088SDavid du Colombier char *netdir = "/net";
15*515d8088SDavid du Colombier int debug = 0;
16*515d8088SDavid du Colombier
17*515d8088SDavid du Colombier static int stripcr = 0;
18*515d8088SDavid du Colombier static int mflag = 0;
19*515d8088SDavid du Colombier static int iflag = -1;
20*515d8088SDavid du Colombier static int nopw = 0, nopka = 0;
21*515d8088SDavid du Colombier static int chpid;
22*515d8088SDavid du Colombier static int reqfd, dfd1, cfd1, dfd2, cfd2, consfd, kconsfd, cctlfd, notefd, keyfd;
23*515d8088SDavid du Colombier
24*515d8088SDavid du Colombier void
usage(void)25*515d8088SDavid du Colombier usage(void)
26*515d8088SDavid du Colombier {
27*515d8088SDavid du Colombier fprint(2, "usage: %s [-dkKmr] [-l user] [-n dir] [-z attr=val] addr "
28*515d8088SDavid du Colombier "[cmd [args]]\n", argv0);
29*515d8088SDavid du Colombier exits("usage");
30*515d8088SDavid du Colombier }
31*515d8088SDavid du Colombier
32*515d8088SDavid du Colombier /*
33*515d8088SDavid du Colombier * this is probably overkill except writing "kill" to notefd;
34*515d8088SDavid du Colombier * file descriptors are closed by the kernel upon exit.
35*515d8088SDavid du Colombier */
36*515d8088SDavid du Colombier static void
shutdown(void)37*515d8088SDavid du Colombier shutdown(void)
38*515d8088SDavid du Colombier {
39*515d8088SDavid du Colombier if (cctlfd > 0) {
40*515d8088SDavid du Colombier fprint(cctlfd, "rawoff");
41*515d8088SDavid du Colombier close(cctlfd);
42*515d8088SDavid du Colombier }
43*515d8088SDavid du Colombier if (consfd > 0)
44*515d8088SDavid du Colombier close(consfd);
45*515d8088SDavid du Colombier if (reqfd > 0) {
46*515d8088SDavid du Colombier fprint(reqfd, "close");
47*515d8088SDavid du Colombier close(reqfd);
48*515d8088SDavid du Colombier }
49*515d8088SDavid du Colombier close(dfd2);
50*515d8088SDavid du Colombier close(dfd1);
51*515d8088SDavid du Colombier close(cfd2);
52*515d8088SDavid du Colombier close(cfd1);
53*515d8088SDavid du Colombier
54*515d8088SDavid du Colombier fprint(notefd, "kill");
55*515d8088SDavid du Colombier close(notefd);
56*515d8088SDavid du Colombier }
57*515d8088SDavid du Colombier
58*515d8088SDavid du Colombier static void
bail(char * sts)59*515d8088SDavid du Colombier bail(char *sts)
60*515d8088SDavid du Colombier {
61*515d8088SDavid du Colombier shutdown();
62*515d8088SDavid du Colombier exits(sts);
63*515d8088SDavid du Colombier }
64*515d8088SDavid du Colombier
65*515d8088SDavid du Colombier int
handler(void *,char * s)66*515d8088SDavid du Colombier handler(void *, char *s)
67*515d8088SDavid du Colombier {
68*515d8088SDavid du Colombier char *nf;
69*515d8088SDavid du Colombier int fd;
70*515d8088SDavid du Colombier
71*515d8088SDavid du Colombier if (strstr(s, "alarm") != nil)
72*515d8088SDavid du Colombier return 0;
73*515d8088SDavid du Colombier if (chpid) {
74*515d8088SDavid du Colombier nf = esmprint("/proc/%d/note", chpid);
75*515d8088SDavid du Colombier fd = open(nf, OWRITE);
76*515d8088SDavid du Colombier fprint(fd, "interrupt");
77*515d8088SDavid du Colombier close(fd);
78*515d8088SDavid du Colombier free(nf);
79*515d8088SDavid du Colombier }
80*515d8088SDavid du Colombier shutdown();
81*515d8088SDavid du Colombier return 1;
82*515d8088SDavid du Colombier }
83*515d8088SDavid du Colombier
84*515d8088SDavid du Colombier static void
parseargs(void)85*515d8088SDavid du Colombier parseargs(void)
86*515d8088SDavid du Colombier {
87*515d8088SDavid du Colombier int n;
88*515d8088SDavid du Colombier char *p, *q;
89*515d8088SDavid du Colombier
90*515d8088SDavid du Colombier q = strchr(remote, '@');
91*515d8088SDavid du Colombier if (q != nil) {
92*515d8088SDavid du Colombier user = remote;
93*515d8088SDavid du Colombier *q++ = 0;
94*515d8088SDavid du Colombier remote = q;
95*515d8088SDavid du Colombier }
96*515d8088SDavid du Colombier
97*515d8088SDavid du Colombier q = strchr(remote, '!');
98*515d8088SDavid du Colombier if (q) {
99*515d8088SDavid du Colombier n = q - remote;
100*515d8088SDavid du Colombier netdir = malloc(n+1);
101*515d8088SDavid du Colombier if (netdir == nil)
102*515d8088SDavid du Colombier sysfatal("out of memory");
103*515d8088SDavid du Colombier strncpy(netdir, remote, n+1);
104*515d8088SDavid du Colombier netdir[n] = '\0';
105*515d8088SDavid du Colombier
106*515d8088SDavid du Colombier p = strrchr(netdir, '/');
107*515d8088SDavid du Colombier if (p == nil) {
108*515d8088SDavid du Colombier free(netdir);
109*515d8088SDavid du Colombier netdir = "/net";
110*515d8088SDavid du Colombier } else if (strcmp(p+1, "ssh") == 0)
111*515d8088SDavid du Colombier *p = '\0';
112*515d8088SDavid du Colombier else
113*515d8088SDavid du Colombier remote = esmprint("%s/ssh", netdir);
114*515d8088SDavid du Colombier }
115*515d8088SDavid du Colombier
116*515d8088SDavid du Colombier }
117*515d8088SDavid du Colombier
118*515d8088SDavid du Colombier static int
catcher(void *,char * s)119*515d8088SDavid du Colombier catcher(void *, char *s)
120*515d8088SDavid du Colombier {
121*515d8088SDavid du Colombier return strstr(s, "alarm") != nil;
122*515d8088SDavid du Colombier }
123*515d8088SDavid du Colombier
124*515d8088SDavid du Colombier static int
timedmount(int fd,int afd,char * mntpt,int flag,char * aname)125*515d8088SDavid du Colombier timedmount(int fd, int afd, char *mntpt, int flag, char *aname)
126*515d8088SDavid du Colombier {
127*515d8088SDavid du Colombier int oalarm, ret;
128*515d8088SDavid du Colombier
129*515d8088SDavid du Colombier atnotify(catcher, 1);
130*515d8088SDavid du Colombier oalarm = alarm(5*1000); /* don't get stuck here */
131*515d8088SDavid du Colombier ret = mount(fd, afd, mntpt, flag, aname);
132*515d8088SDavid du Colombier alarm(oalarm);
133*515d8088SDavid du Colombier atnotify(catcher, 0);
134*515d8088SDavid du Colombier return ret;
135*515d8088SDavid du Colombier }
136*515d8088SDavid du Colombier
137*515d8088SDavid du Colombier static void
mounttunnel(char * srv)138*515d8088SDavid du Colombier mounttunnel(char *srv)
139*515d8088SDavid du Colombier {
140*515d8088SDavid du Colombier int fd;
141*515d8088SDavid du Colombier
142*515d8088SDavid du Colombier if (debug)
143*515d8088SDavid du Colombier fprint(2, "%s: mounting %s on /net\n", argv0, srv);
144*515d8088SDavid du Colombier fd = open(srv, OREAD);
145*515d8088SDavid du Colombier if (fd < 0) {
146*515d8088SDavid du Colombier if (debug)
147*515d8088SDavid du Colombier fprint(2, "%s: can't open %s: %r\n", argv0, srv);
148*515d8088SDavid du Colombier } else if (timedmount(fd, -1, netdir, MBEFORE, "") < 0) {
149*515d8088SDavid du Colombier fprint(2, "can't mount %s on %s: %r\n", srv, netdir);
150*515d8088SDavid du Colombier close(fd);
151*515d8088SDavid du Colombier }
152*515d8088SDavid du Colombier }
153*515d8088SDavid du Colombier
154*515d8088SDavid du Colombier static void
newtunnel(char * myname)155*515d8088SDavid du Colombier newtunnel(char *myname)
156*515d8088SDavid du Colombier {
157*515d8088SDavid du Colombier int kid, pid;
158*515d8088SDavid du Colombier
159*515d8088SDavid du Colombier if(debug)
160*515d8088SDavid du Colombier fprint(2, "%s: starting new netssh for key access\n", argv0);
161*515d8088SDavid du Colombier kid = rfork(RFPROC|RFNOTEG|RFENVG /* |RFFDG */);
162*515d8088SDavid du Colombier if (kid == 0) {
163*515d8088SDavid du Colombier // for (fd = 3; fd < 40; fd++)
164*515d8088SDavid du Colombier // close(fd);
165*515d8088SDavid du Colombier execl("/bin/netssh", "netssh", "-m", netdir, "-s", myname, nil);
166*515d8088SDavid du Colombier sysfatal("no /bin/netssh: %r");
167*515d8088SDavid du Colombier } else if (kid < 0)
168*515d8088SDavid du Colombier sysfatal("fork failed: %r");
169*515d8088SDavid du Colombier while ((pid = waitpid()) != kid && pid >= 0)
170*515d8088SDavid du Colombier ;
171*515d8088SDavid du Colombier }
172*515d8088SDavid du Colombier
173*515d8088SDavid du Colombier static void
starttunnel(void)174*515d8088SDavid du Colombier starttunnel(void)
175*515d8088SDavid du Colombier {
176*515d8088SDavid du Colombier char *keys, *mysrv, *myname;
177*515d8088SDavid du Colombier
178*515d8088SDavid du Colombier keys = esmprint("%s/ssh/keys", netdir);
179*515d8088SDavid du Colombier myname = esmprint("ssh.%s", getuser());
180*515d8088SDavid du Colombier mysrv = esmprint("/srv/%s", myname);
181*515d8088SDavid du Colombier
182*515d8088SDavid du Colombier if (access(keys, ORDWR) < 0)
183*515d8088SDavid du Colombier mounttunnel("/srv/netssh"); /* old name */
184*515d8088SDavid du Colombier if (access(keys, ORDWR) < 0)
185*515d8088SDavid du Colombier mounttunnel("/srv/ssh");
186*515d8088SDavid du Colombier if (access(keys, ORDWR) < 0)
187*515d8088SDavid du Colombier mounttunnel(mysrv);
188*515d8088SDavid du Colombier if (access(keys, ORDWR) < 0)
189*515d8088SDavid du Colombier newtunnel(myname);
190*515d8088SDavid du Colombier if (access(keys, ORDWR) < 0)
191*515d8088SDavid du Colombier mounttunnel(mysrv);
192*515d8088SDavid du Colombier
193*515d8088SDavid du Colombier /* if we *still* can't see our own tunnel, throw a tantrum. */
194*515d8088SDavid du Colombier if (access(keys, ORDWR) < 0)
195*515d8088SDavid du Colombier sysfatal("%s inaccessible: %r", keys); /* WTF? */
196*515d8088SDavid du Colombier
197*515d8088SDavid du Colombier free(myname);
198*515d8088SDavid du Colombier free(mysrv);
199*515d8088SDavid du Colombier free(keys);
200*515d8088SDavid du Colombier }
201*515d8088SDavid du Colombier
202*515d8088SDavid du Colombier int
cmdmode(void)203*515d8088SDavid du Colombier cmdmode(void)
204*515d8088SDavid du Colombier {
205*515d8088SDavid du Colombier int n, m;
206*515d8088SDavid du Colombier char buf[Arbbufsz];
207*515d8088SDavid du Colombier
208*515d8088SDavid du Colombier for(;;) {
209*515d8088SDavid du Colombier reprompt:
210*515d8088SDavid du Colombier print("\n>>> ");
211*515d8088SDavid du Colombier n = 0;
212*515d8088SDavid du Colombier do {
213*515d8088SDavid du Colombier m = read(0, buf + n, sizeof buf - n - 1);
214*515d8088SDavid du Colombier if (m <= 0)
215*515d8088SDavid du Colombier return 1;
216*515d8088SDavid du Colombier write(1, buf + n, m);
217*515d8088SDavid du Colombier n += m;
218*515d8088SDavid du Colombier buf[n] = '\0';
219*515d8088SDavid du Colombier if (buf[n-1] == ('u' & 037))
220*515d8088SDavid du Colombier goto reprompt;
221*515d8088SDavid du Colombier } while (buf[n-1] != '\n' && buf[n-1] != '\r');
222*515d8088SDavid du Colombier switch (buf[0]) {
223*515d8088SDavid du Colombier case '\n':
224*515d8088SDavid du Colombier case '\r':
225*515d8088SDavid du Colombier break;
226*515d8088SDavid du Colombier case 'q':
227*515d8088SDavid du Colombier return 1;
228*515d8088SDavid du Colombier case 'c':
229*515d8088SDavid du Colombier return 0;
230*515d8088SDavid du Colombier case 'r':
231*515d8088SDavid du Colombier stripcr = !stripcr;
232*515d8088SDavid du Colombier return 0;
233*515d8088SDavid du Colombier case 'h':
234*515d8088SDavid du Colombier print("c - continue\n");
235*515d8088SDavid du Colombier print("h - help\n");
236*515d8088SDavid du Colombier print("q - quit\n");
237*515d8088SDavid du Colombier print("r - toggle carriage return stripping\n");
238*515d8088SDavid du Colombier break;
239*515d8088SDavid du Colombier default:
240*515d8088SDavid du Colombier print("unknown command\n");
241*515d8088SDavid du Colombier break;
242*515d8088SDavid du Colombier }
243*515d8088SDavid du Colombier }
244*515d8088SDavid du Colombier }
245*515d8088SDavid du Colombier
246*515d8088SDavid du Colombier static void
keyprompt(char * buf,int size,int n)247*515d8088SDavid du Colombier keyprompt(char *buf, int size, int n)
248*515d8088SDavid du Colombier {
249*515d8088SDavid du Colombier if (*buf == 'c') {
250*515d8088SDavid du Colombier fprint(kconsfd, "The following key has been offered by the server:\n");
251*515d8088SDavid du Colombier write(kconsfd, buf+5, n);
252*515d8088SDavid du Colombier fprint(kconsfd, "\n\n");
253*515d8088SDavid du Colombier fprint(kconsfd, "Add this key? (yes, no, session) ");
254*515d8088SDavid du Colombier } else {
255*515d8088SDavid du Colombier fprint(kconsfd, "The following key does NOT match the known "
256*515d8088SDavid du Colombier "key(s) for the server:\n");
257*515d8088SDavid du Colombier write(kconsfd, buf+5, n);
258*515d8088SDavid du Colombier fprint(kconsfd, "\n\n");
259*515d8088SDavid du Colombier fprint(kconsfd, "Add this key? (yes, no, session, replace) ");
260*515d8088SDavid du Colombier }
261*515d8088SDavid du Colombier n = read(kconsfd, buf, size - 1);
262*515d8088SDavid du Colombier if (n <= 0)
263*515d8088SDavid du Colombier return;
264*515d8088SDavid du Colombier write(keyfd, buf, n); /* user's response -> /net/ssh/keys */
265*515d8088SDavid du Colombier seek(keyfd, 0, 2);
266*515d8088SDavid du Colombier if (readn(keyfd, buf, 5) <= 0)
267*515d8088SDavid du Colombier return;
268*515d8088SDavid du Colombier buf[5] = 0;
269*515d8088SDavid du Colombier n = strtol(buf+1, nil, 10);
270*515d8088SDavid du Colombier n = readn(keyfd, buf+5, n);
271*515d8088SDavid du Colombier if (n <= 0)
272*515d8088SDavid du Colombier return;
273*515d8088SDavid du Colombier buf[n+5] = 0;
274*515d8088SDavid du Colombier
275*515d8088SDavid du Colombier switch (*buf) {
276*515d8088SDavid du Colombier case 'b':
277*515d8088SDavid du Colombier case 'f':
278*515d8088SDavid du Colombier fprint(kconsfd, "%s\n", buf+5);
279*515d8088SDavid du Colombier case 'o':
280*515d8088SDavid du Colombier close(keyfd);
281*515d8088SDavid du Colombier close(kconsfd);
282*515d8088SDavid du Colombier }
283*515d8088SDavid du Colombier }
284*515d8088SDavid du Colombier
285*515d8088SDavid du Colombier /* talk the undocumented /net/ssh/keys protocol */
286*515d8088SDavid du Colombier static void
keyproc(char * buf,int size)287*515d8088SDavid du Colombier keyproc(char *buf, int size)
288*515d8088SDavid du Colombier {
289*515d8088SDavid du Colombier int n;
290*515d8088SDavid du Colombier char *p;
291*515d8088SDavid du Colombier
292*515d8088SDavid du Colombier if (size < 6)
293*515d8088SDavid du Colombier exits("keyproc buffer too small");
294*515d8088SDavid du Colombier p = esmprint("%s/ssh/keys", netdir);
295*515d8088SDavid du Colombier keyfd = open(p, ORDWR);
296*515d8088SDavid du Colombier if (keyfd < 0) {
297*515d8088SDavid du Colombier chpid = 0;
298*515d8088SDavid du Colombier sysfatal("failed to open ssh keys in %s: %r", p);
299*515d8088SDavid du Colombier }
300*515d8088SDavid du Colombier
301*515d8088SDavid du Colombier kconsfd = open("/dev/cons", ORDWR);
302*515d8088SDavid du Colombier if (kconsfd < 0)
303*515d8088SDavid du Colombier nopw = 1;
304*515d8088SDavid du Colombier
305*515d8088SDavid du Colombier buf[0] = 0;
306*515d8088SDavid du Colombier n = read(keyfd, buf, 5); /* reading /net/ssh/keys */
307*515d8088SDavid du Colombier if (n < 0)
308*515d8088SDavid du Colombier sysfatal("%s read: %r", p);
309*515d8088SDavid du Colombier buf[5] = 0;
310*515d8088SDavid du Colombier n = strtol(buf+1, nil, 10);
311*515d8088SDavid du Colombier n = readn(keyfd, buf+5, n);
312*515d8088SDavid du Colombier buf[n < 0? 5: n+5] = 0;
313*515d8088SDavid du Colombier free(p);
314*515d8088SDavid du Colombier
315*515d8088SDavid du Colombier switch (*buf) {
316*515d8088SDavid du Colombier case 'f':
317*515d8088SDavid du Colombier if (kconsfd >= 0)
318*515d8088SDavid du Colombier fprint(kconsfd, "%s\n", buf+5);
319*515d8088SDavid du Colombier /* fall through */
320*515d8088SDavid du Colombier case 'o':
321*515d8088SDavid du Colombier close(keyfd);
322*515d8088SDavid du Colombier if (kconsfd >= 0)
323*515d8088SDavid du Colombier close(kconsfd);
324*515d8088SDavid du Colombier break;
325*515d8088SDavid du Colombier default:
326*515d8088SDavid du Colombier if (kconsfd >= 0)
327*515d8088SDavid du Colombier keyprompt(buf, size, n);
328*515d8088SDavid du Colombier else {
329*515d8088SDavid du Colombier fprint(keyfd, "n");
330*515d8088SDavid du Colombier close(keyfd);
331*515d8088SDavid du Colombier }
332*515d8088SDavid du Colombier break;
333*515d8088SDavid du Colombier }
334*515d8088SDavid du Colombier chpid = 0;
335*515d8088SDavid du Colombier exits(nil);
336*515d8088SDavid du Colombier }
337*515d8088SDavid du Colombier
338*515d8088SDavid du Colombier /*
339*515d8088SDavid du Colombier * start a subproc to copy from network to stdout
340*515d8088SDavid du Colombier * while we copy from stdin to network.
341*515d8088SDavid du Colombier */
342*515d8088SDavid du Colombier static void
bidircopy(char * buf,int size)343*515d8088SDavid du Colombier bidircopy(char *buf, int size)
344*515d8088SDavid du Colombier {
345*515d8088SDavid du Colombier int i, n, lstart;
346*515d8088SDavid du Colombier char *path, *p, *q;
347*515d8088SDavid du Colombier
348*515d8088SDavid du Colombier rfork(RFNOTEG);
349*515d8088SDavid du Colombier path = esmprint("/proc/%d/notepg", getpid());
350*515d8088SDavid du Colombier notefd = open(path, OWRITE);
351*515d8088SDavid du Colombier
352*515d8088SDavid du Colombier switch (rfork(RFPROC|RFMEM|RFNOWAIT)) {
353*515d8088SDavid du Colombier case 0:
354*515d8088SDavid du Colombier while ((n = read(dfd2, buf, size - 1)) > 0) {
355*515d8088SDavid du Colombier if (!stripcr)
356*515d8088SDavid du Colombier p = buf + n;
357*515d8088SDavid du Colombier else
358*515d8088SDavid du Colombier for (i = 0, p = buf, q = buf; i < n; ++i, ++q)
359*515d8088SDavid du Colombier if (*q != '\r')
360*515d8088SDavid du Colombier *p++ = *q;
361*515d8088SDavid du Colombier if (p != buf)
362*515d8088SDavid du Colombier write(1, buf, p-buf);
363*515d8088SDavid du Colombier }
364*515d8088SDavid du Colombier /*
365*515d8088SDavid du Colombier * don't bother; it will be obvious when the user's prompt
366*515d8088SDavid du Colombier * changes.
367*515d8088SDavid du Colombier *
368*515d8088SDavid du Colombier * fprint(2, "%s: Connection closed by server\n", argv0);
369*515d8088SDavid du Colombier */
370*515d8088SDavid du Colombier break;
371*515d8088SDavid du Colombier default:
372*515d8088SDavid du Colombier lstart = 1;
373*515d8088SDavid du Colombier while ((n = read(0, buf, size - 1)) > 0) {
374*515d8088SDavid du Colombier if (!mflag && lstart && buf[0] == 0x1c)
375*515d8088SDavid du Colombier if (cmdmode())
376*515d8088SDavid du Colombier break;
377*515d8088SDavid du Colombier else
378*515d8088SDavid du Colombier continue;
379*515d8088SDavid du Colombier lstart = (buf[n-1] == '\n' || buf[n-1] == '\r');
380*515d8088SDavid du Colombier write(dfd2, buf, n);
381*515d8088SDavid du Colombier }
382*515d8088SDavid du Colombier /*
383*515d8088SDavid du Colombier * don't bother; it will be obvious when the user's prompt
384*515d8088SDavid du Colombier * changes.
385*515d8088SDavid du Colombier *
386*515d8088SDavid du Colombier * fprint(2, "%s: EOF on client side\n", argv0);
387*515d8088SDavid du Colombier */
388*515d8088SDavid du Colombier break;
389*515d8088SDavid du Colombier case -1:
390*515d8088SDavid du Colombier fprint(2, "%s: fork error: %r\n", argv0);
391*515d8088SDavid du Colombier break;
392*515d8088SDavid du Colombier }
393*515d8088SDavid du Colombier
394*515d8088SDavid du Colombier bail(nil);
395*515d8088SDavid du Colombier }
396*515d8088SDavid du Colombier
397*515d8088SDavid du Colombier static int
connect(char * buf,int size)398*515d8088SDavid du Colombier connect(char *buf, int size)
399*515d8088SDavid du Colombier {
400*515d8088SDavid du Colombier int nfd, n;
401*515d8088SDavid du Colombier char *dir, *ds, *nf;
402*515d8088SDavid du Colombier
403*515d8088SDavid du Colombier dir = esmprint("%s/ssh", netdir);
404*515d8088SDavid du Colombier ds = netmkaddr(remote, dir, "22"); /* tcp port 22 is ssh */
405*515d8088SDavid du Colombier free(dir);
406*515d8088SDavid du Colombier
407*515d8088SDavid du Colombier dfd1 = dial(ds, nil, nil, &cfd1);
408*515d8088SDavid du Colombier if (dfd1 < 0) {
409*515d8088SDavid du Colombier fprint(2, "%s: dial conn %s: %r\n", argv0, ds);
410*515d8088SDavid du Colombier if (chpid) {
411*515d8088SDavid du Colombier nf = esmprint("/proc/%d/note", chpid);
412*515d8088SDavid du Colombier nfd = open(nf, OWRITE);
413*515d8088SDavid du Colombier fprint(nfd, "interrupt");
414*515d8088SDavid du Colombier close(nfd);
415*515d8088SDavid du Colombier }
416*515d8088SDavid du Colombier exits("can't dial");
417*515d8088SDavid du Colombier }
418*515d8088SDavid du Colombier
419*515d8088SDavid du Colombier seek(cfd1, 0, 0);
420*515d8088SDavid du Colombier n = read(cfd1, buf, size - 1);
421*515d8088SDavid du Colombier buf[n >= 0? n: 0] = 0;
422*515d8088SDavid du Colombier return atoi(buf);
423*515d8088SDavid du Colombier }
424*515d8088SDavid du Colombier
425*515d8088SDavid du Colombier static int
chanconnect(int conn,char * buf,int size)426*515d8088SDavid du Colombier chanconnect(int conn, char *buf, int size)
427*515d8088SDavid du Colombier {
428*515d8088SDavid du Colombier int n;
429*515d8088SDavid du Colombier char *path;
430*515d8088SDavid du Colombier
431*515d8088SDavid du Colombier path = esmprint("%s/ssh/%d!session", netdir, conn);
432*515d8088SDavid du Colombier dfd2 = dial(path, nil, nil, &cfd2);
433*515d8088SDavid du Colombier if (dfd2 < 0) {
434*515d8088SDavid du Colombier fprint(2, "%s: dial chan %s: %r\n", argv0, path);
435*515d8088SDavid du Colombier bail("dial");
436*515d8088SDavid du Colombier }
437*515d8088SDavid du Colombier free(path);
438*515d8088SDavid du Colombier
439*515d8088SDavid du Colombier n = read(cfd2, buf, size - 1);
440*515d8088SDavid du Colombier buf[n >= 0? n: 0] = 0;
441*515d8088SDavid du Colombier return atoi(buf);
442*515d8088SDavid du Colombier }
443*515d8088SDavid du Colombier
444*515d8088SDavid du Colombier static void
remotecmd(int argc,char * argv[],int conn,int chan,char * buf,int size)445*515d8088SDavid du Colombier remotecmd(int argc, char *argv[], int conn, int chan, char *buf, int size)
446*515d8088SDavid du Colombier {
447*515d8088SDavid du Colombier int i;
448*515d8088SDavid du Colombier char *path, *q, *ep;
449*515d8088SDavid du Colombier
450*515d8088SDavid du Colombier path = esmprint("%s/ssh/%d/%d/request", netdir, conn, chan);
451*515d8088SDavid du Colombier reqfd = open(path, OWRITE);
452*515d8088SDavid du Colombier if (reqfd < 0)
453*515d8088SDavid du Colombier bail("can't open request chan");
454*515d8088SDavid du Colombier if (argc == 0)
455*515d8088SDavid du Colombier if (readfile("/env/TERM", buf, size) < 0)
456*515d8088SDavid du Colombier fprint(reqfd, "shell");
457*515d8088SDavid du Colombier else
458*515d8088SDavid du Colombier fprint(reqfd, "shell %s", buf);
459*515d8088SDavid du Colombier else {
460*515d8088SDavid du Colombier assert(size >= Bigbufsz);
461*515d8088SDavid du Colombier ep = buf + Bigbufsz;
462*515d8088SDavid du Colombier q = seprint(buf, ep, "exec");
463*515d8088SDavid du Colombier for (i = 0; i < argc; ++i)
464*515d8088SDavid du Colombier q = seprint(q, ep, " %q", argv[i]);
465*515d8088SDavid du Colombier if (q >= ep) {
466*515d8088SDavid du Colombier fprint(2, "%s: command too long\n", argv0);
467*515d8088SDavid du Colombier fprint(reqfd, "close");
468*515d8088SDavid du Colombier bail("cmd too long");
469*515d8088SDavid du Colombier }
470*515d8088SDavid du Colombier write(reqfd, buf, q - buf);
471*515d8088SDavid du Colombier }
472*515d8088SDavid du Colombier }
473*515d8088SDavid du Colombier
474*515d8088SDavid du Colombier void
main(int argc,char * argv[])475*515d8088SDavid du Colombier main(int argc, char *argv[])
476*515d8088SDavid du Colombier {
477*515d8088SDavid du Colombier char *whichkey;
478*515d8088SDavid du Colombier int conn, chan, n;
479*515d8088SDavid du Colombier char buf[Copybufsz];
480*515d8088SDavid du Colombier
481*515d8088SDavid du Colombier quotefmtinstall();
482*515d8088SDavid du Colombier reqfd = dfd1 = cfd1 = dfd2 = cfd2 = consfd = kconsfd = cctlfd =
483*515d8088SDavid du Colombier notefd = keyfd = -1;
484*515d8088SDavid du Colombier whichkey = nil;
485*515d8088SDavid du Colombier ARGBEGIN {
486*515d8088SDavid du Colombier case 'A': /* auth protos */
487*515d8088SDavid du Colombier case 'c': /* ciphers */
488*515d8088SDavid du Colombier fprint(2, "%s: sorry, -%c is not supported\n", argv0, ARGC());
489*515d8088SDavid du Colombier break;
490*515d8088SDavid du Colombier case 'a': /* compat? */
491*515d8088SDavid du Colombier case 'C': /* cooked mode */
492*515d8088SDavid du Colombier case 'f': /* agent forwarding */
493*515d8088SDavid du Colombier case 'p': /* force pty */
494*515d8088SDavid du Colombier case 'P': /* force no pty */
495*515d8088SDavid du Colombier case 'R': /* force raw mode on pty */
496*515d8088SDavid du Colombier case 'v': /* scp compat */
497*515d8088SDavid du Colombier case 'w': /* send window-size changes */
498*515d8088SDavid du Colombier case 'x': /* unix compat: no x11 forwarding */
499*515d8088SDavid du Colombier break;
500*515d8088SDavid du Colombier case 'd':
501*515d8088SDavid du Colombier debug++;
502*515d8088SDavid du Colombier break;
503*515d8088SDavid du Colombier case 'I': /* non-interactive */
504*515d8088SDavid du Colombier iflag = 0;
505*515d8088SDavid du Colombier break;
506*515d8088SDavid du Colombier case 'i': /* interactive: scp & rx do it */
507*515d8088SDavid du Colombier iflag = 1;
508*515d8088SDavid du Colombier break;
509*515d8088SDavid du Colombier case 'l':
510*515d8088SDavid du Colombier case 'u':
511*515d8088SDavid du Colombier user = EARGF(usage());
512*515d8088SDavid du Colombier break;
513*515d8088SDavid du Colombier case 'k':
514*515d8088SDavid du Colombier nopka = 1;
515*515d8088SDavid du Colombier break;
516*515d8088SDavid du Colombier case 'K':
517*515d8088SDavid du Colombier nopw = 1;
518*515d8088SDavid du Colombier break;
519*515d8088SDavid du Colombier case 'm':
520*515d8088SDavid du Colombier mflag = 1;
521*515d8088SDavid du Colombier break;
522*515d8088SDavid du Colombier case 'n':
523*515d8088SDavid du Colombier netdir = EARGF(usage());
524*515d8088SDavid du Colombier break;
525*515d8088SDavid du Colombier case 'r':
526*515d8088SDavid du Colombier stripcr = 1;
527*515d8088SDavid du Colombier break;
528*515d8088SDavid du Colombier case 'z':
529*515d8088SDavid du Colombier whichkey = EARGF(usage());
530*515d8088SDavid du Colombier break;
531*515d8088SDavid du Colombier default:
532*515d8088SDavid du Colombier usage();
533*515d8088SDavid du Colombier } ARGEND;
534*515d8088SDavid du Colombier if (argc == 0)
535*515d8088SDavid du Colombier usage();
536*515d8088SDavid du Colombier
537*515d8088SDavid du Colombier if (iflag == -1)
538*515d8088SDavid du Colombier iflag = isatty(0);
539*515d8088SDavid du Colombier remote = *argv++;
540*515d8088SDavid du Colombier --argc;
541*515d8088SDavid du Colombier
542*515d8088SDavid du Colombier parseargs();
543*515d8088SDavid du Colombier
544*515d8088SDavid du Colombier if (!user)
545*515d8088SDavid du Colombier user = getuser();
546*515d8088SDavid du Colombier if (user == nil || remote == nil)
547*515d8088SDavid du Colombier sysfatal("out of memory");
548*515d8088SDavid du Colombier
549*515d8088SDavid du Colombier starttunnel();
550*515d8088SDavid du Colombier
551*515d8088SDavid du Colombier /* fork subproc to handle keys; don't wait for it */
552*515d8088SDavid du Colombier if ((n = rfork(RFPROC|RFMEM|RFFDG|RFNOWAIT)) == 0)
553*515d8088SDavid du Colombier keyproc(buf, sizeof buf);
554*515d8088SDavid du Colombier chpid = n;
555*515d8088SDavid du Colombier atnotify(handler, 1);
556*515d8088SDavid du Colombier
557*515d8088SDavid du Colombier /* connect and learn connection number */
558*515d8088SDavid du Colombier conn = connect(buf, sizeof buf);
559*515d8088SDavid du Colombier
560*515d8088SDavid du Colombier consfd = open("/dev/cons", ORDWR);
561*515d8088SDavid du Colombier cctlfd = open("/dev/consctl", OWRITE);
562*515d8088SDavid du Colombier fprint(cctlfd, "rawon");
563*515d8088SDavid du Colombier if (doauth(cfd1, whichkey) < 0)
564*515d8088SDavid du Colombier bail("doauth");
565*515d8088SDavid du Colombier
566*515d8088SDavid du Colombier /* connect a channel of conn and learn channel number */
567*515d8088SDavid du Colombier chan = chanconnect(conn, buf, sizeof buf);
568*515d8088SDavid du Colombier
569*515d8088SDavid du Colombier /* open request channel, request shell or command execution */
570*515d8088SDavid du Colombier remotecmd(argc, argv, conn, chan, buf, sizeof buf);
571*515d8088SDavid du Colombier
572*515d8088SDavid du Colombier bidircopy(buf, sizeof buf);
573*515d8088SDavid du Colombier }
574*515d8088SDavid du Colombier
575*515d8088SDavid du Colombier int
isatty(int fd)576*515d8088SDavid du Colombier isatty(int fd)
577*515d8088SDavid du Colombier {
578*515d8088SDavid du Colombier char buf[64];
579*515d8088SDavid du Colombier
580*515d8088SDavid du Colombier buf[0] = '\0';
581*515d8088SDavid du Colombier fd2path(fd, buf, sizeof buf);
582*515d8088SDavid du Colombier return strlen(buf) >= 9 && strcmp(buf+strlen(buf)-9, "/dev/cons") == 0;
583*515d8088SDavid du Colombier }
584*515d8088SDavid du Colombier
585*515d8088SDavid du Colombier int
doauth(int cfd1,char * whichkey)586*515d8088SDavid du Colombier doauth(int cfd1, char *whichkey)
587*515d8088SDavid du Colombier {
588*515d8088SDavid du Colombier UserPasswd *up;
589*515d8088SDavid du Colombier int n;
590*515d8088SDavid du Colombier char path[Arbpathlen];
591*515d8088SDavid du Colombier
592*515d8088SDavid du Colombier if (!nopka) {
593*515d8088SDavid du Colombier if (whichkey)
594*515d8088SDavid du Colombier n = fprint(cfd1, "ssh-userauth K %q %q", user, whichkey);
595*515d8088SDavid du Colombier else
596*515d8088SDavid du Colombier n = fprint(cfd1, "ssh-userauth K %q", user);
597*515d8088SDavid du Colombier if (n >= 0)
598*515d8088SDavid du Colombier return 0;
599*515d8088SDavid du Colombier }
600*515d8088SDavid du Colombier if (nopw)
601*515d8088SDavid du Colombier return -1;
602*515d8088SDavid du Colombier up = auth_getuserpasswd(iflag? auth_getkey: nil,
603*515d8088SDavid du Colombier "proto=pass service=ssh server=%q user=%q", remote, user);
604*515d8088SDavid du Colombier if (up == nil) {
605*515d8088SDavid du Colombier fprint(2, "%s: didn't get password: %r\n", argv0);
606*515d8088SDavid du Colombier return -1;
607*515d8088SDavid du Colombier }
608*515d8088SDavid du Colombier n = fprint(cfd1, "ssh-userauth k %q %q", user, up->passwd);
609*515d8088SDavid du Colombier if (n >= 0)
610*515d8088SDavid du Colombier return 0;
611*515d8088SDavid du Colombier
612*515d8088SDavid du Colombier path[0] = '\0';
613*515d8088SDavid du Colombier fd2path(cfd1, path, sizeof path);
614*515d8088SDavid du Colombier fprint(2, "%s: auth ctl msg `ssh-userauth k %q <password>' for %q: %r\n",
615*515d8088SDavid du Colombier argv0, user, path);
616*515d8088SDavid du Colombier return -1;
617*515d8088SDavid du Colombier }
618