From f808c71c90d0de09f34309e6075120e1328eccc6 Mon Sep 17 00:00:00 2001 From: "Charles.Forsyth" Date: Wed, 16 Jan 2008 23:03:53 +0000 Subject: 20080116-2309 --- CHANGES | 2 + appl/lib/factotum.b | 97 ++++++++++++++++++++++++- dis/lib/factotum.dis | Bin 6097 -> 7226 bytes man/2/INDEX | 17 ++++- man/2/factotum | 201 +++++++++++++++++++++++++++++++++++++++++++++++++-- man/2/keyring-crypt | 21 ++++++ module/factotum.m | 14 ++++ 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 Binary files a/dis/lib/factotum.dis and b/dis/lib/factotum.dis 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); -- cgit v1.2.3