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