xref: /inferno-os/appl/lib/registries.b (revision fbc1184c08d18d5ac0f8763a058e015e95353341)
1implement Registries;
2
3include "sys.m";
4	sys: Sys;
5include "bufio.m";
6	bufio: Bufio;
7	Iobuf: import bufio;
8include "string.m";
9	str: String;
10include "keyring.m";
11	keyring: Keyring;
12include "dial.m";
13	dial: Dial;
14include "security.m";
15	auth: Auth;
16include "keyset.m";
17	keyset: Keyset;
18include "registries.m";
19
20init()
21{
22	sys = load Sys Sys->PATH;
23	bufio = checkload(load Bufio Bufio->PATH, Bufio->PATH);
24	keyring = checkload(load Keyring Keyring->PATH, Keyring->PATH);
25	str = checkload(load String String->PATH, String->PATH);
26	keyset = checkload(load Keyset Keyset->PATH, Keyset->PATH);
27	dial = checkload(load Dial Dial->PATH, Dial->PATH);
28	auth = checkload(load Auth Auth->PATH, Auth->PATH);
29	e := keyset->init();
30	if(e != nil)
31		raise sys->sprint("can't init Keyset: %s", e);
32	e = auth->init();
33	if(e != nil)
34		raise sys->sprint("can't init Auth: %s", e);
35}
36
37checkload[T](x: T, s: string): T
38{
39	if(x == nil)
40		raise sys->sprint("can't load %s: %r", s);
41	return x;
42}
43
44Registry.new(dir: string): ref Registry
45{
46	if(dir == nil)
47		dir = "/mnt/registry";
48	r := ref Registry;
49	r.dir = dir;
50	r.indexfd = sys->open(dir + "/index", Sys->OREAD);
51	if(r.indexfd == nil)
52		return nil;
53	return r;
54}
55
56Registry.connect(svc: ref Service, user, keydir: string): ref Registry
57{
58	# XXX broadcast for local registries here.
59	if(svc == nil)
60	#	svc = ref Service("net!$registry!registry", Attributes.new(("auth", "infpk1") :: nil));
61		svc = ref Service("net!$registry!registry", Attributes.new(("auth", "none") :: nil));
62	a := svc.attach(user, keydir);
63	if(a == nil)
64		return nil;
65	if(sys->mount(a.fd, nil, "/mnt/registry", Sys->MREPL, nil) == -1){
66		sys->werrstr(sys->sprint("mount failed: %r"));
67		return nil;
68	}
69	return Registry.new("/mnt/registry");
70}
71
72Registry.services(r: self ref Registry): (list of ref Service, string)
73{
74	sys->seek(r.indexfd, big 0, Sys->SEEKSTART);
75	iob := bufio->fopen(r.indexfd, Sys->OREAD);
76	if(iob == nil)
77		return (nil, sys->sprint("%r"));
78	return (readservices(iob), nil);
79}
80
81Registry.find(r: self ref Registry, a: list of (string, string)): (list of ref Service, string)
82{
83	fd := sys->open(r.dir + "/find", Sys->ORDWR);	# could keep it open if it's a bottleneck
84	if(fd == nil)
85		return (nil, sys->sprint("%r"));
86	s := "";
87	if(a != nil){
88		for(; a != nil; a = tl a){
89			(n, v) := hd a;
90			s += sys->sprint(" %q %q", n, v);
91		}
92		s = s[1:];
93	}
94	if(sys->fprint(fd, "%s", s) == -1)
95		return (nil, sys->sprint("%r"));
96	sys->seek(fd, big 0, Sys->SEEKSTART);
97	iob := bufio->fopen(fd, Sys->OREAD);
98	return (readservices(iob), nil);
99}
100
101readservices(iob: ref Iobuf): list of ref Service
102{
103	services: list of ref Service;
104	while((s := qgets(iob, '\n')) != nil){
105		toks := str->unquoted(s);
106		if(toks == nil || len toks % 2 != 1)
107			continue;
108		svc := ref Service(hd toks, nil);
109		attrs, rattrs: list of (string, string);
110		for(toks = tl toks; toks != nil; toks = tl tl toks)
111			rattrs = (hd toks, hd tl toks) :: rattrs;
112		for(; rattrs != nil; rattrs = tl rattrs)
113			attrs = hd rattrs :: attrs;
114		svc.attrs = ref Attributes(attrs);
115		services = svc :: services;
116	}
117	return rev(services);
118}
119
120rev[T](l: list of T): list of T
121{
122	rl: list of T;
123	for(; l != nil; l = tl l)
124		rl = hd l :: rl;
125	return rl;
126}
127
128Registry.register(r: self ref Registry, addr: string, attrs: ref Attributes, persist: int): (ref Registered, string)
129{
130	fd := sys->open(r.dir + "/new", Sys->OWRITE);
131	if(fd == nil)
132		return (nil, sys->sprint("%r"));
133	s := sys->sprint("%q", addr);
134	for(a := attrs.attrs; a != nil; a = tl a)
135		s += sys->sprint(" %q %q", (hd a).t0, (hd a).t1);
136	if(persist)
137		s += " persist 1";
138	if(sys->fprint(fd, "%s", s) == -1)
139		return (nil, sys->sprint("%r"));
140	return (ref Registered(addr, r, fd), nil);
141}
142
143Registry.unregister(r: self ref Registry, addr: string): string
144{
145	if(sys->remove(r.dir + "/" + addr) == -1)
146		return sys->sprint("%r");
147	return nil;
148}
149
150Attributes.new(attrs: list of (string, string)): ref Attributes
151{
152	return ref Attributes(attrs);
153}
154
155Attributes.set(a: self ref Attributes, attr, val: string)
156{
157	for(al := a.attrs; al != nil; al = tl al)
158		if((hd al).t0 == attr)
159			break;
160	if(al == nil){
161		a.attrs = (attr, val) :: a.attrs;
162		return;
163	}
164	attrs := (attr, val) :: tl al;
165	for(al = a.attrs; al != nil; al = tl al){
166		if((hd al).t0 == attr)
167			break;
168		attrs = hd al :: attrs;
169	}
170	a.attrs = attrs;
171}
172
173Attributes.get(a: self ref Attributes, attr: string): string
174{
175	for(al := a.attrs; al != nil; al = tl al)
176		if((hd al).t0 == attr)
177			return (hd al).t1;
178	return nil;
179}
180
181qgets(iob: ref Iobuf, eoc: int): string
182{
183	inq := 0;
184	s := "";
185	while((c := iob.getc()) >= 0){
186		s[len s] = c;
187		if(inq){
188			if(c == '\''){
189				c = iob.getc();
190				if(c == '\'')
191					s[len s] = c;
192				else{
193					iob.ungetc();
194					inq = 0;
195				}
196			}
197		}else{
198			if(c == eoc)
199				return s;
200			if(c == '\'')
201				inq = 1;
202		}
203	}
204	return s;
205}
206
207Service.attach(svc: self ref Service, localuser, keydir: string): ref Attached
208{
209	# attributes used:
210	# 	auth			type of authentication to perform (auth, none)
211	#	auth.crypt		type of encryption to push (as accepted by ssl(3)'s "alg" operation)
212	#	auth.signer	hash of service's certificate's signer's public key
213
214	c := dial->dial(svc.addr, nil);
215	if(c == nil){
216		sys->werrstr(sys->sprint("cannot dial: %r"));
217		return nil;
218	}
219	attached := ref Attached;
220	authkind := svc.attrs.get("auth");
221	case authkind {
222	"auth" or		# old
223	"infpk1" =>
224		cryptalg := svc.attrs.get("auth.crypt");
225		if(cryptalg == nil)
226			cryptalg = "none";
227		ca := svc.attrs.get("auth.signer");
228		kf: string;
229		if(ca != nil){
230			(kfl, err) := keyset->keysforsigner(nil, ca, nil, keydir);
231			if(kfl == nil){
232				s := "no matching keys found";
233				if(err != nil)
234					s += ": "+err;
235				sys->werrstr(s);
236				return nil;
237			}
238			if(localuser == nil)
239				kf = (hd kfl).t0;
240			else{
241				for(; kfl != nil; kfl = tl kfl)
242					if((hd kfl).t1 == localuser)
243						break;
244				if(kfl == nil){
245					sys->werrstr("no matching user found");
246					return nil;
247				}
248				kf = (hd kfl).t0;
249			}
250		} else {
251			user := readname("/dev/user");
252			if(user == nil)
253				kf = "/lib/keyring/default";
254			else
255				kf = "/usr/" + user + "/keyring/default";
256		}
257		info := keyring->readauthinfo(kf);
258		if(info == nil){
259			sys->werrstr(sys->sprint("cannot read key: %r"));
260			return nil;
261		}
262		(fd, ue) := auth->client(cryptalg, info, c.dfd);
263		if(fd == nil){
264			sys->werrstr(sys->sprint("cannot authenticate: %r"));
265			return nil;
266		}
267		attached.signerpkhash = keyset->pkhash(keyring->pktostr(info.spk));
268		attached.localuser = info.mypk.owner;
269		attached.remoteuser = ue;
270		attached.fd = fd;
271	"" or
272	"none" =>
273		attached.fd = c.dfd;
274	* =>
275		sys->werrstr(sys->sprint("unknown authentication type %q", authkind));
276		return nil;
277	}
278	return attached;
279}
280
281readname(s: string): string
282{
283	fd := sys->open(s, Sys->OREAD);
284	if(fd == nil)
285		return nil;
286	buf := array[Sys->NAMEMAX] of byte;
287	n := sys->read(fd, buf, len buf);
288	if(n <= 0)
289		return nil;
290	return string buf[0:n];
291}
292