diff options
Diffstat (limited to 'appl/lib/registries.b')
| -rw-r--r-- | appl/lib/registries.b | 288 |
1 files changed, 288 insertions, 0 deletions
diff --git a/appl/lib/registries.b b/appl/lib/registries.b new file mode 100644 index 00000000..2feb83e3 --- /dev/null +++ b/appl/lib/registries.b @@ -0,0 +1,288 @@ +implement Registries; + +include "sys.m"; + sys: Sys; +include "bufio.m"; + bufio: Bufio; + Iobuf: import bufio; +include "string.m"; + str: String; +include "keyring.m"; + keyring: Keyring; +include "security.m"; + auth: Auth; +include "keyset.m"; + keyset: Keyset; +include "registries.m"; + +init() +{ + sys = load Sys Sys->PATH; + bufio = checkload(load Bufio Bufio->PATH, Bufio->PATH); + keyring = checkload(load Keyring Keyring->PATH, Keyring->PATH); + str = checkload(load String String->PATH, String->PATH); + keyset = checkload(load Keyset Keyset->PATH, Keyset->PATH); + auth = checkload(load Auth Auth->PATH, Auth->PATH); + e := keyset->init(); + if(e != nil) + raise sys->sprint("can't init Keyset: %s", e); + e = auth->init(); + if(e != nil) + raise sys->sprint("can't init Auth: %s", e); +} + +checkload[T](x: T, s: string): T +{ + if(x == nil) + raise sys->sprint("can't load %s: %r", s); + return x; +} + +Registry.new(dir: string): ref Registry +{ + if(dir == nil) + dir = "/mnt/registry"; + r := ref Registry; + r.dir = dir; + r.indexfd = sys->open(dir + "/index", Sys->OREAD); + if(r.indexfd == nil) + return nil; + return r; +} + +Registry.connect(svc: ref Service, user, keydir: string): ref Registry +{ + # XXX broadcast for local registries here. + if(svc == nil) + # svc = ref Service("net!$registry!registry", Attributes.new(("auth", "infpk1") :: nil)); + svc = ref Service("net!$registry!registry", Attributes.new(("auth", "none") :: nil)); + a := svc.attach(user, keydir); + if(a == nil) + return nil; + if(sys->mount(a.fd, nil, "/mnt/registry", Sys->MREPL, nil) == -1){ + sys->werrstr(sys->sprint("mount failed: %r")); + return nil; + } + return Registry.new("/mnt/registry"); +} + +Registry.services(r: self ref Registry): (list of ref Service, string) +{ + sys->seek(r.indexfd, big 0, Sys->SEEKSTART); + iob := bufio->fopen(r.indexfd, Sys->OREAD); + if(iob == nil) + return (nil, sys->sprint("%r")); + return (readservices(iob), nil); +} + +Registry.find(r: self ref Registry, a: list of (string, string)): (list of ref Service, string) +{ + fd := sys->open(r.dir + "/find", Sys->ORDWR); # could keep it open if it's a bottleneck + if(fd == nil) + return (nil, sys->sprint("%r")); + s := ""; + if(a != nil){ + for(; a != nil; a = tl a){ + (n, v) := hd a; + s += sys->sprint(" %q %q", n, v); + } + s = s[1:]; + } + if(sys->fprint(fd, "%s", s) == -1) + return (nil, sys->sprint("%r")); + sys->seek(fd, big 0, Sys->SEEKSTART); + iob := bufio->fopen(fd, Sys->OREAD); + return (readservices(iob), nil); +} + +readservices(iob: ref Iobuf): list of ref Service +{ + services: list of ref Service; + while((s := qgets(iob, '\n')) != nil){ + toks := str->unquoted(s); + if(toks == nil || len toks % 2 != 1) + continue; + svc := ref Service(hd toks, nil); + attrs, rattrs: list of (string, string); + for(toks = tl toks; toks != nil; toks = tl tl toks) + rattrs = (hd toks, hd tl toks) :: rattrs; + for(; rattrs != nil; rattrs = tl rattrs) + attrs = hd rattrs :: attrs; + svc.attrs = ref Attributes(attrs); + services = svc :: services; + } + return rev(services); +} + +rev[T](l: list of T): list of T +{ + rl: list of T; + for(; l != nil; l = tl l) + rl = hd l :: rl; + return rl; +} + +Registry.register(r: self ref Registry, addr: string, attrs: ref Attributes, persist: int): (ref Registered, string) +{ + fd := sys->open(r.dir + "/new", Sys->OWRITE); + if(fd == nil) + return (nil, sys->sprint("%r")); + s := sys->sprint("%q", addr); + for(a := attrs.attrs; a != nil; a = tl a) + s += sys->sprint(" %q %q", (hd a).t0, (hd a).t1); + if(persist) + s += " persist 1"; + if(sys->fprint(fd, "%s", s) == -1) + return (nil, sys->sprint("%r")); + return (ref Registered(addr, r, fd), nil); +} + +Registry.unregister(r: self ref Registry, addr: string): string +{ + if(sys->remove(r.dir + "/" + addr) == -1) + return sys->sprint("%r"); + return nil; +} + +Attributes.new(attrs: list of (string, string)): ref Attributes +{ + return ref Attributes(attrs); +} + +Attributes.set(a: self ref Attributes, attr, val: string) +{ + for(al := a.attrs; al != nil; al = tl al) + if((hd al).t0 == attr) + break; + if(al == nil){ + a.attrs = (attr, val) :: a.attrs; + return; + } + attrs := (attr, val) :: tl al; + for(al = a.attrs; al != nil; al = tl al){ + if((hd al).t0 == attr) + break; + attrs = hd al :: attrs; + } + a.attrs = attrs; +} + +Attributes.get(a: self ref Attributes, attr: string): string +{ + for(al := a.attrs; al != nil; al = tl al) + if((hd al).t0 == attr) + return (hd al).t1; + return nil; +} + +qgets(iob: ref Iobuf, eoc: int): string +{ + inq := 0; + s := ""; + while((c := iob.getc()) >= 0){ + s[len s] = c; + if(inq){ + if(c == '\''){ + c = iob.getc(); + if(c == '\'') + s[len s] = c; + else{ + iob.ungetc(); + inq = 0; + } + } + }else{ + if(c == eoc) + return s; + if(c == '\'') + inq = 1; + } + } + return s; +} + +Service.attach(svc: self ref Service, localuser, keydir: string): ref Attached +{ + # attributes used: + # auth type of authentication to perform (auth, none) + # auth.crypt type of encryption to push (as accepted by ssl(3)'s "alg" operation) + # auth.signer hash of service's certificate's signer's public key + + (ok, c) := sys->dial(svc.addr, nil); + if(ok == -1){ + sys->werrstr(sys->sprint("cannot dial: %r")); + return nil; + } + attached := ref Attached; + authkind := svc.attrs.get("auth"); + case authkind { + "auth" or # old + "infpk1" => + cryptalg := svc.attrs.get("auth.crypt"); + if(cryptalg == nil) + cryptalg = "none"; + ca := svc.attrs.get("auth.signer"); + kf: string; + if(ca != nil){ + (kfl, err) := keyset->keysforsigner(nil, ca, nil, keydir); + if(kfl == nil){ + s := "no matching keys found"; + if(err != nil) + s += ": "+err; + sys->werrstr(s); + return nil; + } + if(localuser == nil) + kf = (hd kfl).t0; + else{ + for(; kfl != nil; kfl = tl kfl) + if((hd kfl).t1 == localuser) + break; + if(kfl == nil){ + sys->werrstr("no matching user found"); + return nil; + } + kf = (hd kfl).t0; + } + } else { + user := readname("/dev/user"); + if(user == nil) + kf = "/lib/keyring/default"; + else + kf = "/usr/" + user + "/keyring/default"; + } + info := keyring->readauthinfo(kf); + if(info == nil){ + sys->werrstr(sys->sprint("cannot read key: %r")); + return nil; + } + (fd, ue) := auth->client(cryptalg, info, c.dfd); + if(fd == nil){ + sys->werrstr(sys->sprint("cannot authenticate: %r")); + return nil; + } + attached.signerpkhash = keyset->pkhash(keyring->pktostr(info.spk)); + attached.localuser = info.mypk.owner; + attached.remoteuser = ue; + attached.fd = fd; + "" or + "none" => + attached.fd = c.dfd; + * => + sys->werrstr(sys->sprint("unknown authentication type %q", authkind)); + return nil; + } + return attached; +} + +readname(s: string): string +{ + fd := sys->open(s, Sys->OREAD); + if(fd == nil) + return nil; + buf := array[Sys->NAMEMAX] of byte; + n := sys->read(fd, buf, len buf); + if(n <= 0) + return nil; + return string buf[0:n]; +} |
