summaryrefslogtreecommitdiff
path: root/appl/cmd/auth/logind.b
diff options
context:
space:
mode:
Diffstat (limited to 'appl/cmd/auth/logind.b')
-rw-r--r--appl/cmd/auth/logind.b244
1 files changed, 244 insertions, 0 deletions
diff --git a/appl/cmd/auth/logind.b b/appl/cmd/auth/logind.b
new file mode 100644
index 00000000..f9d14616
--- /dev/null
+++ b/appl/cmd/auth/logind.b
@@ -0,0 +1,244 @@
+implement Logind;
+
+#
+# certification service (signer)
+#
+
+include "sys.m";
+ sys: Sys;
+
+include "draw.m";
+
+include "keyring.m";
+ kr: Keyring;
+ IPint: import kr;
+
+include "security.m";
+ ssl: SSL;
+
+include "daytime.m";
+ daytime: Daytime;
+
+Logind: module
+{
+ init: fn(ctxt: ref Draw->Context, argv: list of string);
+};
+
+TimeLimit: con 5*60*1000; # five minutes
+keydb := "/mnt/keys";
+
+stderr: ref Sys->FD;
+
+init(nil: ref Draw->Context, nil: list of string)
+{
+ sys = load Sys Sys->PATH;
+ stderr = sys->open("/dev/cons", sys->OWRITE);
+
+ kr = load Keyring Keyring->PATH;
+
+ ssl = load SSL SSL->PATH;
+ if(ssl == nil)
+ nomod(SSL->PATH);
+
+ daytime = load Daytime Daytime->PATH;
+ if(daytime == nil)
+ nomod(Daytime->PATH);
+
+ (err, c) := ssl->connect(sys->fildes(0));
+ if(c == nil)
+ fatal("pushing ssl: " + err);
+
+ # impose time out to ensure dead network connections recovered well before TCP/IP's long time out
+
+ grpid := sys->pctl(Sys->NEWPGRP,nil);
+ pidc := chan of int;
+ spawn stalker(pidc, grpid);
+ tpid := <-pidc;
+ err = dologin(c);
+ if(err != nil){
+ sys->fprint(stderr, "logind: %s\n", err);
+ kr->puterror(c.dfd, err);
+ }
+ kill(tpid, "kill");
+}
+
+dologin(c: ref Sys->Connection): string
+{
+ ivec: array of byte;
+
+ (info, err) := signerkey("/keydb/signerkey");
+ if(info == nil)
+ return "can't read signer's own key: "+err;
+
+ # get user name; ack
+ s: string;
+ (s, err) = kr->getstring(c.dfd);
+ if(err != nil)
+ return err;
+ name := s;
+ kr->putstring(c.dfd, name);
+
+ # get initialization vector
+ (ivec, err) = kr->getbytearray(c.dfd);
+ if(err != nil)
+ return "can't get initialization vector: "+err;
+
+ # lookup password
+ pw := getsecret(s);
+ if(pw == nil)
+ return sys->sprint("no password entry for %s: %r", s);
+ if(len pw < Keyring->SHA1dlen)
+ return "bad password for "+s+": not SHA1 hashed?";
+ userexp := getexpiry(s);
+ if(userexp < 0)
+ return sys->sprint("expiry time for %s: %r", s);
+
+ # generate our random diffie hellman part
+ bits := info.p.bits();
+ r0 := kr->IPint.random(bits/4, bits);
+
+ # generate alpha0 = alpha**r0 mod p
+ alphar0 := info.alpha.expmod(r0, info.p);
+
+ # start encrypting
+ pwbuf := array[8] of byte;
+ for(i := 0; i < 8; i++)
+ pwbuf[i] = pw[i] ^ pw[8+i];
+ for(i = 0; i < 4; i++)
+ pwbuf[i] ^= pw[16+i];
+ for(i = 0; i < 8; i++)
+ pwbuf[i] ^= ivec[i];
+ err = ssl->secret(c, pwbuf, pwbuf);
+ if(err != nil)
+ return "can't set ssl secret: "+err;
+
+ if(sys->fprint(c.cfd, "alg rc4") < 0)
+ return sys->sprint("can't push alg rc4: %r");
+
+ # send P(alpha**r0 mod p)
+ if(kr->putstring(c.dfd, alphar0.iptob64()) < 0)
+ return sys->sprint("can't send (alpha**r0 mod p): %r");
+
+ # stop encrypting
+ if(sys->fprint(c.cfd, "alg clear") < 0)
+ return sys->sprint("can't clear alg: %r");
+
+ # send alpha, p
+ if(kr->putstring(c.dfd, info.alpha.iptob64()) < 0 ||
+ kr->putstring(c.dfd, info.p.iptob64()) < 0)
+ return sys->sprint("can't send alpha, p: %r");
+
+ # get alpha**r1 mod p
+ (s, err) = kr->getstring(c.dfd);
+ if(err != nil)
+ return "can't get alpha**r1 mod p:"+err;
+ alphar1 := kr->IPint.b64toip(s);
+
+ # compute alpha**(r0*r1) mod p
+ alphar0r1 := alphar1.expmod(r0, info.p);
+
+ # turn on digesting
+ secret := alphar0r1.iptobytes();
+ err = ssl->secret(c, secret, secret);
+ if(err != nil)
+ return "can't set digest secret: "+err;
+ if(sys->fprint(c.cfd, "alg sha1") < 0)
+ return sys->sprint("can't push alg sha1: %r");
+
+ # send our public key
+ if(kr->putstring(c.dfd, kr->pktostr(kr->sktopk(info.mysk))) < 0)
+ return sys->sprint("can't send signer's public key: %r");
+
+ # get his public key
+ (s, err) = kr->getstring(c.dfd);
+ if(err != nil)
+ return "client public key: "+err;
+ hisPKbuf := array of byte s;
+ hisPK := kr->strtopk(s);
+ if(hisPK.owner != name)
+ return "pk name doesn't match user name";
+
+ # sign and return
+ state := kr->sha1(hisPKbuf, len hisPKbuf, nil, nil);
+ cert := kr->sign(info.mysk, userexp, state, "sha1");
+
+ if(kr->putstring(c.dfd, kr->certtostr(cert)) < 0)
+ return sys->sprint("can't send certificate: %r");
+
+ return nil;
+}
+
+nomod(mod: string)
+{
+ fatal(sys->sprint("can't load %s: %r",mod));
+}
+
+fatal(msg: string)
+{
+ sys->fprint(stderr, "logind: %s\n", msg);
+ exit;
+}
+
+signerkey(filename: string): (ref Keyring->Authinfo, string)
+{
+
+ info := kr->readauthinfo(filename);
+ if(info == nil)
+ return (nil, sys->sprint("readauthinfo %r"));
+
+ # validate signer key
+ now := daytime->now();
+ if(info.cert.exp != 0 && info.cert.exp < now)
+ return (nil, sys->sprint("signer key expired"));
+
+ return (info, nil);
+}
+
+getsecret(id: string): array of byte
+{
+ fd := sys->open(sys->sprint("%s/%s/secret", keydb, id), Sys->OREAD);
+ if(fd == nil)
+ return nil;
+ (ok, d) := sys->fstat(fd);
+ if(ok < 0)
+ return nil;
+ a := array[int d.length] of byte;
+ n := sys->read(fd, a, len a);
+ if(n < 0)
+ return nil;
+ return a[0:n];
+}
+
+getexpiry(id: string): int
+{
+ fd := sys->open(sys->sprint("%s/%s/expire", keydb, id), Sys->OREAD);
+ if(fd == nil)
+ return -1;
+ a := array[Sys->NAMEMAX] of byte;
+ n := sys->read(fd, a, len a);
+ if(n < 0)
+ return -1;
+ s := string a[0:n];
+ if(s == "never")
+ return 0;
+ if(s == "expired"){
+ sys->werrstr(sys->sprint("entry for %s expired", id));
+ return -1;
+ }
+ return int s;
+}
+
+stalker(pidc: chan of int, killpid: int)
+{
+ pidc <-= sys->pctl(0, nil);
+ sys->sleep(TimeLimit);
+ sys->fprint(stderr, "logind: login timed out\n");
+ kill(killpid, "killgrp");
+}
+
+kill(pid: int, how: string)
+{
+ fd := sys->open("#p/" + string pid + "/ctl", Sys->OWRITE);
+ if(fd == nil || sys->fprint(fd, "%s", how) < 0)
+ sys->fprint(stderr, "logind: can't %s %d: %r\n", how, pid);
+}