telnetd.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644
  1. #include <u.h>
  2. #include <libc.h>
  3. #include <bio.h>
  4. #include <auth.h>
  5. #include <libsec.h>
  6. #include "../ip/telnet.h"
  7. /* console state (for consctl) */
  8. typedef struct Consstate Consstate;
  9. struct Consstate{
  10. int raw;
  11. int hold;
  12. };
  13. Consstate *cons;
  14. int notefd; /* for sending notes to the child */
  15. int noproto; /* true if we shouldn't be using the telnet protocol */
  16. int trusted; /* true if we need not authenticate - current user
  17. is ok */
  18. int nonone = 1; /* don't allow none logins */
  19. int noworldonly; /* only noworld accounts */
  20. enum
  21. {
  22. Maxpath= 256,
  23. Maxuser= 64,
  24. Maxvar= 32,
  25. };
  26. /* input and output buffers for network connection */
  27. Biobuf netib;
  28. Biobuf childib;
  29. char remotesys[Maxpath]; /* name of remote system */
  30. int alnum(int);
  31. int conssim(void);
  32. int fromchild(char*, int);
  33. int fromnet(char*, int);
  34. int termchange(Biobuf*, int);
  35. int termsub(Biobuf*, uchar*, int);
  36. int xlocchange(Biobuf*, int);
  37. int xlocsub(Biobuf*, uchar*, int);
  38. int challuser(char*);
  39. int noworldlogin(char*);
  40. void* share(int);
  41. int doauth(char*);
  42. #define TELNETLOG "telnet"
  43. void
  44. logit(char *fmt, ...)
  45. {
  46. va_list arg;
  47. char buf[8192];
  48. va_start(arg, fmt);
  49. vseprint(buf, buf + sizeof(buf) / sizeof(*buf), fmt, arg);
  50. va_end(arg);
  51. syslog(0, TELNETLOG, "(%s) %s", remotesys, buf);
  52. }
  53. void
  54. getremote(char *dir)
  55. {
  56. int fd, n;
  57. char remfile[Maxpath];
  58. sprint(remfile, "%s/remote", dir);
  59. fd = open(remfile, OREAD);
  60. if(fd < 0)
  61. strcpy(remotesys, "unknown2");
  62. n = read(fd, remotesys, sizeof(remotesys)-1);
  63. if(n>0)
  64. remotesys[n-1] = 0;
  65. else
  66. strcpy(remotesys, remfile);
  67. close(fd);
  68. }
  69. void
  70. main(int argc, char *argv[])
  71. {
  72. char buf[1024];
  73. int fd;
  74. char user[Maxuser];
  75. int tries = 0;
  76. int childpid;
  77. int n, eofs;
  78. memset(user, 0, sizeof(user));
  79. ARGBEGIN {
  80. case 'n':
  81. opt[Echo].local = 1;
  82. noproto = 1;
  83. break;
  84. case 'p':
  85. noproto = 1;
  86. break;
  87. case 'a':
  88. nonone = 0;
  89. break;
  90. case 't':
  91. trusted = 1;
  92. strncpy(user, getuser(), sizeof(user)-1);
  93. break;
  94. case 'u':
  95. strncpy(user, ARGF(), sizeof(user)-1);
  96. break;
  97. case 'd':
  98. debug = 1;
  99. break;
  100. case 'N':
  101. noworldonly = 1;
  102. break;
  103. } ARGEND
  104. if(argc)
  105. getremote(argv[argc-1]);
  106. else
  107. strcpy(remotesys, "unknown");
  108. /* options we need routines for */
  109. opt[Term].change = termchange;
  110. opt[Term].sub = termsub;
  111. opt[Xloc].sub = xlocsub;
  112. /* setup default telnet options */
  113. if(!noproto){
  114. send3(1, Iac, Will, opt[Echo].code);
  115. send3(1, Iac, Do, opt[Term].code);
  116. send3(1, Iac, Do, opt[Xloc].code);
  117. }
  118. /* shared data for console state */
  119. cons = share(sizeof(Consstate));
  120. if(cons == 0)
  121. fatal("shared memory", 0, 0);
  122. /* authenticate and create new name space */
  123. Binit(&netib, 0, OREAD);
  124. if (!trusted){
  125. while(doauth(user) < 0)
  126. if(++tries == 5){
  127. logit("failed as %s: %r", user);
  128. print("authentication failure:%r\r\n");
  129. exits("authentication");
  130. }
  131. }
  132. logit("logged in as %s", user);
  133. putenv("service", "con");
  134. /* simulate /dev/consctl and /dev/cons using pipes */
  135. fd = conssim();
  136. if(fd < 0)
  137. fatal("simulating", 0, 0);
  138. Binit(&childib, fd, OREAD);
  139. /* start a shell in a different process group */
  140. switch(childpid = rfork(RFPROC|RFNAMEG|RFFDG|RFNOTEG)){
  141. case -1:
  142. fatal("fork", 0, 0);
  143. case 0:
  144. close(fd);
  145. fd = open("/dev/cons", OREAD);
  146. dup(fd, 0);
  147. close(fd);
  148. fd = open("/dev/cons", OWRITE);
  149. dup(fd, 1);
  150. dup(fd, 2);
  151. close(fd);
  152. segdetach(cons);
  153. execl("/bin/rc", "rc", "-il", 0);
  154. fatal("/bin/rc", 0, 0);
  155. default:
  156. sprint(buf, "/proc/%d/notepg", childpid);
  157. notefd = open(buf, OWRITE);
  158. break;
  159. }
  160. /* two processes to shuttle bytes twixt children and network */
  161. switch(fork()){
  162. case -1:
  163. fatal("fork", 0, 0);
  164. case 0:
  165. eofs = 0;
  166. for(;;){
  167. n = fromchild(buf, sizeof(buf));
  168. if(n <= 0){
  169. if(eofs++ > 2)
  170. break;
  171. continue;
  172. }
  173. eofs = 0;
  174. if(write(1, buf, n) != n)
  175. break;
  176. }
  177. break;
  178. default:
  179. while((n = fromnet(buf, sizeof(buf))) >= 0)
  180. if(write(fd, buf, n) != n)
  181. break;
  182. break;
  183. }
  184. /* kill off all server processes */
  185. sprint(buf, "/proc/%d/notepg", getpid());
  186. fd = open(buf, OWRITE);
  187. write(fd, "die", 3);
  188. exits(0);
  189. }
  190. void
  191. prompt(char *p, char *b, int n, int raw)
  192. {
  193. char *e;
  194. int i;
  195. int echo;
  196. echo = opt[Echo].local;
  197. if(raw)
  198. opt[Echo].local = 0;
  199. print("%s: ", p);
  200. for(e = b+n; b < e;){
  201. i = fromnet(b, e-b);
  202. if(i <= 0)
  203. exits("fromnet: hungup");
  204. b += i;
  205. if(*(b-1) == '\n' || *(b-1) == '\r'){
  206. *(b-1) = 0;
  207. break;
  208. }
  209. }
  210. if(raw)
  211. opt[Echo].local = echo;
  212. }
  213. /*
  214. * challenge user
  215. */
  216. int
  217. challuser(char *user)
  218. {
  219. char nchall[64];
  220. char response[64];
  221. Chalstate *ch;
  222. AuthInfo *ai;
  223. if(strcmp(user, "none") == 0){
  224. if(nonone)
  225. return -1;
  226. newns("none", nil);
  227. return 0;
  228. }
  229. if((ch = auth_challenge("proto=p9cr role=server user=%q", user)) == nil)
  230. return -1;
  231. snprint(nchall, sizeof nchall, "challenge: %s\r\nresponse", ch->chal);
  232. prompt(nchall, response, sizeof response, 0);
  233. ch->resp = response;
  234. ch->nresp = strlen(response);
  235. ai = auth_response(ch);
  236. auth_freechal(ch);
  237. if(ai == nil){
  238. rerrstr(response, sizeof response);
  239. print("!%s\n", response);
  240. return -1;
  241. }
  242. if(auth_chuid(ai, nil) < 0)
  243. return -1;
  244. return 0;
  245. }
  246. /*
  247. * use the in the clear apop password to change user id
  248. */
  249. int
  250. noworldlogin(char *user)
  251. {
  252. char password[256];
  253. prompt("password", password, sizeof(password), 1);
  254. if(login(user, password, "/lib/namespace.noworld") < 0)
  255. return -1;
  256. rfork(RFNOMNT); /* sandbox */
  257. return 0;
  258. }
  259. int
  260. doauth(char *user)
  261. {
  262. if(*user == 0)
  263. prompt("user", user, Maxuser, 0);
  264. if(noworld(user))
  265. return noworldlogin(user);
  266. if(noworldonly)
  267. return -1;
  268. return challuser(user);
  269. }
  270. /*
  271. * Process some input from the child, add protocol if needed. If
  272. * the input buffer goes empty, return.
  273. */
  274. int
  275. fromchild(char *bp, int len)
  276. {
  277. int c;
  278. char *start;
  279. for(start = bp; bp-start < len-1; ){
  280. c = Bgetc(&childib);
  281. if(c < 0){
  282. if(bp == start)
  283. return -1;
  284. else
  285. break;
  286. }
  287. if(cons->raw == 0 && c == '\n')
  288. *bp++ = '\r';
  289. *bp++ = c;
  290. if(Bbuffered(&childib) == 0)
  291. break;
  292. }
  293. return bp-start;
  294. }
  295. /*
  296. * Read from the network up to a '\n' or some other break.
  297. *
  298. * If in binary mode, buffer characters but don't
  299. *
  300. * The following characters are special:
  301. * '\r\n's and '\r's get turned into '\n's.
  302. * ^H erases the last character buffered.
  303. * ^U kills the whole line buffered.
  304. * ^W erases the last word
  305. * ^D causes a 0-lenght line to be returned.
  306. * Intr causes an "interrupt" note to be sent to the children.
  307. */
  308. #define ECHO(c) { *ebp++ = (c); }
  309. int
  310. fromnet(char *bp, int len)
  311. {
  312. int c;
  313. char echobuf[1024];
  314. char *ebp;
  315. char *start;
  316. static int crnl;
  317. static int doeof;
  318. /* simulate an EOF as a 0 length input */
  319. if(doeof){
  320. doeof = 0;
  321. return 0;
  322. }
  323. for(ebp = echobuf,start = bp; bp-start < len && ebp-echobuf < sizeof(echobuf); ){
  324. c = Bgetc(&netib);
  325. if(c < 0){
  326. if(bp == start)
  327. return -1;
  328. else
  329. break;
  330. }
  331. /* telnet protocol only */
  332. if(!noproto){
  333. /* protocol messages */
  334. switch(c){
  335. case Iac:
  336. crnl = 0;
  337. c = Bgetc(&netib);
  338. if(c == Iac)
  339. break;
  340. control(&netib, c);
  341. continue;
  342. }
  343. }
  344. /* \r\n or \n\r become \n */
  345. if(c == '\r' || c == '\n'){
  346. if(crnl && crnl != c){
  347. crnl = 0;
  348. continue;
  349. }
  350. if(cons->raw == 0 && opt[Echo].local){
  351. ECHO('\r');
  352. ECHO('\n');
  353. }
  354. crnl = c;
  355. if(cons->raw == 0)
  356. *bp++ = '\n';
  357. else
  358. *bp++ = c;
  359. break;
  360. } else
  361. crnl = 0;
  362. /* raw processing (each character terminates */
  363. if(cons->raw){
  364. *bp++ = c;
  365. break;
  366. }
  367. /* in binary mode, there are no control characters */
  368. if(opt[Binary].local){
  369. if(opt[Echo].local)
  370. ECHO(c);
  371. *bp++ = c;
  372. continue;
  373. }
  374. /* cooked processing */
  375. switch(c){
  376. case 0x00:
  377. if(noproto) /* telnet ignores nulls */
  378. *bp++ = c;
  379. continue;
  380. case 0x04:
  381. if(bp != start)
  382. doeof = 1;
  383. goto out;
  384. case 0x08: /* ^H */
  385. if(start < bp)
  386. bp--;
  387. if(opt[Echo].local)
  388. ECHO(c);
  389. break;
  390. case 0x15: /* ^U */
  391. bp = start;
  392. if(opt[Echo].local){
  393. ECHO('^');
  394. ECHO('U');
  395. ECHO('\r');
  396. ECHO('\n');
  397. }
  398. break;
  399. case 0x17: /* ^W */
  400. if (opt[Echo].local) {
  401. while (--bp >= start && !alnum(*bp))
  402. ECHO('\b');
  403. while (bp >= start && alnum(*bp)) {
  404. ECHO('\b');
  405. bp--;
  406. }
  407. bp++;
  408. }
  409. break;
  410. case 0x7f: /* Del */
  411. write(notefd, "interrupt", 9);
  412. bp = start;
  413. break;
  414. default:
  415. if(opt[Echo].local)
  416. ECHO(c);
  417. *bp++ = c;
  418. }
  419. if(ebp != echobuf)
  420. write(1, echobuf, ebp-echobuf);
  421. ebp = echobuf;
  422. }
  423. out:
  424. if(ebp != echobuf)
  425. write(1, echobuf, ebp-echobuf);
  426. return bp - start;
  427. }
  428. int
  429. termchange(Biobuf *bp, int cmd)
  430. {
  431. char buf[8];
  432. char *p = buf;
  433. if(cmd != Will)
  434. return 0;
  435. /* ask other side to send term type info */
  436. *p++ = Iac;
  437. *p++ = Sb;
  438. *p++ = opt[Term].code;
  439. *p++ = 1;
  440. *p++ = Iac;
  441. *p++ = Se;
  442. return iwrite(Bfildes(bp), buf, p-buf);
  443. }
  444. int
  445. termsub(Biobuf *bp, uchar *sub, int n)
  446. {
  447. char term[Maxvar];
  448. USED(bp);
  449. if(n-- < 1 || sub[0] != 0)
  450. return 0;
  451. if(n >= sizeof term)
  452. n = sizeof term;
  453. strncpy(term, (char*)sub, n);
  454. putenv("TERM", term);
  455. return 0;
  456. }
  457. int
  458. xlocchange(Biobuf *bp, int cmd)
  459. {
  460. char buf[8];
  461. char *p = buf;
  462. if(cmd != Will)
  463. return 0;
  464. /* ask other side to send x display info */
  465. *p++ = Iac;
  466. *p++ = Sb;
  467. *p++ = opt[Xloc].code;
  468. *p++ = 1;
  469. *p++ = Iac;
  470. *p++ = Se;
  471. return iwrite(Bfildes(bp), buf, p-buf);
  472. }
  473. int
  474. xlocsub(Biobuf *bp, uchar *sub, int n)
  475. {
  476. char xloc[Maxvar];
  477. USED(bp);
  478. if(n-- < 1 || sub[0] != 0)
  479. return 0;
  480. if(n >= sizeof xloc)
  481. n = sizeof xloc;
  482. strncpy(xloc, (char*)sub, n);
  483. putenv("DISPLAY", xloc);
  484. return 0;
  485. }
  486. /*
  487. * create a shared segment. Make is start 2 meg higher than the current
  488. * end of process memory.
  489. */
  490. void*
  491. share(int len)
  492. {
  493. ulong vastart;
  494. vastart = ((ulong)sbrk(0)) + 2*1024*1024;
  495. if(segattach(0, "shared", (void *)vastart, len) < 0)
  496. return 0;
  497. return (void*)vastart;
  498. }
  499. /*
  500. * bind a pipe onto consctl and keep reading it to
  501. * get changes to console state.
  502. */
  503. int
  504. conssim(void)
  505. {
  506. int i, n;
  507. int fd;
  508. int tries;
  509. char buf[128];
  510. char *field[10];
  511. /* a pipe to simulate the /dev/cons */
  512. if(bind("#|", "/mnt/cons/cons", MREPL) < 0)
  513. fatal("/dev/cons1", 0, 0);
  514. if(bind("/mnt/cons/cons/data1", "/dev/cons", MREPL) < 0)
  515. fatal("/dev/cons2", 0, 0);
  516. /* a pipe to simulate consctl */
  517. if(bind("#|", "/mnt/cons/consctl", MBEFORE) < 0
  518. || bind("/mnt/cons/consctl/data1", "/dev/consctl", MREPL) < 0)
  519. fatal("/dev/consctl", 0, 0);
  520. /* a process to read /dev/consctl and set the state in cons */
  521. switch(fork()){
  522. case -1:
  523. fatal("forking", 0, 0);
  524. case 0:
  525. break;
  526. default:
  527. return open("/mnt/cons/cons/data", ORDWR);
  528. }
  529. for(tries = 0; tries < 100; tries++){
  530. cons->raw = 0;
  531. cons->hold = 0;
  532. fd = open("/mnt/cons/consctl/data", OREAD);
  533. if(fd < 0)
  534. continue;
  535. tries = 0;
  536. for(;;){
  537. n = read(fd, buf, sizeof(buf)-1);
  538. if(n <= 0)
  539. break;
  540. buf[n] = 0;
  541. n = getfields(buf, field, 10, 1, " ");
  542. for(i = 0; i < n; i++){
  543. if(strcmp(field[i], "rawon") == 0) {
  544. if(debug) fprint(2, "raw = 1\n");
  545. cons->raw = 1;
  546. } else if(strcmp(field[i], "rawoff") == 0) {
  547. if(debug) fprint(2, "raw = 0\n");
  548. cons->raw = 0;
  549. } else if(strcmp(field[i], "holdon") == 0) {
  550. cons->hold = 1;
  551. if(debug) fprint(2, "raw = 1\n");
  552. } else if(strcmp(field[i], "holdoff") == 0) {
  553. cons->hold = 0;
  554. if(debug) fprint(2, "raw = 0\n");
  555. }
  556. }
  557. }
  558. close(fd);
  559. }
  560. exits(0);
  561. return -1;
  562. }
  563. int
  564. alnum(int c)
  565. {
  566. /*
  567. * Hard to get absolutely right. Use what we know about ASCII
  568. * and assume anything above the Latin control characters is
  569. * potentially an alphanumeric.
  570. */
  571. if(c <= ' ')
  572. return 0;
  573. if(0x7F<=c && c<=0xA0)
  574. return 0;
  575. if(strchr("!\"#$%&'()*+,-./:;<=>?@`[\\]^{|}~", c))
  576. return 0;
  577. return 1;
  578. }