summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES3
-rw-r--r--appl/cmd/auth/ai2key.b144
-rw-r--r--appl/cmd/auth/factotum/proto/infauth.b66
-rw-r--r--appl/cmd/auth/mkfile1
-rw-r--r--dis/auth/ai2key.disbin0 -> 2710 bytes
-rw-r--r--dis/auth/proto/infauth.disbin7727 -> 8649 bytes
-rw-r--r--libinterp/keyring.c2
-rw-r--r--man/8/INDEX3
-rw-r--r--man/8/ai2key262
9 files changed, 468 insertions, 13 deletions
diff --git a/CHANGES b/CHANGES
index fb77d7cc..072845f3 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,6 @@
+20081021
+ /appl/cmd/auth/ai2key.b new command to convert authinfo files to factotum keys
+ /appl/cmd/auth/factotum/proto/infauth.b allow new key format
20081019
/appl/cmd/man2html.b implement "\ " => " "
/man/3/prog change way a literal " was formatted
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\
diff --git a/dis/auth/ai2key.dis b/dis/auth/ai2key.dis
new file mode 100644
index 00000000..72e0b498
--- /dev/null
+++ b/dis/auth/ai2key.dis
Binary files differ
diff --git a/dis/auth/proto/infauth.dis b/dis/auth/proto/infauth.dis
index 53d5eb68..1efeabd5 100644
--- a/dis/auth/proto/infauth.dis
+++ b/dis/auth/proto/infauth.dis
Binary files differ
diff --git a/libinterp/keyring.c b/libinterp/keyring.c
index b0f0a0fa..69815282 100644
--- a/libinterp/keyring.c
+++ b/libinterp/keyring.c
@@ -1696,7 +1696,7 @@ if(0)print("Y");
buf[n] = 0;
hiscert = strtocert(buf);
if(hiscert == H){
- err = "bad certificate";
+ err = "bad certificate syntax";
goto out;
}
certimmutable(hiscert); /* hide from the garbage collector */
diff --git a/man/8/INDEX b/man/8/INDEX
index c520eacb..3c633861 100644
--- a/man/8/INDEX
+++ b/man/8/INDEX
@@ -1,4 +1,5 @@
intro 0intro
+ai2key ai2key
applylog applylog
updatelog applylog
bootpd bootpd
@@ -15,6 +16,7 @@ csquery cs
dhcp dhcp
dns dns
dnsquery dns
+dsagen ai2key
fpgaload fpgaload
ftl ftl
getauthinfo getauthinfo
@@ -39,6 +41,7 @@ prep prep
rdbgsrv rdbgsrv
register register
rip rip
+rsagen ai2key
rstyxd rstyxd
styxd rstyxd
shutdown shutdown
diff --git a/man/8/ai2key b/man/8/ai2key
new file mode 100644
index 00000000..99149b94
--- /dev/null
+++ b/man/8/ai2key
@@ -0,0 +1,262 @@
+.TH AI2KEY 8
+.SH NAME
+ai2key, dsagen, rsagen \- generate and reformat public keys
+.SH SYNOPSIS
+.B ai2key
+[
+.BI -t " tag"
+]
+.I keyfile
+...
+.P
+.B dsagen
+[
+.BI -t " tag"
+]
+.PP
+.B rsagen
+[
+.BI -b " nbits"
+] [
+.BI -t " tag"
+]
+.SH DESCRIPTION
+.IR Factotum (4)
+represents public keys as lists of attribute-value pairs, each key on a single line prefixed with the string
+.BR key .
+.PP
+.I Ai2key
+converts the original Inferno representation of authentication data,
+in the format defined for
+.B authinfo
+by
+.IR keytext (6),
+to an attribute-value format accepted by
+.IR factotum (4)
+for the
+.B infauth
+authentication protocol.
+For each
+.I keyfile
+it writes a single line on standard output, containing
+the following fields:
+.IP
+.EX
+.fi
+.ti -3n
+key proto=infauth
+[
+.I tag
+]
+.BI "sigalg=" pkalg - hashalg
+[
+.BI dom= host
+]
+.BI server= host
+[
+.BI service= svc
+]
+.BI "user=" name
+.BI "signer=" name
+.BI "pk=" pk
+.BI "!sk=" sk
+.BI spk= pk
+.BI cert= cert
+.BI dh-alpha= hex
+.BI dh-p= hex
+.EE
+.PP
+where
+.RS
+.TP 15n
+.I pkalg
+is
+.BR dsa ,
+.B elgamal
+or
+.BR rsa
+.PD 0
+.ns
+.TP
+.I hashalg
+is
+.B md5
+or
+.BR sha1
+.br
+.ns
+.TP
+.B user
+is the user name associated with the key, as vouched for by the supporting
+certificate
+.BR cert
+.br
+.ns
+.TP
+.B signer
+is the user name associated with the key that signed the certificate
+.br
+.ns
+.TP
+.B pk
+is the user's public key
+.br
+.ns
+.TP
+.B !sk
+is the user's private (secret) key
+.br
+.ns
+.TP
+.B spk
+is the signer's public key
+.br
+.ns
+.TP
+.B cert
+is the
+.I certificate
+.br
+.ns
+.TP
+.BR dh-alpha ,\ dh-p
+are the Diffie-Hellman parameters shared by the user and file servers.
+.RE
+.PD
+.PP
+The key is tagged by one or more of
+.BR dom ,
+.B server
+and
+.BR service ,
+derived from the file name
+.IR keyfile .
+The server is
+.RB ` * '
+if
+.I keyfile
+is
+.BR default .
+Otherwise
+.I keyfile
+has the form
+.IP
+[
+.IB net !
+]
+.I host
+[
+.BI ! srv
+]
+.PP
+and
+.B server
+and
+.B service
+are set accordingly;
+.B dom
+is set if
+.I host
+looks like a domain name.
+Key and certificate values have the form defined in
+.IR keytext (6);
+.I hex
+is a large number in hexadecimal.
+.PP
+.I Dsagen
+prints a randomly-generated DSA private key using the NIST-recommended algorithm.
+If
+.I tag
+text is specified, it is printed after the
+.B proto
+attribute-value pair.
+Typically,
+.I tag
+is a sequence of attribute-value comments describing the key.
+A DSA key has the following attributes
+.RS
+.TP 8n
+.B p
+prime public modulus
+.PD 0
+.TP
+.B q
+prime group order; divides
+.BR p -1
+.TP
+.B alpha
+group generator
+.TP
+.B key
+.BR alpha ^ !secret
+mod
+.B p
+.TP
+.B !secret
+the secret exponent
+.RE
+.PD
+.PP
+.I Rsagen
+prints a randomly generated RSA private key
+whose
+.B n
+has exactly
+.I nbits
+(default 1024)
+significant bits.
+The key has the following attributes:
+.RS
+.TP
+.B size
+the number of significant bits in
+.B n
+.PD 0
+.TP
+.B ek
+the encryption exponent
+.TP
+.B n
+the product of
+.B !p
+and
+.B !q
+.TP
+.B !dk
+the decryption exponent
+.TP
+.B !p
+a large prime
+.TP
+.B !q
+another large prime
+.TP
+.B "!kp\fR, \fL!kq\fR, \fL!c2
+parameters derived from the other attributes, cached to speed decryption
+.RE
+.PD
+.PP
+All the numbers in
+.I dsagen
+and
+.I rsagen
+output are in hexadecimal except RSA's
+.BR size ,
+which is decimal.
+A public key omits the attributes beginning with
+.L ! .
+A key may have other attributes as well, for example a
+.B service
+attribute identifying how this key is typically used,
+but to these utilities such attributes are merely comments.
+They can be provided in a
+.I tag
+argument.
+.SH SOURCE
+.B /appl/cmd/auth/ai2key.b
+.br
+.B /appl/cmd/auth/dsagen.b
+.br
+.B /appl/cmd/auth/rsagen.b
+.SH "SEE ALSO"
+.IR factotum (4)