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