sshsession.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512
  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. /*
  10. * ssh server - serve SSH protocol v2
  11. * /net/ssh does most of the work; we copy bytes back and forth
  12. */
  13. #include <u.h>
  14. #include <libc.h>
  15. #include <ip.h>
  16. #include <auth.h>
  17. #include "ssh2.h"
  18. char *confine(char *, char *);
  19. char *get_string(char *, char *);
  20. void newchannel(int, char *, int);
  21. void runcmd(int, int, char *, char *, char *, char *);
  22. int errfd, toppid, sflag, tflag, prevent;
  23. int debug;
  24. char *idstring;
  25. char *netdir; /* /net/ssh/<conn> */
  26. char *nsfile = nil;
  27. char *restdir;
  28. char *shell;
  29. char *srvpt;
  30. char *uname;
  31. void
  32. usage(void)
  33. {
  34. fprint(2, "usage: %s [-i id] [-s shell] [-r dir] [-R dir] [-S srvpt] "
  35. "[-n ns] [-t] [netdir]\n", argv0);
  36. exits("usage");
  37. }
  38. static int
  39. getctlfd(void)
  40. {
  41. int ctlfd;
  42. char *name;
  43. name = smprint("%s/clone", netdir);
  44. ctlfd = -1;
  45. if (name)
  46. ctlfd = open(name, ORDWR);
  47. if (ctlfd < 0) {
  48. syslog(0, "ssh", "server can't clone: %s: %r", name);
  49. exits("open clone");
  50. }
  51. free(name);
  52. return ctlfd;
  53. }
  54. static int
  55. getdatafd(int ctlfd)
  56. {
  57. int fd;
  58. char *name;
  59. name = smprint("%s/data", netdir);
  60. fd = -1;
  61. if (name)
  62. fd = open(name, OREAD);
  63. if (fd < 0) {
  64. syslog(0, "ssh", "can't open %s: %r", name);
  65. hangup(ctlfd);
  66. exits("open data");
  67. }
  68. free(name);
  69. return fd;
  70. }
  71. static void
  72. auth(char *buf, int n, int ctlfd)
  73. {
  74. int fd;
  75. fd = open("#¤/capuse", OWRITE);
  76. if (fd < 0) {
  77. syslog(0, "ssh", "server can't open capuse: %r");
  78. hangup(ctlfd);
  79. exits("capuse");
  80. }
  81. if (write(fd, buf, n) != n) {
  82. syslog(0, "ssh", "server write `%.*s' to capuse failed: %r",
  83. n, buf);
  84. hangup(ctlfd);
  85. exits("capuse");
  86. }
  87. close(fd);
  88. }
  89. /*
  90. * mount tunnel if there isn't one visible.
  91. */
  92. static void
  93. mounttunnel(int ctlfd)
  94. {
  95. int fd;
  96. char *p, *np, *q;
  97. if (access(netdir, AEXIST) >= 0)
  98. return;
  99. p = smprint("/srv/%s", srvpt? srvpt: "ssh");
  100. np = strdup(netdir);
  101. if (p == nil || np == nil)
  102. sysfatal("out of memory");
  103. q = strstr(np, "/ssh");
  104. if (q != nil)
  105. *q = '\0';
  106. fd = open(p, ORDWR);
  107. if (fd < 0) {
  108. syslog(0, "ssh", "can't open %s: %r", p);
  109. hangup(ctlfd);
  110. exits("open");
  111. }
  112. if (mount(fd, -1, np, MBEFORE, "", 'M') < 0) {
  113. syslog(0, "ssh", "can't mount %s in %s: %r", p, np);
  114. hangup(ctlfd);
  115. exits("can't mount");
  116. }
  117. free(p);
  118. free(np);
  119. }
  120. static int
  121. authnewns(int ctlfd, char *buf, int size, int n)
  122. {
  123. char *p, *q;
  124. USED(size);
  125. if (n <= 0)
  126. return 0;
  127. buf[n] = '\0';
  128. if (strcmp(buf, "n/a") == 0)
  129. return 0;
  130. auth(buf, n, ctlfd);
  131. p = strchr(buf, '@');
  132. if (p == nil)
  133. return 0;
  134. ++p;
  135. q = strchr(p, '@');
  136. if (q) {
  137. *q = '\0';
  138. uname = strdup(p);
  139. }
  140. if (!tflag && newns(p, nsfile) < 0) {
  141. syslog(0, "ssh", "server: newns(%s,%s) failed: %r", p, nsfile);
  142. return -1;
  143. }
  144. return 0;
  145. }
  146. static void
  147. listenloop(char *listfile, int ctlfd, char *buf, int size)
  148. {
  149. int fd, n;
  150. while ((fd = open(listfile, ORDWR)) >= 0) {
  151. n = read(fd, buf, size - 1);
  152. fprint(errfd, "read from listen file returned %d\n", n);
  153. if (n <= 0) {
  154. syslog(0, "ssh", "read on listen failed: %r");
  155. break;
  156. }
  157. buf[n >= 0? n: 0] = '\0';
  158. fprint(errfd, "read %s\n", buf);
  159. switch (fork()) {
  160. case 0: /* child */
  161. close(ctlfd);
  162. newchannel(fd, netdir, atoi(buf)); /* never returns */
  163. case -1:
  164. syslog(0, "ssh", "fork failed: %r");
  165. hangup(ctlfd);
  166. exits("fork");
  167. }
  168. close(fd);
  169. }
  170. if (fd < 0)
  171. syslog(0, "ssh", "listen failed: %r");
  172. }
  173. void
  174. main(int argc, char *argv[])
  175. {
  176. char *listfile;
  177. int ctlfd, fd, n;
  178. char buf[Arbpathlen], path[Arbpathlen];
  179. rfork(RFNOTEG);
  180. toppid = getpid();
  181. shell = "/bin/rc -il";
  182. ARGBEGIN {
  183. case 'd':
  184. debug++;
  185. break;
  186. case 'i':
  187. idstring = EARGF(usage());
  188. break;
  189. case 'n':
  190. nsfile = EARGF(usage());
  191. break;
  192. case 'R':
  193. prevent = 1;
  194. /* fall through */
  195. case 'r':
  196. restdir = EARGF(usage());
  197. break;
  198. case 's':
  199. sflag = 1;
  200. shell = EARGF(usage());
  201. break;
  202. case 'S':
  203. srvpt = EARGF(usage());
  204. break;
  205. case 't':
  206. tflag = 1;
  207. break;
  208. default:
  209. usage();
  210. break;
  211. } ARGEND;
  212. errfd = -1;
  213. if (debug)
  214. errfd = 2;
  215. /* work out network connection's directory */
  216. if (argc >= 1)
  217. netdir = argv[0];
  218. else /* invoked by listen1 */
  219. netdir = getenv("net");
  220. if (netdir == nil) {
  221. syslog(0, "ssh", "server netdir is nil");
  222. exits("nil netdir");
  223. }
  224. syslog(0, "ssh", "server netdir is %s", netdir);
  225. uname = getenv("user");
  226. if (uname == nil)
  227. uname = "none";
  228. /* extract dfd and cfd from netdir */
  229. ctlfd = getctlfd();
  230. fd = getdatafd(ctlfd);
  231. n = read(fd, buf, sizeof buf - 1);
  232. if (n < 0) {
  233. syslog(0, "ssh", "server read error for data file: %r");
  234. hangup(ctlfd);
  235. exits("read cap");
  236. }
  237. close(fd);
  238. authnewns(ctlfd, buf, sizeof buf, n);
  239. /* get connection number in buf */
  240. n = read(ctlfd, buf, sizeof buf - 1);
  241. buf[n >= 0? n: 0] = '\0';
  242. /* tell netssh our id string */
  243. fd2path(ctlfd, path, sizeof path);
  244. if (0 && idstring) { /* was for coexistence */
  245. syslog(0, "ssh", "server conn %s, writing \"id %s\" to %s",
  246. buf, idstring, path);
  247. fprint(ctlfd, "id %s", idstring);
  248. }
  249. /* announce */
  250. fprint(ctlfd, "announce session");
  251. /* construct listen file name */
  252. listfile = smprint("%s/%s/listen", netdir, buf);
  253. if (listfile == nil) {
  254. syslog(0, "ssh", "out of memory");
  255. exits("out of memory");
  256. }
  257. syslog(0, "ssh", "server listen is %s", listfile);
  258. mounttunnel(ctlfd);
  259. listenloop(listfile, ctlfd, buf, sizeof buf);
  260. hangup(ctlfd);
  261. exits(nil);
  262. }
  263. /* an abbreviation. note the assumed variables. */
  264. #define REPLY(s) if (want_reply) fprint(reqfd, s);
  265. static void
  266. forkshell(char *cmd, int reqfd, int datafd, int want_reply)
  267. {
  268. switch (fork()) {
  269. case 0:
  270. if (sflag)
  271. snprint(cmd, sizeof cmd, "-s%s", shell);
  272. else
  273. cmd[0] = '\0';
  274. USED(cmd);
  275. syslog(0, "ssh", "server starting ssh shell for %s", uname);
  276. /* runcmd doesn't return */
  277. runcmd(reqfd, datafd, "con", "/bin/ip/telnetd", "-nt", nil);
  278. case -1:
  279. REPLY("failure");
  280. syslog(0, "ssh", "server can't fork: %r");
  281. exits("fork");
  282. }
  283. }
  284. static void
  285. forkcmd(char *cmd, char *p, int reqfd, int datafd, int want_reply)
  286. {
  287. char *q;
  288. switch (fork()) {
  289. case 0:
  290. if (restdir && chdir(restdir) < 0) {
  291. syslog(0, "ssh", "can't chdir(%s): %r", restdir);
  292. exits("can't chdir");
  293. }
  294. if (!prevent || (q = getenv("sshsession")) &&
  295. strcmp(q, "allow") == 0)
  296. get_string(p+1, cmd);
  297. else
  298. confine(p+1, cmd);
  299. syslog(0, "ssh", "server running `%s' for %s", cmd, uname);
  300. /* runcmd doesn't return */
  301. runcmd(reqfd, datafd, "rx", "/bin/rc", "-lc", cmd);
  302. case -1:
  303. REPLY("failure");
  304. syslog(0, "ssh", "server can't fork: %r");
  305. exits("fork");
  306. }
  307. }
  308. void
  309. newchannel(int fd, char *conndir, int channum)
  310. {
  311. char *p, *reqfile, *datafile;
  312. int n, reqfd, datafd, want_reply, already_done;
  313. char buf[Maxpayload], cmd[Bigbufsz];
  314. close(fd);
  315. already_done = 0;
  316. reqfile = smprint("%s/%d/request", conndir, channum);
  317. if (reqfile == nil)
  318. sysfatal("out of memory");
  319. reqfd = open(reqfile, ORDWR);
  320. if (reqfd < 0) {
  321. syslog(0, "ssh", "can't open request file %s: %r", reqfile);
  322. exits("net");
  323. }
  324. datafile = smprint("%s/%d/data", conndir, channum);
  325. if (datafile == nil)
  326. sysfatal("out of memory");
  327. datafd = open(datafile, ORDWR);
  328. if (datafd < 0) {
  329. syslog(0, "ssh", "can't open data file %s: %r", datafile);
  330. exits("net");
  331. }
  332. while ((n = read(reqfd, buf, sizeof buf - 1)) > 0) {
  333. fprint(errfd, "read from request file returned %d\n", n);
  334. for (p = buf; p < buf + n && *p != ' '; ++p)
  335. ;
  336. *p++ = '\0';
  337. want_reply = (*p == 't');
  338. /* shell, exec, and various flavours of failure */
  339. if (strcmp(buf, "shell") == 0) {
  340. if (already_done) {
  341. REPLY("failure");
  342. continue;
  343. }
  344. forkshell(cmd, reqfd, datafd, want_reply);
  345. already_done = 1;
  346. REPLY("success");
  347. } else if (strcmp(buf, "exec") == 0) {
  348. if (already_done) {
  349. REPLY("failure");
  350. continue;
  351. }
  352. forkcmd(cmd, p, reqfd, datafd, want_reply);
  353. already_done = 1;
  354. REPLY("success");
  355. } else if (strcmp(buf, "pty-req") == 0 ||
  356. strcmp(buf, "window-change") == 0) {
  357. REPLY("success");
  358. } else if (strcmp(buf, "x11-req") == 0 ||
  359. strcmp(buf, "env") == 0 || strcmp(buf, "subsystem") == 0) {
  360. REPLY("failure");
  361. } else if (strcmp(buf, "xon-xoff") == 0 ||
  362. strcmp(buf, "signal") == 0 ||
  363. strcmp(buf, "exit-status") == 0 ||
  364. strcmp(buf, "exit-signal") == 0) {
  365. ;
  366. } else
  367. syslog(0, "ssh", "server unknown channel request: %s",
  368. buf);
  369. }
  370. if (n < 0)
  371. syslog(0, "ssh", "server read failed: %r");
  372. exits(nil);
  373. }
  374. char *
  375. get_string(char *q, char *s)
  376. {
  377. int n;
  378. n = nhgetl(q);
  379. q += 4;
  380. memmove(s, q, n);
  381. s[n] = '\0';
  382. q += n;
  383. return q;
  384. }
  385. char *
  386. confine(char *q, char *s)
  387. {
  388. int i, n, m;
  389. char *p, *e, *r, *buf, *toks[Maxtoks];
  390. n = nhgetl(q);
  391. q += 4;
  392. buf = malloc(n+1);
  393. if (buf == nil)
  394. return nil;
  395. memmove(buf, q, n);
  396. buf[n] = 0;
  397. m = tokenize(buf, toks, nelem(toks));
  398. e = s + n + 1;
  399. for (i = 0, r = s; i < m; ++i) {
  400. p = strrchr(toks[i], '/');
  401. if (p == nil)
  402. r = seprint(r, e, "%s ", toks[i]);
  403. else if (p[0] != '\0' && p[1] != '\0')
  404. r = seprint(r, e, "%s ", p+1);
  405. else
  406. r = seprint(r, e, ". ");
  407. }
  408. free(buf);
  409. q += n;
  410. return q;
  411. }
  412. void
  413. runcmd(int reqfd, int datafd, char *svc, char *cmd, char *arg1,
  414. char *arg2)
  415. {
  416. char *p;
  417. int fd, cmdpid, child;
  418. cmdpid = rfork(RFPROC|RFMEM|RFNOTEG|RFFDG|RFENVG);
  419. switch (cmdpid) {
  420. case -1:
  421. syslog(0, "ssh", "fork failed: %r");
  422. exits("fork");
  423. case 0:
  424. if (restdir == nil) {
  425. p = smprint("/usr/%s", uname);
  426. if (p && access(p, AREAD) == 0 && chdir(p) < 0) {
  427. syslog(0, "ssh", "can't chdir(%s): %r", p);
  428. exits("can't chdir");
  429. }
  430. free(p);
  431. }
  432. p = strrchr(cmd, '/');
  433. if (p)
  434. ++p;
  435. else
  436. p = cmd;
  437. dup(datafd, 0);
  438. dup(datafd, 1);
  439. dup(datafd, 2);
  440. close(datafd);
  441. putenv("service", svc);
  442. fprint(errfd, "starting %s\n", cmd);
  443. execl(cmd, p, arg1, arg2, nil);
  444. syslog(0, "ssh", "cannot exec %s: %r", cmd);
  445. exits("exec");
  446. default:
  447. close(datafd);
  448. fprint(errfd, "waiting for child %d\n", cmdpid);
  449. while ((child = waitpid()) != cmdpid && child != -1)
  450. fprint(errfd, "child %d passed\n", child);
  451. if (child == -1)
  452. fprint(errfd, "wait failed: %r\n");
  453. syslog(0, "ssh", "server closing ssh session for %s", uname);
  454. fprint(errfd, "closing connection\n");
  455. fprint(reqfd, "close");
  456. p = smprint("/proc/%d/notepg", toppid);
  457. if (p) {
  458. fd = open(p, OWRITE);
  459. fprint(fd, "interrupt");
  460. close(fd);
  461. }
  462. exits(nil);
  463. }
  464. }