ssh.c 9.4 KB

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