xref: /inferno-os/appl/lib/newns.b (revision b2e326f2d4281f7380036516b62928f8c02850d4)
1implement Newns;
2#
3# Build a new namespace from a file
4#
5#	new	create a new namespace from current directory (use cd)
6#	fork	split the namespace before modification
7#	nodev	disallow device attaches
8#	bind	[-abrci] from to
9#	mount	[-abrci9] [net!]machine[!svc] to [spec]
10#	import [-abrci9] [net!]machine[!svc] [remotedir] dir
11#	unmount	[-i] [from] to
12#   	cd	directory
13#
14#	-i to bind/mount/unmount means continue in the face of errors
15#
16include "sys.m";
17	sys: Sys;
18	FD, FileIO: import Sys;
19	stderr: ref FD;
20
21include "draw.m";
22
23include "bufio.m";
24	bio: Bufio;
25	Iobuf: import bio;
26
27include "dial.m";
28	dial: Dial;
29	Connection: import dial;
30
31include "newns.m";
32
33#include "sh.m";
34
35include "keyring.m";
36	kr: Keyring;
37
38include "security.m";
39	au: Auth;
40
41include "factotum.m";
42
43include "arg.m";
44	arg: Arg;
45
46include "string.m";
47	str: String;
48
49newns(user: string, file: string): string
50{
51	sys = load Sys Sys->PATH;
52	kr = load Keyring Keyring->PATH;
53	stderr = sys->fildes(2);
54
55	# Could do some authentication here, and bail if no good FIXME
56	if(user == nil)
57		;
58	bio = load Bufio Bufio->PATH;
59	if(bio == nil)
60		return sys->sprint("cannot load %s: %r", Bufio->PATH);
61
62	arg = load Arg Arg->PATH;
63	if (arg == nil)
64		return sys->sprint("cannot load %s: %r", Arg->PATH);
65
66	au = load Auth Auth->PATH;
67	if(au == nil)
68		return sys->sprint("cannot load %s: %r", Auth->PATH);
69	err := au->init();
70	if(err != nil)
71		return "Auth->init: "+err;
72
73	str = load String String->PATH;		# no check, because we'll live without it
74
75	if(file == nil){
76		file = "namespace";
77		if(sys->stat(file).t0 < 0)
78			file = "/lib/namespace";
79	}
80
81	mfp := bio->open(file, bio->OREAD);
82	if(mfp==nil)
83      		return sys->sprint("cannot open %q: %r", file);
84
85	if(0 && user != nil){
86		sys->pctl(Sys->FORKENV, nil);
87		setenv("user", user);
88		setenv("home", "/usr/"+user);
89	}
90
91	facfd := sys->open("/mnt/factotum/rpc", Sys->ORDWR);
92	return nsfile(mfp, facfd);
93}
94
95nsfile(b: ref Iobuf, facfd: ref Sys->FD): string
96{
97	e := "";
98	while((l := b.gets('\n')) != nil){
99		if(str != nil)
100			slist := str->unquoted(l);
101		else
102			(nil, slist) = sys->tokenize(l, " \t\n\r");	# old way, in absence of String
103		if(slist == nil)
104			continue;
105		e = nsop(expand(slist), facfd);
106		if(e != "")
107			break;
108   	}
109	return e;
110}
111
112expand(l: list of string): list of string
113{
114	nl: list of string;
115	for(; l != nil; l = tl l){
116		s := hd l;
117		for(i := 0; i < len s; i++)
118			if(s[i] == '$'){
119				for(j := i+1; j < len s; j++)
120					if((c := s[j]) == '.' || c == '/' || c == '$')
121						break;
122				if(j > i+1){
123					(ok, v) := getenv(s[i+1:j]);
124					if(!ok)
125						return nil;
126					s = s[0:i] + v + s[j:];
127					i = i + len v;
128				}
129			}
130		nl = s :: nl;
131	}
132	l = nil;
133	for(; nl != nil; nl = tl nl)
134		l = hd nl :: l;
135	return l;
136}
137
138nsop(argv: list of string, facfd: ref Sys->FD): string
139{
140	# ignore comments
141	if(argv == nil || (hd argv)[0] == '#')
142		return nil;
143
144	e := "";
145	c := 0;
146	cmdstr := hd argv;
147	case cmdstr {
148	"." =>
149		if(tl argv == nil)
150			return ".: needs a filename";
151		nsf := hd tl argv;
152		mfp := bio->open(nsf, bio->OREAD);
153		if(mfp==nil)
154      			return sys->sprint("can't open %q for read %r", nsf);
155		e = nsfile(mfp, facfd);
156	"new" =>
157		c = Sys->NEWNS | Sys->FORKENV;
158	"clear" =>
159		if(sys->pctl(Sys->FORKNS, nil) < 0 ||
160		   sys->bind("#/", "/", Sys->MREPL) < 0 ||
161		   sys->chdir("/") < 0 ||
162		   sys->pctl(Sys->NEWNS, nil) < 0)
163			return sys->sprint("%r");
164		return nil;
165	"fork"  =>
166		c = Sys->FORKNS;
167	"nodev" =>
168		c = Sys->NODEVS;
169	"bind" =>
170		e = bind(argv);
171	"mount" =>
172		e = mount(argv, facfd);
173	"unmount" =>
174		e = unmount(argv);
175	"import" =>
176		e = import9(argv, facfd);
177   	"cd" =>
178   		if(len argv != 2)
179			return "cd: must have one argument";
180		if(sys->chdir(hd tl argv) < 0)
181			return sys->sprint("%r");
182	* =>
183      		e = "invalid namespace command";
184	}
185	if(c != 0) {
186		if(sys->pctl(c, nil) < 0)
187			return sys->sprint("%r");
188	}
189	return e;
190}
191
192Moptres: adt {
193	argv: list of string;
194	flags: int;
195	alg: string;
196	keyfile: string;
197	ignore: int;
198	use9: int;
199	doauth: int;
200};
201
202mopt(argv: list of string): (ref Moptres, string)
203{
204	r := ref Moptres(nil, 0, "none", nil, 0, 0, 1);
205
206	arg->init(argv);
207	while ((opt := arg->opt()) != 0) {
208		case opt {
209		'i' => r.ignore = 1;
210		'a' => r.flags |= sys->MAFTER;
211		'b' => r.flags |= sys->MBEFORE;
212		'c' => r.flags |= sys->MCREATE;
213		'r' => r.flags |= sys->MREPL;
214		'k' =>
215			if((r.keyfile = arg->arg()) == nil)
216				return (nil, "mount: missing arg to -k option");
217		'C' =>
218			if((r.alg = arg->arg()) == nil)
219				return (nil, "mount: missing arg to -C option");
220		'9' =>
221			r.use9 = 1;
222		'A' =>
223			r.doauth = 0;
224		 *  =>
225			return (nil, sys->sprint("mount: bad option -%c", opt));
226		}
227	}
228	if((r.flags & (Sys->MAFTER|Sys->MBEFORE)) == 0)
229		r.flags |= Sys->MREPL;
230
231	r.argv = arg->argv();
232	return (r, nil);
233}
234
235bind(argv: list of string): string
236{
237	(r, err) := mopt(argv);
238	if(err != nil)
239		return err;
240
241	if(len r.argv < 2)
242		return "bind: too few args";
243
244	from := hd r.argv;
245	r.argv = tl r.argv;
246	todir := hd r.argv;
247	if(sys->bind(from, todir, r.flags) < 0)
248		return ig(r, sys->sprint("bind %s %s: %r", from, todir));
249
250	return nil;
251}
252
253mount(argv: list of string, facfd: ref Sys->FD): string
254{
255	fd: ref Sys->FD;
256
257	(r, err) := mopt(argv);
258	if(err != nil)
259		return err;
260
261	if(len r.argv < 2)
262		return ig(r, "mount: too few args");
263
264	if(dial == nil){
265		dial = load Dial Dial->PATH;
266		if(dial == nil)
267			return ig(r, "mount: can't load Dial");
268	}
269
270	addr := hd r.argv;
271	r.argv = tl r.argv;
272	dest := dial->netmkaddr(addr, "net", "styx");
273	dir := hd r.argv;
274	r.argv = tl r.argv;
275	if(r.argv != nil)
276		spec := hd r.argv;
277
278	c := dial->dial(dest, nil);
279	if(c == nil)
280		return ig(r, sys->sprint("dial: %s: %r", dest));
281
282	if(r.doauth != 1){
283		if(sys->mount(c.dfd, nil, dir, r.flags, spec) < 0)
284			return ig(r, sys->sprint("mount %q %q: %r", addr, dir));
285		return nil;
286	}
287
288	if(r.use9){
289		factotum := load Factotum Factotum->PATH;
290		if(factotum == nil)
291			return ig(r, sys->sprint("cannot load %s: %r", Factotum->PATH));
292		factotum->init();
293		afd := sys->fauth(fd, spec);
294		if(afd != nil)
295			factotum->proxy(afd, facfd, "proto=p9any role=client");	# ignore result; if it fails, mount will fail
296		if(sys->mount(fd, afd, dir, r.flags, spec) < 0)
297			return ig(r, sys->sprint("mount %q %q: %r", addr, dir));
298		return nil;
299	}
300
301	user := user();
302	kd := "/usr/" + user + "/keyring/";
303	cert: string;
304	if (r.keyfile != nil) {
305		cert = r.keyfile;
306		if (cert[0] != '/')
307			cert = kd + cert;
308		if(sys->stat(cert).t0 < 0)
309			return ig(r, sys->sprint("cannot find certificate %q: %r", cert));
310	} else {
311		cert = kd + addr;
312		if(sys->stat(cert).t0 < 0)
313			cert = kd + "default";
314	}
315	ai := kr->readauthinfo(cert);
316	if(ai == nil)
317		return ig(r, sys->sprint("cannot read certificate from %q: %r", cert));
318
319	err = au->init();
320	if (err != nil)
321		return ig(r, sys->sprint("auth->init: %r"));
322	(fd, err) = au->client(r.alg, ai, c.dfd);
323	if(fd == nil)
324		return ig(r, sys->sprint("auth: %r"));
325
326	if(sys->mount(fd, nil, dir, r.flags, spec) < 0)
327		return ig(r, sys->sprint("mount %q %q: %r", addr, dir));
328
329	return nil;
330}
331
332import9(argv: list of string, facfd: ref Sys->FD): string
333{
334	(r, err) := mopt(argv);
335	if(err != nil)
336		return err;
337
338	if(len r.argv < 2)
339		return "import: too few args";
340	if(facfd == nil)
341		return ig(r, "import: no factotum");
342	factotum := load Factotum Factotum->PATH;
343	if(factotum == nil)
344		return ig(r, sys->sprint("cannot load %s: %r", Factotum->PATH));
345	factotum->init();
346	addr := hd r.argv;
347	r.argv = tl r.argv;
348	rdir := hd r.argv;
349	r.argv = tl r.argv;
350	dir := rdir;
351	if(r.argv != nil)
352		dir = hd r.argv;
353
354	if(dial == nil){
355		dial = load Dial Dial->PATH;
356		if(dial == nil)
357			return ig(r, "import: can't load Dial");
358	}
359
360	dest := dial->netmkaddr(addr, "net", "17007");	# exportfs; might not be in inferno's ndb yet
361	c := dial->dial(dest, nil);
362	if(c == nil)
363		return ig(r, sys->sprint("import: %s: %r", dest));
364	fd := c.dfd;
365	if(factotum->proxy(fd, facfd, "proto=p9any role=client") == nil)
366		return ig(r, sys->sprint("import: %s: %r", dest));
367	if(sys->fprint(fd, "%s", rdir) < 0)
368		return ig(r, sys->sprint("import: %s: %r", dest));
369	buf := array[256] of byte;
370	if((n := sys->read(fd, buf, len buf)) != 2 || buf[0] != byte 'O' || buf[1] != byte 'K'){
371		if(n >= 4)
372			sys->werrstr(string buf[0:n]);
373		return ig(r, sys->sprint("import: %s: %r", dest));
374	}
375	# TO DO: new style: impo aan|nofilter clear|ssl|tls\n
376	afd := sys->fauth(fd, "");
377	if(afd != nil)
378		factotum->proxy(afd, facfd, "proto=p9any role=client");
379	if(sys->mount(fd, afd, dir, r.flags, "") < 0)
380		return ig(r, sys->sprint("import %q %q: %r", addr, dir));
381	return nil;
382}
383
384unmount(argv: list of string): string
385{
386	(r, err) := mopt(argv);
387	if(err != nil)
388		return err;
389
390	from, tu: string;
391	case len r.argv {
392	* =>
393		return "unmount: takes 1 or 2 args";
394	1 =>
395		from = nil;
396		tu = hd r.argv;
397	2 =>
398		from = hd r.argv;
399		tu = hd tl r.argv;
400	}
401
402	if(sys->unmount(from, tu) < 0)
403		return ig(r, sys->sprint("unmount: %r"));
404
405	return nil;
406}
407
408ig(r: ref Moptres, e: string): string
409{
410	if(r.ignore)
411		return nil;
412	return e;
413}
414
415user(): string
416{
417	sys = load Sys Sys->PATH;
418
419	fd := sys->open("/dev/user", sys->OREAD);
420	if(fd == nil)
421		return "";
422
423	buf := array[Sys->NAMEMAX] of byte;
424	n := sys->read(fd, buf, len buf);
425	if(n < 0)
426		return "";
427
428	return string buf[0:n];
429}
430
431getenv(name: string): (int, string)
432{
433	fd := sys->open("#e/"+name, Sys->OREAD);
434	if(fd == nil)
435		return (0, nil);
436	b := array[256] of byte;
437	n := sys->read(fd, b, len b);
438	if(n <= 0)
439		return (1, "");
440	for(i := 0; i < n; i++)
441		if(b[i] == byte 0 || b[i] == byte '\n')
442			break;
443	return (1, string b[0:i]);
444}
445
446setenv(name: string, val: string)
447{
448	fd := sys->create("#e/"+name, Sys->OWRITE, 8r664);
449	if(fd != nil)
450		sys->fprint(fd, "%s", val);
451}
452
453newuser(user: string, cap: string, nsfile: string): string
454{
455	if(cap == nil)
456		return "no capability";
457
458	sys = load Sys Sys->PATH;
459	fd := sys->open("#¤/capuse", Sys->OWRITE);
460	if(fd == nil)
461		return sys->sprint("opening #¤/capuse: %r");
462
463	b := array of byte cap;
464	if(sys->write(fd, b, len b) < 0)
465		return sys->sprint("writing %s to #¤/capuse: %r", cap);
466
467	# mount factotum as new user (probably unhelpful if not factotum owner)
468	sys->unmount(nil, "/mnt/factotum");
469	sys->bind("#sfactotum", "/mnt/factotum", Sys->MREPL);
470
471	return newns(user, nsfile);
472}
473