xref: /plan9/sys/src/cmd/ip/telnetd.c (revision aef377881c258b55253e6d1b6004ee9da79af528)
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