xref: /inferno-os/appl/cmd/auth/logind.b (revision 62d7827bc358c000db9ff48fe61bd28ac352a884)
1implement Logind;
2
3#
4# certification service (signer)
5#
6
7include "sys.m";
8	sys: Sys;
9
10include "draw.m";
11
12include "keyring.m";
13	kr: Keyring;
14	IPint: import kr;
15
16include "dial.m";
17
18include "security.m";
19	ssl: SSL;
20
21include "daytime.m";
22	daytime: Daytime;
23
24Logind: module
25{
26	init:	fn(ctxt: ref Draw->Context, argv: list of string);
27};
28
29TimeLimit: con 5*60*1000;	# five minutes
30keydb := "/mnt/keys";
31
32stderr: ref Sys->FD;
33
34init(nil: ref Draw->Context, nil: list of string)
35{
36	sys = load Sys Sys->PATH;
37	stderr = sys->open("/dev/cons", sys->OWRITE);
38
39	kr = load Keyring Keyring->PATH;
40
41	ssl = load SSL SSL->PATH;
42	if(ssl == nil)
43		nomod(SSL->PATH);
44
45	daytime = load Daytime Daytime->PATH;
46	if(daytime == nil)
47		nomod(Daytime->PATH);
48
49	(err, c) := ssl->connect(sys->fildes(0));
50	if(c == nil)
51		fatal("pushing ssl: " + err);
52
53	# impose time out to ensure dead network connections recovered well before TCP/IP's long time out
54
55	grpid := sys->pctl(Sys->NEWPGRP,nil);
56	pidc := chan of int;
57	spawn stalker(pidc, grpid);
58	tpid := <-pidc;
59	err = dologin(c);
60	if(err != nil){
61		sys->fprint(stderr, "logind: %s\n", err);
62		kr->puterror(c.dfd, err);
63	}
64	kill(tpid, "kill");
65}
66
67dologin(c: ref Dial->Connection): string
68{
69	ivec: array of byte;
70
71	(info, err) := signerkey("/keydb/signerkey");
72	if(info == nil)
73		return "can't read signer's own key: "+err;
74
75	# get user name; ack
76	s: string;
77	(s, err) = kr->getstring(c.dfd);
78	if(err != nil)
79		return err;
80	name := s;
81	kr->putstring(c.dfd, name);
82
83	# get initialization vector
84	(ivec, err) = kr->getbytearray(c.dfd);
85	if(err != nil)
86		return "can't get initialization vector: "+err;
87
88	# lookup password
89	pw := getsecret(s);
90	if(pw == nil)
91		return sys->sprint("no password entry for %s: %r", s);
92	if(len pw < Keyring->SHA1dlen)
93		return "bad password for "+s+": not SHA1 hashed?";
94	userexp := getexpiry(s);
95	if(userexp < 0)
96		return sys->sprint("expiry time for %s: %r", s);
97
98	# generate our random diffie hellman part
99	bits := info.p.bits();
100	r0 := kr->IPint.random(bits/4, bits);
101
102	# generate alpha0 = alpha**r0 mod p
103	alphar0 := info.alpha.expmod(r0, info.p);
104
105	# start encrypting
106	pwbuf := array[8] of byte;
107	for(i := 0; i < 8; i++)
108		pwbuf[i] = pw[i] ^ pw[8+i];
109	for(i = 0; i < 4; i++)
110		pwbuf[i] ^= pw[16+i];
111	for(i = 0; i < 8; i++)
112		pwbuf[i] ^= ivec[i];
113	err = ssl->secret(c, pwbuf, pwbuf);
114	if(err != nil)
115		return "can't set ssl secret: "+err;
116
117	if(sys->fprint(c.cfd, "alg rc4") < 0)
118		return sys->sprint("can't push alg rc4: %r");
119
120	# send P(alpha**r0 mod p)
121	if(kr->putstring(c.dfd, alphar0.iptob64()) < 0)
122		return sys->sprint("can't send (alpha**r0 mod p): %r");
123
124	# stop encrypting
125	if(sys->fprint(c.cfd, "alg clear") < 0)
126		return sys->sprint("can't clear alg: %r");
127
128	# send alpha, p
129	if(kr->putstring(c.dfd, info.alpha.iptob64()) < 0 ||
130	   kr->putstring(c.dfd, info.p.iptob64()) < 0)
131		return sys->sprint("can't send alpha, p: %r");
132
133	# get alpha**r1 mod p
134	(s, err) = kr->getstring(c.dfd);
135	if(err != nil)
136		return "can't get alpha**r1 mod p:"+err;
137	alphar1 := kr->IPint.b64toip(s);
138
139	# compute alpha**(r0*r1) mod p
140	alphar0r1 := alphar1.expmod(r0, info.p);
141
142	# turn on digesting
143	secret := alphar0r1.iptobytes();
144	err = ssl->secret(c, secret, secret);
145	if(err != nil)
146		return "can't set digest secret: "+err;
147	if(sys->fprint(c.cfd, "alg sha1") < 0)
148		return sys->sprint("can't push alg sha1: %r");
149
150	# send our public key
151	if(kr->putstring(c.dfd, kr->pktostr(kr->sktopk(info.mysk))) < 0)
152		return sys->sprint("can't send signer's public key: %r");
153
154	# get his public key
155	(s, err) = kr->getstring(c.dfd);
156	if(err != nil)
157		return "client public key: "+err;
158	hisPKbuf := array of byte s;
159	hisPK := kr->strtopk(s);
160	if(hisPK.owner != name)
161		return "pk name doesn't match user name";
162
163	# sign and return
164	state := kr->sha1(hisPKbuf, len hisPKbuf, nil, nil);
165	cert := kr->sign(info.mysk, userexp, state, "sha1");
166
167	if(kr->putstring(c.dfd, kr->certtostr(cert)) < 0)
168		return sys->sprint("can't send certificate: %r");
169
170	return nil;
171}
172
173nomod(mod: string)
174{
175	fatal(sys->sprint("can't load %s: %r",mod));
176}
177
178fatal(msg: string)
179{
180	sys->fprint(stderr, "logind: %s\n", msg);
181	exit;
182}
183
184signerkey(filename: string): (ref Keyring->Authinfo, string)
185{
186
187	info := kr->readauthinfo(filename);
188	if(info == nil)
189		return (nil, sys->sprint("readauthinfo %r"));
190
191	# validate signer key
192	now := daytime->now();
193	if(info.cert.exp != 0 && info.cert.exp < now)
194		return (nil, sys->sprint("signer key expired"));
195
196	return (info, nil);
197}
198
199getsecret(id: string): array of byte
200{
201	fd := sys->open(sys->sprint("%s/%s/secret", keydb, id), Sys->OREAD);
202	if(fd == nil)
203		return nil;
204	(ok, d) := sys->fstat(fd);
205	if(ok < 0)
206		return nil;
207	a := array[int d.length] of byte;
208	n := sys->read(fd, a, len a);
209	if(n < 0)
210		return nil;
211	return a[0:n];
212}
213
214getexpiry(id: string): int
215{
216	fd := sys->open(sys->sprint("%s/%s/expire", keydb, id), Sys->OREAD);
217	if(fd == nil)
218		return -1;
219	a := array[Sys->NAMEMAX] of byte;
220	n := sys->read(fd, a, len a);
221	if(n < 0)
222		return -1;
223	s := string a[0:n];
224	if(s == "never")
225		return 0;
226	if(s == "expired"){
227		sys->werrstr(sys->sprint("entry for %s expired", id));
228		return -1;
229	}
230	return int s;
231}
232
233stalker(pidc: chan of int, killpid: int)
234{
235	pidc <-= sys->pctl(0, nil);
236	sys->sleep(TimeLimit);
237	sys->fprint(stderr, "logind: login timed out\n");
238	kill(killpid, "killgrp");
239}
240
241kill(pid: int, how: string)
242{
243	fd := sys->open("#p/" + string pid + "/ctl", Sys->OWRITE);
244	if(fd == nil || sys->fprint(fd, "%s", how) < 0)
245		sys->fprint(stderr, "logind: can't %s %d: %r\n", how, pid);
246}
247