diff options
Diffstat (limited to 'appl')
| -rw-r--r-- | appl/cmd/auth/ai2key.b | 144 | ||||
| -rw-r--r-- | appl/cmd/auth/factotum/proto/infauth.b | 66 | ||||
| -rw-r--r-- | appl/cmd/auth/mkfile | 1 |
3 files changed, 199 insertions, 12 deletions
diff --git a/appl/cmd/auth/ai2key.b b/appl/cmd/auth/ai2key.b new file mode 100644 index 00000000..aff86d57 --- /dev/null +++ b/appl/cmd/auth/ai2key.b @@ -0,0 +1,144 @@ +implement Ai2fact; + +# authinfo to factotum key set +# intermediate version, for use until revised Inferno authentication is ready + + +# converts an old authinfo entry in keyring directory to a key for factotum +# +# keys are in proto=infauth, and include the data for the signed certificate, and the diffie-helman parameters + +include "sys.m"; + sys: Sys; + +include "draw.m"; + +include "keyring.m"; + keyring: Keyring; + Certificate, IPint, PK, SK: import keyring; + +include "daytime.m"; + daytime: Daytime; + +include "arg.m"; + +Ai2fact: module +{ + init: fn(nil: ref Draw->Context, nil: list of string); +}; + +init(nil: ref Draw->Context, args: list of string) +{ + sys = load Sys Sys->PATH; + keyring = load Keyring Keyring->PATH; + daytime = load Daytime Daytime->PATH; + + arg := load Arg Arg->PATH; + arg->init(args); + arg->setusage("ai2key [-t 'attr=value attr=value ...'] keyfile ..."); + tag: string; + while((o := arg->opt()) != 0) + case o { + 't' => + tag = arg->earg(); + * => + arg->usage(); + } + args = arg->argv(); + if(args == nil) + arg->usage(); + arg = nil; + + now := daytime->now(); + for(; args != nil; args = tl args){ + keyfile := hd args; + ai := keyring->readauthinfo(keyfile); + if(ai == nil) + error(sys->sprint("cannot read %s: %r", keyfile)); + if(ai.cert.exp != 0 && ai.cert.exp <= now){ + sys->fprint(sys->fildes(2), "ai2key: %s: certificate expired -- key ignored\n", keyfile); + continue; + } + + if(ai.cert.exp != 0) + expires := sys->sprint(" expires=%ud", ai.cert.exp); + ha := ai.cert.ha; + if(ha == "sha") + ha = "sha1"; + + if(tag != nil) + tag = " "+tag; + + sys->print("key proto=infauth%s %s sigalg=%s-%s user=%q signer=%q pk=%s !sk=%s spk=%s cert=%s dh-alpha=%s dh-p=%s%s\n", + tag, locations(filename(keyfile)), ai.cert.sa.name, ha, ai.mypk.owner, ai.spk.owner, pktostr(ai.mypk), sktostr(ai.mysk), + pktostr(ai.spk), certtostr(ai.cert), ai.alpha.iptostr(16), ai.p.iptostr(16), expires); + } +} + +error(e: string) +{ + sys->fprint(sys->fildes(2), "ai2key: %s\n", e); + raise "fail:error"; +} + +filename(s: string): string +{ + (nil, fld) := sys->tokenize(s, "/"); + for(; fld != nil && tl fld != nil; fld = tl fld){ + # skip + } + return hd fld; +} + +# guess plausible domain, server and service attributes from the file name +locations(file: string): string +{ + if(file == "default") + return "dom=* server=*"; + (nf, flds) := sys->tokenize(file, "!"); + case nf { + * => + return sys->sprint("%s", server(file)); + 2 => + return sys->sprint("%s", server(hd tl flds)); + 3 => + # ignore network component + return sys->sprint("%s service=%q", server(hd tl flds), hd tl tl flds); + } +} + +server(name: string): string +{ + # if the name contains dot(s), we'll treat it as a domain name + if(sys->tokenize(name, ".").t0 > 1) + return sys->sprint("dom=%q server=%q", name, name); + return sys->sprint("server=%q", name); +} + +certtostr(c: ref Certificate): string +{ + return dnl(keyring->certtostr(c)); +} + +pktostr(pk: ref PK): string +{ + return dnl(keyring->pktostr(pk)); +} + +sktostr(sk: ref SK): string +{ + return dnl(keyring->sktostr(sk)); +} + +dnl(s: string): string +{ + for(i := 0; i < len s; i++) + if(s[i] == '\n') + s[i] = '^'; + while(--i > 0 && s[i] == '^'){ + # skip + } + if(i != len s) + return s[0: i+1]; + return s; +} diff --git a/appl/cmd/auth/factotum/proto/infauth.b b/appl/cmd/auth/factotum/proto/infauth.b index cfc847eb..e9b87553 100644 --- a/appl/cmd/auth/factotum/proto/infauth.b +++ b/appl/cmd/auth/factotum/proto/infauth.b @@ -139,9 +139,8 @@ interaction(attrs: list of ref Attr, io: ref IO): string senderr(io, e); break; Error1 => - senderr(io, "missing your authentication data"); - x: string = e; - return "remote: "+x; + senderr(io, "failed"); # acknowledge error + return remote(e); } { @@ -151,8 +150,7 @@ interaction(attrs: list of ref Attr, io: ref IO): string Error0 => return e; Error1 => - x: string = e; - return "remote: "+x; + return remote(e); } if(err != nil) return err; @@ -160,13 +158,26 @@ interaction(attrs: list of ref Attr, io: ref IO): string return negotiatecrypto(io, key, ai, rattrs); } +remote(s: string): string +{ + # account for strange earlier interface + if(len s < 6 || s[0: 6] != "remote") + return "remote: "+s; + return s; +} + +# TO DO: exchange attr/value pairs, covered by hmac (use part of secret up to hmac block size of 64 bytes) +# the old scheme can be distinguished either by a prefix "attrs " or simply because the string contains "=", +# and the server side can then reply. the hmac is to prevent tampering. negotiatecrypto(io: ref IO, key: ref Key, ai: ref Authinfo, attrs: list of ref Sexp): string { role := authio->lookattrval(key.attrs, "role"); alg: string; { if(role == "client"){ - alg = authio->lookattrval(key.attrs, "alg"); + alg = authio->lookattrval(key.attrs, ":alg"); + if(alg == nil) + alg = authio->lookattrval(key.attrs, "alg"); # old way if(alg == nil) alg = "md5/rc4_256"; sendmsg(io, array of byte alg); @@ -297,7 +308,8 @@ getmsg(io: ref IO): array of byte raises (Error0, Error1) if(len buf != m) raise Error0("io error: (impossible?) msg length " + string m); if(h[0] == '!'){ -sys->print("got remote error: %s, len %d\n", string buf, len string buf); + if(0) + sys->print("got remote error: %q, len %d\n", string buf, len string buf); raise Error1(string buf); } return buf; @@ -318,16 +330,46 @@ senderr(io: ref IO, e: string) io.write(buf, len buf); } +# both the s-expression and k=v form are interim, until all +# the factotum implementations can manage public keys +# the s-expression form was the original one used by Inferno factotum +# the form in which Authinfo components are separate attributes is the +# one now used by Plan 9 and Plan 9 Ports factotum implementations keytoauthinfo(key:ref Key): (ref Keyring->Authinfo, string) { - if((s := authio->lookattrval(key.secrets, "!authinfo")) == nil){ - # XXX could look up authinfo by hash at this point - return (nil, "no authinfo attribute"); - } + if((s := authio->lookattrval(key.secrets, "!authinfo")) != nil) + return strtoauthinfo(s); + # TO DO: could look up authinfo by hash + ai := ref Keyring->Authinfo; + if((s = kv(key.secrets, "!sk")) == nil || (ai.mysk = keyring->strtosk(s)) == nil) + return (nil, "bad secret key"); + if((s = kv(key.attrs, "pk")) == nil || (ai.mypk = keyring->strtopk(s)) == nil) + return (nil, "bad public key"); + if((s = kv(key.attrs, "cert")) == nil || (ai.cert = keyring->strtocert(s)) == nil) + return (nil, "bad certificate"); + if((s = kv(key.attrs, "spk")) == nil || (ai.spk = keyring->strtopk(s)) == nil) + return (nil, "bad signer public key"); + if((s = kv(key.attrs, "dh-alpha")) == nil || (ai.alpha = IPint.strtoip(s, 16)) == nil) + return (nil, "bad value for alpha"); + if((s = kv(key.attrs, "dh-p")) == nil || (ai.p = IPint.strtoip(s, 16)) == nil) + return (nil, "bad value for p"); + return (ai, nil); +} - return strtoauthinfo(s); +kv(a: list of ref Attr, name: string): string +{ + return rnl(authio->lookattrval(a, name)); +} + +rnl(s: string): string +{ + for(i := 0; i < len s; i++) + if(s[i] == '^') + s[i] = '\n'; + return s; } +# s-expression form strtoauthinfo(s: string): (ref Keyring->Authinfo, string) { (se, err, nil) := Sexp.parse(s); diff --git a/appl/cmd/auth/mkfile b/appl/cmd/auth/mkfile index 5782599e..71d31533 100644 --- a/appl/cmd/auth/mkfile +++ b/appl/cmd/auth/mkfile @@ -5,6 +5,7 @@ DIRS=\ TARG=\ aescbc.dis\ + ai2key.dis\ changelogin.dis\ countersigner.dis\ convpasswd.dis\ |
