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];}
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];
}