ssh.c 9.4 KB

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