Subversion Repositories planix.SVN

Rev

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

Rev Author Line No. Line
2 - 1
/*
2
 * ssh server - serve SSH protocol v2
3
 *	/net/ssh does most of the work; we copy bytes back and forth
4
 */
5
#include <u.h>
6
#include <libc.h>
7
#include <ip.h>
8
#include <auth.h>
9
#include "ssh2.h"
10
 
11
char *confine(char *, char *);
12
char *get_string(char *, char *);
13
void newchannel(int, char *, int);
14
void runcmd(int, int, char *, char *, char *, char *);
15
 
16
int errfd, toppid, sflag, tflag, prevent;
17
int debug;
18
char *idstring;
19
char *netdir;				/* /net/ssh/<conn> */
20
char *nsfile = nil;
21
char *restdir;
22
char *shell;
23
char *srvpt;
24
char *uname;
25
 
26
void
27
usage(void)
28
{
29
	fprint(2, "usage: %s [-i id] [-s shell] [-r dir] [-R dir] [-S srvpt] "
30
		"[-n ns] [-t] [netdir]\n", argv0);
31
	exits("usage");
32
}
33
 
34
static int
35
getctlfd(void)
36
{
37
	int ctlfd;
38
	char *name;
39
 
40
	name = smprint("%s/clone", netdir);
41
	ctlfd = -1;
42
	if (name)
43
		ctlfd = open(name, ORDWR);
44
	if (ctlfd < 0) {
45
		syslog(0, "ssh", "server can't clone: %s: %r", name);
46
		exits("open clone");
47
	}
48
	free(name);
49
	return ctlfd;
50
}
51
 
52
static int
53
getdatafd(int ctlfd)
54
{
55
	int fd;
56
	char *name;
57
 
58
	name = smprint("%s/data", netdir);
59
	fd = -1;
60
	if (name)
61
		fd = open(name, OREAD);
62
	if (fd < 0) {
63
		syslog(0, "ssh", "can't open %s: %r", name);
64
		hangup(ctlfd);
65
		exits("open data");
66
	}
67
	free(name);
68
	return fd;
69
}
70
 
71
static void
72
auth(char *buf, int n, int ctlfd)
73
{
74
	int fd;
75
 
76
	fd = open("#ยค/capuse", OWRITE);
77
	if (fd < 0) {
78
		syslog(0, "ssh", "server can't open capuse: %r");
79
		hangup(ctlfd);
80
		exits("capuse");
81
	}
82
	if (write(fd, buf, n) != n) {
83
		syslog(0, "ssh", "server write `%.*s' to capuse failed: %r",
84
			n, buf);
85
		hangup(ctlfd);
86
		exits("capuse");
87
	}
88
	close(fd);
89
}
90
 
91
/*
92
 * mount tunnel if there isn't one visible.
93
 */
94
static void
95
mounttunnel(int ctlfd)
96
{
97
	int fd;
98
	char *p, *np, *q;
99
 
100
	if (access(netdir, AEXIST) >= 0)
101
		return;
102
 
103
	p = smprint("/srv/%s", srvpt? srvpt: "ssh");
104
	np = strdup(netdir);
105
	if (p == nil || np == nil)
106
		sysfatal("out of memory");
107
	q = strstr(np, "/ssh");
108
	if (q != nil)
109
		*q = '\0';
110
	fd = open(p, ORDWR);
111
	if (fd < 0) {
112
		syslog(0, "ssh", "can't open %s: %r", p);
113
		hangup(ctlfd);
114
		exits("open");
115
	}
116
	if (mount(fd, -1, np, MBEFORE, "") < 0) {
117
		syslog(0, "ssh", "can't mount %s in %s: %r", p, np);
118
		hangup(ctlfd);
119
		exits("can't mount");
120
	}
121
	free(p);
122
	free(np);
123
}
124
 
125
static int
126
authnewns(int ctlfd, char *buf, int size, int n)
127
{
128
	char *p, *q;
129
 
130
	USED(size);
131
	if (n <= 0)
132
		return 0;
133
	buf[n] = '\0';
134
	if (strcmp(buf, "n/a") == 0)
135
		return 0;
136
 
137
	auth(buf, n, ctlfd);
138
 
139
	p = strchr(buf, '@');
140
	if (p == nil)
141
		return 0;
142
	++p;
143
	q = strchr(p, '@');
144
	if (q) {
145
		*q = '\0';
146
		uname = strdup(p);
147
	}
148
	if (!tflag && newns(p, nsfile) < 0) {
149
		syslog(0, "ssh", "server: newns(%s,%s) failed: %r", p, nsfile);
150
		return -1;
151
	}
152
	return 0;
153
}
154
 
155
static void
156
listenloop(char *listfile, int ctlfd, char *buf, int size)
157
{
158
	int fd, n;
159
 
160
	while ((fd = open(listfile, ORDWR)) >= 0) {
161
		n = read(fd, buf, size - 1);
162
		fprint(errfd, "read from listen file returned %d\n", n);
163
		if (n <= 0) {
164
			syslog(0, "ssh", "read on listen failed: %r");
165
			break;
166
		}
167
		buf[n >= 0? n: 0] = '\0';
168
		fprint(errfd, "read %s\n", buf);
169
 
170
		switch (fork()) {
171
		case 0:					/* child */
172
			close(ctlfd);
173
			newchannel(fd, netdir, atoi(buf));  /* never returns */
174
		case -1:
175
			syslog(0, "ssh", "fork failed: %r");
176
			hangup(ctlfd);
177
			exits("fork");
178
		}
179
		close(fd);
180
	}
181
	if (fd < 0)
182
		syslog(0, "ssh", "listen failed: %r");
183
}
184
 
185
void
186
main(int argc, char *argv[])
187
{
188
	char *listfile;
189
	int ctlfd, fd, n;
190
	char buf[Arbpathlen], path[Arbpathlen];
191
 
192
	rfork(RFNOTEG);
193
	toppid = getpid();
194
	shell = "/bin/rc -il";
195
	ARGBEGIN {
196
	case 'd':
197
		debug++;
198
		break;
199
	case 'i':
200
		idstring = EARGF(usage());
201
		break;
202
	case 'n':
203
		nsfile = EARGF(usage());
204
		break;
205
	case 'R':
206
		prevent = 1;
207
		/* fall through */
208
	case 'r':
209
		restdir = EARGF(usage());
210
		break;
211
	case 's':
212
		sflag = 1;
213
		shell = EARGF(usage());
214
		break;
215
	case 'S':
216
		srvpt = EARGF(usage());
217
		break;
218
	case 't':
219
		tflag = 1;
220
		break;
221
	default:
222
		usage();
223
		break;
224
	} ARGEND;
225
 
226
	errfd = -1;
227
	if (debug)
228
		errfd = 2;
229
 
230
	/* work out network connection's directory */
231
	if (argc >= 1)
232
		netdir = argv[0];
233
	else				/* invoked by listen1 */
234
		netdir = getenv("net");
235
	if (netdir == nil) {
236
		syslog(0, "ssh", "server netdir is nil");
237
		exits("nil netdir");
238
	}
239
	syslog(0, "ssh", "server netdir is %s", netdir);
240
 
241
	uname = getenv("user");
242
	if (uname == nil)
243
		uname = "none";
244
 
245
	/* extract dfd and cfd from netdir */
246
	ctlfd = getctlfd();
247
	fd = getdatafd(ctlfd);
248
 
249
	n = read(fd, buf, sizeof buf - 1);
250
	if (n < 0) {
251
		syslog(0, "ssh", "server read error for data file: %r");
252
		hangup(ctlfd);
253
		exits("read cap");
254
	}
255
	close(fd);
256
	authnewns(ctlfd, buf, sizeof buf, n);
257
 
258
	/* get connection number in buf */
259
	n = read(ctlfd, buf, sizeof buf - 1);
260
	buf[n >= 0? n: 0] = '\0';
261
 
262
	/* tell netssh our id string */
263
	fd2path(ctlfd, path, sizeof path);
264
	if (0 && idstring) {			/* was for coexistence */
265
		syslog(0, "ssh", "server conn %s, writing \"id %s\" to %s",
266
			buf, idstring, path);
267
		fprint(ctlfd, "id %s", idstring);
268
	}
269
 
270
	/* announce */
271
	fprint(ctlfd, "announce session");
272
 
273
	/* construct listen file name */
274
	listfile = smprint("%s/%s/listen", netdir, buf);
275
	if (listfile == nil) {
276
		syslog(0, "ssh", "out of memory");
277
		exits("out of memory");
278
	}
279
	syslog(0, "ssh", "server listen is %s", listfile);
280
 
281
	mounttunnel(ctlfd);
282
	listenloop(listfile, ctlfd, buf, sizeof buf);
283
	hangup(ctlfd);
284
	exits(nil);
285
}
286
 
287
/* an abbreviation.  note the assumed variables. */
288
#define REPLY(s)	if (want_reply) fprint(reqfd, s);
289
 
290
static void
291
forkshell(char *cmd, int reqfd, int datafd, int want_reply)
292
{
293
	switch (fork()) {
294
	case 0:
295
		if (sflag)
296
			snprint(cmd, sizeof cmd, "-s%s", shell);
297
		else
298
			cmd[0] = '\0';
299
		USED(cmd);
300
		syslog(0, "ssh", "server starting ssh shell for %s", uname);
301
		/* runcmd doesn't return */
302
		runcmd(reqfd, datafd, "con", "/bin/ip/telnetd", "-nt", nil);
303
	case -1:
304
		REPLY("failure");
305
		syslog(0, "ssh", "server can't fork: %r");
306
		exits("fork");
307
	}
308
}
309
 
310
static void
311
forkcmd(char *cmd, char *p, int reqfd, int datafd, int want_reply)
312
{
313
	char *q;
314
 
315
	switch (fork()) {
316
	case 0:
317
		if (restdir && chdir(restdir) < 0) {
318
			syslog(0, "ssh", "can't chdir(%s): %r", restdir);
319
			exits("can't chdir");
320
		}
321
		if (!prevent || (q = getenv("sshsession")) &&
322
		    strcmp(q, "allow") == 0)
323
			get_string(p+1, cmd);
324
		else
325
			confine(p+1, cmd);
326
		syslog(0, "ssh", "server running `%s' for %s", cmd, uname);
327
		/* runcmd doesn't return */
328
		runcmd(reqfd, datafd, "rx", "/bin/rc", "-lc", cmd);
329
	case -1:
330
		REPLY("failure");
331
		syslog(0, "ssh", "server can't fork: %r");
332
		exits("fork");
333
	}
334
}
335
 
336
void
337
newchannel(int fd, char *conndir, int channum)
338
{
339
	char *p, *reqfile, *datafile;
340
	int n, reqfd, datafd, want_reply, already_done;
341
	char buf[Maxpayload], cmd[Bigbufsz];
342
 
343
	close(fd);
344
 
345
	already_done = 0;
346
	reqfile = smprint("%s/%d/request", conndir, channum);
347
	if (reqfile == nil)
348
		sysfatal("out of memory");
349
	reqfd = open(reqfile, ORDWR);
350
	if (reqfd < 0) {
351
		syslog(0, "ssh", "can't open request file %s: %r", reqfile);
352
		exits("net");
353
	}
354
	datafile = smprint("%s/%d/data", conndir, channum);
355
	if (datafile == nil)
356
		sysfatal("out of memory");
357
	datafd = open(datafile, ORDWR);
358
	if (datafd < 0) {
359
		syslog(0, "ssh", "can't open data file %s: %r", datafile);
360
		exits("net");
361
	}
362
	while ((n = read(reqfd, buf, sizeof buf - 1)) > 0) {
363
		fprint(errfd, "read from request file returned %d\n", n);
364
		for (p = buf; p < buf + n && *p != ' '; ++p)
365
			;
366
		*p++ = '\0';
367
		want_reply = (*p == 't');
368
		/* shell, exec, and various flavours of failure */
369
		if (strcmp(buf, "shell") == 0) {
370
			if (already_done) {
371
				REPLY("failure");
372
				continue;
373
			}
374
			forkshell(cmd, reqfd, datafd, want_reply);
375
			already_done = 1;
376
			REPLY("success");
377
		} else if (strcmp(buf, "exec") == 0) {
378
			if (already_done) {
379
				REPLY("failure");
380
				continue;
381
			}
382
			forkcmd(cmd, p, reqfd, datafd, want_reply);
383
			already_done = 1;
384
			REPLY("success");
385
		} else if (strcmp(buf, "pty-req") == 0 ||
386
		    strcmp(buf, "window-change") == 0) {
387
			REPLY("success");
388
		} else if (strcmp(buf, "x11-req") == 0 ||
389
		    strcmp(buf, "env") == 0 || strcmp(buf, "subsystem") == 0) {
390
			REPLY("failure");
391
		} else if (strcmp(buf, "xon-xoff") == 0 ||
392
		    strcmp(buf, "signal") == 0 ||
393
		    strcmp(buf, "exit-status") == 0 ||
394
		    strcmp(buf, "exit-signal") == 0) {
395
			;
396
		} else
397
			syslog(0, "ssh", "server unknown channel request: %s",
398
				buf);
399
	}
400
	if (n < 0)
401
		syslog(0, "ssh", "server read failed: %r");
402
	exits(nil);
403
}
404
 
405
char *
406
get_string(char *q, char *s)
407
{
408
	int n;
409
 
410
	n = nhgetl(q);
411
	q += 4;
412
	memmove(s, q, n);
413
	s[n] = '\0';
414
	q += n;
415
	return q;
416
}
417
 
418
char *
419
confine(char *q, char *s)
420
{
421
	int i, n, m;
422
	char *p, *e, *r, *buf, *toks[Maxtoks];
423
 
424
	n = nhgetl(q);
425
	q += 4;
426
	buf = malloc(n+1);
427
	if (buf == nil)
428
		return nil;
429
	memmove(buf, q, n);
430
	buf[n]  = 0;
431
	m = tokenize(buf, toks, nelem(toks));
432
	e = s + n + 1;
433
	for (i = 0, r = s; i < m; ++i) {
434
		p = strrchr(toks[i], '/');
435
		if (p == nil)
436
			r = seprint(r, e, "%s ", toks[i]);
437
		else if (p[0] != '\0' && p[1] != '\0')
438
			r = seprint(r, e, "%s ", p+1);
439
		else
440
			r = seprint(r, e, ". ");
441
	}
442
	free(buf);
443
	q += n;
444
	return q;
445
}
446
 
447
void
448
runcmd(int reqfd, int datafd, char *svc, char *cmd, char *arg1, char *arg2)
449
{
450
	char *p;
451
	int fd, cmdpid, child;
452
 
453
	cmdpid = rfork(RFPROC|RFMEM|RFNOTEG|RFFDG|RFENVG);
454
	switch (cmdpid) {
455
	case -1:
456
		syslog(0, "ssh", "fork failed: %r");
457
		exits("fork");
458
	case 0:
459
		if (restdir == nil) {
460
			p = smprint("/usr/%s", uname);
461
			if (p && access(p, AREAD) == 0 && chdir(p) < 0) {
462
				syslog(0, "ssh", "can't chdir(%s): %r", p);
463
				exits("can't chdir");
464
			}
465
			free(p);
466
		}
467
		p = strrchr(cmd, '/');
468
		if (p)
469
			++p;
470
		else
471
			p = cmd;
472
 
473
		dup(datafd, 0);
474
		dup(datafd, 1);
475
		dup(datafd, 2);
476
		close(datafd);
477
		putenv("service", svc);
478
		fprint(errfd, "starting %s\n", cmd);
479
		execl(cmd, p, arg1, arg2, nil);
480
 
481
		syslog(0, "ssh", "cannot exec %s: %r", cmd);
482
		exits("exec");
483
	default:
484
		close(datafd);
485
		fprint(errfd, "waiting for child %d\n", cmdpid);
486
		while ((child = waitpid()) != cmdpid && child != -1)
487
			fprint(errfd, "child %d passed\n", child);
488
		if (child == -1)
489
			fprint(errfd, "wait failed: %r\n");
490
 
491
		syslog(0, "ssh", "server closing ssh session for %s", uname);
492
		fprint(errfd, "closing connection\n");
493
		fprint(reqfd, "close");
494
		p = smprint("/proc/%d/notepg", toppid);
495
		if (p) {
496
			fd = open(p, OWRITE);
497
			fprint(fd, "interrupt");
498
			close(fd);
499
		}
500
		exits(nil);
501
	}
502
}