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