ref: 42dfac6916ebbdac65cbec8b3e1a80c3ee41423c
dir: /appl/cmd/auth/logind.b/
implement Logind;
#
# certification service (signer)
#
include "sys.m";
sys: Sys;
include "draw.m";
include "keyring.m";
kr: Keyring;
IPint: import kr;
include "dial.m";
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 Dial->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);
}