summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCharles.Forsyth <devnull@localhost>2008-01-16 23:03:53 +0000
committerCharles.Forsyth <devnull@localhost>2008-01-16 23:03:53 +0000
commitf808c71c90d0de09f34309e6075120e1328eccc6 (patch)
tree774d5eebb6995ceaa3b3f6287585e199f34edd09
parent8ea28ab820ed0fca4c8351622182074b37a3fa71 (diff)
20080116-2309
-rw-r--r--CHANGES2
-rw-r--r--appl/lib/factotum.b97
-rw-r--r--dis/lib/factotum.disbin6097 -> 7226 bytes
-rw-r--r--man/2/INDEX17
-rw-r--r--man/2/factotum201
-rw-r--r--man/2/keyring-crypt21
-rw-r--r--module/factotum.m14
7 files changed, 341 insertions, 11 deletions
diff --git a/CHANGES b/CHANGES
index 7b7e6f06..a41dd9d0 100644
--- a/CHANGES
+++ b/CHANGES
@@ -5,6 +5,8 @@
ftpfs uses new dial(2)
add qbypass to emu/port/qio.c
sed fix issue 49
+ add blowfish to keyring-crypt(2)
+ add challenge/response to factotum(2)
20080115
add blowfish to keyring (not yet documented)
add explicit public and private key types to keyring (in development: not yet documented)
diff --git a/appl/lib/factotum.b b/appl/lib/factotum.b
index 0b3ff1a8..97138fe1 100644
--- a/appl/lib/factotum.b
+++ b/appl/lib/factotum.b
@@ -57,12 +57,17 @@ Authinfo.unpack(a: array of byte): (int, ref Authinfo)
return (n, ai);
}
+open(): ref Sys->FD
+{
+ return sys->open("/mnt/factotum/rpc", Sys->ORDWR);
+}
+
mount(fd: ref Sys->FD, mnt: string, flags: int, aname: string, keyspec: string): (int, ref Authinfo)
{
ai: ref Authinfo;
afd := sys->fauth(fd, aname);
if(afd != nil){
- ai = proxy(afd, sys->open("/mnt/factotum/rpc", Sys->ORDWR), "proto=p9any role=client "+keyspec);
+ ai = proxy(afd, open(), "proto=p9any role=client "+keyspec);
if(debug && ai == nil){
sys->print("proxy failed: %r\n");
return (-1, nil);
@@ -286,12 +291,16 @@ done:
donec <-= (ai, err);
}
+#
+# insecure passwords, role=client
+#
+
getuserpasswd(keyspec: string): (string, string)
{
str := load String String->PATH;
if(str == nil)
return (nil, nil);
- fd := sys->open("/mnt/factotum/rpc", Sys->ORDWR);
+ fd := open();
if(fd == nil)
return (nil, nil);
if(((o, a) := dorpc(fd, "start", array of byte keyspec)).t0 != "ok" ||
@@ -307,6 +316,90 @@ getuserpasswd(keyspec: string): (string, string)
return (hd flds, hd tl flds);
}
+#
+# challenge/response, role=server
+#
+
+challenge(keyspec: string): ref Challenge
+{
+ c := ref Challenge;
+ if((c.afd = open()) == nil)
+ return nil;
+ if(rpc(c.afd, "start", array of byte keyspec).t0 != "ok")
+ return nil;
+ (w, val) := rpc(c.afd, "read", nil);
+ if(w != "ok")
+ return nil;
+ c.chal = string val;
+ return c;
+}
+
+response(c: ref Challenge, resp: string): ref Authinfo
+{
+ if(c.afd == nil){
+ sys->werrstr("auth_response: connection not open");
+ return nil;
+ }
+ if(resp == nil){
+ sys->werrstr("auth_response: nil response");
+ return nil;
+ }
+
+ if(c.user != nil){
+ if(rpc(c.afd, "write", array of byte c.user).t0 != "ok"){
+ # we're out of phase with factotum; give up
+ c.afd = nil;
+ return nil;
+ }
+ }
+
+ if(rpc(c.afd, "write", array of byte resp).t0 != "ok"){
+ # don't close the connection; we might try again
+ return nil;
+ }
+
+ (w, val) := rpc(c.afd, "read", nil);
+ if(w != "done"){
+ sys->werrstr(sys->sprint("unexpected factotum reply: %q %q", w, string val));
+ c.afd = nil;
+ return nil;
+ }
+ ai := Authinfo.read(c.afd);
+ c.afd = nil;
+ return ai;
+}
+
+#
+# challenge/response, role=client
+#
+
+respond(chal: string, keyspec: string): (string, string)
+{
+ if((afd := open()) == nil)
+ return (nil, nil);
+
+ if(dorpc(afd, "start", array of byte keyspec).t0 != "ok" ||
+ dorpc(afd, "write", array of byte chal).t0 != "ok")
+ return (nil, nil);
+ (o, resp) := dorpc(afd, "read", nil);
+ if(o != "ok")
+ return (nil, nil);
+
+ return (string resp, findattrval(rpcattrs(afd), "user"));
+}
+
+rpcattrs(afd: ref Sys->FD): list of ref Attr
+{
+ (o, a) := rpc(afd, "attr", nil);
+ if(o != "ok")
+ return nil;
+ return parseattrs(string a);
+}
+
+#
+# attributes
+#
+
parseattrs(s: string): list of ref Attr
{
str := load String String->PATH;
diff --git a/dis/lib/factotum.dis b/dis/lib/factotum.dis
index 27722fc8..868c7a84 100644
--- a/dis/lib/factotum.dis
+++ b/dis/lib/factotum.dis
Binary files differ
diff --git a/man/2/INDEX b/man/2/INDEX
index 8e1c43ca..c1c27311 100644
--- a/man/2/INDEX
+++ b/man/2/INDEX
@@ -94,10 +94,25 @@ encoding encoding
env env
ether ether
exception exception
+attrtext factotum
+challenge factotum
+copyattrs factotum
+delattr factotum
factotum factotum
+factotum factotum factotum
+findattr factotum
+findattrval factotum
+getuserpassd factotum
mount factotum
-proxy factotum
+open factotum
+parseattrs factotum
+proxy
+publicattrs factotum
+respond factotum
+response factotum
rpc factotum
+rpcattrs factotum
+takeattrs factotum
expand filepat
filepat filepat
match filepat
diff --git a/man/2/factotum b/man/2/factotum
index b13062aa..180ed100 100644
--- a/man/2/factotum
+++ b/man/2/factotum
@@ -1,6 +1,7 @@
.TH FACTOTUM 2
.SH NAME
-Factotum: mount, proxy, rpc \- client interface to factotum
+Factotum: attrtext, challenge, copyattrs, delattr, findattr, findattrval, getuserpassd, mount, open, parseattrs, proxy,
+publicattrs, takeattrs, respond, response, rpc, rpcattrs \- client interface to factotum
.SH SYNOPSIS
.EX
include "factotum.m";
@@ -18,11 +19,41 @@ AuthRpcMax: con \fR...\fP;
init: fn();
mount: fn(fd: ref Sys->FD, mnt: string, flags: int, aname: string):
(int, ref Authinfo);
+
getuserpasswd: fn(keyspec: string): (string, string);
-rpc: fn(facfd: ref Sys->FD, verb: string, a: array of byte):
- (string, array of byte);
-proxy: fn(afd: ref Sys->FD, facfd: ref Sys->FD, arg: string):
- ref Authinfo;
+
+Challenge: adt {
+ user: string; # user (some protocols)
+ chal: string; # challenge string
+};
+
+challenge: fn(keyspec: string): ref Challenge;
+response: fn(c: ref Challenge, resp: string): ref Authinfo;
+respond: fn(chal: string, keyspec: string): (string, string);
+
+open: fn(): ref Sys->FD;
+rpc: fn(facfd: ref Sys->FD, verb: string, a: array of byte):
+ (string, array of byte);
+proxy: fn(afd: ref Sys->FD, facfd: ref Sys->FD, arg: string):
+ ref Authinfo;
+rpcattrs: fn(facfd: ref Sys->FD): list of ref Attr;
+
+Attr: adt {
+ tag: int; # Aattr, Aval, or Aquery
+ name: string;
+ val: string;
+
+ text: fn(a: self ref Attr): string;
+};
+
+parseattrs: fn(s: string): list of ref Attr;
+copyattrs: fn(l: list of ref Attr): list of ref Attr;
+delattr: fn(l: list of ref Attr, n: string): list of ref Attr;
+takeattrs: fn(l: list of ref Attr, names: list of string): list of ref Attr;
+findattr: fn(l: list of ref Attr, n: string): ref Attr;
+findattrval: fn(l: list of ref Attr, n: string): string;
+publicattrs: fn(l: list of ref Attr): list of ref Attr;
+attrtext: fn(l: list of ref Attr): string;
.EE
.SH DESCRIPTION
.B Factotum
@@ -34,6 +65,12 @@ It can also interact with Plan 9's
if that is in the name space (as well as or instead of
.IR factotum (4)).
.PP
+.B Factotum
+supports both the basic RPC interface to
+.I factotum
+and various high-level operations built on that.
+The high-level functions will be described first.
+.PP
.B Init
must be called before any other function.
.PP
@@ -92,6 +129,151 @@ and matches the given
.IR keyspec .
The tuple values are nil if no entry matches or the caller lacks permission to see them.
.PP
+The pair of functions
+.B challenge
+and
+.B response
+give servers access to challenge/response protocols provided by
+.IR factotum .
+All such authentication protocols are embedded in an application-level protocol
+such as FTP, IMAP, POP3 and so on, in a way specific to that protocol.
+These functions calculate parameters for application-specific messages.
+.PP
+A server calls
+.B challenge
+to get a challenge value to present to the user who will answer the challenge.
+The server verifies the user's response by calling
+.BR response ,
+which returns an
+.B Authinfo
+value (as described above) if the response is correct.
+The specific challenge/response protocol is selected by the
+.B proto
+attribute in the
+.I keyspec
+for
+.BR challenge ,
+which opens a connection to
+.IR factotum ,
+obtains a string representing a challenge value,
+and returns a reference to a
+.B Challenge
+adt that gives the challenge and a user name.
+Some protocols take the user name from a
+.B user
+attribute in the
+.IR keyspec ,
+and others negotiate it within the authentication protocol.
+In the latter case, the
+.B user
+field of the resulting
+.B Challenge
+gives the name negotiated.
+.PP
+In the other direction, a process uses
+.B respond
+to calculate the correct response to a given challenge.
+.I Chal
+is the challenge presented by a server, and
+.I keyspec
+gives the attributes of the relevant key in the local
+.IR factotum .
+.B Respond
+interacts with
+.I factotum
+and returns a tuple
+.RI ( response,\ user )
+that gives the
+.I response
+string to return to the server, and an optional
+.I user
+name, depending on the protocol.
+On error, the
+.I response
+is nil, and the system error string contains a diagnostic.
+.PP
+.IR Factotum (4)
+represents keys as attribute value pairs in textual form, with space-separated fields.
+A field can be a value-less
+.IR attribute ,
+an
+.IB attribute = value
+pair, or
+.IB attribute ?
+to mark a required attribute with unknown value.
+An
+.I attribute
+name that begins with an exclamation mark
+.RB ( ! )
+is to be considered hidden or secret.
+Values containing white space or special characters can be quoted using the conventions of
+.IR sh (1).
+.PP
+.B Parseattrs
+parses a string
+.I s
+of that form into a list of
+.B Attr
+references.
+Each
+.B Attr
+has a
+.B tag
+that identifies the value as
+.B Aattr
+(a value-less attribute), a
+.B Aval
+(an attribute-value pair), or
+.B Aquery
+(an attribute for which a value is needed).
+Other operations include:
+.TP 15n
+.B copyattrs
+Return a copy of attribute list
+.IR l .
+.TP
+.B delattr
+Return a copy of
+.I l
+removing all attributes with name
+.IR n .
+.TP
+.B takeattrs
+Return the subset of list
+.I l
+that contains only the attributes listed in
+.IR names .
+.TP
+.B findattr
+Return a reference to the first
+.B Attr
+in
+.I l
+with the name
+.IR n .
+.TP
+.B findattrval
+Return the value of the first attribute in
+.I l
+with name
+.IR n ;
+return nil if the attribute is not found or has no value.
+.TP
+.B publicattrs
+Return a copy of
+.I l
+in which all secret attributes have been removed.
+.TP
+.B attrtext
+Return the textual representation of attribute list
+.IR l ,
+acceptable to
+.BR parseattrs .
+.PP
+The low-level interfaces to
+.I factotum
+are as follows.
+.PP
.B Proxy
links an authenticating server on
.I afd
@@ -101,16 +283,19 @@ agent on
.IR facfd .
Typically
.I facfd
-is the result of
+is the result of calling
+.BR Factotum->open ,
+which is equivalent to
.IP
.EX
sys->open("/mnt/factotum/rpc", Sys->ORDWR)
.EE
.PP
.I Afd
-is typically the result of
+is a file descriptor that represents a connection to the service that requires authentication.
+It is typically the result of
.IR sys-open (2),
-.IR sys-dial (2),
+.IR dial (2),
or
.IR sys-fauth (2).
.I Params
diff --git a/man/2/keyring-crypt b/man/2/keyring-crypt
index a083e543..6a5be85e 100644
--- a/man/2/keyring-crypt
+++ b/man/2/keyring-crypt
@@ -15,6 +15,12 @@ aessetup: fn(key: array of byte, ivec: array of byte): ref AESstate;
aescbc: fn(state: ref AESstate, buf: array of byte,
n: int, direction: int);
+BFbsize: con 8;
+
+blowfishsetup: fn(key: array of byte, ivec: array of byte): ref BFstate;
+blowfishcbc: fn(state: ref BFstate, buf: array of byte,
+ n: int, direction: int);
+
DESbsize: con 8;
dessetup: fn(key: array of byte, ivec: array of byte): ref DESstate;
@@ -88,6 +94,21 @@ should be
bytes of random data: random enough to be unlikely to be reused but
not cryptographically strongly unpredictable.
.TP
+.B blowfish
+Bruce Schneier's symmetric block cipher.
+The
+.I key
+is any length from 4 to 56 bytes.
+.I Ivec
+if non-nil is
+.B BFbsize
+bytes of random data.
+For
+.BR blowfishcbc ,
+.I n
+must be a multiple of
+.BR BFbsize .
+.TP
.B des
The older Data Encryption Standard, DES.
.I Key
diff --git a/module/factotum.m b/module/factotum.m
index 85165ebd..5d1df5f8 100644
--- a/module/factotum.m
+++ b/module/factotum.m
@@ -9,6 +9,7 @@ Factotum: module
suid: string; # server id
cap: string; # capability (only valid on server side)
secret: array of byte;
+ # TO DO: add attrs
unpack: fn(a: array of byte): (int, ref Authinfo);
read: fn(fd: ref Sys->FD): ref Authinfo;
@@ -20,6 +21,7 @@ Factotum: module
AuthRpcMax: con 4096;
init: fn();
+ open: fn(): ref Sys->FD;
rpc: fn(fd: ref Sys->FD, verb: string, a: array of byte): (string, array of byte);
proxy: fn(afd: ref Sys->FD, facfd: ref Sys->FD, arg: string): ref Authinfo;
genproxy: fn(
@@ -28,9 +30,21 @@ Factotum: module
donec: chan of (ref Authinfo, string),
afd: ref Sys->FD,
params: string);
+ rpcattrs: fn(afd: ref Sys->FD): list of ref Attr;
getuserpasswd: fn(keyspec: string): (string, string);
+ # challenge/response
+ Challenge: adt {
+ user: string;
+ chal: string;
+ afd: ref Sys->FD;
+ };
+
+ challenge: fn(keyspec: string): ref Challenge;
+ response: fn(c: ref Challenge, resp: string): ref Authinfo;
+ respond: fn(chal: string, keyspec: string): (string, string);
+
dump: fn(a: array of byte): string;
setdebug: fn(i: int);