]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/ssh/ssh.c
python: update python build configuration to new ape capabilities like getaddrinfo...
[plan9front.git] / sys / src / cmd / ssh / ssh.c
1 #include "ssh.h"
2
3 int cooked = 0;         /* user wants cooked mode */
4 int raw = 0;            /* console is in raw mode */
5 int crstrip;
6 int interactive = -1;
7 int usemenu = 1;
8 int isatty(int);
9 int rawhack;
10 int forwardagent = 0;
11 char *buildcmd(int, char**);
12 void fromnet(Conn*);
13 void fromstdin(Conn*);
14 void winchanges(Conn*);
15 static void     sendwritemsg(Conn *c, char *buf, int n);
16
17 Cipher *allcipher[] = {
18         &cipherrc4,
19         &cipherblowfish,
20         &cipher3des,
21         &cipherdes,
22         &ciphernone,
23         &ciphertwiddle,
24 };
25
26 Auth *allauth[] = {
27         &authpassword,
28         &authrsa,
29         &authtis,
30 };
31
32 char *cipherlist = "blowfish rc4 3des";
33 char *authlist = "rsa password tis";
34
35 Cipher*
36 findcipher(char *name, Cipher **list, int nlist)
37 {
38         int i;
39
40         for(i=0; i<nlist; i++)
41                 if(strcmp(name, list[i]->name) == 0)
42                         return list[i];
43         error("unknown cipher %s", name);
44         return nil;
45 }
46
47 Auth*
48 findauth(char *name, Auth **list, int nlist)
49 {
50         int i;
51
52         for(i=0; i<nlist; i++)
53                 if(strcmp(name, list[i]->name) == 0)
54                         return list[i];
55         error("unknown auth %s", name);
56         return nil;
57 }
58
59 void
60 usage(void)
61 {
62         fprint(2, "usage: ssh [-CiImPpRr] [-A authlist] [-c cipherlist] [user@]hostname [cmd [args]]\n");
63         exits("usage");
64 }
65
66 void
67 main(int argc, char **argv)
68 {
69         int i, dowinchange, fd, usepty;
70         char *host, *cmd, *user, *p;
71         char *f[16];
72         Conn c;
73         Msg *m;
74
75         fmtinstall('B', mpfmt);
76         fmtinstall('H', encodefmt);
77         atexit(atexitkiller);
78         atexitkill(getpid());
79
80         dowinchange = 0;
81         if(getenv("LINES"))
82                 dowinchange = 1;
83         usepty = -1;
84         user = nil;
85         ARGBEGIN{
86         case 'B':       /* undocumented, debugging */
87                 doabort = 1;
88                 break;
89         case 'D':       /* undocumented, debugging */
90                 debuglevel = strtol(EARGF(usage()), nil, 0);
91                 break;
92         case 'l':       /* deprecated */
93         case 'u':
94                 user = EARGF(usage());
95                 break;
96         case 'a':       /* used by Unix scp implementations; we must ignore them. */
97         case 'x':
98                 break;
99
100         case 'A':
101                 authlist = EARGF(usage());
102                 break;
103         case 'C':
104                 cooked = 1;
105                 break;
106         case 'c':
107                 cipherlist = EARGF(usage());
108                 break;
109         case 'f':
110                 forwardagent = 1;
111                 break;
112         case 'I':
113                 interactive = 0;
114                 break;
115         case 'i':
116                 interactive = 1;
117                 break;
118         case 'm':
119                 usemenu = 0;
120                 break;
121         case 'P':
122                 usepty = 0;
123                 break;
124         case 'p':
125                 usepty = 1;
126                 break;
127         case 'R':
128                 rawhack = 1;
129                 break;
130         case 'r':
131                 crstrip = 1;
132                 break;
133         default:
134                 usage();
135         }ARGEND
136
137         if(argc < 1)
138                 usage();
139
140         host = argv[0];
141
142         cmd = nil;
143         if(argc > 1)
144                 cmd = buildcmd(argc-1, argv+1);
145
146         if((p = strchr(host, '@')) != nil){
147                 *p++ = '\0';
148                 user = host;
149                 host = p;
150         }
151         if(user == nil)
152                 user = getenv("user");
153         if(user == nil)
154                 sysfatal("cannot find user name");
155
156         privatefactotum();
157         if(interactive==-1)
158                 interactive = isatty(0);
159
160         if((fd = dial(netmkaddr(host, "tcp", "ssh"), nil, nil, nil)) < 0)
161                 sysfatal("dialing %s: %r", host);
162
163         memset(&c, 0, sizeof c);
164         c.interactive = interactive;
165         c.fd[0] = c.fd[1] = fd;
166         c.user = user;
167         c.host = host;
168         setaliases(&c, host);
169
170         c.nokcipher = getfields(cipherlist, f, nelem(f), 1, ", ");
171         c.okcipher = emalloc(sizeof(Cipher*)*c.nokcipher);
172         for(i=0; i<c.nokcipher; i++)
173                 c.okcipher[i] = findcipher(f[i], allcipher, nelem(allcipher));
174
175         c.nokauth = getfields(authlist, f, nelem(f), 1, ", ");
176         c.okauth = emalloc(sizeof(Auth*)*c.nokauth);
177         for(i=0; i<c.nokauth; i++)
178                 c.okauth[i] = findauth(f[i], allauth, nelem(allauth));
179
180         sshclienthandshake(&c);
181
182         if(forwardagent){
183                 if(startagent(&c) < 0)
184                         forwardagent = 0;
185         }
186         if(usepty == -1)
187                 usepty = cmd==nil;
188         if(usepty)
189                 requestpty(&c);
190         if(cmd){
191                 m = allocmsg(&c, SSH_CMSG_EXEC_CMD, 4+strlen(cmd));
192                 putstring(m, cmd);
193         }else
194                 m = allocmsg(&c, SSH_CMSG_EXEC_SHELL, 0);
195         sendmsg(m);
196
197         fromstdin(&c);
198         rfork(RFNOTEG); /* only fromstdin gets notes */
199         if(dowinchange)
200                 winchanges(&c);
201         fromnet(&c);
202         exits(0);
203 }
204
205 int
206 isatty(int fd)
207 {
208         char buf[64];
209
210         buf[0] = '\0';
211         fd2path(fd, buf, sizeof buf);
212         if(strlen(buf)>=9 && strcmp(buf+strlen(buf)-9, "/dev/cons")==0)
213                 return 1;
214         return 0;
215 }
216
217 char*
218 buildcmd(int argc, char **argv)
219 {
220         int i, len;
221         char *s, *t;
222
223         len = argc-1;
224         for(i=0; i<argc; i++)
225                 len += strlen(argv[i]);
226         s = emalloc(len+1);
227         t = s;
228         for(i=0; i<argc; i++){
229                 if(i)
230                         *t++ = ' ';
231                 strcpy(t, argv[i]);
232                 t += strlen(t);
233         }
234         return s;
235 }
236
237 void
238 fromnet(Conn *c)
239 {
240         int fd, len;
241         char *s, *es, *r, *w;
242         ulong ex;
243         char buf[64];
244         Msg *m;
245
246         for(;;){
247                 m = recvmsg(c, -1);
248                 if(m == nil)
249                         break;
250                 switch(m->type){
251                 default:
252                         badmsg(m, 0);
253
254                 case SSH_SMSG_EXITSTATUS:
255                         ex = getlong(m);
256                         if(ex==0)
257                                 exits(0);
258                         sprint(buf, "%lud", ex);
259                         exits(buf);
260
261                 case SSH_MSG_DISCONNECT:
262                         s = getstring(m);
263                         error("disconnect: %s", s);
264
265                 /*
266                  * If we ever add reverse port forwarding, we'll have to
267                  * revisit this.  It assumes that the agent connections are
268                  * the only ones.
269                  */
270                 case SSH_SMSG_AGENT_OPEN:
271                         if(!forwardagent)
272                                 error("server tried to use agent forwarding");
273                         handleagentopen(m);
274                         break;
275                 case SSH_MSG_CHANNEL_INPUT_EOF:
276                         if(!forwardagent)
277                                 error("server tried to use agent forwarding");
278                         handleagentieof(m);
279                         break;
280                 case SSH_MSG_CHANNEL_OUTPUT_CLOSED:
281                         if(!forwardagent)
282                                 error("server tried to use agent forwarding");
283                         handleagentoclose(m);
284                         break;
285                 case SSH_MSG_CHANNEL_DATA:
286                         if(!forwardagent)
287                                 error("server tried to use agent forwarding");
288                         handleagentmsg(m);
289                         break;
290
291                 case SSH_SMSG_STDOUT_DATA:
292                         fd = 1;
293                         goto Dataout;
294                 case SSH_SMSG_STDERR_DATA:
295                         fd = 2;
296                         goto Dataout;
297                 Dataout:
298                         len = getlong(m);
299                         s = (char*)getbytes(m, len);
300                         if(crstrip){
301                                 es = s+len;
302                                 for(r=w=s; r<es; r++)
303                                         if(*r != '\r')
304                                                 *w++ = *r;
305                                 len = w-s;
306                         }
307                         write(fd, s, len);
308                         break;
309                 }
310                 free(m);
311         }
312 }               
313
314 /*
315  * Lifted from telnet.c, con.c
316  */
317
318 static int consctl = -1;
319 static int outfd1=1, outfd2=2;  /* changed during system */
320 static void system(Conn*, char*);
321
322 /*
323  *  turn keyboard raw mode on
324  */
325 static void
326 rawon(void)
327 {
328         if(raw)
329                 return;
330         if(cooked)
331                 return;
332         if(consctl < 0)
333                 consctl = open("/dev/consctl", OWRITE);
334         if(consctl < 0)
335                 return;
336         if(write(consctl, "rawon", 5) != 5)
337                 return;
338         raw = 1;
339 }
340
341 /*
342  *  turn keyboard raw mode off
343  */
344 static void
345 rawoff(void)
346 {
347         if(raw == 0)
348                 return;
349         if(consctl < 0)
350                 return;
351         if(write(consctl, "rawoff", 6) != 6)
352                 return;
353         close(consctl);
354         consctl = -1;
355         raw = 0;
356 }
357
358 /*
359  *  control menu
360  */
361 #define STDHELP "\t(q)uit, (i)nterrupt, toggle printing (r)eturns, (.)continue, (!cmd)\n"
362
363 static int
364 menu(Conn *c)
365 {
366         char buf[1024];
367         long n;
368         int done;
369         int wasraw;
370
371         wasraw = raw;
372         if(wasraw)
373                 rawoff();
374
375         buf[0] = '?';
376         fprint(2, ">>> ");
377         for(done = 0; !done; ){
378                 n = read(0, buf, sizeof(buf)-1);
379                 if(n <= 0)
380                         return -1;
381                 buf[n] = 0;
382                 switch(buf[0]){
383                 case '!':
384                         print(buf);
385                         system(c, buf+1);
386                         print("!\n");
387                         done = 1;
388                         break;
389                 case 'i':
390                         buf[0] = 0x1c;
391                         sendwritemsg(c, buf, 1);
392                         done = 1;
393                         break;
394                 case '.':
395                 case 'q':
396                         done = 1;
397                         break;
398                 case 'r':
399                         crstrip = 1-crstrip;
400                         done = 1;
401                         break;
402                 default:
403                         fprint(2, STDHELP);
404                         break;
405                 }
406                 if(!done)
407                         fprint(2, ">>> ");
408         }
409
410         if(wasraw)
411                 rawon();
412         else
413                 rawoff();
414         return buf[0];
415 }
416
417 static void
418 sendwritemsg(Conn *c, char *buf, int n)
419 {
420         Msg *m;
421
422         if(n==0)
423                 m = allocmsg(c, SSH_CMSG_EOF, 0);
424         else{
425                 m = allocmsg(c, SSH_CMSG_STDIN_DATA, 4+n);
426                 putlong(m, n);
427                 putbytes(m, buf, n);
428         }
429         sendmsg(m);
430 }
431
432 /*
433  *  run a command with the network connection as standard IO
434  */
435 static void
436 system(Conn *c, char *cmd)
437 {
438         int pid;
439         int p;
440         int pfd[2];
441         int n;
442         int wasconsctl;
443         char buf[4096];
444
445         if(pipe(pfd) < 0){
446                 perror("pipe");
447                 return;
448         }
449         outfd1 = outfd2 = pfd[1];
450
451         wasconsctl = consctl;
452         close(consctl);
453         consctl = -1;
454         switch(pid = fork()){
455         case -1:
456                 perror("con");
457                 return;
458         case 0:
459                 close(pfd[1]);
460                 dup(pfd[0], 0);
461                 dup(pfd[0], 1);
462                 close(c->fd[0]);        /* same as c->fd[1] */
463                 close(pfd[0]);
464                 if(*cmd)
465                         execl("/bin/rc", "rc", "-c", cmd, nil);
466                 else
467                         execl("/bin/rc", "rc", nil);
468                 perror("con");
469                 exits("exec");
470                 break;
471         default:
472                 close(pfd[0]);
473                 while((n = read(pfd[1], buf, sizeof(buf))) > 0)
474                         sendwritemsg(c, buf, n);
475                 p = waitpid();
476                 outfd1 = 1;
477                 outfd2 = 2;
478                 close(pfd[1]);
479                 if(p < 0 || p != pid)
480                         return;
481                 break;
482         }
483         if(wasconsctl >= 0){
484                 consctl = open("/dev/consctl", OWRITE);
485                 if(consctl < 0)
486                         error("cannot open consctl");
487         }
488 }
489
490 static void
491 cookedcatchint(void*, char *msg)
492 {
493         if(strstr(msg, "interrupt"))
494                 noted(NCONT);
495         else if(strstr(msg, "kill"))
496                 noted(NDFLT);
497         else
498                 noted(NCONT);
499 }
500
501 static int
502 wasintr(void)
503 {
504         char err[64];
505
506         rerrstr(err, sizeof err);
507         return strstr(err, "interrupt") != 0;
508 }
509
510 void
511 fromstdin(Conn *c)
512 {
513         int n;
514         char buf[1024];
515         int pid;
516         int eofs;
517
518         switch(pid = rfork(RFMEM|RFPROC|RFNOWAIT)){
519         case -1:
520                 error("fork: %r");
521         case 0:
522                 break;
523         default:
524                 atexitkill(pid);
525                 return;
526         }
527
528         atexit(atexitkiller);
529         if(interactive)
530                 rawon();
531
532         notify(cookedcatchint);
533
534         eofs = 0;
535         for(;;){
536                 n = read(0, buf, sizeof(buf));
537                 if(n < 0){
538                         if(wasintr()){
539                                 if(!raw){
540                                         buf[0] = 0x7f;
541                                         n = 1;
542                                 }else
543                                         continue;
544                         }else
545                                 break;
546                 }
547                 if(n == 0){
548                         if(!c->interactive || ++eofs > 32)
549                                 break;
550                 }else
551                         eofs = 0;
552                 if(interactive && usemenu && n && memchr(buf, 0x1c, n)) {
553                         if(menu(c)=='q'){
554                                 sendwritemsg(c, "", 0);
555                                 exits("quit");
556                         }
557                         continue;
558                 }
559                 if(!raw && n==0){
560                         buf[0] = 0x4;
561                         n = 1;
562                 }
563                 sendwritemsg(c, buf, n);
564         }
565         sendwritemsg(c, "", 0);
566         atexitdont(atexitkiller);
567         exits(nil);
568 }
569
570 void
571 winchanges(Conn *c)
572 {
573         int nrow, ncol, width, height;
574         int pid;
575
576         switch(pid = rfork(RFMEM|RFPROC|RFNOWAIT)){
577         case -1:
578                 error("fork: %r");
579         case 0:
580                 break;
581         default:
582                 atexitkill(pid);
583                 return;
584         }
585
586         for(;;){
587                 if(readgeom(&nrow, &ncol, &width, &height) < 0)
588                         break;
589                 sendwindowsize(c, nrow, ncol, width, height);
590         }
591         exits(nil);
592 }