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. if(dowinchange)
  174. winchanges(&c);
  175. fromstdin(&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, 0);
  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(consctl < 0)
  293. return;
  294. if(write(consctl, "rawon", 5) != 5)
  295. return;
  296. raw = 1;
  297. }
  298. /*
  299. * turn keyboard raw mode off
  300. */
  301. static void
  302. rawoff(void)
  303. {
  304. if(raw == 0)
  305. return;
  306. if(consctl < 0)
  307. return;
  308. if(write(consctl, "rawoff", 6) != 6)
  309. return;
  310. raw = 0;
  311. }
  312. /*
  313. * control menu
  314. */
  315. #define STDHELP "\t(q)uit, (i)nterrupt, toggle printing (r)eturns, (.)continue, (!cmd)\n"
  316. static int
  317. menu(Conn *c)
  318. {
  319. char buf[1024];
  320. long n;
  321. int done;
  322. int wasraw;
  323. wasraw = raw;
  324. if(wasraw)
  325. rawoff();
  326. buf[0] = '?';
  327. fprint(2, ">>> ");
  328. for(done = 0; !done; ){
  329. n = read(0, buf, sizeof(buf)-1);
  330. if(n <= 0)
  331. return -1;
  332. buf[n] = 0;
  333. switch(buf[0]){
  334. case '!':
  335. print(buf);
  336. system(c, buf+1);
  337. print("!\n");
  338. done = 1;
  339. break;
  340. case 'i':
  341. buf[0] = 0x1c;
  342. sendwritemsg(c, buf, 1);
  343. done = 1;
  344. break;
  345. case '.':
  346. case 'q':
  347. done = 1;
  348. break;
  349. case 'r':
  350. crstrip = 1-crstrip;
  351. done = 1;
  352. break;
  353. default:
  354. fprint(2, STDHELP);
  355. break;
  356. }
  357. if(!done)
  358. fprint(2, ">>> ");
  359. }
  360. if(wasraw)
  361. rawon();
  362. else
  363. rawoff();
  364. return buf[0];
  365. }
  366. static void
  367. sendwritemsg(Conn *c, char *buf, int n)
  368. {
  369. Msg *m;
  370. if(n==0)
  371. m = allocmsg(c, SSH_CMSG_EOF, 0);
  372. else{
  373. m = allocmsg(c, SSH_CMSG_STDIN_DATA, 4+n);
  374. putlong(m, n);
  375. putbytes(m, buf, n);
  376. }
  377. sendmsg(m);
  378. }
  379. /*
  380. * run a command with the network connection as standard IO
  381. */
  382. static void
  383. system(Conn *c, char *cmd)
  384. {
  385. int pid;
  386. int p;
  387. int pfd[2];
  388. int n;
  389. int wasconsctl;
  390. char buf[4096];
  391. if(pipe(pfd) < 0){
  392. perror("pipe");
  393. return;
  394. }
  395. outfd1 = outfd2 = pfd[1];
  396. wasconsctl = consctl;
  397. close(consctl);
  398. consctl = -1;
  399. switch(pid = fork()){
  400. case -1:
  401. perror("con");
  402. return;
  403. case 0:
  404. close(pfd[1]);
  405. dup(pfd[0], 0);
  406. dup(pfd[0], 1);
  407. close(c->fd[0]); /* same as c->fd[1] */
  408. close(pfd[0]);
  409. if(*cmd)
  410. execl("/bin/rc", "rc", "-c", cmd, 0);
  411. else
  412. execl("/bin/rc", "rc", 0);
  413. perror("con");
  414. exits("exec");
  415. break;
  416. default:
  417. close(pfd[0]);
  418. while((n = read(pfd[1], buf, sizeof(buf))) > 0)
  419. sendwritemsg(c, buf, n);
  420. p = waitpid();
  421. outfd1 = 1;
  422. outfd2 = 2;
  423. close(pfd[1]);
  424. if(p < 0 || p != pid)
  425. return;
  426. break;
  427. }
  428. if(wasconsctl >= 0){
  429. consctl = open("/dev/consctl", OWRITE);
  430. if(consctl < 0)
  431. error("cannot open consctl");
  432. }
  433. }
  434. static void
  435. cookedcatchint(void*, char *msg)
  436. {
  437. if(strstr(msg, "interrupt"))
  438. noted(NCONT);
  439. else if(strstr(msg, "kill"))
  440. noted(NDFLT);
  441. else
  442. noted(NCONT);
  443. }
  444. static int
  445. wasintr(void)
  446. {
  447. char err[64];
  448. rerrstr(err, sizeof err);
  449. return strstr(err, "interrupt") != 0;
  450. }
  451. void
  452. fromstdin(Conn *c)
  453. {
  454. int n;
  455. char buf[1024];
  456. int pid;
  457. int eofs;
  458. switch(pid = rfork(RFMEM|RFPROC|RFNOWAIT)){
  459. case -1:
  460. error("fork: %r");
  461. case 0:
  462. break;
  463. default:
  464. rfork(RFNOTEG);
  465. atexitkill(pid);
  466. return;
  467. }
  468. atexit(atexitkiller);
  469. if(interactive){
  470. consctl = open("/dev/consctl", OWRITE);
  471. if(!cooked)
  472. rawon();
  473. }else
  474. consctl = -1;
  475. notify(cookedcatchint);
  476. eofs = 0;
  477. for(;;){
  478. n = read(0, buf, sizeof(buf));
  479. if(n < 0){
  480. if(wasintr()){
  481. if(!raw){
  482. buf[0] = 0x7f;
  483. n = 1;
  484. }else
  485. continue;
  486. }else
  487. break;
  488. }
  489. if(n == 0){
  490. if(++eofs > 32)
  491. break;
  492. }else
  493. eofs = 0;
  494. if(interactive && usemenu && n && memchr(buf, 0x1c, n)) {
  495. if(menu(c)=='q'){
  496. sendwritemsg(c, "", 0);
  497. exits("quit");
  498. }
  499. continue;
  500. }
  501. if(!raw && n==0){
  502. buf[0] = 0x4;
  503. n = 1;
  504. }
  505. sendwritemsg(c, buf, n);
  506. }
  507. sendwritemsg(c, "", 0);
  508. atexitdont(atexitkiller);
  509. exits(nil);
  510. }
  511. void
  512. winchanges(Conn *c)
  513. {
  514. int nrow, ncol, width, height;
  515. int pid;
  516. switch(pid = rfork(RFMEM|RFPROC|RFNOWAIT)){
  517. case -1:
  518. error("fork: %r");
  519. case 0:
  520. break;
  521. default:
  522. rfork(RFNOTEG);
  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. }