implement Keyfs; ## Copyright © 2002,2003 Vita Nuova Holdings Limited. All rights reserved.# include "sys.m"; sys: Sys; Qid: import Sys; include "draw.m"; include "keyring.m"; kr: Keyring; AESbsize, AESstate: import kr; include "rand.m"; rand: Rand; include "styx.m"; styx: Styx; Tmsg, Rmsg: import styx; include "styxservers.m"; styxservers: Styxservers; Fid, Styxserver, Navigator, Navop: import styxservers; Enotfound, Eperm, Ebadarg, Edot: import styxservers; include "arg.m"; Keyfs: module{ init: fn(nil: ref Draw->Context, nil: list of string);}; User: adt{ x: int; # table index name: string; secret: array of byte; # eg, password hashed by SHA1 expire: int; # expiration time (epoch seconds) status: int; failed: int; # count of failed attempts path: big;}; Qroot, Quser, Qsecret, Qlog, Qstatus, Qexpire: con iota;files := array[] of { (Qsecret, "secret"), (Qlog, "log"), (Qstatus, "status"), (Qexpire, "expire")}; Maxsecret: con 255;Maxname: con 255;Maxfail: con 50;users: array of ref User;Sok, Sdisabled: con iota;status := array[] of {Sok => "ok", Sdisabled => "disabled" };Never: con 0; # expiry time Eremoved: con "user has been removed"; pathgen := 0;keyversion := 0;user: string;now: int; usage(){ sys->fprint(sys->fildes(2), "Usage: keyfs [-D] [-m mountpoint] [keyfile]\n"); raise "fail:usage";} nomod(s: string){ sys->fprint(sys->fildes(2), "keyfs: can't load %s: %r\n", s); raise "fail:load";} init(nil: ref Draw->Context, args: list of string){ sys = load Sys Sys->PATH; sys->pctl(Sys->NEWPGRP, nil); kr = load Keyring Keyring->PATH; if(kr == nil) nomod(Keyring->PATH); styx = load Styx Styx->PATH; if(styx == nil) nomod(Styx->PATH); styxservers = load Styxservers Styxservers->PATH; if(styxservers == nil) nomod(Styxservers->PATH); rand = load Rand Rand->PATH; if(rand == nil) nomod(Rand->PATH); styx->init(); styxservers->init(styx); rand->init(sys->millisec()); arg := load Arg Arg->PATH; if(arg == nil) nomod(Arg->PATH); arg->init(args); arg->setusage("keyfs [-m mntpt] [-D] [-n nvramfile] [keyfile]"); mountpt := "/mnt/keys"; keyfile := "/keydb/keys"; nvram: string; while((o := arg->opt()) != 0) case o { 'm' => mountpt = arg->earg(); 'D' => styxservers->traceset(1); 'n' => nvram = arg->earg(); * => usage(); } args = arg->argv(); arg = nil; if(args != nil) keyfile = hd args; pwd, err: string; if(nvram != nil){ pwd = rf(nvram); if(pwd == nil) error(sys->sprint("can't read %s: %r", nvram)); } if(pwd == nil){ (pwd, err) = readconsline("Key: ", 1); if(pwd == nil || err == "exit") exit; if(err != nil) error(sys->sprint("couldn't get key: %s", err)); (rc, d) := sys->stat(keyfile); if(rc == -1 || d.length == big 0){ pwd0 := pwd; (pwd, err) = readconsline("Confirm key: ", 1); if(pwd == nil || err == "exit") exit; if(pwd != pwd0) error("key mismatch"); for(i := 0; i < len pwd0; i++) pwd0[i] = ' '; # clear it out } } thekey = hashkey(pwd); for(i:=0; i<len pwd; i++) pwd[i] = ' '; # clear it out sys->pctl(Sys->NEWPGRP|Sys->FORKFD, nil); # immediately avoid sharing keyfd readkeys(keyfile); user = rf("/dev/user"); if(user == nil) user = "keyfs"; fds := array[2] of ref Sys->FD; if(sys->pipe(fds) < 0) error(sys->sprint("can't create pipe: %r")); navops := chan of ref Navop; spawn navigator(navops); (tchan, srv) := Styxserver.new(fds[0], Navigator.new(navops), big Qroot); fds[0] = nil; pidc := chan of int; spawn serveloop(tchan, srv, pidc, navops, keyfile); <-pidc; if(sys->mount(fds[1], nil, mountpt, Sys->MREPL|Sys->MCREATE, nil) < 0) error(sys->sprint("mount on %s failed: %r", mountpt));} rf(f: string): string{ fd := sys->open(f, Sys->OREAD); if(fd == nil) return nil; b := array[256] of byte; n := sys->read(fd, b, len b); if(n < 0) return nil; return string b[0:n];} quit(err: string){ fd := sys->open("/prog/"+string sys->pctl(0, nil)+"/ctl", Sys->OWRITE); if(fd != nil) sys->fprint(fd, "killgrp"); if(err != nil) raise "fail:"+err; exit;} error(s: string){ sys->fprint(sys->fildes(2), "keyfs: %s\n", s); quit("error");} thekey: array of byte; hashkey(s: string): array of byte{ key := array of byte s; skey := array[Keyring->SHA1dlen] of byte; sha := kr->sha1(array of byte "aescbc file", 11, nil, nil); kr->sha1(key, len key, skey, sha); for(i:=0; i<len key; i++) key[i] = byte 0; # clear it out#{sys->print("HEX="); for(i:=0;i<len skey&&i<AESbsize; i++)sys->print("%.2ux", int skey[i]);sys->print("\n");} return skey[0:AESbsize];} readconsline(prompt: string, raw: int): (string, string){ fd := sys->open("/dev/cons", Sys->ORDWR); if(fd == nil) return (nil, sys->sprint("can't open cons: %r")); sys->fprint(fd, "%s", prompt); fdctl: ref Sys->FD; if(raw){ fdctl = sys->open("/dev/consctl", sys->OWRITE); if(fdctl == nil || sys->fprint(fdctl, "rawon") < 0) return (nil, sys->sprint("can't open consctl: %r")); } line := array[256] of byte; o := 0; err: string; buf := array[1] of byte; Read: while((r := sys->read(fd, buf, len buf)) > 0){ c := int buf[0]; case c { 16r7F => err = "interrupt"; break Read; '\b' => if(o > 0) o--; '\n' or '\r' or 16r4 => break Read; * => if(o > len line){ err = "line too long"; break Read; } line[o++] = byte c; } } sys->fprint(fd, "\n"); if(r < 0) err = sys->sprint("can't read cons: %r"); if(raw) sys->fprint(fdctl, "rawoff"); if(err != nil) return (nil, err); return (string line[0:o], err);} serveloop(tchan: chan of ref Tmsg, srv: ref Styxserver, pidc: chan of int, navops: chan of ref Navop, keyfile: string){ pidc <-= sys->pctl(Sys->FORKNS|Sys->NEWFD, 1::2::srv.fd.fd::nil); while((gm := <-tchan) != nil){ now = time(); pick m := gm { Readerror => error(sys->sprint("mount read error: %s", m.error)); Create => (c, mode, nil, err) := srv.cancreate(m); if(c == nil){ srv.reply(ref Rmsg.Error(m.tag, err)); break; } case TYPE(c.path) { # parent Qroot => if((m.perm & Sys->DMDIR) == 0){ srv.reply(ref Rmsg.Error(m.tag, Eperm)); break; } u := findusername(m.name); if(u != nil){ srv.reply(ref Rmsg.Error(m.tag, "user already exists")); continue; } if(len m.name > Maxname){ srv.reply(ref Rmsg.Error(m.tag, "user name too long")); continue; } u = newuser(m.name, nil); qid := Qid((u.path | big Quser), 0, Sys->QTDIR); c.open(mode, qid); writekeys(keyfile); srv.reply(ref Rmsg.Create(m.tag, qid, srv.iounit())); * => srv.reply(ref Rmsg.Error(m.tag, Eperm)); break; } Read => (c, err) := srv.canread(m); if(c == nil){ srv.reply(ref Rmsg.Error(m.tag, err)); break; } if(c.qtype & Sys->QTDIR){ srv.read(m); # does readdir break; } u := finduserpath(c.path); if(u == nil){ srv.reply(ref Rmsg.Error(m.tag, Eremoved)); break; } case TYPE(c.path) { Qsecret => if(u.status != Sok){ srv.reply(ref Rmsg.Error(m.tag, "user disabled")); break; } if(u.expire < now && u.expire != Never){ srv.reply(ref Rmsg.Error(m.tag, "user expired")); break; } srv.reply(styxservers->readbytes(m, u.secret)); Qlog => srv.reply(styxservers->readstr(m, sys->sprint("%d", u.failed))); Qstatus => s := status[u.status]; if(u.status == Sok && u.expire != Never && u.expire < now) s = "expired"; srv.reply(styxservers->readstr(m, s)); Qexpire => s: string; if(u.expire != Never) s = sys->sprint("%ud", u.expire); else s = "never"; srv.reply(styxservers->readstr(m, s)); * => srv.reply(ref Rmsg.Error(m.tag, Eperm)); } Write => (c, merr) := srv.canwrite(m); if(c == nil){ srv.reply(ref Rmsg.Error(m.tag, merr)); break; } u := finduserpath(c.path); if(u == nil){ srv.reply(ref Rmsg.Error(m.tag, Eremoved)); break; } Case: case TYPE(c.path) { Qsecret => if(m.offset != big 0 || len m.data > Maxsecret){ srv.reply(ref Rmsg.Error(m.tag, "illegal write")); break; } u.secret = m.data; writekeys(keyfile); srv.reply(ref Rmsg.Write(m.tag, len m.data)); Qexpire => s := trim(string m.data); if(s != "never"){ if(!isnumeric(s)){ srv.reply(ref Rmsg.Error(m.tag, "illegal expiry time")); break; } u.expire = int s; }else u.expire = Never; u.failed = 0; writekeys(keyfile); srv.reply(ref Rmsg.Write(m.tag, len m.data)); Qstatus => s := trim(string m.data); for(i := 0; i < len status; i++) if(s == status[i]){ u.status = i; if(i == Sok) u.failed = 0; writekeys(keyfile); srv.reply(ref Rmsg.Write(m.tag, len m.data)); break Case; } srv.reply(ref Rmsg.Error(m.tag, "unknown status")); Qlog => s := trim(string m.data); if(s != "good" && s != "ok"){ if(++u.failed >= Maxfail) u.status = Sdisabled; }else u.failed = 0; writekeys(keyfile); srv.reply(ref Rmsg.Write(m.tag, len m.data)); * => srv.reply(ref Rmsg.Error(m.tag, Eperm)); } Remove => c := srv.getfid(m.fid); if(c == nil){ srv.remove(m); # let it diagnose the errors break; } case TYPE(c.path) { Quser => srv.delfid(c); u := finduserpath(c.path); if(u == nil){ srv.reply(ref Rmsg.Error(m.tag, Eremoved)); break; } removeuser(u); writekeys(keyfile); srv.reply(ref Rmsg.Remove(m.tag)); Qsecret => srv.delfid(c); u := finduserpath(c.path); if(u == nil){ srv.reply(ref Rmsg.Error(m.tag, Eremoved)); break; } u.secret = nil; writekeys(keyfile); srv.reply(ref Rmsg.Remove(m.tag)); * => srv.remove(m); # let it reject it } Wstat => # rename user c := srv.getfid(m.fid); if(c == nil || TYPE(c.path) != Quser){ srv.default(gm); # let it reject it break; } u := finduserpath(c.path); if(u == nil){ srv.reply(ref Rmsg.Error(m.tag, Eremoved)); break; } if((new := m.stat.name) == nil){ srv.default(gm); break; } if(new == "." || new == ".."){ srv.reply(ref Rmsg.Error(m.tag, Edot)); break; } if(findusername(new) != nil){ srv.reply(ref Rmsg.Error(m.tag, "user already exists")); break; } # unhashuser(u); u.name = new; # hashuser(u); writekeys(keyfile); srv.reply(ref Rmsg.Wstat(m.tag)); * => srv.default(gm); } } navops <-= nil; # shut down navigator} trim(s: string): string{ (nf, flds) := sys->tokenize(s, " \t\n"); if(nf == 0) return nil; return hd flds;} isnumeric(s: string): int{ for(i:=0; i<len s; i++) if(!(s[i]>='0' && s[i]<='9')) return 0; return i>0;} TYPE(path: big): int{ return int path & 16rF;} INDEX(path: big): int{ return (int path & 16rFFFF) >> 4;}Lines 1–500 of 806. Load more
implement Keyfs; # # Copyright © 2002,2003 Vita Nuova Holdings Limited. All rights reserved. # include "sys.m"; sys: Sys; Qid: import Sys; include "draw.m"; include "keyring.m"; kr: Keyring; AESbsize, AESstate: import kr; include "rand.m"; rand: Rand; include "styx.m"; styx: Styx; Tmsg, Rmsg: import styx; include "styxservers.m"; styxservers: Styxservers; Fid, Styxserver, Navigator, Navop: import styxservers; Enotfound, Eperm, Ebadarg, Edot: import styxservers; include "arg.m"; Keyfs: module { init: fn(nil: ref Draw->Context, nil: list of string); }; User: adt { x: int; # table index name: string; secret: array of byte; # eg, password hashed by SHA1 expire: int; # expiration time (epoch seconds) status: int; failed: int; # count of failed attempts path: big; }; Qroot, Quser, Qsecret, Qlog, Qstatus, Qexpire: con iota; files := array[] of { (Qsecret, "secret"), (Qlog, "log"), (Qstatus, "status"), (Qexpire, "expire") }; Maxsecret: con 255; Maxname: con 255; Maxfail: con 50; users: array of ref User; Sok, Sdisabled: con iota; status := array[] of {Sok => "ok", Sdisabled => "disabled" }; Never: con 0; # expiry time Eremoved: con "user has been removed"; pathgen := 0; keyversion := 0; user: string; now: int; usage() { sys->fprint(sys->fildes(2), "Usage: keyfs [-D] [-m mountpoint] [keyfile]\n"); raise "fail:usage"; } nomod(s: string) { sys->fprint(sys->fildes(2), "keyfs: can't load %s: %r\n", s); raise "fail:load"; } init(nil: ref Draw->Context, args: list of string) { sys = load Sys Sys->PATH; sys->pctl(Sys->NEWPGRP, nil); kr = load Keyring Keyring->PATH; if(kr == nil) nomod(Keyring->PATH); styx = load Styx Styx->PATH; if(styx == nil) nomod(Styx->PATH); styxservers = load Styxservers Styxservers->PATH; if(styxservers == nil) nomod(Styxservers->PATH); rand = load Rand Rand->PATH; if(rand == nil) nomod(Rand->PATH); styx->init(); styxservers->init(styx); rand->init(sys->millisec()); arg := load Arg Arg->PATH; if(arg == nil) nomod(Arg->PATH); arg->init(args); arg->setusage("keyfs [-m mntpt] [-D] [-n nvramfile] [keyfile]"); mountpt := "/mnt/keys"; keyfile := "/keydb/keys"; nvram: string; while((o := arg->opt()) != 0) case o { 'm' => mountpt = arg->earg(); 'D' => styxservers->traceset(1); 'n' => nvram = arg->earg(); * => usage(); } args = arg->argv(); arg = nil; if(args != nil) keyfile = hd args; pwd, err: string; if(nvram != nil){ pwd = rf(nvram); if(pwd == nil) error(sys->sprint("can't read %s: %r", nvram)); } if(pwd == nil){ (pwd, err) = readconsline("Key: ", 1); if(pwd == nil || err == "exit") exit; if(err != nil) error(sys->sprint("couldn't get key: %s", err)); (rc, d) := sys->stat(keyfile); if(rc == -1 || d.length == big 0){ pwd0 := pwd; (pwd, err) = readconsline("Confirm key: ", 1); if(pwd == nil || err == "exit") exit; if(pwd != pwd0) error("key mismatch"); for(i := 0; i < len pwd0; i++) pwd0[i] = ' '; # clear it out } } thekey = hashkey(pwd); for(i:=0; i<len pwd; i++) pwd[i] = ' '; # clear it out sys->pctl(Sys->NEWPGRP|Sys->FORKFD, nil); # immediately avoid sharing keyfd readkeys(keyfile); user = rf("/dev/user"); if(user == nil) user = "keyfs"; fds := array[2] of ref Sys->FD; if(sys->pipe(fds) < 0) error(sys->sprint("can't create pipe: %r")); navops := chan of ref Navop; spawn navigator(navops); (tchan, srv) := Styxserver.new(fds[0], Navigator.new(navops), big Qroot); fds[0] = nil; pidc := chan of int; spawn serveloop(tchan, srv, pidc, navops, keyfile); <-pidc; if(sys->mount(fds[1], nil, mountpt, Sys->MREPL|Sys->MCREATE, nil) < 0) error(sys->sprint("mount on %s failed: %r", mountpt)); } rf(f: string): string { fd := sys->open(f, Sys->OREAD); if(fd == nil) return nil; b := array[256] of byte; n := sys->read(fd, b, len b); if(n < 0) return nil; return string b[0:n]; } quit(err: string) { fd := sys->open("/prog/"+string sys->pctl(0, nil)+"/ctl", Sys->OWRITE); if(fd != nil) sys->fprint(fd, "killgrp"); if(err != nil) raise "fail:"+err; exit; } error(s: string) { sys->fprint(sys->fildes(2), "keyfs: %s\n", s); quit("error"); } thekey: array of byte; hashkey(s: string): array of byte { key := array of byte s; skey := array[Keyring->SHA1dlen] of byte; sha := kr->sha1(array of byte "aescbc file", 11, nil, nil); kr->sha1(key, len key, skey, sha); for(i:=0; i<len key; i++) key[i] = byte 0; # clear it out #{sys->print("HEX="); for(i:=0;i<len skey&&i<AESbsize; i++)sys->print("%.2ux", int skey[i]);sys->print("\n");} return skey[0:AESbsize]; } readconsline(prompt: string, raw: int): (string, string) { fd := sys->open("/dev/cons", Sys->ORDWR); if(fd == nil) return (nil, sys->sprint("can't open cons: %r")); sys->fprint(fd, "%s", prompt); fdctl: ref Sys->FD; if(raw){ fdctl = sys->open("/dev/consctl", sys->OWRITE); if(fdctl == nil || sys->fprint(fdctl, "rawon") < 0) return (nil, sys->sprint("can't open consctl: %r")); } line := array[256] of byte; o := 0; err: string; buf := array[1] of byte; Read: while((r := sys->read(fd, buf, len buf)) > 0){ c := int buf[0]; case c { 16r7F => err = "interrupt"; break Read; '\b' => if(o > 0) o--; '\n' or '\r' or 16r4 => break Read; * => if(o > len line){ err = "line too long"; break Read; } line[o++] = byte c; } } sys->fprint(fd, "\n"); if(r < 0) err = sys->sprint("can't read cons: %r"); if(raw) sys->fprint(fdctl, "rawoff"); if(err != nil) return (nil, err); return (string line[0:o], err); } serveloop(tchan: chan of ref Tmsg, srv: ref Styxserver, pidc: chan of int, navops: chan of ref Navop, keyfile: string) { pidc <-= sys->pctl(Sys->FORKNS|Sys->NEWFD, 1::2::srv.fd.fd::nil); while((gm := <-tchan) != nil){ now = time(); pick m := gm { Readerror => error(sys->sprint("mount read error: %s", m.error)); Create => (c, mode, nil, err) := srv.cancreate(m); if(c == nil){ srv.reply(ref Rmsg.Error(m.tag, err)); break; } case TYPE(c.path) { # parent Qroot => if((m.perm & Sys->DMDIR) == 0){ srv.reply(ref Rmsg.Error(m.tag, Eperm)); break; } u := findusername(m.name); if(u != nil){ srv.reply(ref Rmsg.Error(m.tag, "user already exists")); continue; } if(len m.name > Maxname){ srv.reply(ref Rmsg.Error(m.tag, "user name too long")); continue; } u = newuser(m.name, nil); qid := Qid((u.path | big Quser), 0, Sys->QTDIR); c.open(mode, qid); writekeys(keyfile); srv.reply(ref Rmsg.Create(m.tag, qid, srv.iounit())); * => srv.reply(ref Rmsg.Error(m.tag, Eperm)); break; } Read => (c, err) := srv.canread(m); if(c == nil){ srv.reply(ref Rmsg.Error(m.tag, err)); break; } if(c.qtype & Sys->QTDIR){ srv.read(m); # does readdir break; } u := finduserpath(c.path); if(u == nil){ srv.reply(ref Rmsg.Error(m.tag, Eremoved)); break; } case TYPE(c.path) { Qsecret => if(u.status != Sok){ srv.reply(ref Rmsg.Error(m.tag, "user disabled")); break; } if(u.expire < now && u.expire != Never){ srv.reply(ref Rmsg.Error(m.tag, "user expired")); break; } srv.reply(styxservers->readbytes(m, u.secret)); Qlog => srv.reply(styxservers->readstr(m, sys->sprint("%d", u.failed))); Qstatus => s := status[u.status]; if(u.status == Sok && u.expire != Never && u.expire < now) s = "expired"; srv.reply(styxservers->readstr(m, s)); Qexpire => s: string; if(u.expire != Never) s = sys->sprint("%ud", u.expire); else s = "never"; srv.reply(styxservers->readstr(m, s)); * => srv.reply(ref Rmsg.Error(m.tag, Eperm)); } Write => (c, merr) := srv.canwrite(m); if(c == nil){ srv.reply(ref Rmsg.Error(m.tag, merr)); break; } u := finduserpath(c.path); if(u == nil){ srv.reply(ref Rmsg.Error(m.tag, Eremoved)); break; } Case: case TYPE(c.path) { Qsecret => if(m.offset != big 0 || len m.data > Maxsecret){ srv.reply(ref Rmsg.Error(m.tag, "illegal write")); break; } u.secret = m.data; writekeys(keyfile); srv.reply(ref Rmsg.Write(m.tag, len m.data)); Qexpire => s := trim(string m.data); if(s != "never"){ if(!isnumeric(s)){ srv.reply(ref Rmsg.Error(m.tag, "illegal expiry time")); break; } u.expire = int s; }else u.expire = Never; u.failed = 0; writekeys(keyfile); srv.reply(ref Rmsg.Write(m.tag, len m.data)); Qstatus => s := trim(string m.data); for(i := 0; i < len status; i++) if(s == status[i]){ u.status = i; if(i == Sok) u.failed = 0; writekeys(keyfile); srv.reply(ref Rmsg.Write(m.tag, len m.data)); break Case; } srv.reply(ref Rmsg.Error(m.tag, "unknown status")); Qlog => s := trim(string m.data); if(s != "good" && s != "ok"){ if(++u.failed >= Maxfail) u.status = Sdisabled; }else u.failed = 0; writekeys(keyfile); srv.reply(ref Rmsg.Write(m.tag, len m.data)); * => srv.reply(ref Rmsg.Error(m.tag, Eperm)); } Remove => c := srv.getfid(m.fid); if(c == nil){ srv.remove(m); # let it diagnose the errors break; } case TYPE(c.path) { Quser => srv.delfid(c); u := finduserpath(c.path); if(u == nil){ srv.reply(ref Rmsg.Error(m.tag, Eremoved)); break; } removeuser(u); writekeys(keyfile); srv.reply(ref Rmsg.Remove(m.tag)); Qsecret => srv.delfid(c); u := finduserpath(c.path); if(u == nil){ srv.reply(ref Rmsg.Error(m.tag, Eremoved)); break; } u.secret = nil; writekeys(keyfile); srv.reply(ref Rmsg.Remove(m.tag)); * => srv.remove(m); # let it reject it } Wstat => # rename user c := srv.getfid(m.fid); if(c == nil || TYPE(c.path) != Quser){ srv.default(gm); # let it reject it break; } u := finduserpath(c.path); if(u == nil){ srv.reply(ref Rmsg.Error(m.tag, Eremoved)); break; } if((new := m.stat.name) == nil){ srv.default(gm); break; } if(new == "." || new == ".."){ srv.reply(ref Rmsg.Error(m.tag, Edot)); break; } if(findusername(new) != nil){ srv.reply(ref Rmsg.Error(m.tag, "user already exists")); break; } # unhashuser(u); u.name = new; # hashuser(u); writekeys(keyfile); srv.reply(ref Rmsg.Wstat(m.tag)); * => srv.default(gm); } } navops <-= nil; # shut down navigator } trim(s: string): string { (nf, flds) := sys->tokenize(s, " \t\n"); if(nf == 0) return nil; return hd flds; } isnumeric(s: string): int { for(i:=0; i<len s; i++) if(!(s[i]>='0' && s[i]<='9')) return 0; return i>0; } TYPE(path: big): int { return int path & 16rF; } INDEX(path: big): int { return (int path & 16rFFFF) >> 4; }