xref: /inferno-os/appl/lib/auth.b (revision 37da2899f40661e3e9631e497da8dc59b971cbd0)
1# Inferno authentication protocol
2implement Auth;
3
4include "sys.m";
5	sys: Sys;
6
7include "keyring.m";
8
9include "security.m";
10	ssl: SSL;
11
12init(): string
13{
14	if(sys == nil)
15		sys = load Sys Sys->PATH;
16	return nil;
17}
18
19server(algs: list of string, ai: ref Keyring->Authinfo, fd: ref Sys->FD, setid: int): (ref Sys->FD, string)
20{
21	if(sys == nil)
22		sys = load Sys Sys->PATH;
23	kr := load Keyring Keyring->PATH;
24	if(kr == nil)
25		return (nil, sys->sprint("%r"));
26
27	# mutual authentication
28	(id_or_err, secret) := kr->auth(fd, ai, setid);
29
30	if(secret == nil){
31		if(ai == nil && id_or_err == "no authentication information")
32			id_or_err = "no server certificate";
33		return (nil, id_or_err);
34	}
35	if(0)
36		sys->fprint(sys->fildes(2), "secret is %s\n", dump(secret));
37
38	# have got a secret, get algorithm from client
39	# check if the client algorithm is in the server algorithm list
40	# client algorithm ::= ident (' ' ident)*
41	# where ident is defined by ssl(3)
42	algbuf := string kr->getmsg(fd);
43	if(algbuf == nil)
44		return (nil, sys->sprint("can't read client ssl algorithm: %r"));
45	alg := "";
46	(nil, calgs) := sys->tokenize(algbuf, " /");
47	for(; calgs != nil; calgs = tl calgs){
48		calg := hd calgs;
49		if(algs != nil){	# otherwise we suck it and see
50			for(sl := algs; sl != nil; sl = tl sl)
51				if(hd sl == calg)
52					break;
53			if(sl == nil)
54				return (nil, "unsupported client algorithm: " + calg);
55		}
56		alg += calg + " ";
57	}
58	if(alg != nil)
59		alg = alg[0:len alg - 1];
60
61	# don't push ssl if server supports nossl
62	if(alg == nil || alg == "none")
63		return (fd, id_or_err);
64
65	# push ssl and turn on algorithms
66	ssl = load SSL SSL->PATH;
67	if(ssl == nil)
68		return (nil, sys->sprint("can't load ssl: %r"));
69	(c, err) := pushssl(fd, secret, secret, alg);
70	if(c == nil)
71		return (nil, "push ssl: " + err);
72	return (c, id_or_err);
73}
74
75client(alg: string, ai: ref Keyring->Authinfo, fd: ref Sys->FD): (ref Sys->FD, string)
76{
77	if(sys == nil)
78		sys = load Sys Sys->PATH;
79	kr := load Keyring Keyring->PATH;
80	if(kr == nil)
81		return (nil, sys->sprint("%r"));
82
83	if(alg == nil)
84		alg = "none";
85
86	# mutual authentication
87	(id_or_err, secret) := kr->auth(fd, ai, 0);
88	if(secret == nil)
89		return (nil, id_or_err);
90
91	# send algorithm
92	buf := array of byte alg;
93	if(kr->sendmsg(fd, buf, len buf) < 0)
94		return (nil, sys->sprint("can't send ssl algorithm: %r"));
95
96	# don't push ssl if server supports no ssl connection
97	if(alg == "none")
98		return (fd, id_or_err);
99
100	# push ssl and turn on algorithm
101	ssl = load SSL SSL->PATH;
102	if(ssl == nil)
103		return (nil, sys->sprint("can't load ssl: %r"));
104	(c, err) := pushssl(fd, secret, secret, alg);
105	if(c == nil)
106		return (nil, "push ssl: " + err);
107	return (c, id_or_err);
108}
109
110auth(ai: ref Keyring->Authinfo, keyspec: string, alg: string, fd: ref Sys->FD): (ref Sys->FD, ref Keyring->Authinfo, string)
111{
112	if(sys == nil)
113		sys = load Sys Sys->PATH;
114	kr := load Keyring Keyring->PATH;
115	if(kr == nil)
116		return (nil, nil, sys->sprint("can't load %s: %r", Keyring->PATH));
117	if(alg == nil)
118		alg = "none";
119	if(ai == nil && keyspec != nil){
120		ai = key(keyspec);
121		if(ai == nil)
122			return (nil, nil, sys->sprint("can't obtain key: %r"));
123	}
124
125	# mutual authentication
126	(id_or_err, secret) := kr->auth(fd, ai, 0);
127	if(secret == nil)
128		return (nil, nil, id_or_err);
129
130	# send algorithm
131	buf := array of byte alg;
132	if(kr->sendmsg(fd, buf, len buf) < 0)
133		return (nil, nil, sys->sprint("can't send ssl algorithm: %r"));
134
135	if(0){		# TO DO
136		hisalg := string kr->getmsg(fd);
137		if(hisalg == nil)
138			return (nil, nil, sys->sprint("can't get remote algorithm: %r"));
139		# TO DO: compare the two, sort it out if they aren't equal
140	}
141
142	# don't push ssl if server supports no ssl connection
143	if(alg == "none")
144		return (fd, nil, id_or_err);
145
146	# push ssl and turn on algorithm
147	ssl = load SSL SSL->PATH;
148	if(ssl == nil)
149		return (nil, nil, sys->sprint("can't load ssl: %r"));
150	(c, err) := pushssl(fd, secret, secret, alg);
151	if(c == nil)
152		return (nil, nil, "push ssl: " + err);
153	return (c, nil, id_or_err);
154}
155
156dump(b: array of byte): string
157{
158	s := "";
159	for(i := 0; i < len b; i++)
160		s += sys->sprint("%.2ux", int b[i]);
161	return s;
162}
163
164# push an SSLv2 Record Layer onto the fd
165pushssl(fd: ref Sys->FD, secretin, secretout: array of byte, alg: string): (ref Sys->FD, string)
166{
167	(err, c) := ssl->connect(fd);
168	if(err != nil)
169		return (nil, "can't connect ssl: " + err);
170
171	err = ssl->secret(c, secretin, secretout);
172	if(err != nil)
173		return (nil, "can't write secret: " + err);
174
175	if(sys->fprint(c.cfd, "alg %s", alg) < 0)
176		return (nil, sys->sprint("can't push algorithm %s: %r", alg));
177
178	return (c.dfd, nil);
179}
180
181key(keyspec: string): ref Keyring->Authinfo
182{
183	f := keyfile(keyspec);
184	if(f == nil)
185		return nil;
186	kr := load Keyring Keyring->PATH;
187	if(kr == nil){
188		sys->werrstr(sys->sprint("can't load %s: %r", Keyring->PATH));
189		return nil;
190	}
191	return kr->readauthinfo(f);
192}
193
194#
195# look for key in old style keyring directory;
196# closest match to [net!]addr[!svc]
197#
198
199keyfile(keyspec: string): string
200{
201	if(sys == nil)
202		sys = load Sys Sys->PATH;
203	al := parseattr(keyspec);
204	keyname := get(al, "key");
205	if(keyname != nil){
206		# explicit keyname overrides rest of spec
207		if(keyname[0] == '/' ||
208		   len keyname > 2 && keyname[0:2]=="./" ||
209		   len keyname > 3 && keyname[0:3]=="../")
210			return keyname;	# don't add directory
211		return keydir()+keyname;
212	}
213	net := "net";
214	svc := get(al, "service");
215	addr := get(al, "server");
216	(nf, flds) := sys->tokenize(addr, "!");	# compatibility
217	if(nf > 1){
218		net = hd flds;
219		addr = hd tl flds;
220	}
221	if(addr != nil)
222		keyname = addr;
223	else
224		keyname = "default";
225	kd := keydir();
226	dom := get(al, "dom");
227	if(dom != nil){
228		if((cert := exists(kd+dom)) != nil)
229			return cert;
230	}
231	if(keyname == "default")
232		return kd+"default";
233	if(net == "net")
234		l := "net!" :: "tcp!" :: nil;
235	else
236		l = net+"!" :: nil;
237	if(svc != nil){
238		for(nl := l; nl != nil; nl = tl nl){
239			cert := exists(kd+(hd nl)+keyname+"!"+svc);	# most specific
240			if(cert != nil)
241				return cert;
242		}
243	}
244	for(nl := l; nl != nil; nl = tl nl){
245		cert := exists(kd+(hd nl)+keyname);
246		if(cert != nil)
247			return cert;
248	}
249	cert := exists(kd+keyname);	# unadorned
250	if(cert != nil)
251		return cert;
252	if(keyname != "default"){
253		cert = exists(kd+"default");
254		if(cert != nil)
255			return cert;
256	}
257	return kd+keyname;
258}
259
260keydir(): string
261{
262	fd := sys->open("/dev/user", Sys->OREAD);
263	if(fd == nil)
264		return nil;
265	b := array[Sys->NAMEMAX] of byte;
266	nr := sys->read(fd, b, len b);
267	if(nr <= 0){
268		sys->werrstr("can't read /dev/user");
269		return nil;
270	}
271	user := string b[0:nr];
272	return "/usr/" + user + "/keyring/";
273}
274
275exists(f: string): string
276{
277	(ok, nil) := sys->stat(f);
278	if(0)sys->fprint(sys->fildes(2), "exists: %q %d\n", f, ok>=0);
279	if(ok >= 0)
280		return f;
281	return nil;
282}
283
284Aattr, Aval, Aquery: con iota;
285
286Attr: adt {
287	tag:	int;
288	name:	string;
289	val:	string;
290};
291
292parseattr(s: string): list of ref Attr
293{
294	(nil, fld) := sys->tokenize(s, " \t\n");	# should do quoting; later
295	rfld := fld;
296	for(fld = nil; rfld != nil; rfld = tl rfld)
297		fld = (hd rfld) :: fld;
298	attrs: list of ref Attr;
299	for(; fld != nil; fld = tl fld){
300		n := hd fld;
301		a := "";
302		tag := Aattr;
303		for(i:=0; i<len n; i++)
304			if(n[i] == '='){
305				a = n[i+1:];
306				n = n[0:i];
307				tag = Aval;
308			}
309		if(len n == 0)
310			continue;
311		if(tag == Aattr && len n > 1 && n[len n-1] == '?'){
312			tag = Aquery;
313			n = n[0:len n-1];
314		}
315		attrs = ref Attr(tag, n, a) :: attrs;
316	}
317	return attrs;
318}
319
320get(al: list of ref Attr, n: string): string
321{
322	for(; al != nil; al = tl al)
323		if((a := hd al).name == n && a.tag == Aval)
324			return a.val;
325	return nil;
326}
327