Warning: Attempt to read property "date" on null in /usr/local/www/websvn.planix.org/blame.php on line 247

Warning: Attempt to read property "msg" on null in /usr/local/www/websvn.planix.org/blame.php on line 247
WebSVN – planix.SVN – Blame – /os/branches/feature_unix/sys/src/cmd/ssh1/ssh1.c – Rev 2

Subversion Repositories planix.SVN

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 1
/* remote login via ssh v1 */
2
#include "ssh.h"
3
 
4
int cooked = 0;		/* user wants cooked mode */
5
int raw = 0;		/* console is in raw mode */
6
int crstrip;
7
int interactive = -1;
8
int usemenu = 1;
9
int isatty(int);
10
int rawhack;
11
int forwardagent = 0;
12
char *buildcmd(int, char**);
13
void fromnet(Conn*);
14
void fromstdin(Conn*);
15
void winchanges(Conn*);
16
static void	sendwritemsg(Conn *c, char *buf, int n);
17
 
18
/*
19
 * Lifted from telnet.c, con.c
20
 */
21
static int consctl = -1;
22
static int outfd = 1;			/* changed during system */
23
static void system(Conn*, char*);
24
 
25
Cipher *allcipher[] = {
26
	&cipherrc4,
27
	&cipherblowfish,
28
	&cipher3des,
29
	&cipherdes,
30
	&ciphernone,
31
	&ciphertwiddle,
32
};
33
 
34
Auth *allauth[] = {
35
	&authpassword,
36
	&authrsa,
37
	&authtis,
38
};
39
 
40
char *cipherlist = "blowfish rc4 3des";
41
char *authlist = "rsa password tis";
42
 
43
Cipher*
44
findcipher(char *name, Cipher **list, int nlist)
45
{
46
	int i;
47
 
48
	for(i=0; i<nlist; i++)
49
		if(strcmp(name, list[i]->name) == 0)
50
			return list[i];
51
	error("unknown cipher %s", name);
52
	return nil;
53
}
54
 
55
Auth*
56
findauth(char *name, Auth **list, int nlist)
57
{
58
	int i;
59
 
60
	for(i=0; i<nlist; i++)
61
		if(strcmp(name, list[i]->name) == 0)
62
			return list[i];
63
	error("unknown auth %s", name);
64
	return nil;
65
}
66
 
67
void
68
usage(void)
69
{
70
	fprint(2, "usage: ssh [-CiImPpRr] [-A authlist] [-c cipherlist] [user@]hostname [cmd [args]]\n");
71
	exits("usage");
72
}
73
 
74
void
75
main(int argc, char **argv)
76
{
77
	int i, dowinchange, fd, usepty;
78
	char *host, *cmd, *user, *p;
79
	char *f[16];
80
	Conn c;
81
	Msg *m;
82
 
83
	fmtinstall('B', mpfmt);
84
	fmtinstall('H', encodefmt);
85
	atexit(atexitkiller);
86
	atexitkill(getpid());
87
 
88
	dowinchange = 0;
89
	if(getenv("LINES"))
90
		dowinchange = 1;
91
	usepty = -1;
92
	user = nil;
93
	ARGBEGIN{
94
	case 'B':	/* undocumented, debugging */
95
		doabort = 1;
96
		break;
97
	case 'D':	/* undocumented, debugging */
98
		debuglevel = strtol(EARGF(usage()), nil, 0);
99
		break;
100
	case 'l':	/* deprecated */
101
	case 'u':
102
		user = EARGF(usage());
103
		break;
104
	case 'a':	/* used by Unix scp implementations; we must ignore them. */
105
	case 'x':
106
		break;
107
 
108
	case 'A':
109
		authlist = EARGF(usage());
110
		break;
111
	case 'C':
112
		cooked = 1;
113
		break;
114
	case 'c':
115
		cipherlist = EARGF(usage());
116
		break;
117
	case 'f':
118
		forwardagent = 1;
119
		break;
120
	case 'I':
121
		interactive = 0;
122
		break;
123
	case 'i':
124
		interactive = 1;
125
		break;
126
	case 'm':
127
		usemenu = 0;
128
		break;
129
	case 'P':
130
		usepty = 0;
131
		break;
132
	case 'p':
133
		usepty = 1;
134
		break;
135
	case 'R':
136
		rawhack = 1;
137
		break;
138
	case 'r':
139
		crstrip = 1;
140
		break;
141
	default:
142
		usage();
143
	}ARGEND
144
 
145
	if(argc < 1)
146
		usage();
147
 
148
	host = argv[0];
149
 
150
	cmd = nil;
151
	if(argc > 1)
152
		cmd = buildcmd(argc-1, argv+1);
153
 
154
	if((p = strchr(host, '@')) != nil){
155
		*p++ = '\0';
156
		user = host;
157
		host = p;
158
	}
159
	if(user == nil)
160
		user = getenv("user");
161
	if(user == nil)
162
		sysfatal("cannot find user name");
163
 
164
	privatefactotum();
165
	if(interactive==-1)
166
		interactive = isatty(0);
167
 
168
	if((fd = dial(netmkaddr(host, "tcp", "ssh"), nil, nil, nil)) < 0)
169
		sysfatal("dialing %s: %r", host);
170
 
171
	memset(&c, 0, sizeof c);
172
	c.interactive = interactive;
173
	c.fd[0] = c.fd[1] = fd;
174
	c.user = user;
175
	c.host = host;
176
	setaliases(&c, host);
177
 
178
	c.nokcipher = getfields(cipherlist, f, nelem(f), 1, ", ");
179
	c.okcipher = emalloc(sizeof(Cipher*)*c.nokcipher);
180
	for(i=0; i<c.nokcipher; i++)
181
		c.okcipher[i] = findcipher(f[i], allcipher, nelem(allcipher));
182
 
183
	c.nokauth = getfields(authlist, f, nelem(f), 1, ", ");
184
	c.okauth = emalloc(sizeof(Auth*)*c.nokauth);
185
	for(i=0; i<c.nokauth; i++)
186
		c.okauth[i] = findauth(f[i], allauth, nelem(allauth));
187
 
188
	sshclienthandshake(&c);
189
 
190
	if(forwardagent){
191
		if(startagent(&c) < 0)
192
			forwardagent = 0;
193
	}
194
	if(usepty == -1)
195
		usepty = cmd==nil;
196
	if(usepty)
197
		requestpty(&c);
198
	if(cmd){
199
		m = allocmsg(&c, SSH_CMSG_EXEC_CMD, 4+strlen(cmd));
200
		putstring(m, cmd);
201
	}else
202
		m = allocmsg(&c, SSH_CMSG_EXEC_SHELL, 0);
203
	sendmsg(m);
204
 
205
	fromstdin(&c);
206
	rfork(RFNOTEG);	/* only fromstdin gets notes */
207
	if(dowinchange)
208
		winchanges(&c);
209
	fromnet(&c);
210
	exits(0);
211
}
212
 
213
int
214
isatty(int fd)
215
{
216
	char buf[64];
217
 
218
	buf[0] = '\0';
219
	fd2path(fd, buf, sizeof buf);
220
	if(strlen(buf)>=9 && strcmp(buf+strlen(buf)-9, "/dev/cons")==0)
221
		return 1;
222
	return 0;
223
}
224
 
225
char*
226
buildcmd(int argc, char **argv)
227
{
228
	int i, len;
229
	char *s, *t;
230
 
231
	len = argc-1;
232
	for(i=0; i<argc; i++)
233
		len += strlen(argv[i]);
234
	s = emalloc(len+1);
235
	t = s;
236
	for(i=0; i<argc; i++){
237
		if(i)
238
			*t++ = ' ';
239
		strcpy(t, argv[i]);
240
		t += strlen(t);
241
	}
242
	return s;
243
}
244
 
245
 
246
void
247
fromnet(Conn *c)
248
{
249
	int fd, len;
250
	char *s, *es, *r, *w;
251
	ulong ex;
252
	char buf[64];
253
	Msg *m;
254
 
255
	for(;;){
256
		m = recvmsg(c, -1);
257
		if(m == nil)
258
			break;
259
		switch(m->type){
260
		default:
261
			badmsg(m, 0);
262
 
263
		case SSH_SMSG_EXITSTATUS:
264
			ex = getlong(m);
265
			if(ex==0)
266
				exits(0);
267
			sprint(buf, "%lud", ex);
268
			exits(buf);
269
 
270
		case SSH_MSG_DISCONNECT:
271
			s = getstring(m);
272
			error("disconnect: %s", s);
273
 
274
		/*
275
		 * If we ever add reverse port forwarding, we'll have to
276
		 * revisit this.  It assumes that the agent connections are
277
		 * the only ones.
278
		 */
279
		case SSH_SMSG_AGENT_OPEN:
280
			if(!forwardagent)
281
				error("server tried to use agent forwarding");
282
			handleagentopen(m);
283
			break;
284
		case SSH_MSG_CHANNEL_INPUT_EOF:
285
			if(!forwardagent)
286
				error("server tried to use agent forwarding");
287
			handleagentieof(m);
288
			break;
289
		case SSH_MSG_CHANNEL_OUTPUT_CLOSED:
290
			if(!forwardagent)
291
				error("server tried to use agent forwarding");
292
			handleagentoclose(m);
293
			break;
294
		case SSH_MSG_CHANNEL_DATA:
295
			if(!forwardagent)
296
				error("server tried to use agent forwarding");
297
			handleagentmsg(m);
298
			break;
299
 
300
		case SSH_SMSG_STDOUT_DATA:
301
			fd = outfd;
302
			goto Dataout;
303
		case SSH_SMSG_STDERR_DATA:
304
			fd = 2;
305
			goto Dataout;
306
		Dataout:
307
			len = getlong(m);
308
			s = (char*)getbytes(m, len);
309
			if(crstrip){
310
				es = s+len;
311
				for(r=w=s; r<es; r++)
312
					if(*r != '\r')
313
						*w++ = *r;
314
				len = w-s;
315
			}
316
			write(fd, s, len);
317
			break;
318
		}
319
		free(m);
320
	}
321
}		
322
 
323
/*
324
 *  turn keyboard raw mode on
325
 */
326
static void
327
rawon(void)
328
{
329
	if(raw)
330
		return;
331
	if(cooked)
332
		return;
333
	if(consctl < 0)
334
		consctl = open("/dev/consctl", OWRITE);
335
	if(consctl < 0)
336
		return;
337
	if(write(consctl, "rawon", 5) != 5)
338
		return;
339
	raw = 1;
340
}
341
 
342
/*
343
 *  turn keyboard raw mode off
344
 */
345
static void
346
rawoff(void)
347
{
348
	if(raw == 0)
349
		return;
350
	if(consctl < 0)
351
		return;
352
	if(write(consctl, "rawoff", 6) != 6)
353
		return;
354
	close(consctl);
355
	consctl = -1;
356
	raw = 0;
357
}
358
 
359
/*
360
 *  control menu
361
 */
362
#define STDHELP	"\t(q)uit, (i)nterrupt, toggle printing (r)eturns, (.)continue, (!cmd)\n"
363
 
364
static int
365
menu(Conn *c)
366
{
367
	char buf[1024];
368
	long n;
369
	int done;
370
	int wasraw;
371
 
372
	wasraw = raw;
373
	if(wasraw)
374
		rawoff();
375
 
376
	buf[0] = '?';
377
	fprint(2, ">>> ");
378
	for(done = 0; !done; ){
379
		n = read(0, buf, sizeof(buf)-1);
380
		if(n <= 0)
381
			return -1;
382
		buf[n] = 0;
383
		switch(buf[0]){
384
		case '!':
385
			print(buf);
386
			system(c, buf+1);
387
			print("!\n");
388
			done = 1;
389
			break;
390
		case 'i':
391
			buf[0] = 0x1c;
392
			sendwritemsg(c, buf, 1);
393
			done = 1;
394
			break;
395
		case '.':
396
		case 'q':
397
			done = 1;
398
			break;
399
		case 'r':
400
			crstrip = 1-crstrip;
401
			done = 1;
402
			break;
403
		default:
404
			fprint(2, STDHELP);
405
			break;
406
		}
407
		if(!done)
408
			fprint(2, ">>> ");
409
	}
410
 
411
	if(wasraw)
412
		rawon();
413
	else
414
		rawoff();
415
	return buf[0];
416
}
417
 
418
static void
419
sendwritemsg(Conn *c, char *buf, int n)
420
{
421
	Msg *m;
422
 
423
	if(n==0)
424
		m = allocmsg(c, SSH_CMSG_EOF, 0);
425
	else{
426
		m = allocmsg(c, SSH_CMSG_STDIN_DATA, 4+n);
427
		putlong(m, n);
428
		putbytes(m, buf, n);
429
	}
430
	sendmsg(m);
431
}
432
 
433
/*
434
 *  run a command with the network connection as standard IO
435
 */
436
static void
437
system(Conn *c, char *cmd)
438
{
439
	int pid;
440
	int p;
441
	int pfd[2];
442
	int n;
443
	int wasconsctl;
444
	char buf[4096];
445
 
446
	if(pipe(pfd) < 0){
447
		perror("pipe");
448
		return;
449
	}
450
	outfd = pfd[1];
451
 
452
	wasconsctl = consctl;
453
	close(consctl);
454
	consctl = -1;
455
	switch(pid = fork()){
456
	case -1:
457
		perror("con");
458
		return;
459
	case 0:
460
		close(pfd[1]);
461
		dup(pfd[0], 0);
462
		dup(pfd[0], 1);
463
		close(c->fd[0]);	/* same as c->fd[1] */
464
		close(pfd[0]);
465
		if(*cmd)
466
			execl("/bin/rc", "rc", "-c", cmd, nil);
467
		else
468
			execl("/bin/rc", "rc", nil);
469
		perror("con");
470
		exits("exec");
471
		break;
472
	default:
473
		close(pfd[0]);
474
		while((n = read(pfd[1], buf, sizeof(buf))) > 0)
475
			sendwritemsg(c, buf, n);
476
		p = waitpid();
477
		outfd = 1;
478
		close(pfd[1]);
479
		if(p < 0 || p != pid)
480
			return;
481
		break;
482
	}
483
	if(wasconsctl >= 0){
484
		consctl = open("/dev/consctl", OWRITE);
485
		if(consctl < 0)
486
			error("cannot open consctl");
487
	}
488
}
489
 
490
static void
491
cookedcatchint(void*, char *msg)
492
{
493
	if(strstr(msg, "interrupt"))
494
		noted(NCONT);
495
	else if(strstr(msg, "kill"))
496
		noted(NDFLT);
497
	else
498
		noted(NCONT);
499
}
500
 
501
static int
502
wasintr(void)
503
{
504
	char err[64];
505
 
506
	rerrstr(err, sizeof err);
507
	return strstr(err, "interrupt") != 0;
508
}
509
 
510
void
511
fromstdin(Conn *c)
512
{
513
	int n;
514
	char buf[1024];
515
	int pid;
516
	int eofs;
517
 
518
	switch(pid = rfork(RFMEM|RFPROC|RFNOWAIT)){
519
	case -1:
520
		error("fork: %r");
521
	case 0:
522
		break;
523
	default:
524
		atexitkill(pid);
525
		return;
526
	}
527
 
528
	atexit(atexitkiller);
529
	if(interactive)
530
		rawon();
531
 
532
	notify(cookedcatchint);
533
 
534
	eofs = 0;
535
	for(;;){
536
		n = read(0, buf, sizeof(buf));
537
		if(n < 0){
538
			if(wasintr()){
539
				if(!raw){
540
					buf[0] = 0x7f;
541
					n = 1;
542
				}else
543
					continue;
544
			}else
545
				break;
546
		}
547
		if(n == 0){
548
			if(!c->interactive || ++eofs > 32)
549
				break;
550
		}else
551
			eofs = 0;
552
		if(interactive && usemenu && n && memchr(buf, 0x1c, n)) {
553
			if(menu(c)=='q'){
554
				sendwritemsg(c, "", 0);
555
				exits("quit");
556
			}
557
			continue;
558
		}
559
		if(!raw && n==0){
560
			buf[0] = 0x4;
561
			n = 1;
562
		}
563
		sendwritemsg(c, buf, n);
564
	}
565
	sendwritemsg(c, "", 0);
566
	if(n >= 0)				/* weren't hung up upon? */
567
		atexitdont(atexitkiller);
568
	exits(nil);
569
}
570
 
571
void
572
winchanges(Conn *c)
573
{
574
	int nrow, ncol, width, height;
575
	int pid;
576
 
577
	switch(pid = rfork(RFMEM|RFPROC|RFNOWAIT)){
578
	case -1:
579
		error("fork: %r");
580
	case 0:
581
		break;
582
	default:
583
		atexitkill(pid);
584
		return;
585
	}
586
 
587
	for(;;){
588
		if(readgeom(&nrow, &ncol, &width, &height) < 0)
589
			break;
590
		sendwindowsize(c, nrow, ncol, width, height);
591
	}
592
	exits(nil);
593
}