Subversion Repositories planix.SVN

Rev

Rev 2 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 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
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
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
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
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
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
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
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
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
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
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
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
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
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*
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
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
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
}