ssh1.c 9.9 KB

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