ssh1.c 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592
  1. /* remote login via ssh v1 */
  2. #include "ssh.h"
  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. * Lifted from telnet.c, con.c
  18. */
  19. static int consctl = -1;
  20. static int outfd = 1; /* changed during system */
  21. static void system(Conn*, char*);
  22. Cipher *allcipher[] = {
  23. &cipherrc4,
  24. &cipherblowfish,
  25. &cipher3des,
  26. &cipherdes,
  27. &ciphernone,
  28. &ciphertwiddle,
  29. };
  30. Auth *allauth[] = {
  31. &authpassword,
  32. &authrsa,
  33. &authtis,
  34. };
  35. char *cipherlist = "blowfish rc4 3des";
  36. char *authlist = "rsa password tis";
  37. Cipher*
  38. findcipher(char *name, Cipher **list, int nlist)
  39. {
  40. int i;
  41. for(i=0; i<nlist; i++)
  42. if(strcmp(name, list[i]->name) == 0)
  43. return list[i];
  44. error("unknown cipher %s", name);
  45. return nil;
  46. }
  47. Auth*
  48. findauth(char *name, Auth **list, int nlist)
  49. {
  50. int i;
  51. for(i=0; i<nlist; i++)
  52. if(strcmp(name, list[i]->name) == 0)
  53. return list[i];
  54. error("unknown auth %s", name);
  55. return nil;
  56. }
  57. void
  58. usage(void)
  59. {
  60. fprint(2, "usage: ssh [-CiImPpRr] [-A authlist] [-c cipherlist] [user@]hostname [cmd [args]]\n");
  61. exits("usage");
  62. }
  63. void
  64. main(int argc, char **argv)
  65. {
  66. int i, dowinchange, fd, usepty;
  67. char *host, *cmd, *user, *p;
  68. char *f[16];
  69. Conn c;
  70. Msg *m;
  71. fmtinstall('B', mpfmt);
  72. fmtinstall('H', encodefmt);
  73. atexit(atexitkiller);
  74. atexitkill(getpid());
  75. dowinchange = 0;
  76. if(getenv("LINES"))
  77. dowinchange = 1;
  78. usepty = -1;
  79. user = nil;
  80. ARGBEGIN{
  81. case 'B': /* undocumented, debugging */
  82. doabort = 1;
  83. break;
  84. case 'D': /* undocumented, debugging */
  85. debuglevel = strtol(EARGF(usage()), nil, 0);
  86. break;
  87. case 'l': /* deprecated */
  88. case 'u':
  89. user = EARGF(usage());
  90. break;
  91. case 'a': /* used by Unix scp implementations; we must ignore them. */
  92. case 'x':
  93. break;
  94. case 'A':
  95. authlist = EARGF(usage());
  96. break;
  97. case 'C':
  98. cooked = 1;
  99. break;
  100. case 'c':
  101. cipherlist = EARGF(usage());
  102. break;
  103. case 'f':
  104. forwardagent = 1;
  105. break;
  106. case 'I':
  107. interactive = 0;
  108. break;
  109. case 'i':
  110. interactive = 1;
  111. break;
  112. case 'm':
  113. usemenu = 0;
  114. break;
  115. case 'P':
  116. usepty = 0;
  117. break;
  118. case 'p':
  119. usepty = 1;
  120. break;
  121. case 'R':
  122. rawhack = 1;
  123. break;
  124. case 'r':
  125. crstrip = 1;
  126. break;
  127. default:
  128. usage();
  129. }ARGEND
  130. if(argc < 1)
  131. usage();
  132. host = argv[0];
  133. cmd = nil;
  134. if(argc > 1)
  135. cmd = buildcmd(argc-1, argv+1);
  136. if((p = strchr(host, '@')) != nil){
  137. *p++ = '\0';
  138. user = host;
  139. host = p;
  140. }
  141. if(user == nil)
  142. user = getenv("user");
  143. if(user == nil)
  144. sysfatal("cannot find user name");
  145. privatefactotum();
  146. if(interactive==-1)
  147. interactive = isatty(0);
  148. if((fd = dial(netmkaddr(host, "tcp", "ssh"), nil, nil, nil)) < 0)
  149. sysfatal("dialing %s: %r", host);
  150. memset(&c, 0, sizeof c);
  151. c.interactive = interactive;
  152. c.fd[0] = c.fd[1] = fd;
  153. c.user = user;
  154. c.host = host;
  155. setaliases(&c, host);
  156. c.nokcipher = getfields(cipherlist, f, nelem(f), 1, ", ");
  157. c.okcipher = emalloc(sizeof(Cipher*)*c.nokcipher);
  158. for(i=0; i<c.nokcipher; i++)
  159. c.okcipher[i] = findcipher(f[i], allcipher, nelem(allcipher));
  160. c.nokauth = getfields(authlist, f, nelem(f), 1, ", ");
  161. c.okauth = emalloc(sizeof(Auth*)*c.nokauth);
  162. for(i=0; i<c.nokauth; i++)
  163. c.okauth[i] = findauth(f[i], allauth, nelem(allauth));
  164. sshclienthandshake(&c);
  165. if(forwardagent){
  166. if(startagent(&c) < 0)
  167. forwardagent = 0;
  168. }
  169. if(usepty == -1)
  170. usepty = cmd==nil;
  171. if(usepty)
  172. requestpty(&c);
  173. if(cmd){
  174. m = allocmsg(&c, SSH_CMSG_EXEC_CMD, 4+strlen(cmd));
  175. putstring(m, cmd);
  176. }else
  177. m = allocmsg(&c, SSH_CMSG_EXEC_SHELL, 0);
  178. sendmsg(m);
  179. fromstdin(&c);
  180. rfork(RFNOTEG); /* only fromstdin gets notes */
  181. if(dowinchange)
  182. winchanges(&c);
  183. fromnet(&c);
  184. exits(0);
  185. }
  186. int
  187. isatty(int fd)
  188. {
  189. char buf[64];
  190. buf[0] = '\0';
  191. fd2path(fd, buf, sizeof buf);
  192. if(strlen(buf)>=9 && strcmp(buf+strlen(buf)-9, "/dev/cons")==0)
  193. return 1;
  194. return 0;
  195. }
  196. char*
  197. buildcmd(int argc, char **argv)
  198. {
  199. int i, len;
  200. char *s, *t;
  201. len = argc-1;
  202. for(i=0; i<argc; i++)
  203. len += strlen(argv[i]);
  204. s = emalloc(len+1);
  205. t = s;
  206. for(i=0; i<argc; i++){
  207. if(i)
  208. *t++ = ' ';
  209. strcpy(t, argv[i]);
  210. t += strlen(t);
  211. }
  212. return s;
  213. }
  214. void
  215. fromnet(Conn *c)
  216. {
  217. int fd, len;
  218. char *s, *es, *r, *w;
  219. ulong ex;
  220. char buf[64];
  221. Msg *m;
  222. for(;;){
  223. m = recvmsg(c, -1);
  224. if(m == nil)
  225. break;
  226. switch(m->type){
  227. default:
  228. badmsg(m, 0);
  229. case SSH_SMSG_EXITSTATUS:
  230. ex = getlong(m);
  231. if(ex==0)
  232. exits(0);
  233. sprint(buf, "%lud", ex);
  234. exits(buf);
  235. case SSH_MSG_DISCONNECT:
  236. s = getstring(m);
  237. error("disconnect: %s", s);
  238. /*
  239. * If we ever add reverse port forwarding, we'll have to
  240. * revisit this. It assumes that the agent connections are
  241. * the only ones.
  242. */
  243. case SSH_SMSG_AGENT_OPEN:
  244. if(!forwardagent)
  245. error("server tried to use agent forwarding");
  246. handleagentopen(m);
  247. break;
  248. case SSH_MSG_CHANNEL_INPUT_EOF:
  249. if(!forwardagent)
  250. error("server tried to use agent forwarding");
  251. handleagentieof(m);
  252. break;
  253. case SSH_MSG_CHANNEL_OUTPUT_CLOSED:
  254. if(!forwardagent)
  255. error("server tried to use agent forwarding");
  256. handleagentoclose(m);
  257. break;
  258. case SSH_MSG_CHANNEL_DATA:
  259. if(!forwardagent)
  260. error("server tried to use agent forwarding");
  261. handleagentmsg(m);
  262. break;
  263. case SSH_SMSG_STDOUT_DATA:
  264. fd = outfd;
  265. goto Dataout;
  266. case SSH_SMSG_STDERR_DATA:
  267. fd = 2;
  268. goto Dataout;
  269. Dataout:
  270. len = getlong(m);
  271. s = (char*)getbytes(m, len);
  272. if(crstrip){
  273. es = s+len;
  274. for(r=w=s; r<es; r++)
  275. if(*r != '\r')
  276. *w++ = *r;
  277. len = w-s;
  278. }
  279. write(fd, s, len);
  280. break;
  281. }
  282. free(m);
  283. }
  284. }
  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. outfd = 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, nil);
  418. else
  419. execl("/bin/rc", "rc", nil);
  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. outfd = 1;
  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. }