Small tweaks
Lots of new code imported.
This commit is contained in:
parent
a770daa795
commit
2277c5d7bb
86 changed files with 12444 additions and 91 deletions
1
src/cmd/factotum/BUGS
Normal file
1
src/cmd/factotum/BUGS
Normal file
|
|
@ -0,0 +1 @@
|
|||
key, delkey, wipe should be in ctl not rpc.
|
||||
348
src/cmd/factotum/apop.c
Normal file
348
src/cmd/factotum/apop.c
Normal file
|
|
@ -0,0 +1,348 @@
|
|||
/*
|
||||
* APOP, CRAM - MD5 challenge/response authentication
|
||||
*
|
||||
* The client does not authenticate the server, hence no CAI.
|
||||
*
|
||||
* Protocol:
|
||||
*
|
||||
* S -> C: random@domain
|
||||
* C -> S: hex-response
|
||||
* S -> C: ok
|
||||
*
|
||||
* Note that this is the protocol between factotum and the local
|
||||
* program, not between the two factotums. The information
|
||||
* exchanged here is wrapped in the APOP protocol by the local
|
||||
* programs.
|
||||
*
|
||||
* If S sends "bad [msg]" instead of "ok", that is a hint that the key is bad.
|
||||
* The protocol goes back to "C -> S: user".
|
||||
*/
|
||||
|
||||
#include "std.h"
|
||||
#include "dat.h"
|
||||
|
||||
static int
|
||||
apopcheck(Key *k)
|
||||
{
|
||||
if(!strfindattr(k->attr, "user") || !strfindattr(k->privattr, "!password")){
|
||||
werrstr("need user and !password attributes");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
apopclient(Conv *c)
|
||||
{
|
||||
char *chal, *pw, *res;
|
||||
int astype, nchal, npw, ntry, ret;
|
||||
uchar resp[MD5dlen];
|
||||
Attr *attr;
|
||||
DigestState *ds;
|
||||
Key *k;
|
||||
|
||||
chal = nil;
|
||||
k = nil;
|
||||
res = nil;
|
||||
ret = -1;
|
||||
attr = c->attr;
|
||||
|
||||
if(c->proto == &apop)
|
||||
astype = AuthApop;
|
||||
else if(c->proto == &cram)
|
||||
astype = AuthCram;
|
||||
else{
|
||||
werrstr("bad proto");
|
||||
goto out;
|
||||
}
|
||||
|
||||
c->state = "find key";
|
||||
k = keyfetch(c, "%A %s", attr, c->proto->keyprompt);
|
||||
if(k == nil)
|
||||
goto out;
|
||||
|
||||
c->state = "read challenge";
|
||||
if((nchal = convreadm(c, &chal)) < 0)
|
||||
goto out;
|
||||
|
||||
for(ntry=1;; ntry++){
|
||||
if(c->attr != attr)
|
||||
freeattr(c->attr);
|
||||
c->attr = addattrs(copyattr(attr), k->attr);
|
||||
if((pw = strfindattr(k->privattr, "!password")) == nil){
|
||||
werrstr("key has no password (cannot happen?)");
|
||||
goto out;
|
||||
}
|
||||
npw = strlen(pw);
|
||||
|
||||
switch(astype){
|
||||
case AuthApop:
|
||||
ds = md5((uchar*)chal, nchal, nil, nil);
|
||||
md5((uchar*)pw, npw, resp, ds);
|
||||
break;
|
||||
case AuthCram:
|
||||
hmac_md5((uchar*)chal, nchal, (uchar*)pw, npw, resp, nil);
|
||||
break;
|
||||
}
|
||||
|
||||
/* C->S: APOP user hex-response\n */
|
||||
if(ntry == 1)
|
||||
c->state = "write user";
|
||||
else{
|
||||
sprint(c->statebuf, "write user (auth attempt #%d)", ntry);
|
||||
c->state = c->statebuf;
|
||||
}
|
||||
if(convprint(c, "%s", strfindattr(k->attr, "user")) < 0)
|
||||
goto out;
|
||||
|
||||
c->state = "write response";
|
||||
if(convprint(c, "%.*H", sizeof resp, resp) < 0)
|
||||
goto out;
|
||||
|
||||
c->state = "read result";
|
||||
if(convreadm(c, &res) < 0)
|
||||
goto out;
|
||||
|
||||
if(strcmp(res, "ok") == 0)
|
||||
break;
|
||||
|
||||
if(strncmp(res, "bad ", 4) != 0){
|
||||
werrstr("bad result: %s", res);
|
||||
goto out;
|
||||
}
|
||||
|
||||
c->state = "replace key";
|
||||
if((k = keyreplace(c, k, "%s", res+4)) == nil){
|
||||
c->state = "auth failed";
|
||||
werrstr("%s", res+4);
|
||||
goto out;
|
||||
}
|
||||
free(res);
|
||||
res = nil;
|
||||
}
|
||||
|
||||
werrstr("succeeded");
|
||||
ret = 0;
|
||||
|
||||
out:
|
||||
keyclose(k);
|
||||
free(chal);
|
||||
if(c->attr != attr)
|
||||
freeattr(attr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* shared with auth dialing routines */
|
||||
typedef struct ServerState ServerState;
|
||||
struct ServerState
|
||||
{
|
||||
int asfd;
|
||||
Key *k;
|
||||
Ticketreq tr;
|
||||
Ticket t;
|
||||
char *dom;
|
||||
char *hostid;
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
APOPCHALLEN = 128,
|
||||
};
|
||||
|
||||
static int apopchal(ServerState*, int, char[APOPCHALLEN]);
|
||||
static int apopresp(ServerState*, char*, char*);
|
||||
|
||||
static int
|
||||
apopserver(Conv *c)
|
||||
{
|
||||
char chal[APOPCHALLEN], *user, *resp;
|
||||
ServerState s;
|
||||
int astype, ret;
|
||||
Attr *a;
|
||||
|
||||
ret = -1;
|
||||
user = nil;
|
||||
resp = nil;
|
||||
memset(&s, 0, sizeof s);
|
||||
s.asfd = -1;
|
||||
|
||||
if(c->proto == &apop)
|
||||
astype = AuthApop;
|
||||
else if(c->proto == &cram)
|
||||
astype = AuthCram;
|
||||
else{
|
||||
werrstr("bad proto");
|
||||
goto out;
|
||||
}
|
||||
|
||||
c->state = "find key";
|
||||
if((s.k = plan9authkey(c->attr)) == nil)
|
||||
goto out;
|
||||
|
||||
a = copyattr(s.k->attr);
|
||||
a = delattr(a, "proto");
|
||||
c->attr = addattrs(c->attr, a);
|
||||
freeattr(a);
|
||||
|
||||
c->state = "authdial";
|
||||
s.hostid = strfindattr(s.k->attr, "user");
|
||||
s.dom = strfindattr(s.k->attr, "dom");
|
||||
if((s.asfd = xioauthdial(nil, s.dom)) < 0){
|
||||
werrstr("authdial %s: %r", s.dom);
|
||||
goto out;
|
||||
}
|
||||
|
||||
c->state = "authchal";
|
||||
if(apopchal(&s, astype, chal) < 0)
|
||||
goto out;
|
||||
|
||||
c->state = "write challenge";
|
||||
if(convprint(c, "%s", chal) < 0)
|
||||
goto out;
|
||||
|
||||
for(;;){
|
||||
c->state = "read user";
|
||||
if(convreadm(c, &user) < 0)
|
||||
goto out;
|
||||
|
||||
c->state = "read response";
|
||||
if(convreadm(c, &resp) < 0)
|
||||
goto out;
|
||||
|
||||
c->state = "authwrite";
|
||||
switch(apopresp(&s, user, resp)){
|
||||
case -1:
|
||||
goto out;
|
||||
case 0:
|
||||
c->state = "write status";
|
||||
if(convprint(c, "bad authentication failed") < 0)
|
||||
goto out;
|
||||
break;
|
||||
case 1:
|
||||
c->state = "write status";
|
||||
if(convprint(c, "ok") < 0)
|
||||
goto out;
|
||||
goto ok;
|
||||
}
|
||||
free(user);
|
||||
free(resp);
|
||||
user = nil;
|
||||
resp = nil;
|
||||
}
|
||||
|
||||
ok:
|
||||
ret = 0;
|
||||
c->attr = addcap(c->attr, c->sysuser, &s.t);
|
||||
|
||||
out:
|
||||
keyclose(s.k);
|
||||
free(user);
|
||||
free(resp);
|
||||
// xioclose(s.asfd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
apopchal(ServerState *s, int astype, char chal[APOPCHALLEN])
|
||||
{
|
||||
char trbuf[TICKREQLEN];
|
||||
Ticketreq tr;
|
||||
|
||||
memset(&tr, 0, sizeof tr);
|
||||
|
||||
tr.type = astype;
|
||||
|
||||
if(strlen(s->hostid) >= sizeof tr.hostid){
|
||||
werrstr("hostid too long");
|
||||
return -1;
|
||||
}
|
||||
strcpy(tr.hostid, s->hostid);
|
||||
|
||||
if(strlen(s->dom) >= sizeof tr.authdom){
|
||||
werrstr("domain too long");
|
||||
return -1;
|
||||
}
|
||||
strcpy(tr.authdom, s->dom);
|
||||
|
||||
convTR2M(&tr, trbuf);
|
||||
if(xiowrite(s->asfd, trbuf, TICKREQLEN) != TICKREQLEN)
|
||||
return -1;
|
||||
|
||||
if(xioasrdresp(s->asfd, chal, APOPCHALLEN) <= 5)
|
||||
return -1;
|
||||
|
||||
s->tr = tr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
apopresp(ServerState *s, char *user, char *resp)
|
||||
{
|
||||
char tabuf[TICKETLEN+AUTHENTLEN];
|
||||
char trbuf[TICKREQLEN];
|
||||
int len;
|
||||
Authenticator a;
|
||||
Ticket t;
|
||||
Ticketreq tr;
|
||||
|
||||
tr = s->tr;
|
||||
if(memrandom(tr.chal, CHALLEN) < 0)
|
||||
return -1;
|
||||
|
||||
if(strlen(user) >= sizeof tr.uid){
|
||||
werrstr("uid too long");
|
||||
return -1;
|
||||
}
|
||||
strcpy(tr.uid, user);
|
||||
|
||||
convTR2M(&tr, trbuf);
|
||||
if(xiowrite(s->asfd, trbuf, TICKREQLEN) != TICKREQLEN)
|
||||
return -1;
|
||||
|
||||
len = strlen(resp);
|
||||
if(xiowrite(s->asfd, resp, len) != len)
|
||||
return -1;
|
||||
|
||||
if(xioasrdresp(s->asfd, tabuf, TICKETLEN+AUTHENTLEN) != TICKETLEN+AUTHENTLEN)
|
||||
return 0;
|
||||
|
||||
convM2T(tabuf, &t, s->k->priv);
|
||||
if(t.num != AuthTs
|
||||
|| memcmp(t.chal, tr.chal, sizeof tr.chal) != 0){
|
||||
werrstr("key mismatch with auth server");
|
||||
return -1;
|
||||
}
|
||||
|
||||
convM2A(tabuf+TICKETLEN, &a, t.key);
|
||||
if(a.num != AuthAc
|
||||
|| memcmp(a.chal, tr.chal, sizeof a.chal) != 0
|
||||
|| a.id != 0){
|
||||
werrstr("key2 mismatch with auth server");
|
||||
return -1;
|
||||
}
|
||||
|
||||
s->t = t;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static Role
|
||||
apoproles[] =
|
||||
{
|
||||
"client", apopclient,
|
||||
"server", apopserver,
|
||||
0
|
||||
};
|
||||
|
||||
Proto apop = {
|
||||
.name= "apop",
|
||||
.roles= apoproles,
|
||||
.checkkey= apopcheck,
|
||||
.keyprompt= "user? !password?",
|
||||
};
|
||||
|
||||
Proto cram = {
|
||||
.name= "cram",
|
||||
.roles= apoproles,
|
||||
.checkkey= apopcheck,
|
||||
.keyprompt= "user? !password?",
|
||||
};
|
||||
228
src/cmd/factotum/attr.c
Normal file
228
src/cmd/factotum/attr.c
Normal file
|
|
@ -0,0 +1,228 @@
|
|||
#include "std.h"
|
||||
#include "dat.h"
|
||||
|
||||
Attr*
|
||||
addattr(Attr *a, char *fmt, ...)
|
||||
{
|
||||
char buf[1024];
|
||||
va_list arg;
|
||||
Attr *b;
|
||||
|
||||
va_start(arg, fmt);
|
||||
vseprint(buf, buf+sizeof buf, fmt, arg);
|
||||
va_end(arg);
|
||||
b = _parseattr(buf);
|
||||
a = addattrs(a, b);
|
||||
setmalloctag(a, getcallerpc(&a));
|
||||
_freeattr(b);
|
||||
return a;
|
||||
}
|
||||
|
||||
/*
|
||||
* add attributes in list b to list a. If any attributes are in
|
||||
* both lists, replace those in a by those in b.
|
||||
*/
|
||||
Attr*
|
||||
addattrs(Attr *a, Attr *b)
|
||||
{
|
||||
int found;
|
||||
Attr **l, *aa;
|
||||
|
||||
for(; b; b=b->next){
|
||||
switch(b->type){
|
||||
case AttrNameval:
|
||||
for(l=&a; *l; ){
|
||||
if(strcmp((*l)->name, b->name) != 0){
|
||||
l=&(*l)->next;
|
||||
continue;
|
||||
}
|
||||
aa = *l;
|
||||
*l = aa->next;
|
||||
aa->next = nil;
|
||||
freeattr(aa);
|
||||
}
|
||||
*l = mkattr(AttrNameval, b->name, b->val, nil);
|
||||
break;
|
||||
case AttrQuery:
|
||||
found = 0;
|
||||
for(l=&a; *l; l=&(*l)->next)
|
||||
if((*l)->type==AttrNameval && strcmp((*l)->name, b->name) == 0)
|
||||
found++;
|
||||
if(!found)
|
||||
*l = mkattr(AttrQuery, b->name, b->val, nil);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
void
|
||||
setmalloctaghere(void *v)
|
||||
{
|
||||
setmalloctag(v, getcallerpc(&v));
|
||||
}
|
||||
|
||||
Attr*
|
||||
sortattr(Attr *a)
|
||||
{
|
||||
int i;
|
||||
Attr *anext, *a0, *a1, **l;
|
||||
|
||||
if(a == nil || a->next == nil)
|
||||
return a;
|
||||
|
||||
/* cut list in halves */
|
||||
a0 = nil;
|
||||
a1 = nil;
|
||||
i = 0;
|
||||
for(; a; a=anext){
|
||||
anext = a->next;
|
||||
if(i++%2){
|
||||
a->next = a0;
|
||||
a0 = a;
|
||||
}else{
|
||||
a->next = a1;
|
||||
a1 = a;
|
||||
}
|
||||
}
|
||||
|
||||
/* sort */
|
||||
a0 = sortattr(a0);
|
||||
a1 = sortattr(a1);
|
||||
|
||||
/* merge */
|
||||
l = &a;
|
||||
while(a0 || a1){
|
||||
if(a1==nil){
|
||||
anext = a0;
|
||||
a0 = a0->next;
|
||||
}else if(a0==nil){
|
||||
anext = a1;
|
||||
a1 = a1->next;
|
||||
}else if(strcmp(a0->name, a1->name) < 0){
|
||||
anext = a0;
|
||||
a0 = a0->next;
|
||||
}else{
|
||||
anext = a1;
|
||||
a1 = a1->next;
|
||||
}
|
||||
*l = anext;
|
||||
l = &(*l)->next;
|
||||
}
|
||||
*l = nil;
|
||||
return a;
|
||||
}
|
||||
|
||||
int
|
||||
attrnamefmt(Fmt *fmt)
|
||||
{
|
||||
char *b, buf[1024], *ebuf;
|
||||
Attr *a;
|
||||
|
||||
ebuf = buf+sizeof buf;
|
||||
b = buf;
|
||||
strcpy(buf, " ");
|
||||
for(a=va_arg(fmt->args, Attr*); a; a=a->next){
|
||||
if(a->name == nil)
|
||||
continue;
|
||||
b = seprint(b, ebuf, " %q?", a->name);
|
||||
}
|
||||
return fmtstrcpy(fmt, buf+1);
|
||||
}
|
||||
|
||||
static int
|
||||
hasqueries(Attr *a)
|
||||
{
|
||||
for(; a; a=a->next)
|
||||
if(a->type == AttrQuery)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
char *ignored[] = {
|
||||
"role",
|
||||
};
|
||||
|
||||
static int
|
||||
ignoreattr(char *s)
|
||||
{
|
||||
int i;
|
||||
|
||||
for(i=0; i<nelem(ignored); i++)
|
||||
if(strcmp(ignored[i], s)==0)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
hasname(Attr *a0, Attr *a1, char *name)
|
||||
{
|
||||
return _findattr(a0, name) || _findattr(a1, name);
|
||||
}
|
||||
|
||||
static int
|
||||
hasnameval(Attr *a0, Attr *a1, char *name, char *val)
|
||||
{
|
||||
Attr *a;
|
||||
|
||||
for(a=_findattr(a0, name); a; a=_findattr(a->next, name))
|
||||
if(strcmp(a->val, val) == 0)
|
||||
return 1;
|
||||
for(a=_findattr(a1, name); a; a=_findattr(a->next, name))
|
||||
if(strcmp(a->val, val) == 0)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
matchattr(Attr *pat, Attr *a0, Attr *a1)
|
||||
{
|
||||
int type;
|
||||
|
||||
for(; pat; pat=pat->next){
|
||||
type = pat->type;
|
||||
if(ignoreattr(pat->name))
|
||||
type = AttrDefault;
|
||||
switch(type){
|
||||
case AttrQuery: /* name=something be present */
|
||||
if(!hasname(a0, a1, pat->name))
|
||||
return 0;
|
||||
break;
|
||||
case AttrNameval: /* name=val must be present */
|
||||
if(!hasnameval(a0, a1, pat->name, pat->val))
|
||||
return 0;
|
||||
break;
|
||||
case AttrDefault: /* name=val must be present if name=anything is present */
|
||||
if(hasname(a0, a1, pat->name) && !hasnameval(a0, a1, pat->name, pat->val))
|
||||
return 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
Attr*
|
||||
parseattrfmtv(char *fmt, va_list arg)
|
||||
{
|
||||
char *s;
|
||||
Attr *a;
|
||||
|
||||
s = vsmprint(fmt, arg);
|
||||
if(s == nil)
|
||||
sysfatal("vsmprint: out of memory");
|
||||
a = parseattr(s);
|
||||
free(s);
|
||||
return a;
|
||||
}
|
||||
|
||||
Attr*
|
||||
parseattrfmt(char *fmt, ...)
|
||||
{
|
||||
va_list arg;
|
||||
Attr *a;
|
||||
|
||||
va_start(arg, fmt);
|
||||
a = parseattrfmtv(fmt, arg);
|
||||
va_end(arg);
|
||||
return a;
|
||||
}
|
||||
424
src/cmd/factotum/chap.c
Normal file
424
src/cmd/factotum/chap.c
Normal file
|
|
@ -0,0 +1,424 @@
|
|||
/*
|
||||
* CHAP, MSCHAP
|
||||
*
|
||||
* The client does not authenticate the server, hence no CAI
|
||||
*
|
||||
* Protocol:
|
||||
*
|
||||
* S -> C: random 8-byte challenge
|
||||
* C -> S: user in UTF-8
|
||||
* C -> S: Chapreply or MSchapreply structure
|
||||
* S -> C: ok or 'bad why'
|
||||
*
|
||||
* The chap protocol requires the client to give it id=%d, the id of
|
||||
* the PPP message containing the challenge, which is used
|
||||
* as part of the response. Because the client protocol is message-id
|
||||
* specific, there is no point in looping to try multiple keys.
|
||||
*
|
||||
* The MS chap protocol actually uses two different hashes, an
|
||||
* older insecure one called the LM (Lan Manager) hash, and a newer
|
||||
* more secure one called the NT hash. By default we send back only
|
||||
* the NT hash, because the LM hash can help an eavesdropper run
|
||||
* a brute force attack. If the key has an lm attribute, then we send only the
|
||||
* LM hash.
|
||||
*/
|
||||
|
||||
#include "std.h"
|
||||
#include "dat.h"
|
||||
|
||||
enum {
|
||||
ChapChallen = 8,
|
||||
|
||||
MShashlen = 16,
|
||||
MSchallen = 8,
|
||||
MSresplen = 24,
|
||||
};
|
||||
|
||||
static int
|
||||
chapcheck(Key *k)
|
||||
{
|
||||
if(!strfindattr(k->attr, "user") || !strfindattr(k->privattr, "!password")){
|
||||
werrstr("need user and !password attributes");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
nthash(uchar hash[MShashlen], char *passwd)
|
||||
{
|
||||
uchar buf[512];
|
||||
int i;
|
||||
|
||||
for(i=0; *passwd && i<sizeof(buf); passwd++) {
|
||||
buf[i++] = *passwd;
|
||||
buf[i++] = 0;
|
||||
}
|
||||
|
||||
memset(hash, 0, 16);
|
||||
|
||||
md4(buf, i, hash, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
desencrypt(uchar data[8], uchar key[7])
|
||||
{
|
||||
ulong ekey[32];
|
||||
|
||||
key_setup(key, ekey);
|
||||
block_cipher(ekey, data, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
lmhash(uchar hash[MShashlen], char *passwd)
|
||||
{
|
||||
uchar buf[14];
|
||||
char *stdtext = "KGS!@#$%";
|
||||
int i;
|
||||
|
||||
strncpy((char*)buf, passwd, sizeof(buf));
|
||||
for(i=0; i<sizeof(buf); i++)
|
||||
if(buf[i] >= 'a' && buf[i] <= 'z')
|
||||
buf[i] += 'A' - 'a';
|
||||
|
||||
memset(hash, 0, 16);
|
||||
memcpy(hash, stdtext, 8);
|
||||
memcpy(hash+8, stdtext, 8);
|
||||
|
||||
desencrypt(hash, buf);
|
||||
desencrypt(hash+8, buf+7);
|
||||
}
|
||||
|
||||
static void
|
||||
mschalresp(uchar resp[MSresplen], uchar hash[MShashlen], uchar chal[MSchallen])
|
||||
{
|
||||
int i;
|
||||
uchar buf[21];
|
||||
|
||||
memset(buf, 0, sizeof(buf));
|
||||
memcpy(buf, hash, MShashlen);
|
||||
|
||||
for(i=0; i<3; i++) {
|
||||
memmove(resp+i*MSchallen, chal, MSchallen);
|
||||
desencrypt(resp+i*MSchallen, buf+i*7);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
chapclient(Conv *c)
|
||||
{
|
||||
int id, astype, nchal, npw, ret;
|
||||
uchar *chal;
|
||||
char *s, *pw, *user, *res;
|
||||
Attr *attr;
|
||||
Key *k;
|
||||
Chapreply cr;
|
||||
MSchapreply mscr;
|
||||
DigestState *ds;
|
||||
|
||||
ret = -1;
|
||||
chal = nil;
|
||||
k = nil;
|
||||
attr = c->attr;
|
||||
|
||||
if(c->proto == &chap){
|
||||
astype = AuthChap;
|
||||
s = strfindattr(attr, "id");
|
||||
if(s == nil || *s == 0){
|
||||
werrstr("need id=n attr in start message");
|
||||
goto out;
|
||||
}
|
||||
id = strtol(s, &s, 10);
|
||||
if(*s != 0 || id < 0 || id >= 256){
|
||||
werrstr("bad id=n attr in start message");
|
||||
goto out;
|
||||
}
|
||||
cr.id = id;
|
||||
}else if(c->proto == &mschap)
|
||||
astype = AuthMSchap;
|
||||
else{
|
||||
werrstr("bad proto");
|
||||
goto out;
|
||||
}
|
||||
|
||||
c->state = "find key";
|
||||
k = keyfetch(c, "%A %s", attr, c->proto->keyprompt);
|
||||
if(k == nil)
|
||||
goto out;
|
||||
|
||||
c->attr = addattrs(copyattr(attr), k->attr);
|
||||
|
||||
c->state = "read challenge";
|
||||
if((nchal = convreadm(c, (char**)&chal)) < 0)
|
||||
goto out;
|
||||
if(astype == AuthMSchap && nchal != MSchallen)
|
||||
c->state = "write user";
|
||||
if((user = strfindattr(k->attr, "user")) == nil){
|
||||
werrstr("key has no user (cannot happen?)");
|
||||
goto out;
|
||||
}
|
||||
if(convprint(c, "%s", user) < 0)
|
||||
goto out;
|
||||
|
||||
c->state = "write response";
|
||||
if((pw = strfindattr(k->privattr, "!password")) == nil){
|
||||
werrstr("key has no password (cannot happen?)");
|
||||
goto out;
|
||||
}
|
||||
npw = strlen(pw);
|
||||
|
||||
if(astype == AuthChap){
|
||||
ds = md5(&cr.id, 1, 0, 0);
|
||||
md5((uchar*)pw, npw, 0, ds);
|
||||
md5(chal, nchal, (uchar*)cr.resp, ds);
|
||||
if(convwrite(c, &cr, sizeof cr) < 0)
|
||||
goto out;
|
||||
}else{
|
||||
uchar hash[MShashlen];
|
||||
|
||||
memset(&mscr, 0, sizeof mscr);
|
||||
if(strfindattr(k->attr, "lm")){
|
||||
lmhash(hash, pw);
|
||||
mschalresp((uchar*)mscr.LMresp, hash, chal);
|
||||
}else{
|
||||
nthash(hash, pw);
|
||||
mschalresp((uchar*)mscr.NTresp, hash, chal);
|
||||
}
|
||||
if(convwrite(c, &mscr, sizeof mscr) < 0)
|
||||
goto out;
|
||||
}
|
||||
|
||||
c->state = "read result";
|
||||
if(convreadm(c, &res) < 0)
|
||||
goto out;
|
||||
if(strcmp(res, "ok") == 0){
|
||||
ret = 0;
|
||||
werrstr("succeeded");
|
||||
goto out;
|
||||
}
|
||||
if(strncmp(res, "bad ", 4) != 0){
|
||||
werrstr("bad result: %s", res);
|
||||
goto out;
|
||||
}
|
||||
|
||||
c->state = "replace key";
|
||||
keyevict(c, k, "%s", res+4);
|
||||
werrstr("%s", res+4);
|
||||
|
||||
out:
|
||||
free(res);
|
||||
keyclose(k);
|
||||
free(chal);
|
||||
if(c->attr != attr)
|
||||
freeattr(attr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* shared with auth dialing routines */
|
||||
typedef struct ServerState ServerState;
|
||||
struct ServerState
|
||||
{
|
||||
int asfd;
|
||||
Key *k;
|
||||
Ticketreq tr;
|
||||
Ticket t;
|
||||
char *dom;
|
||||
char *hostid;
|
||||
};
|
||||
|
||||
static int chapchal(ServerState*, int, char[ChapChallen]);
|
||||
static int chapresp(ServerState*, char*, char*);
|
||||
|
||||
static int
|
||||
chapserver(Conv *c)
|
||||
{
|
||||
char chal[ChapChallen], *user, *resp;
|
||||
ServerState s;
|
||||
int astype, ret;
|
||||
Attr *a;
|
||||
|
||||
ret = -1;
|
||||
user = nil;
|
||||
resp = nil;
|
||||
memset(&s, 0, sizeof s);
|
||||
s.asfd = -1;
|
||||
|
||||
if(c->proto == &chap)
|
||||
astype = AuthChap;
|
||||
else if(c->proto == &mschap)
|
||||
astype = AuthMSchap;
|
||||
else{
|
||||
werrstr("bad proto");
|
||||
goto out;
|
||||
}
|
||||
|
||||
c->state = "find key";
|
||||
if((s.k = plan9authkey(c->attr)) == nil)
|
||||
goto out;
|
||||
|
||||
a = copyattr(s.k->attr);
|
||||
a = delattr(a, "proto");
|
||||
c->attr = addattrs(c->attr, a);
|
||||
freeattr(a);
|
||||
|
||||
c->state = "authdial";
|
||||
s.hostid = strfindattr(s.k->attr, "user");
|
||||
s.dom = strfindattr(s.k->attr, "dom");
|
||||
if((s.asfd = xioauthdial(nil, s.dom)) < 0){
|
||||
werrstr("authdial %s: %r", s.dom);
|
||||
goto out;
|
||||
}
|
||||
|
||||
c->state = "authchal";
|
||||
if(chapchal(&s, astype, chal) < 0)
|
||||
goto out;
|
||||
|
||||
c->state = "write challenge";
|
||||
if(convprint(c, "%s", chal) < 0)
|
||||
goto out;
|
||||
|
||||
c->state = "read user";
|
||||
if(convreadm(c, &user) < 0)
|
||||
goto out;
|
||||
|
||||
c->state = "read response";
|
||||
if(convreadm(c, &resp) < 0)
|
||||
goto out;
|
||||
|
||||
c->state = "authwrite";
|
||||
switch(chapresp(&s, user, resp)){
|
||||
default:
|
||||
fprint(2, "factotum: bad result from chapresp\n");
|
||||
goto out;
|
||||
case -1:
|
||||
goto out;
|
||||
case 0:
|
||||
c->state = "write status";
|
||||
if(convprint(c, "bad authentication failed") < 0)
|
||||
goto out;
|
||||
goto out;
|
||||
|
||||
case 1:
|
||||
c->state = "write status";
|
||||
if(convprint(c, "ok") < 0)
|
||||
goto out;
|
||||
goto ok;
|
||||
}
|
||||
|
||||
ok:
|
||||
ret = 0;
|
||||
c->attr = addcap(c->attr, c->sysuser, &s.t);
|
||||
|
||||
out:
|
||||
keyclose(s.k);
|
||||
free(user);
|
||||
free(resp);
|
||||
// xioclose(s.asfd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
chapchal(ServerState *s, int astype, char chal[ChapChallen])
|
||||
{
|
||||
char trbuf[TICKREQLEN];
|
||||
Ticketreq tr;
|
||||
|
||||
memset(&tr, 0, sizeof tr);
|
||||
|
||||
tr.type = astype;
|
||||
|
||||
if(strlen(s->hostid) >= sizeof tr.hostid){
|
||||
werrstr("hostid too long");
|
||||
return -1;
|
||||
}
|
||||
strcpy(tr.hostid, s->hostid);
|
||||
|
||||
if(strlen(s->dom) >= sizeof tr.authdom){
|
||||
werrstr("domain too long");
|
||||
return -1;
|
||||
}
|
||||
strcpy(tr.authdom, s->dom);
|
||||
|
||||
convTR2M(&tr, trbuf);
|
||||
if(xiowrite(s->asfd, trbuf, TICKREQLEN) != TICKREQLEN)
|
||||
return -1;
|
||||
|
||||
if(xioasrdresp(s->asfd, chal, ChapChallen) <= 5)
|
||||
return -1;
|
||||
|
||||
s->tr = tr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
chapresp(ServerState *s, char *user, char *resp)
|
||||
{
|
||||
char tabuf[TICKETLEN+AUTHENTLEN];
|
||||
char trbuf[TICKREQLEN];
|
||||
int len;
|
||||
Authenticator a;
|
||||
Ticket t;
|
||||
Ticketreq tr;
|
||||
|
||||
tr = s->tr;
|
||||
if(memrandom(tr.chal, CHALLEN) < 0)
|
||||
return -1;
|
||||
|
||||
if(strlen(user) >= sizeof tr.uid){
|
||||
werrstr("uid too long");
|
||||
return -1;
|
||||
}
|
||||
strcpy(tr.uid, user);
|
||||
|
||||
convTR2M(&tr, trbuf);
|
||||
if(xiowrite(s->asfd, trbuf, TICKREQLEN) != TICKREQLEN)
|
||||
return -1;
|
||||
|
||||
len = strlen(resp);
|
||||
if(xiowrite(s->asfd, resp, len) != len)
|
||||
return -1;
|
||||
|
||||
if(xioasrdresp(s->asfd, tabuf, TICKETLEN+AUTHENTLEN) != TICKETLEN+AUTHENTLEN)
|
||||
return 0;
|
||||
|
||||
convM2T(tabuf, &t, s->k->priv);
|
||||
if(t.num != AuthTs
|
||||
|| memcmp(t.chal, tr.chal, sizeof tr.chal) != 0){
|
||||
werrstr("key mismatch with auth server");
|
||||
return -1;
|
||||
}
|
||||
|
||||
convM2A(tabuf+TICKETLEN, &a, t.key);
|
||||
if(a.num != AuthAc
|
||||
|| memcmp(a.chal, tr.chal, sizeof a.chal) != 0
|
||||
|| a.id != 0){
|
||||
werrstr("key2 mismatch with auth server");
|
||||
return -1;
|
||||
}
|
||||
|
||||
s->t = t;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static Role
|
||||
chaproles[] =
|
||||
{
|
||||
"client", chapclient,
|
||||
"server", chapserver,
|
||||
0
|
||||
};
|
||||
|
||||
Proto chap = {
|
||||
.name= "chap",
|
||||
.roles= chaproles,
|
||||
.checkkey= chapcheck,
|
||||
.keyprompt= "user? !password?",
|
||||
};
|
||||
|
||||
Proto mschap = {
|
||||
.name= "mschap",
|
||||
.roles= chaproles,
|
||||
.checkkey= chapcheck,
|
||||
.keyprompt= "user? !password?",
|
||||
};
|
||||
|
||||
139
src/cmd/factotum/confirm.c
Normal file
139
src/cmd/factotum/confirm.c
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
#include "std.h"
|
||||
#include "dat.h"
|
||||
|
||||
Logbuf confbuf;
|
||||
|
||||
void
|
||||
confirmread(Req *r)
|
||||
{
|
||||
lbread(&confbuf, r);
|
||||
}
|
||||
|
||||
void
|
||||
confirmflush(Req *r)
|
||||
{
|
||||
lbflush(&confbuf, r);
|
||||
}
|
||||
|
||||
int
|
||||
confirmwrite(char *s)
|
||||
{
|
||||
char *t, *ans;
|
||||
int allow;
|
||||
ulong tag;
|
||||
Attr *a;
|
||||
Conv *c;
|
||||
|
||||
a = _parseattr(s);
|
||||
if(a == nil){
|
||||
werrstr("bad attr");
|
||||
return -1;
|
||||
}
|
||||
if((t = _strfindattr(a, "tag")) == nil){
|
||||
werrstr("no tag");
|
||||
return -1;
|
||||
}
|
||||
tag = strtoul(t, 0, 0);
|
||||
if((ans = _strfindattr(a, "answer")) == nil){
|
||||
werrstr("no answer");
|
||||
return -1;
|
||||
}
|
||||
if(strcmp(ans, "yes") == 0)
|
||||
allow = 1;
|
||||
else if(strcmp(ans, "no") == 0)
|
||||
allow = 0;
|
||||
else{
|
||||
werrstr("bad answer");
|
||||
return -1;
|
||||
}
|
||||
for(c=conv; c; c=c->next){
|
||||
if(tag == c->tag){
|
||||
nbsendul(c->keywait, allow);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(c == nil){
|
||||
werrstr("tag not found");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
confirmkey(Conv *c, Key *k)
|
||||
{
|
||||
if(*confirminuse == 0)
|
||||
return -1;
|
||||
|
||||
lbappend(&confbuf, "confirm tag=%lud %A %N", c->tag, k->attr, k->privattr);
|
||||
c->state = "keyconfirm";
|
||||
return recvul(c->keywait);
|
||||
}
|
||||
|
||||
Logbuf needkeybuf;
|
||||
|
||||
void
|
||||
needkeyread(Req *r)
|
||||
{
|
||||
lbread(&needkeybuf, r);
|
||||
}
|
||||
|
||||
void
|
||||
needkeyflush(Req *r)
|
||||
{
|
||||
lbflush(&needkeybuf, r);
|
||||
}
|
||||
|
||||
int
|
||||
needkeywrite(char *s)
|
||||
{
|
||||
char *t;
|
||||
ulong tag;
|
||||
Attr *a;
|
||||
Conv *c;
|
||||
|
||||
a = _parseattr(s);
|
||||
if(a == nil){
|
||||
werrstr("empty write");
|
||||
return -1;
|
||||
}
|
||||
if((t = _strfindattr(a, "tag")) == nil){
|
||||
werrstr("no tag");
|
||||
freeattr(a);
|
||||
return -1;
|
||||
}
|
||||
tag = strtoul(t, 0, 0);
|
||||
for(c=conv; c; c=c->next)
|
||||
if(c->tag == tag){
|
||||
nbsendul(c->keywait, 0);
|
||||
break;
|
||||
}
|
||||
if(c == nil){
|
||||
werrstr("tag not found");
|
||||
freeattr(a);
|
||||
return -1;
|
||||
}
|
||||
freeattr(a);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
needkey(Conv *c, Attr *a)
|
||||
{
|
||||
if(c == nil || *needkeyinuse == 0)
|
||||
return -1;
|
||||
|
||||
lbappend(&needkeybuf, "needkey tag=%lud %A", c->tag, a);
|
||||
return nbrecvul(c->keywait);
|
||||
}
|
||||
|
||||
int
|
||||
badkey(Conv *c, Key *k, char *msg, Attr *a)
|
||||
{
|
||||
if(c == nil || *needkeyinuse == 0)
|
||||
return -1;
|
||||
|
||||
lbappend(&needkeybuf, "badkey tag=%lud %A %N\n%s\n%A",
|
||||
c->tag, k->attr, k->privattr, msg, a);
|
||||
return nbrecvul(c->keywait);
|
||||
}
|
||||
254
src/cmd/factotum/conv.c
Normal file
254
src/cmd/factotum/conv.c
Normal file
|
|
@ -0,0 +1,254 @@
|
|||
#include "std.h"
|
||||
#include "dat.h"
|
||||
|
||||
Conv *conv;
|
||||
|
||||
ulong taggen = 1;
|
||||
|
||||
Conv*
|
||||
convalloc(char *sysuser)
|
||||
{
|
||||
Conv *c;
|
||||
|
||||
c = mallocz(sizeof(Conv), 1);
|
||||
if(c == nil)
|
||||
return nil;
|
||||
c->ref = 1;
|
||||
c->tag = taggen++;
|
||||
c->next = conv;
|
||||
c->sysuser = estrdup(sysuser);
|
||||
c->state = "nascent";
|
||||
c->rpcwait = chancreate(sizeof(void*), 0);
|
||||
c->keywait = chancreate(sizeof(void*), 0);
|
||||
strcpy(c->err, "protocol has not started");
|
||||
conv = c;
|
||||
convreset(c);
|
||||
return c;
|
||||
}
|
||||
|
||||
void
|
||||
convreset(Conv *c)
|
||||
{
|
||||
if(c->ref != 1){
|
||||
c->hangup = 1;
|
||||
nbsendp(c->rpcwait, 0);
|
||||
while(c->ref > 1)
|
||||
yield();
|
||||
c->hangup = 0;
|
||||
}
|
||||
c->state = "nascent";
|
||||
c->err[0] = '\0';
|
||||
freeattr(c->attr);
|
||||
c->attr = nil;
|
||||
c->proto = nil;
|
||||
c->rpc.op = 0;
|
||||
c->active = 0;
|
||||
c->done = 0;
|
||||
c->hangup = 0;
|
||||
}
|
||||
|
||||
void
|
||||
convhangup(Conv *c)
|
||||
{
|
||||
c->hangup = 1;
|
||||
c->rpc.op = 0;
|
||||
(*c->kickreply)(c);
|
||||
nbsendp(c->rpcwait, 0);
|
||||
}
|
||||
|
||||
void
|
||||
convclose(Conv *c)
|
||||
{
|
||||
Conv *p;
|
||||
|
||||
if(c == nil)
|
||||
return;
|
||||
|
||||
if(--c->ref > 0)
|
||||
return;
|
||||
|
||||
if(c == conv){
|
||||
conv = c->next;
|
||||
goto free;
|
||||
}
|
||||
for(p=conv; p && p->next!=c; p=p->next)
|
||||
;
|
||||
if(p == nil){
|
||||
print("cannot find conv in list\n");
|
||||
return;
|
||||
}
|
||||
p->next = c->next;
|
||||
|
||||
free:
|
||||
c->next = nil;
|
||||
free(c);
|
||||
}
|
||||
|
||||
static Rpc*
|
||||
convgetrpc(Conv *c, int want)
|
||||
{
|
||||
for(;;){
|
||||
if(c->hangup){
|
||||
werrstr("hangup");
|
||||
return nil;
|
||||
}
|
||||
if(c->rpc.op == RpcUnknown){
|
||||
recvp(c->rpcwait);
|
||||
if(c->hangup){
|
||||
werrstr("hangup");
|
||||
return nil;
|
||||
}
|
||||
if(c->rpc.op == RpcUnknown)
|
||||
continue;
|
||||
}
|
||||
if(want < 0 || c->rpc.op == want)
|
||||
return &c->rpc;
|
||||
rpcrespond(c, "phase in state '%s' want '%s'", c->state, rpcname[want]);
|
||||
}
|
||||
return nil; /* not reached */
|
||||
}
|
||||
|
||||
/* read until the done function tells us that's enough */
|
||||
int
|
||||
convreadfn(Conv *c, int (*done)(void*, int), char **ps)
|
||||
{
|
||||
int n;
|
||||
Rpc *r;
|
||||
char *s;
|
||||
|
||||
for(;;){
|
||||
r = convgetrpc(c, RpcWrite);
|
||||
if(r == nil)
|
||||
return -1;
|
||||
n = (*done)(r->data, r->count);
|
||||
if(n == r->count)
|
||||
break;
|
||||
rpcrespond(c, "toosmall %d", n);
|
||||
}
|
||||
|
||||
s = emalloc(r->count+1);
|
||||
memmove(s, r->data, r->count);
|
||||
s[r->count] = 0;
|
||||
*ps = s;
|
||||
rpcrespond(c, "ok");
|
||||
return r->count;
|
||||
}
|
||||
|
||||
/*
|
||||
* read until we get a non-zero write. assumes remote side
|
||||
* knows something about the protocol (is not auth_proxy).
|
||||
* the remote side typically won't bother with the zero-length
|
||||
* write to find out the length -- the loop is there only so the
|
||||
* test program can call auth_proxy on both sides of a pipe
|
||||
* to play a conversation.
|
||||
*/
|
||||
int
|
||||
convreadm(Conv *c, char **ps)
|
||||
{
|
||||
char *s;
|
||||
Rpc *r;
|
||||
|
||||
for(;;){
|
||||
r = convgetrpc(c, RpcWrite);
|
||||
if(r == nil)
|
||||
return -1;
|
||||
if(r->count > 0)
|
||||
break;
|
||||
rpcrespond(c, "toosmall %d", AuthRpcMax);
|
||||
}
|
||||
s = emalloc(r->count+1);
|
||||
memmove(s, r->data, r->count);
|
||||
s[r->count] = 0;
|
||||
*ps = s;
|
||||
rpcrespond(c, "ok");
|
||||
return r->count;
|
||||
}
|
||||
|
||||
/* read exactly count bytes */
|
||||
int
|
||||
convread(Conv *c, void *data, int count)
|
||||
{
|
||||
Rpc *r;
|
||||
|
||||
for(;;){
|
||||
r = convgetrpc(c, RpcWrite);
|
||||
if(r == nil)
|
||||
return -1;
|
||||
if(r->count == count)
|
||||
break;
|
||||
if(r->count < count)
|
||||
rpcrespond(c, "toosmall %d", count);
|
||||
else
|
||||
rpcrespond(c, "error too much data; want %d got %d", count, r->count);
|
||||
}
|
||||
memmove(data, r->data, count);
|
||||
rpcrespond(c, "ok");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* write exactly count bytes */
|
||||
int
|
||||
convwrite(Conv *c, void *data, int count)
|
||||
{
|
||||
Rpc *r;
|
||||
|
||||
for(;;){
|
||||
r = convgetrpc(c, RpcRead);
|
||||
if(r == nil)
|
||||
return -1;
|
||||
break;
|
||||
}
|
||||
rpcrespondn(c, "ok", data, count);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* print to the conversation */
|
||||
int
|
||||
convprint(Conv *c, char *fmt, ...)
|
||||
{
|
||||
char *s;
|
||||
va_list arg;
|
||||
int ret;
|
||||
|
||||
va_start(arg, fmt);
|
||||
s = vsmprint(fmt, arg);
|
||||
va_end(arg);
|
||||
if(s == nil)
|
||||
return -1;
|
||||
ret = convwrite(c, s, strlen(s));
|
||||
free(s);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* ask for a key */
|
||||
int
|
||||
convneedkey(Conv *c, Attr *a)
|
||||
{
|
||||
/*
|
||||
* Piggyback key requests in the usual RPC channel.
|
||||
* Wait for the next RPC and then send a key request
|
||||
* in response. The keys get added out-of-band (via the
|
||||
* ctl file), so assume the key has been added when the
|
||||
* next request comes in.
|
||||
*/
|
||||
if(convgetrpc(c, -1) == nil)
|
||||
return -1;
|
||||
rpcrespond(c, "needkey %A", a);
|
||||
if(convgetrpc(c, -1) == nil)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ask for a replacement for a bad key*/
|
||||
int
|
||||
convbadkey(Conv *c, Key *k, char *msg, Attr *a)
|
||||
{
|
||||
if(convgetrpc(c, -1) == nil)
|
||||
return -1;
|
||||
rpcrespond(c, "badkey %A %N\n%s\n%A",
|
||||
k->attr, k->privattr, msg, a);
|
||||
if(convgetrpc(c, -1) == nil)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
1117
src/cmd/factotum/cpu.c
Normal file
1117
src/cmd/factotum/cpu.c
Normal file
File diff suppressed because it is too large
Load diff
158
src/cmd/factotum/ctl.c
Normal file
158
src/cmd/factotum/ctl.c
Normal file
|
|
@ -0,0 +1,158 @@
|
|||
#include "std.h"
|
||||
#include "dat.h"
|
||||
|
||||
/*
|
||||
* key attr=val... - add a key
|
||||
* the attr=val pairs are protocol-specific.
|
||||
* for example, both of these are valid:
|
||||
* key p9sk1 gre cs.bell-labs.com mysecret
|
||||
* key p9sk1 gre cs.bell-labs.com 11223344556677 fmt=des7hex
|
||||
* delkey ... - delete a key
|
||||
* if given, the attr=val pairs are used to narrow the search
|
||||
* [maybe should require a password?]
|
||||
*
|
||||
* debug - toggle debugging
|
||||
*/
|
||||
|
||||
static char *msg[] = {
|
||||
"key",
|
||||
"delkey",
|
||||
"debug",
|
||||
};
|
||||
|
||||
static int
|
||||
classify(char *s)
|
||||
{
|
||||
int i;
|
||||
|
||||
for(i=0; i<nelem(msg); i++)
|
||||
if(strcmp(msg[i], s) == 0)
|
||||
return i;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int
|
||||
ctlwrite(char *a)
|
||||
{
|
||||
char *p;
|
||||
int i, nmatch, ret;
|
||||
Attr *attr, **l, **lpriv, **lprotos, *pa, *priv, *protos;
|
||||
Key *k;
|
||||
Proto *proto;
|
||||
|
||||
if(a[0] == '#' || a[0] == '\0')
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* it would be nice to emit a warning of some sort here.
|
||||
* we ignore all but the first line of the write. this helps
|
||||
* both with things like "echo delkey >/mnt/factotum/ctl"
|
||||
* and writes that (incorrectly) contain multiple key lines.
|
||||
*/
|
||||
if(p = strchr(a, '\n')){
|
||||
if(p[1] != '\0'){
|
||||
werrstr("multiline write not allowed");
|
||||
return -1;
|
||||
}
|
||||
*p = '\0';
|
||||
}
|
||||
|
||||
if((p = strchr(a, ' ')) == nil)
|
||||
p = "";
|
||||
else
|
||||
*p++ = '\0';
|
||||
switch(classify(a)){
|
||||
default:
|
||||
werrstr("unknown verb");
|
||||
return -1;
|
||||
case 0: /* key */
|
||||
attr = parseattr(p);
|
||||
/* separate out proto= attributes */
|
||||
lprotos = &protos;
|
||||
for(l=&attr; (*l); ){
|
||||
if(strcmp((*l)->name, "proto") == 0){
|
||||
*lprotos = *l;
|
||||
lprotos = &(*l)->next;
|
||||
*l = (*l)->next;
|
||||
}else
|
||||
l = &(*l)->next;
|
||||
}
|
||||
*lprotos = nil;
|
||||
if(protos == nil){
|
||||
werrstr("key without protos");
|
||||
freeattr(attr);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* separate out private attributes */
|
||||
lpriv = &priv;
|
||||
for(l=&attr; (*l); ){
|
||||
if((*l)->name[0] == '!'){
|
||||
*lpriv = *l;
|
||||
lpriv = &(*l)->next;
|
||||
*l = (*l)->next;
|
||||
}else
|
||||
l = &(*l)->next;
|
||||
}
|
||||
*lpriv = nil;
|
||||
|
||||
/* add keys */
|
||||
ret = 0;
|
||||
for(pa=protos; pa; pa=pa->next){
|
||||
if((proto = protolookup(pa->val)) == nil){
|
||||
werrstr("unknown proto %s", pa->val);
|
||||
ret = -1;
|
||||
continue;
|
||||
}
|
||||
if(proto->checkkey == nil){
|
||||
werrstr("proto %s does not accept keys", proto->name);
|
||||
ret = -1;
|
||||
continue;
|
||||
}
|
||||
k = emalloc(sizeof(Key));
|
||||
k->attr = mkattr(AttrNameval, "proto", proto->name, copyattr(attr));
|
||||
k->privattr = copyattr(priv);
|
||||
k->ref = 1;
|
||||
k->proto = proto;
|
||||
if((*proto->checkkey)(k) < 0){
|
||||
ret = -1;
|
||||
keyclose(k);
|
||||
continue;
|
||||
}
|
||||
keyadd(k);
|
||||
keyclose(k);
|
||||
}
|
||||
freeattr(attr);
|
||||
freeattr(priv);
|
||||
freeattr(protos);
|
||||
return ret;
|
||||
case 1: /* delkey */
|
||||
nmatch = 0;
|
||||
attr = parseattr(p);
|
||||
for(pa=attr; pa; pa=pa->next){
|
||||
if(pa->type != AttrQuery && pa->name[0]=='!'){
|
||||
werrstr("only !private? patterns are allowed for private fields");
|
||||
freeattr(attr);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
for(i=0; i<ring.nkey; ){
|
||||
if(matchattr(attr, ring.key[i]->attr, ring.key[i]->privattr)){
|
||||
nmatch++;
|
||||
keyclose(ring.key[i]);
|
||||
ring.nkey--;
|
||||
memmove(&ring.key[i], &ring.key[i+1], (ring.nkey-i)*sizeof(ring.key[0]));
|
||||
}else
|
||||
i++;
|
||||
}
|
||||
freeattr(attr);
|
||||
if(nmatch == 0){
|
||||
werrstr("found no keys to delete");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
case 2: /* debug */
|
||||
debug ^= 1;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
224
src/cmd/factotum/dat.h
Normal file
224
src/cmd/factotum/dat.h
Normal file
|
|
@ -0,0 +1,224 @@
|
|||
enum
|
||||
{
|
||||
MaxRpc = 2048, /* max size of any protocol message */
|
||||
|
||||
/* keep in sync with rpc.c:/rpcname */
|
||||
RpcUnknown = 0, /* Rpc.op */
|
||||
RpcAuthinfo,
|
||||
RpcAttr,
|
||||
RpcRead,
|
||||
RpcStart,
|
||||
RpcWrite,
|
||||
|
||||
/* thread stack size */
|
||||
STACK = 8192,
|
||||
};
|
||||
|
||||
typedef struct Conv Conv;
|
||||
typedef struct Key Key;
|
||||
typedef struct Logbuf Logbuf;
|
||||
typedef struct Proto Proto;
|
||||
typedef struct Ring Ring;
|
||||
typedef struct Role Role;
|
||||
typedef struct Rpc Rpc;
|
||||
|
||||
struct Rpc
|
||||
{
|
||||
int op;
|
||||
void *data;
|
||||
int count;
|
||||
};
|
||||
|
||||
struct Conv
|
||||
{
|
||||
int ref; /* ref count */
|
||||
int hangup; /* flag: please hang up */
|
||||
int active; /* flag: there is an active thread */
|
||||
int done; /* flag: conversation finished successfully */
|
||||
ulong tag; /* identifying tag */
|
||||
Conv *next; /* in linked list */
|
||||
char *sysuser; /* system name for user speaking to us */
|
||||
char *state; /* for debugging */
|
||||
char statebuf[128]; /* for formatted states */
|
||||
char err[ERRMAX]; /* last error */
|
||||
|
||||
Attr *attr; /* current attributes */
|
||||
Proto *proto; /* protocol */
|
||||
|
||||
Channel *rpcwait; /* wait here for an rpc */
|
||||
Rpc rpc; /* current rpc. op==RpcUnknown means none */
|
||||
char rpcbuf[MaxRpc]; /* buffer for rpc */
|
||||
char reply[MaxRpc]; /* buffer for response */
|
||||
int nreply; /* count of response */
|
||||
void (*kickreply)(Conv*); /* call to send response */
|
||||
Req *req; /* 9P call to read response */
|
||||
|
||||
Channel *keywait; /* wait here for key confirmation */
|
||||
|
||||
};
|
||||
|
||||
struct Key
|
||||
{
|
||||
int ref; /* ref count */
|
||||
ulong tag; /* identifying tag: sequence number */
|
||||
Attr *attr; /* public attributes */
|
||||
Attr *privattr; /* private attributes, like !password */
|
||||
Proto *proto; /* protocol owner of key */
|
||||
void *priv; /* protocol-specific storage */
|
||||
};
|
||||
|
||||
struct Logbuf
|
||||
{
|
||||
Req *wait;
|
||||
Req **waitlast;
|
||||
int rp;
|
||||
int wp;
|
||||
char *msg[128];
|
||||
};
|
||||
|
||||
struct Ring
|
||||
{
|
||||
Key **key;
|
||||
int nkey;
|
||||
};
|
||||
|
||||
struct Proto
|
||||
{
|
||||
char *name; /* name of protocol */
|
||||
Role *roles; /* list of roles and service functions */
|
||||
char *keyprompt; /* required attributes for key proto=name */
|
||||
int (*checkkey)(Key*); /* initialize k->priv or reject key */
|
||||
void (*closekey)(Key*); /* free k->priv */
|
||||
};
|
||||
|
||||
struct Role
|
||||
{
|
||||
char *name; /* name of role */
|
||||
int (*fn)(Conv*); /* service function */
|
||||
};
|
||||
|
||||
extern char *authaddr; /* plan9.c */
|
||||
extern int *confirminuse; /* fs.c */
|
||||
extern Conv* conv; /* conv.c */
|
||||
extern int debug; /* main.c */
|
||||
extern char *factname; /* main.c */
|
||||
extern Srv fs; /* fs.c */
|
||||
extern int *needkeyinuse; /* fs.c */
|
||||
extern char *owner; /* main.c */
|
||||
extern Proto *prototab[]; /* main.c */
|
||||
extern Ring ring; /* key.c */
|
||||
extern char *rpcname[]; /* rpc.c */
|
||||
|
||||
extern char Easproto[]; /* err.c */
|
||||
|
||||
extern Proto apop; /* apop.c */
|
||||
extern Proto chap; /* chap.c */
|
||||
extern Proto cram; /* cram.c */
|
||||
extern Proto mschap; /* mschap.c */
|
||||
extern Proto p9any; /* p9any.c */
|
||||
extern Proto p9sk1; /* p9sk1.c */
|
||||
extern Proto p9sk2; /* p9sk2.c */
|
||||
|
||||
/* provided by lib9p */
|
||||
#define emalloc emalloc9p
|
||||
#define erealloc erealloc9p
|
||||
#define estrdup estrdup9p
|
||||
|
||||
/* hidden in libauth */
|
||||
#define attrfmt _attrfmt
|
||||
#define copyattr _copyattr
|
||||
#define delattr _delattr
|
||||
#define findattr _findattr
|
||||
#define freeattr _freeattr
|
||||
#define mkattr _mkattr
|
||||
#define parseattr _parseattr
|
||||
#define strfindattr _strfindattr
|
||||
|
||||
extern Attr* addattr(Attr*, char*, ...);
|
||||
/* #pragma varargck argpos addattr 2 */
|
||||
extern Attr* addattrs(Attr*, Attr*);
|
||||
extern Attr* sortattr(Attr*);
|
||||
extern int attrnamefmt(Fmt*);
|
||||
/* #pragma varargck type "N" Attr* */
|
||||
extern int matchattr(Attr*, Attr*, Attr*);
|
||||
extern Attr* parseattrfmt(char*, ...);
|
||||
/* #pragma varargck argpos parseattrfmt 1 */
|
||||
extern Attr* parseattrfmtv(char*, va_list);
|
||||
|
||||
extern void confirmflush(Req*);
|
||||
extern void confirmread(Req*);
|
||||
extern int confirmwrite(char*);
|
||||
extern int needkey(Conv*, Attr*);
|
||||
extern int badkey(Conv*, Key*, char*, Attr*);
|
||||
extern int confirmkey(Conv*, Key*);
|
||||
|
||||
extern Conv* convalloc(char*);
|
||||
extern void convclose(Conv*);
|
||||
extern void convhangup(Conv*);
|
||||
extern int convneedkey(Conv*, Attr*);
|
||||
extern int convbadkey(Conv*, Key*, char*, Attr*);
|
||||
extern int convread(Conv*, void*, int);
|
||||
extern int convreadm(Conv*, char**);
|
||||
extern int convprint(Conv*, char*, ...);
|
||||
/* #pragma varargck argpos convprint 2 */
|
||||
extern int convreadfn(Conv*, int(*)(void*, int), char**);
|
||||
extern void convreset(Conv*);
|
||||
extern int convwrite(Conv*, void*, int);
|
||||
|
||||
extern int ctlwrite(char*);
|
||||
|
||||
extern char* estrappend(char*, char*, ...);
|
||||
/* #pragma varargck argpos estrappend 2 */
|
||||
extern int hexparse(char*, uchar*, int);
|
||||
|
||||
extern void keyadd(Key*);
|
||||
extern Key* keylookup(char*, ...);
|
||||
/* #pragma varargck argpos keylookup 1 */
|
||||
extern Key* keyfetch(Conv*, char*, ...);
|
||||
/* #pragma varargck argpos keyfetch 2 */
|
||||
extern void keyclose(Key*);
|
||||
extern void keyevict(Conv*, Key*, char*, ...);
|
||||
/* #pragma varargck argpos keyevict 3 */
|
||||
extern Key* keyreplace(Conv*, Key*, char*, ...);
|
||||
/* #pragma varargck argpos keyreplace 3 */
|
||||
|
||||
extern void lbkick(Logbuf*);
|
||||
extern void lbappend(Logbuf*, char*, ...);
|
||||
extern void lbvappend(Logbuf*, char*, va_list);
|
||||
/* #pragma varargck argpos lbappend 2 */
|
||||
extern void lbread(Logbuf*, Req*);
|
||||
extern void lbflush(Logbuf*, Req*);
|
||||
extern void flog(char*, ...);
|
||||
/* #pragma varargck argpos flog 1 */
|
||||
|
||||
extern void logflush(Req*);
|
||||
extern void logread(Req*);
|
||||
extern void logwrite(Req*);
|
||||
|
||||
extern void needkeyread(Req*);
|
||||
extern void needkeyflush(Req*);
|
||||
extern int needkeywrite(char*);
|
||||
extern int needkeyqueue(void);
|
||||
|
||||
extern Attr* addcap(Attr*, char*, Ticket*);
|
||||
extern Key* plan9authkey(Attr*);
|
||||
extern int _authdial(char*, char*);
|
||||
|
||||
extern int memrandom(void*, int);
|
||||
|
||||
extern Proto* protolookup(char*);
|
||||
|
||||
extern char* readcons(char *prompt, char *def, int raw);
|
||||
|
||||
extern int rpcwrite(Conv*, void*, int);
|
||||
extern void rpcrespond(Conv*, char*, ...);
|
||||
/* #pragma varargck argpos rpcrespond 2 */
|
||||
extern void rpcrespondn(Conv*, char*, void*, int);
|
||||
extern void rpcexec(Conv*);
|
||||
|
||||
extern int xioauthdial(char*, char*);
|
||||
extern void xioclose(int);
|
||||
extern int xiodial(char*, char*, char*, int*);
|
||||
extern int xiowrite(int, void*, int);
|
||||
extern int xioasrdresp(int, void*, int);
|
||||
extern int xioasgetticket(int, char*, char*);
|
||||
1686
src/cmd/factotum/fs.acid
Normal file
1686
src/cmd/factotum/fs.acid
Normal file
File diff suppressed because it is too large
Load diff
524
src/cmd/factotum/fs.c
Normal file
524
src/cmd/factotum/fs.c
Normal file
|
|
@ -0,0 +1,524 @@
|
|||
#include "std.h"
|
||||
#include "dat.h"
|
||||
|
||||
enum
|
||||
{
|
||||
Qroot,
|
||||
Qfactotum,
|
||||
Qrpc,
|
||||
Qkeylist,
|
||||
Qprotolist,
|
||||
Qconfirm,
|
||||
Qlog,
|
||||
Qctl,
|
||||
Qneedkey,
|
||||
Qconv,
|
||||
};
|
||||
|
||||
Qid
|
||||
mkqid(int type, int path)
|
||||
{
|
||||
Qid q;
|
||||
|
||||
q.type = type;
|
||||
q.path = path;
|
||||
q.vers = 0;
|
||||
return q;
|
||||
}
|
||||
|
||||
static struct
|
||||
{
|
||||
char *name;
|
||||
int qidpath;
|
||||
ulong perm;
|
||||
} dirtab[] = {
|
||||
/* positions of confirm and needkey known below */
|
||||
"confirm", Qconfirm, 0600|DMEXCL,
|
||||
"needkey", Qneedkey, 0600|DMEXCL,
|
||||
"ctl", Qctl, 0600,
|
||||
"rpc", Qrpc, 0666,
|
||||
"proto", Qprotolist, 0444,
|
||||
"log", Qlog, 0600|DMEXCL,
|
||||
"conv", Qconv, 0400,
|
||||
};
|
||||
|
||||
static void
|
||||
fillstat(Dir *dir, char *name, int type, int path, ulong perm)
|
||||
{
|
||||
dir->name = estrdup(name);
|
||||
dir->uid = estrdup(owner);
|
||||
dir->gid = estrdup(owner);
|
||||
dir->mode = perm;
|
||||
dir->length = 0;
|
||||
dir->qid = mkqid(type, path);
|
||||
dir->atime = time(0);
|
||||
dir->mtime = time(0);
|
||||
dir->muid = estrdup("");
|
||||
}
|
||||
|
||||
static int
|
||||
rootdirgen(int n, Dir *dir, void *v)
|
||||
{
|
||||
USED(v);
|
||||
|
||||
if(n > 0)
|
||||
return -1;
|
||||
|
||||
fillstat(dir, factname, QTDIR, Qfactotum, DMDIR|0555);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
fsdirgen(int n, Dir *dir, void *v)
|
||||
{
|
||||
USED(v);
|
||||
|
||||
if(n >= nelem(dirtab))
|
||||
return -1;
|
||||
fillstat(dir, dirtab[n].name, 0, dirtab[n].qidpath, dirtab[n].perm);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static char*
|
||||
fswalk1(Fid *fid, char *name, Qid *qid)
|
||||
{
|
||||
int i;
|
||||
|
||||
switch((int)fid->qid.path){
|
||||
default:
|
||||
return "fswalk1: cannot happen";
|
||||
case Qroot:
|
||||
if(strcmp(name, factname) == 0){
|
||||
*qid = mkqid(QTDIR, Qfactotum);
|
||||
fid->qid = *qid;
|
||||
return nil;
|
||||
}
|
||||
if(strcmp(name, "..") == 0){
|
||||
*qid = fid->qid;
|
||||
return nil;
|
||||
}
|
||||
return "not found";
|
||||
case Qfactotum:
|
||||
for(i=0; i<nelem(dirtab); i++)
|
||||
if(strcmp(name, dirtab[i].name) == 0){
|
||||
*qid = mkqid(0, dirtab[i].qidpath);
|
||||
fid->qid = *qid;
|
||||
return nil;
|
||||
}
|
||||
if(strcmp(name, "..") == 0){
|
||||
*qid = mkqid(QTDIR, Qroot);
|
||||
fid->qid = *qid;
|
||||
return nil;
|
||||
}
|
||||
return "not found";
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
fsstat(Req *r)
|
||||
{
|
||||
int i, path;
|
||||
|
||||
path = r->fid->qid.path;
|
||||
switch(path){
|
||||
case Qroot:
|
||||
fillstat(&r->d, "/", QTDIR, Qroot, 0555|DMDIR);
|
||||
break;
|
||||
case Qfactotum:
|
||||
fillstat(&r->d, "factotum", QTDIR, Qfactotum, 0555|DMDIR);
|
||||
break;
|
||||
default:
|
||||
for(i=0; i<nelem(dirtab); i++)
|
||||
if(dirtab[i].qidpath == path){
|
||||
fillstat(&r->d, dirtab[i].name, 0, dirtab[i].qidpath, dirtab[i].perm);
|
||||
goto Break2;
|
||||
}
|
||||
respond(r, "file not found");
|
||||
break;
|
||||
}
|
||||
Break2:
|
||||
respond(r, nil);
|
||||
}
|
||||
|
||||
static int
|
||||
readlist(int off, int (*gen)(int, char*, uint), Req *r)
|
||||
{
|
||||
char *a, *ea;
|
||||
int n;
|
||||
|
||||
a = r->ofcall.data;
|
||||
ea = a+r->ifcall.count;
|
||||
for(;;){
|
||||
n = (*gen)(off, a, ea-a);
|
||||
if(n == 0){
|
||||
r->ofcall.count = a - (char*)r->ofcall.data;
|
||||
return off;
|
||||
}
|
||||
a += n;
|
||||
off++;
|
||||
}
|
||||
return -1; /* not reached */
|
||||
}
|
||||
|
||||
static int
|
||||
keylist(int i, char *a, uint nn)
|
||||
{
|
||||
int n;
|
||||
char buf[512];
|
||||
Key *k;
|
||||
|
||||
if(i >= ring.nkey)
|
||||
return 0;
|
||||
|
||||
k = ring.key[i];
|
||||
k->attr = sortattr(k->attr);
|
||||
n = snprint(buf, sizeof buf, "key %A %N\n", k->attr, k->privattr);
|
||||
if(n >= sizeof(buf)-5)
|
||||
strcpy(buf+sizeof(buf)-5, "...\n");
|
||||
n = strlen(buf);
|
||||
if(n > nn)
|
||||
return 0;
|
||||
memmove(a, buf, n);
|
||||
return n;
|
||||
}
|
||||
|
||||
static int
|
||||
protolist(int i, char *a, uint n)
|
||||
{
|
||||
if(prototab[i] == nil)
|
||||
return 0;
|
||||
if(strlen(prototab[i]->name)+1 > n)
|
||||
return 0;
|
||||
n = strlen(prototab[i]->name)+1;
|
||||
memmove(a, prototab[i]->name, n-1);
|
||||
a[n-1] = '\n';
|
||||
return n;
|
||||
}
|
||||
|
||||
/* BUG this is O(n^2) to fill in the list */
|
||||
static int
|
||||
convlist(int i, char *a, uint nn)
|
||||
{
|
||||
Conv *c;
|
||||
char buf[512];
|
||||
int n;
|
||||
|
||||
for(c=conv; c && i-- > 0; c=c->next)
|
||||
;
|
||||
|
||||
if(c == nil)
|
||||
return 0;
|
||||
|
||||
if(c->state)
|
||||
n = snprint(buf, sizeof buf, "conv state=%q %A\n", c->state, c->attr);
|
||||
else
|
||||
n = snprint(buf, sizeof buf, "conv state=closed err=%q\n", c->err);
|
||||
|
||||
if(n >= sizeof(buf)-5)
|
||||
strcpy(buf+sizeof(buf)-5, "...\n");
|
||||
n = strlen(buf);
|
||||
if(n > nn)
|
||||
return 0;
|
||||
memmove(a, buf, n);
|
||||
return n;
|
||||
}
|
||||
|
||||
static void
|
||||
fskickreply(Conv *c)
|
||||
{
|
||||
Req *r;
|
||||
|
||||
if(c->hangup){
|
||||
if(c->req){
|
||||
respond(c->req, "hangup");
|
||||
c->req = nil;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if(!c->req || !c->nreply)
|
||||
return;
|
||||
|
||||
r = c->req;
|
||||
r->ofcall.count = c->nreply;
|
||||
r->ofcall.data = c->reply;
|
||||
if(r->ofcall.count > r->ifcall.count)
|
||||
r->ofcall.count = r->ifcall.count;
|
||||
respond(r, nil);
|
||||
c->req = nil;
|
||||
c->nreply = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Some of the file system work happens in the fs proc, but
|
||||
* fsopen, fsread, fswrite, fsdestroyfid, and fsflush happen in
|
||||
* the main proc so that they can access the various shared
|
||||
* data structures without worrying about locking.
|
||||
*/
|
||||
static int inuse[nelem(dirtab)];
|
||||
int *confirminuse = &inuse[0];
|
||||
int *needkeyinuse = &inuse[1];
|
||||
static void
|
||||
fsopen(Req *r)
|
||||
{
|
||||
int i, *inusep, perm;
|
||||
static int need[4] = { 4, 2, 6, 1 };
|
||||
Conv *c;
|
||||
|
||||
inusep = nil;
|
||||
perm = 5; /* directory */
|
||||
for(i=0; i<nelem(dirtab); i++)
|
||||
if(dirtab[i].qidpath == r->fid->qid.path){
|
||||
if(dirtab[i].perm & DMEXCL)
|
||||
inusep = &inuse[i];
|
||||
if(strcmp(r->fid->uid, owner) == 0)
|
||||
perm = dirtab[i].perm>>6;
|
||||
else
|
||||
perm = dirtab[i].perm;
|
||||
break;
|
||||
}
|
||||
|
||||
if((r->ifcall.mode&~(OMASK|OTRUNC))
|
||||
|| (need[r->ifcall.mode&3] & ~perm)){
|
||||
respond(r, "permission denied");
|
||||
return;
|
||||
}
|
||||
|
||||
if(inusep){
|
||||
if(*inusep){
|
||||
respond(r, "file in use");
|
||||
return;
|
||||
}
|
||||
*inusep = 1;
|
||||
}
|
||||
|
||||
if(r->fid->qid.path == Qrpc){
|
||||
if((c = convalloc(r->fid->uid)) == nil){
|
||||
char e[ERRMAX];
|
||||
|
||||
rerrstr(e, sizeof e);
|
||||
respond(r, e);
|
||||
return;
|
||||
}
|
||||
c->kickreply = fskickreply;
|
||||
r->fid->aux = c;
|
||||
}
|
||||
|
||||
respond(r, nil);
|
||||
}
|
||||
|
||||
static void
|
||||
fsread(Req *r)
|
||||
{
|
||||
Conv *c;
|
||||
|
||||
switch((int)r->fid->qid.path){
|
||||
default:
|
||||
respond(r, "fsread: cannot happen");
|
||||
break;
|
||||
case Qroot:
|
||||
dirread9p(r, rootdirgen, nil);
|
||||
respond(r, nil);
|
||||
break;
|
||||
case Qfactotum:
|
||||
dirread9p(r, fsdirgen, nil);
|
||||
respond(r, nil);
|
||||
break;
|
||||
case Qrpc:
|
||||
c = r->fid->aux;
|
||||
if(c->rpc.op == RpcUnknown){
|
||||
respond(r, "no rpc pending");
|
||||
break;
|
||||
}
|
||||
if(c->req){
|
||||
respond(r, "read already pending");
|
||||
break;
|
||||
}
|
||||
c->req = r;
|
||||
if(c->nreply)
|
||||
(*c->kickreply)(c);
|
||||
else
|
||||
rpcexec(c);
|
||||
break;
|
||||
case Qconfirm:
|
||||
confirmread(r);
|
||||
break;
|
||||
case Qlog:
|
||||
logread(r);
|
||||
break;
|
||||
case Qctl:
|
||||
r->fid->aux = (void*)readlist((int)r->fid->aux, keylist, r);
|
||||
respond(r, nil);
|
||||
break;
|
||||
case Qneedkey:
|
||||
needkeyread(r);
|
||||
break;
|
||||
case Qprotolist:
|
||||
r->fid->aux = (void*)readlist((int)r->fid->aux, protolist, r);
|
||||
respond(r, nil);
|
||||
break;
|
||||
case Qconv:
|
||||
r->fid->aux = (void*)readlist((int)r->fid->aux, convlist, r);
|
||||
respond(r, nil);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
fswrite(Req *r)
|
||||
{
|
||||
int ret;
|
||||
char err[ERRMAX], *s;
|
||||
int (*strfn)(char*);
|
||||
|
||||
switch((int)r->fid->qid.path){
|
||||
default:
|
||||
respond(r, "fswrite: cannot happen");
|
||||
break;
|
||||
case Qrpc:
|
||||
if(rpcwrite(r->fid->aux, r->ifcall.data, r->ifcall.count) < 0){
|
||||
rerrstr(err, sizeof err);
|
||||
respond(r, err);
|
||||
}else{
|
||||
r->ofcall.count = r->ifcall.count;
|
||||
respond(r, nil);
|
||||
}
|
||||
break;
|
||||
case Qneedkey:
|
||||
strfn = needkeywrite;
|
||||
goto string;
|
||||
case Qctl:
|
||||
strfn = ctlwrite;
|
||||
goto string;
|
||||
case Qconfirm:
|
||||
strfn = confirmwrite;
|
||||
string:
|
||||
s = emalloc(r->ifcall.count+1);
|
||||
memmove(s, r->ifcall.data, r->ifcall.count);
|
||||
s[r->ifcall.count] = '\0';
|
||||
ret = (*strfn)(s);
|
||||
free(s);
|
||||
if(ret < 0){
|
||||
rerrstr(err, sizeof err);
|
||||
respond(r, err);
|
||||
}else{
|
||||
r->ofcall.count = r->ifcall.count;
|
||||
respond(r, nil);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
fsflush(Req *r)
|
||||
{
|
||||
confirmflush(r);
|
||||
logflush(r);
|
||||
}
|
||||
|
||||
static void
|
||||
fsdestroyfid(Fid *fid)
|
||||
{
|
||||
if(fid->qid.path == Qrpc){
|
||||
convhangup(fid->aux);
|
||||
convclose(fid->aux);
|
||||
}
|
||||
}
|
||||
|
||||
static Channel *creq;
|
||||
static Channel *cfid, *cfidr;
|
||||
|
||||
static void
|
||||
fsreqthread(void *v)
|
||||
{
|
||||
Req *r;
|
||||
|
||||
USED(v);
|
||||
|
||||
creq = chancreate(sizeof(Req*), 0);
|
||||
|
||||
while((r = recvp(creq)) != nil){
|
||||
switch(r->ifcall.type){
|
||||
default:
|
||||
respond(r, "bug in fsreqthread");
|
||||
break;
|
||||
case Topen:
|
||||
fsopen(r);
|
||||
break;
|
||||
case Tread:
|
||||
fsread(r);
|
||||
break;
|
||||
case Twrite:
|
||||
fswrite(r);
|
||||
break;
|
||||
case Tflush:
|
||||
fsflush(r);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
fsclunkthread(void *v)
|
||||
{
|
||||
Fid *f;
|
||||
|
||||
USED(v);
|
||||
cfid = chancreate(sizeof(Fid*), 0);
|
||||
cfidr = chancreate(sizeof(Fid*), 0);
|
||||
|
||||
while((f = recvp(cfid)) != nil){
|
||||
fsdestroyfid(f);
|
||||
sendp(cfidr, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
fsproc(void *v)
|
||||
{
|
||||
USED(v);
|
||||
|
||||
threadcreate(fsreqthread, nil, STACK);
|
||||
threadcreate(fsclunkthread, nil, STACK);
|
||||
threadexits(nil);
|
||||
}
|
||||
|
||||
static void
|
||||
fsattach(Req *r)
|
||||
{
|
||||
static int first = 1;
|
||||
|
||||
if(first){
|
||||
proccreate(fsproc, nil, STACK);
|
||||
first = 0;
|
||||
}
|
||||
|
||||
r->fid->qid = mkqid(QTDIR, Qroot);
|
||||
r->ofcall.qid = r->fid->qid;
|
||||
respond(r, nil);
|
||||
}
|
||||
|
||||
static void
|
||||
fssend(Req *r)
|
||||
{
|
||||
sendp(creq, r);
|
||||
}
|
||||
|
||||
static void
|
||||
fssendclunk(Fid *f)
|
||||
{
|
||||
sendp(cfid, f);
|
||||
recvp(cfidr);
|
||||
}
|
||||
|
||||
Srv fs = {
|
||||
.attach= fsattach,
|
||||
.walk1= fswalk1,
|
||||
.open= fssend,
|
||||
.read= fssend,
|
||||
.write= fssend,
|
||||
.stat= fsstat,
|
||||
.flush= fssend,
|
||||
.destroyfid= fssendclunk,
|
||||
};
|
||||
|
||||
3
src/cmd/factotum/guide
Executable file
3
src/cmd/factotum/guide
Executable file
|
|
@ -0,0 +1,3 @@
|
|||
kill 8.out|rc
|
||||
unmount /srv/factotum /mnt
|
||||
8.out
|
||||
6
src/cmd/factotum/guide2
Normal file
6
src/cmd/factotum/guide2
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
kill 8.out|rc
|
||||
unmount /mnt/factotum
|
||||
8.out -m /mnt/factotum
|
||||
cat /mnt/factotum/log &
|
||||
unmount /factotum
|
||||
bind 8.out /factotum
|
||||
190
src/cmd/factotum/key.c
Normal file
190
src/cmd/factotum/key.c
Normal file
|
|
@ -0,0 +1,190 @@
|
|||
#include "std.h"
|
||||
#include "dat.h"
|
||||
|
||||
Ring ring;
|
||||
|
||||
Key*
|
||||
keylookup(char *fmt, ...)
|
||||
{
|
||||
int i;
|
||||
Attr *a;
|
||||
Key *k;
|
||||
va_list arg;
|
||||
|
||||
va_start(arg, fmt);
|
||||
a = parseattrfmtv(fmt, arg);
|
||||
va_end(arg);
|
||||
|
||||
for(i=0; i<ring.nkey; i++){
|
||||
k = ring.key[i];
|
||||
if(matchattr(a, k->attr, k->privattr)){
|
||||
k->ref++;
|
||||
freeattr(a);
|
||||
return k;
|
||||
}
|
||||
}
|
||||
freeattr(a);
|
||||
werrstr("no key found");
|
||||
return nil;
|
||||
}
|
||||
|
||||
Key*
|
||||
keyfetch(Conv *c, char *fmt, ...)
|
||||
{
|
||||
int i, tag;
|
||||
Attr *a;
|
||||
Key *k;
|
||||
va_list arg;
|
||||
|
||||
va_start(arg, fmt);
|
||||
a = parseattrfmtv(fmt, arg);
|
||||
va_end(arg);
|
||||
|
||||
tag = 0;
|
||||
|
||||
for(i=0; i<ring.nkey; i++){
|
||||
k = ring.key[i];
|
||||
if(tag < k->tag)
|
||||
tag = k->tag;
|
||||
if(matchattr(a, k->attr, k->privattr)){
|
||||
k->ref++;
|
||||
if(strfindattr(k->attr, "confirm") && confirmkey(c, k) != 1){
|
||||
k->ref--;
|
||||
continue;
|
||||
}
|
||||
freeattr(a);
|
||||
return k;
|
||||
}
|
||||
}
|
||||
|
||||
if(needkey(c, a) < 0)
|
||||
convneedkey(c, a);
|
||||
|
||||
for(i=0; i<ring.nkey; i++){
|
||||
k = ring.key[i];
|
||||
if(k->tag <= tag)
|
||||
continue;
|
||||
if(matchattr(a, k->attr, k->privattr)){
|
||||
k->ref++;
|
||||
if(strfindattr(k->attr, "confirm") && confirmkey(c, k) != 1){
|
||||
k->ref--;
|
||||
continue;
|
||||
}
|
||||
freeattr(a);
|
||||
return k;
|
||||
}
|
||||
}
|
||||
freeattr(a);
|
||||
werrstr("no key found");
|
||||
return nil;
|
||||
}
|
||||
|
||||
static int taggen;
|
||||
|
||||
void
|
||||
keyadd(Key *k)
|
||||
{
|
||||
int i;
|
||||
|
||||
k->ref++;
|
||||
k->tag = ++taggen;
|
||||
for(i=0; i<ring.nkey; i++){
|
||||
if(matchattr(k->attr, ring.key[i]->attr, nil)
|
||||
&& matchattr(ring.key[i]->attr, k->attr, nil)){
|
||||
keyclose(ring.key[i]);
|
||||
ring.key[i] = k;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ring.key = erealloc(ring.key, (ring.nkey+1)*sizeof(ring.key[0]));
|
||||
ring.key[ring.nkey++] = k;
|
||||
}
|
||||
|
||||
void
|
||||
keyclose(Key *k)
|
||||
{
|
||||
if(k == nil)
|
||||
return;
|
||||
|
||||
if(--k->ref > 0)
|
||||
return;
|
||||
|
||||
if(k->proto->closekey)
|
||||
(*k->proto->closekey)(k);
|
||||
|
||||
freeattr(k->attr);
|
||||
freeattr(k->privattr);
|
||||
free(k);
|
||||
}
|
||||
|
||||
Key*
|
||||
keyreplace(Conv *c, Key *k, char *fmt, ...)
|
||||
{
|
||||
Key *kk;
|
||||
char *msg;
|
||||
Attr *a, *b, *bp;
|
||||
va_list arg;
|
||||
|
||||
va_start(arg, fmt);
|
||||
msg = vsmprint(fmt, arg);
|
||||
if(msg == nil)
|
||||
sysfatal("out of memory");
|
||||
va_end(arg);
|
||||
|
||||
/* replace prompted values with prompts */
|
||||
a = copyattr(k->attr);
|
||||
bp = parseattr(k->proto->keyprompt);
|
||||
for(b=bp; b; b=b->next){
|
||||
a = delattr(a, b->name);
|
||||
a = addattr(a, "%q?", b->name);
|
||||
}
|
||||
freeattr(bp);
|
||||
|
||||
if(badkey(c, k, msg, a) < 0)
|
||||
convbadkey(c, k, msg, a);
|
||||
kk = keylookup("%A", a);
|
||||
freeattr(a);
|
||||
keyclose(k);
|
||||
if(kk == k){
|
||||
keyclose(kk);
|
||||
werrstr("%s", msg);
|
||||
return nil;
|
||||
}
|
||||
|
||||
if(strfindattr(kk->attr, "confirm")){
|
||||
if(confirmkey(c, kk) != 1){
|
||||
werrstr("key use not confirmed");
|
||||
keyclose(kk);
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
return kk;
|
||||
}
|
||||
|
||||
void
|
||||
keyevict(Conv *c, Key *k, char *fmt, ...)
|
||||
{
|
||||
char *msg;
|
||||
Attr *a, *b, *bp;
|
||||
va_list arg;
|
||||
|
||||
va_start(arg, fmt);
|
||||
msg = vsmprint(fmt, arg);
|
||||
if(msg == nil)
|
||||
sysfatal("out of memory");
|
||||
va_end(arg);
|
||||
|
||||
/* replace prompted values with prompts */
|
||||
a = copyattr(k->attr);
|
||||
bp = parseattr(k->proto->keyprompt);
|
||||
for(b=bp; b; b=b->next){
|
||||
a = delattr(a, b->name);
|
||||
a = addattr(a, "%q?", b->name);
|
||||
}
|
||||
freeattr(bp);
|
||||
|
||||
if(badkey(c, k, msg, nil) < 0)
|
||||
convbadkey(c, k, msg, nil);
|
||||
keyclose(k);
|
||||
}
|
||||
121
src/cmd/factotum/log.c
Normal file
121
src/cmd/factotum/log.c
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
#include "std.h"
|
||||
#include "dat.h"
|
||||
|
||||
void
|
||||
lbkick(Logbuf *lb)
|
||||
{
|
||||
char *s;
|
||||
int n;
|
||||
Req *r;
|
||||
|
||||
while(lb->wait && lb->rp != lb->wp){
|
||||
r = lb->wait;
|
||||
lb->wait = r->aux;
|
||||
if(lb->wait == nil)
|
||||
lb->waitlast = &lb->wait;
|
||||
r->aux = nil;
|
||||
if(r->ifcall.count < 5){
|
||||
respond(r, "factotum: read request count too short");
|
||||
continue;
|
||||
}
|
||||
s = lb->msg[lb->rp];
|
||||
lb->msg[lb->rp] = nil;
|
||||
if(++lb->rp == nelem(lb->msg))
|
||||
lb->rp = 0;
|
||||
n = r->ifcall.count;
|
||||
if(n < strlen(s)+1+1){
|
||||
memmove(r->ofcall.data, s, n-5);
|
||||
n -= 5;
|
||||
r->ofcall.data[n] = '\0';
|
||||
/* look for first byte of UTF-8 sequence by skipping continuation bytes */
|
||||
while(n>0 && (r->ofcall.data[--n]&0xC0)==0x80)
|
||||
;
|
||||
strcpy(r->ofcall.data+n, "...\n");
|
||||
}else{
|
||||
strcpy(r->ofcall.data, s);
|
||||
strcat(r->ofcall.data, "\n");
|
||||
}
|
||||
r->ofcall.count = strlen(r->ofcall.data);
|
||||
free(s);
|
||||
respond(r, nil);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
lbread(Logbuf *lb, Req *r)
|
||||
{
|
||||
if(lb->waitlast == nil)
|
||||
lb->waitlast = &lb->wait;
|
||||
*(lb->waitlast) = r;
|
||||
lb->waitlast = (Req**)&r->aux;
|
||||
r->aux = nil;
|
||||
lbkick(lb);
|
||||
}
|
||||
|
||||
void
|
||||
lbflush(Logbuf *lb, Req *r)
|
||||
{
|
||||
Req **l;
|
||||
|
||||
for(l=&lb->wait; *l; l=(Req**)&(*l)->aux){
|
||||
if(*l == r){
|
||||
*l = r->aux;
|
||||
r->aux = nil;
|
||||
if(*l == nil)
|
||||
lb->waitlast = l;
|
||||
closereq(r);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
lbappend(Logbuf *lb, char *fmt, ...)
|
||||
{
|
||||
va_list arg;
|
||||
|
||||
va_start(arg, fmt);
|
||||
lbvappend(lb, fmt, arg);
|
||||
va_end(arg);
|
||||
}
|
||||
|
||||
void
|
||||
lbvappend(Logbuf *lb, char *fmt, va_list arg)
|
||||
{
|
||||
char *s;
|
||||
|
||||
s = smprint(fmt, arg);
|
||||
if(s == nil)
|
||||
sysfatal("out of memory");
|
||||
if(lb->msg[lb->wp])
|
||||
free(lb->msg[lb->wp]);
|
||||
lb->msg[lb->wp] = s;
|
||||
if(++lb->wp == nelem(lb->msg))
|
||||
lb->wp = 0;
|
||||
lbkick(lb);
|
||||
}
|
||||
|
||||
Logbuf logbuf;
|
||||
|
||||
void
|
||||
logread(Req *r)
|
||||
{
|
||||
lbread(&logbuf, r);
|
||||
}
|
||||
|
||||
void
|
||||
logflush(Req *r)
|
||||
{
|
||||
lbflush(&logbuf, r);
|
||||
}
|
||||
|
||||
void
|
||||
flog(char *fmt, ...)
|
||||
{
|
||||
va_list arg;
|
||||
|
||||
va_start(arg, fmt);
|
||||
lbvappend(&logbuf, fmt, arg);
|
||||
va_end(arg);
|
||||
}
|
||||
|
||||
163
src/cmd/factotum/main.c
Normal file
163
src/cmd/factotum/main.c
Normal file
|
|
@ -0,0 +1,163 @@
|
|||
#include "std.h"
|
||||
#include "dat.h"
|
||||
|
||||
int debug;
|
||||
char *factname = "factotum";
|
||||
char *service = nil;
|
||||
char *owner;
|
||||
char *authaddr;
|
||||
void gflag(char*);
|
||||
|
||||
void
|
||||
usage(void)
|
||||
{
|
||||
fprint(2, "usage: factotum [-Dd] [-a authaddr] [-m mtpt]\n");
|
||||
fprint(2, " or factotum -g keypattern\n");
|
||||
fprint(2, " or factotum -g 'badkeyattr\nmsg\nkeypattern'");
|
||||
exits("usage");
|
||||
}
|
||||
|
||||
void
|
||||
threadmain(int argc, char *argv[])
|
||||
{
|
||||
char *mtpt;
|
||||
|
||||
mtpt = "/mnt";
|
||||
owner = getuser();
|
||||
quotefmtinstall();
|
||||
fmtinstall('A', attrfmt);
|
||||
fmtinstall('H', encodefmt);
|
||||
fmtinstall('N', attrnamefmt);
|
||||
|
||||
if(argc == 3 && strcmp(argv[1], "-g") == 0){
|
||||
gflag(argv[2]);
|
||||
exits(nil);
|
||||
}
|
||||
|
||||
ARGBEGIN{
|
||||
default:
|
||||
usage();
|
||||
case 'D':
|
||||
chatty9p++;
|
||||
break;
|
||||
case 'a':
|
||||
authaddr = EARGF(usage());
|
||||
break;
|
||||
case 'g':
|
||||
usage();
|
||||
case 'm':
|
||||
mtpt = EARGF(usage());
|
||||
break;
|
||||
case 's':
|
||||
service = EARGF(usage());
|
||||
break;
|
||||
}ARGEND
|
||||
|
||||
if(argc != 0)
|
||||
usage();
|
||||
|
||||
threadpostmountsrv(&fs, service, mtpt, MBEFORE);
|
||||
threadexits(nil);
|
||||
}
|
||||
|
||||
/*
|
||||
* prompt user for a key. don't care about memory leaks, runs standalone
|
||||
*/
|
||||
static Attr*
|
||||
promptforkey(int fd, char *params)
|
||||
{
|
||||
char *v;
|
||||
Attr *a, *attr;
|
||||
char *def;
|
||||
|
||||
attr = _parseattr(params);
|
||||
fprint(fd, "!adding key:");
|
||||
for(a=attr; a; a=a->next)
|
||||
if(a->type != AttrQuery && a->name[0] != '!')
|
||||
fprint(fd, " %q=%q", a->name, a->val);
|
||||
fprint(fd, "\n");
|
||||
|
||||
for(a=attr; a; a=a->next){
|
||||
v = a->name;
|
||||
if(a->type != AttrQuery || v[0]=='!')
|
||||
continue;
|
||||
def = nil;
|
||||
if(strcmp(v, "user") == 0)
|
||||
def = getuser();
|
||||
a->val = readcons(v, def, 0);
|
||||
if(a->val == nil)
|
||||
sysfatal("user terminated key input");
|
||||
a->type = AttrNameval;
|
||||
}
|
||||
for(a=attr; a; a=a->next){
|
||||
v = a->name;
|
||||
if(a->type != AttrQuery || v[0]!='!')
|
||||
continue;
|
||||
def = nil;
|
||||
if(strcmp(v+1, "user") == 0)
|
||||
def = getuser();
|
||||
a->val = readcons(v+1, def, 1);
|
||||
if(a->val == nil)
|
||||
sysfatal("user terminated key input");
|
||||
a->type = AttrNameval;
|
||||
}
|
||||
fprint(fd, "!\n");
|
||||
close(fd);
|
||||
return attr;
|
||||
}
|
||||
|
||||
/*
|
||||
* send a key to the mounted factotum
|
||||
*/
|
||||
static int
|
||||
sendkey(Attr *attr)
|
||||
{
|
||||
int fd, rv;
|
||||
char buf[1024];
|
||||
|
||||
fd = open("/mnt/factotum/ctl", ORDWR);
|
||||
if(fd < 0)
|
||||
sysfatal("opening /mnt/factotum/ctl: %r");
|
||||
rv = fprint(fd, "key %A\n", attr);
|
||||
read(fd, buf, sizeof buf);
|
||||
close(fd);
|
||||
return rv;
|
||||
}
|
||||
|
||||
static void
|
||||
askuser(int fd, char *params)
|
||||
{
|
||||
Attr *attr;
|
||||
|
||||
attr = promptforkey(fd, params);
|
||||
if(attr == nil)
|
||||
sysfatal("no key supplied");
|
||||
if(sendkey(attr) < 0)
|
||||
sysfatal("sending key to factotum: %r");
|
||||
}
|
||||
|
||||
void
|
||||
gflag(char *s)
|
||||
{
|
||||
char *f[4];
|
||||
int nf;
|
||||
int fd;
|
||||
|
||||
if((fd = open("/dev/cons", ORDWR)) < 0)
|
||||
sysfatal("open /dev/cons: %r");
|
||||
|
||||
nf = getfields(s, f, nelem(f), 0, "\n");
|
||||
if(nf == 1){ /* needkey or old badkey */
|
||||
fprint(fd, "\n");
|
||||
askuser(fd, s);
|
||||
exits(nil);
|
||||
}
|
||||
if(nf == 3){ /* new badkey */
|
||||
fprint(fd, "\n");
|
||||
fprint(fd, "!replace: %s\n", f[0]);
|
||||
fprint(fd, "!because: %s\n", f[1]);
|
||||
askuser(fd, f[2]);
|
||||
exits(nil);
|
||||
}
|
||||
usage();
|
||||
}
|
||||
34
src/cmd/factotum/mkfile
Normal file
34
src/cmd/factotum/mkfile
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
PLAN9=../../..
|
||||
<$PLAN9/src/mkhdr
|
||||
|
||||
TARG=factotum
|
||||
PROTO=\
|
||||
apop.$O\
|
||||
chap.$O\
|
||||
p9any.$O\
|
||||
p9sk1.$O\
|
||||
|
||||
OFILES=\
|
||||
$PROTO\
|
||||
attr.$O\
|
||||
confirm.$O\
|
||||
conv.$O\
|
||||
ctl.$O\
|
||||
fs.$O\
|
||||
key.$O\
|
||||
log.$O\
|
||||
main.$O\
|
||||
plan9.$O\
|
||||
proto.$O\
|
||||
rpc.$O\
|
||||
util.$O\
|
||||
xio.$O\
|
||||
|
||||
HFILES=dat.h
|
||||
|
||||
SHORTLIB=auth authsrv sec mp 9p thread 9
|
||||
<$PLAN9/src/mkone
|
||||
|
||||
$O.test: test.$O
|
||||
$LD -o $target $prereq
|
||||
|
||||
266
src/cmd/factotum/p9any.c
Normal file
266
src/cmd/factotum/p9any.c
Normal file
|
|
@ -0,0 +1,266 @@
|
|||
#include "std.h"
|
||||
#include "dat.h"
|
||||
|
||||
/*
|
||||
* p9any - protocol negotiator
|
||||
*
|
||||
* Protocol:
|
||||
* S->C: v.2 proto@dom proto@dom proto@dom... NUL
|
||||
* C->S: proto dom NUL
|
||||
* [negotiated proto continues]
|
||||
*/
|
||||
|
||||
static Proto* okproto[] =
|
||||
{
|
||||
&p9sk1,
|
||||
nil,
|
||||
};
|
||||
|
||||
static int
|
||||
rolecall(Role *r, char *name, Conv *c)
|
||||
{
|
||||
for(; r->name; r++)
|
||||
if(strcmp(r->name, name) == 0)
|
||||
return (*r->fn)(c);
|
||||
werrstr("unknown role");
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int
|
||||
hasnul(void *v, int n)
|
||||
{
|
||||
char *c;
|
||||
|
||||
c = v;
|
||||
if(n > 0 && c[n-1] == '\0')
|
||||
return n;
|
||||
else
|
||||
return n+1;
|
||||
}
|
||||
|
||||
static int
|
||||
p9anyserver(Conv *c)
|
||||
{
|
||||
char *s, *dom;
|
||||
int i, j, n, m, ret;
|
||||
char *tok[3];
|
||||
Attr *attr;
|
||||
Key *k;
|
||||
|
||||
ret = -1;
|
||||
s = estrdup("v.2");
|
||||
n = 0;
|
||||
attr = delattr(copyattr(c->attr), "proto");
|
||||
|
||||
for(i=0; i<ring.nkey; i++){
|
||||
k = ring.key[i];
|
||||
for(j=0; okproto[j]; j++)
|
||||
if(k->proto == okproto[j]
|
||||
&& (dom = strfindattr(k->attr, "dom")) != nil
|
||||
&& matchattr(attr, k->attr, k->privattr)){
|
||||
s = estrappend(s, " %s@%s", k->proto->name, dom);
|
||||
n++;
|
||||
}
|
||||
}
|
||||
|
||||
if(n == 0){
|
||||
werrstr("no valid keys");
|
||||
goto out;
|
||||
}
|
||||
|
||||
c->state = "write offer";
|
||||
if(convwrite(c, s, strlen(s)+1) < 0)
|
||||
goto out;
|
||||
free(s);
|
||||
s = nil;
|
||||
|
||||
c->state = "read choice";
|
||||
if(convreadfn(c, hasnul, &s) < 0)
|
||||
goto out;
|
||||
|
||||
m = tokenize(s, tok, nelem(tok));
|
||||
if(m != 2){
|
||||
werrstr("bad protocol message");
|
||||
goto out;
|
||||
}
|
||||
|
||||
for(i=0; okproto[i]; i++)
|
||||
if(strcmp(okproto[i]->name, tok[0]) == 0)
|
||||
break;
|
||||
if(!okproto[i]){
|
||||
werrstr("bad chosen protocol %q", tok[0]);
|
||||
goto out;
|
||||
}
|
||||
|
||||
c->state = "write ok";
|
||||
if(convwrite(c, "OK\0", 3) < 0)
|
||||
goto out;
|
||||
|
||||
c->state = "start choice";
|
||||
attr = addattr(attr, "proto=%q dom=%q", tok[0], tok[1]);
|
||||
free(c->attr);
|
||||
c->attr = attr;
|
||||
attr = nil;
|
||||
c->proto = okproto[i];
|
||||
|
||||
if(rolecall(c->proto->roles, "server", c) < 0){
|
||||
werrstr("%s: %r", tok[0]);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
|
||||
out:
|
||||
free(s);
|
||||
freeattr(attr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
p9anyclient(Conv *c)
|
||||
{
|
||||
char *s, **f, *tok[20], ok[3], *q, *user, *dom;
|
||||
int i, n, ret, version;
|
||||
Key *k;
|
||||
Attr *attr;
|
||||
Proto *p;
|
||||
|
||||
ret = -1;
|
||||
s = nil;
|
||||
k = nil;
|
||||
|
||||
user = strfindattr(c->attr, "user");
|
||||
dom = strfindattr(c->attr, "dom");
|
||||
|
||||
/*
|
||||
* if the user is the factotum owner, any key will do.
|
||||
* if not, then if we have a speakfor key,
|
||||
* we will only vouch for the user's local identity.
|
||||
*
|
||||
* this logic is duplicated in p9sk1.c
|
||||
*/
|
||||
attr = delattr(copyattr(c->attr), "role");
|
||||
attr = delattr(attr, "proto");
|
||||
if(strcmp(c->sysuser, owner) == 0)
|
||||
attr = addattr(attr, "role=client");
|
||||
else if(user==nil || strcmp(c->sysuser, user)==0){
|
||||
attr = delattr(attr, "user");
|
||||
attr = addattr(attr, "role=speakfor");
|
||||
}else{
|
||||
werrstr("will not authenticate for %q as %q", c->sysuser, user);
|
||||
goto out;
|
||||
}
|
||||
|
||||
c->state = "read offer";
|
||||
if(convreadfn(c, hasnul, &s) < 0)
|
||||
goto out;
|
||||
|
||||
c->state = "look for keys";
|
||||
n = tokenize(s, tok, nelem(tok));
|
||||
f = tok;
|
||||
version = 1;
|
||||
if(n > 0 && memcmp(f[0], "v.", 2) == 0){
|
||||
version = atoi(f[0]+2);
|
||||
if(version != 2){
|
||||
werrstr("unknown p9any version: %s", f[0]);
|
||||
goto out;
|
||||
}
|
||||
f++;
|
||||
n--;
|
||||
}
|
||||
|
||||
/* look for keys that don't need confirmation */
|
||||
for(i=0; i<n; i++){
|
||||
if((q = strchr(f[i], '@')) == nil)
|
||||
continue;
|
||||
if(dom && strcmp(q+1, dom) != 0)
|
||||
continue;
|
||||
*q++ = '\0';
|
||||
if((k = keylookup("%A proto=%q dom=%q", attr, f[i], q))
|
||||
&& strfindattr(k->attr, "confirm") == nil)
|
||||
goto found;
|
||||
*--q = '@';
|
||||
}
|
||||
|
||||
/* look for any keys at all */
|
||||
for(i=0; i<n; i++){
|
||||
if((q = strchr(f[i], '@')) == nil)
|
||||
continue;
|
||||
if(dom && strcmp(q+1, dom) != 0)
|
||||
continue;
|
||||
*q++ = '\0';
|
||||
if(k = keyfetch(c, "%A proto=%q dom=%q", attr, f[i], q))
|
||||
goto found;
|
||||
*--q = '@';
|
||||
}
|
||||
|
||||
/* ask for new keys */
|
||||
c->state = "ask for keys";
|
||||
for(i=0; i<n; i++){
|
||||
if((q = strchr(f[i], '@')) == nil)
|
||||
continue;
|
||||
if(dom && strcmp(q+1, dom) != 0)
|
||||
continue;
|
||||
*q++ = '\0';
|
||||
p = protolookup(f[i]);
|
||||
if(p == nil || p->keyprompt == nil){
|
||||
*--q = '@';
|
||||
continue;
|
||||
}
|
||||
if(k = keyfetch(c, "%A proto=%q dom=%q %s", attr, f[i], q, p->keyprompt))
|
||||
goto found;
|
||||
*--q = '@';
|
||||
}
|
||||
|
||||
/* nothing worked */
|
||||
werrstr("unable to find common key");
|
||||
goto out;
|
||||
|
||||
found:
|
||||
/* f[i] is the chosen protocol, q the chosen domain */
|
||||
attr = addattr(attr, "proto=%q dom=%q", f[i], q);
|
||||
c->state = "write choice";
|
||||
/* have a key: go for it */
|
||||
if(convprint(c, "%q %q", f[i], q) < 0
|
||||
|| convwrite(c, "\0", 1) < 0)
|
||||
goto out;
|
||||
|
||||
if(version == 2){
|
||||
c->state = "read ok";
|
||||
if(convread(c, ok, 3) < 0 || memcmp(ok, "OK\0", 3) != 0)
|
||||
goto out;
|
||||
}
|
||||
|
||||
c->state = "start choice";
|
||||
c->proto = protolookup(f[i]);
|
||||
freeattr(c->attr);
|
||||
c->attr = attr;
|
||||
attr = nil;
|
||||
|
||||
if(rolecall(c->proto->roles, "client", c) < 0){
|
||||
werrstr("%s: %r", c->proto->name);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
|
||||
out:
|
||||
keyclose(k);
|
||||
freeattr(attr);
|
||||
free(s);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static Role
|
||||
p9anyroles[] =
|
||||
{
|
||||
"client", p9anyclient,
|
||||
"server", p9anyserver,
|
||||
0
|
||||
};
|
||||
|
||||
Proto p9any = {
|
||||
.name= "p9any",
|
||||
.roles= p9anyroles,
|
||||
};
|
||||
|
||||
545
src/cmd/factotum/p9cr.c
Normal file
545
src/cmd/factotum/p9cr.c
Normal file
|
|
@ -0,0 +1,545 @@
|
|||
/*
|
||||
* p9cr, vnc - one-sided challenge/response authentication
|
||||
*
|
||||
* Protocol:
|
||||
*
|
||||
* C -> S: user
|
||||
* S -> C: challenge
|
||||
* C -> S: response
|
||||
* S -> C: ok or bad
|
||||
*
|
||||
* Note that this is the protocol between factotum and the local
|
||||
* program, not between the two factotums. The information
|
||||
* exchanged here is wrapped in other protocols by the local
|
||||
* programs.
|
||||
*/
|
||||
|
||||
#include "std.h"
|
||||
#include "dat.h"
|
||||
|
||||
static int
|
||||
p9crcheck(Key *k)
|
||||
{
|
||||
if(!strfindattr(k->attr, "user") || !strfindattr(k->privattr, "!password")){
|
||||
werrstr("need user and !password attributes");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
p9crclient(Conv *c)
|
||||
{
|
||||
char *chal, *pw, *res, *user;
|
||||
int astype, nchal, npw, ntry, ret;
|
||||
uchar resp[MD5dlen];
|
||||
Attr *attr;
|
||||
DigestState *ds;
|
||||
Key *k;
|
||||
|
||||
chal = nil;
|
||||
k = nil;
|
||||
res = nil;
|
||||
ret = -1;
|
||||
attr = c->attr;
|
||||
|
||||
if(c->proto == &p9cr){
|
||||
astype = AuthChal;
|
||||
challen = NETCHLEN;
|
||||
}else if(c->proto == &vnc){
|
||||
astype = AuthVnc;
|
||||
challen = MAXCHAL;
|
||||
}else{
|
||||
werrstr("bad proto");
|
||||
goto out;
|
||||
}
|
||||
|
||||
c->state = "find key";
|
||||
k = keyfetch(c, "%A %s", attr, c->proto->keyprompt);
|
||||
if(k == nil)
|
||||
goto out;
|
||||
|
||||
for(ntry=1;; ntry++){
|
||||
if(c->attr != attr)
|
||||
freeattr(c->attr);
|
||||
c->attr = addattrs(copyattr(attr), k->attr);
|
||||
if((pw = strfindattr(k->privattr, "!password")) == nil){
|
||||
werrstr("key has no !password (cannot happen)");
|
||||
goto out;
|
||||
}
|
||||
npw = strlen(pw);
|
||||
|
||||
if((user = strfindattr(k->attr, "user")) == nil){
|
||||
werrstr("key has no user (cannot happen)");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if(convprint(c, "%s", user) < 0)
|
||||
goto out;
|
||||
|
||||
if(convreadm(c, &chal) < 0)
|
||||
goto out;
|
||||
|
||||
if((nresp = (*response)(chal, resp)) < 0)
|
||||
goto out;
|
||||
|
||||
if(convwrite(c, resp, nresp) < 0)
|
||||
goto out;
|
||||
|
||||
if(convreadm(c, &res) < 0)
|
||||
goto out;
|
||||
|
||||
if(strcmp(res, "ok") == 0)
|
||||
break;
|
||||
|
||||
if((k = keyreplace(c, k, "%s", res)) == nil){
|
||||
c->state = "auth failed";
|
||||
werrstr("%s", res);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
werrstr("succeeded");
|
||||
ret = 0;
|
||||
|
||||
out:
|
||||
keyclose(k);
|
||||
free(chal);
|
||||
if(c->attr != attr)
|
||||
freeattr(attr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
p9crserver(Conv *c)
|
||||
{
|
||||
char chal[APOPCHALLEN], *user, *resp;
|
||||
ServerState s;
|
||||
int astype, ret;
|
||||
Attr *a;
|
||||
|
||||
ret = -1;
|
||||
user = nil;
|
||||
resp = nil;
|
||||
memset(&s, 0, sizeof s);
|
||||
s.asfd = -1;
|
||||
|
||||
if(c->proto == &apop)
|
||||
astype = AuthApop;
|
||||
else if(c->proto == &cram)
|
||||
astype = AuthCram;
|
||||
else{
|
||||
werrstr("bad proto");
|
||||
goto out;
|
||||
}
|
||||
|
||||
c->state = "find key";
|
||||
if((s.k = plan9authkey(c->attr)) == nil)
|
||||
goto out;
|
||||
|
||||
a = copyattr(s.k->attr);
|
||||
a = delattr(a, "proto");
|
||||
c->attr = addattrs(c->attr, a);
|
||||
freeattr(a);
|
||||
|
||||
c->state = "authdial";
|
||||
s.hostid = strfindattr(s.k->attr, "user");
|
||||
s.dom = strfindattr(s.k->attr, "dom");
|
||||
if((s.asfd = xioauthdial(nil, s.dom)) < 0){
|
||||
werrstr("authdial %s: %r", s.dom);
|
||||
goto out;
|
||||
}
|
||||
|
||||
c->state = "authchal";
|
||||
if(p9crchal(&s, astype, chal) < 0)
|
||||
goto out;
|
||||
|
||||
c->state = "write challenge";
|
||||
if(convprint(c, "%s", chal) < 0)
|
||||
goto out;
|
||||
|
||||
for(;;){
|
||||
c->state = "read user";
|
||||
if(convreadm(c, &user) < 0)
|
||||
goto out;
|
||||
|
||||
c->state = "read response";
|
||||
if(convreadm(c, &resp) < 0)
|
||||
goto out;
|
||||
|
||||
c->state = "authwrite";
|
||||
switch(apopresp(&s, user, resp)){
|
||||
case -1:
|
||||
goto out;
|
||||
case 0:
|
||||
c->state = "write status";
|
||||
if(convprint(c, "bad authentication failed") < 0)
|
||||
goto out;
|
||||
break;
|
||||
case 1:
|
||||
c->state = "write status";
|
||||
if(convprint(c, "ok") < 0)
|
||||
goto out;
|
||||
goto ok;
|
||||
}
|
||||
free(user);
|
||||
free(resp);
|
||||
user = nil;
|
||||
resp = nil;
|
||||
}
|
||||
|
||||
ok:
|
||||
ret = 0;
|
||||
c->attr = addcap(c->attr, c->sysuser, &s.t);
|
||||
|
||||
out:
|
||||
keyclose(s.k);
|
||||
free(user);
|
||||
free(resp);
|
||||
// xioclose(s.asfd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
enum
|
||||
{
|
||||
MAXCHAL = 64,
|
||||
};
|
||||
|
||||
typedef struct State State;
|
||||
struct State
|
||||
{
|
||||
Key *key;
|
||||
int astype;
|
||||
int asfd;
|
||||
Ticket t;
|
||||
Ticketreq tr;
|
||||
char chal[MAXCHAL];
|
||||
int challen;
|
||||
char resp[MAXCHAL];
|
||||
int resplen;
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
CNeedChal,
|
||||
CHaveResp,
|
||||
|
||||
SHaveChal,
|
||||
SNeedResp,
|
||||
|
||||
Maxphase,
|
||||
};
|
||||
|
||||
static char *phasenames[Maxphase] =
|
||||
{
|
||||
[CNeedChal] "CNeedChal",
|
||||
[CHaveResp] "CHaveResp",
|
||||
|
||||
[SHaveChal] "SHaveChal",
|
||||
[SNeedResp] "SNeedResp",
|
||||
};
|
||||
|
||||
static void
|
||||
p9crclose(Fsstate *fss)
|
||||
{
|
||||
State *s;
|
||||
|
||||
s = fss->ps;
|
||||
if(s->asfd >= 0){
|
||||
close(s->asfd);
|
||||
s->asfd = -1;
|
||||
}
|
||||
free(s);
|
||||
}
|
||||
|
||||
static int getchal(State*, Fsstate*);
|
||||
|
||||
static int
|
||||
p9crinit(Proto *p, Fsstate *fss)
|
||||
{
|
||||
int iscli, ret;
|
||||
char *user;
|
||||
State *s;
|
||||
Attr *attr;
|
||||
|
||||
if((iscli = isclient(_str_findattr(fss->attr, "role"))) < 0)
|
||||
return failure(fss, nil);
|
||||
|
||||
s = emalloc(sizeof(*s));
|
||||
s->asfd = -1;
|
||||
if(p == &p9cr){
|
||||
s->astype = AuthChal;
|
||||
s->challen = NETCHLEN;
|
||||
}else if(p == &vnc){
|
||||
s->astype = AuthVNC;
|
||||
s->challen = Maxchal;
|
||||
}else
|
||||
abort();
|
||||
|
||||
if(iscli){
|
||||
fss->phase = CNeedChal;
|
||||
if(p == &p9cr)
|
||||
attr = setattr(_copyattr(fss->attr), "proto=p9sk1");
|
||||
else
|
||||
attr = nil;
|
||||
ret = findkey(&s->key, fss, Kuser, 0, attr ? attr : fss->attr,
|
||||
"role=client %s", p->keyprompt);
|
||||
_freeattr(attr);
|
||||
if(ret != RpcOk){
|
||||
free(s);
|
||||
return ret;
|
||||
}
|
||||
fss->ps = s;
|
||||
}else{
|
||||
if((ret = findp9authkey(&s->key, fss)) != RpcOk){
|
||||
free(s);
|
||||
return ret;
|
||||
}
|
||||
if((user = _str_findattr(fss->attr, "user")) == nil){
|
||||
free(s);
|
||||
return failure(fss, "no user name specified in start msg");
|
||||
}
|
||||
if(strlen(user) >= sizeof s->tr.uid){
|
||||
free(s);
|
||||
return failure(fss, "user name too long");
|
||||
}
|
||||
fss->ps = s;
|
||||
strcpy(s->tr.uid, user);
|
||||
ret = getchal(s, fss);
|
||||
if(ret != RpcOk){
|
||||
p9crclose(fss); /* frees s */
|
||||
fss->ps = nil;
|
||||
}
|
||||
}
|
||||
fss->phasename = phasenames;
|
||||
fss->maxphase = Maxphase;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
p9crread(Fsstate *fss, void *va, uint *n)
|
||||
{
|
||||
int m;
|
||||
State *s;
|
||||
|
||||
s = fss->ps;
|
||||
switch(fss->phase){
|
||||
default:
|
||||
return phaseerror(fss, "read");
|
||||
|
||||
case CHaveResp:
|
||||
if(s->resplen < *n)
|
||||
*n = s->resplen;
|
||||
memmove(va, s->resp, *n);
|
||||
fss->phase = Established;
|
||||
return RpcOk;
|
||||
|
||||
case SHaveChal:
|
||||
if(s->astype == AuthChal)
|
||||
m = strlen(s->chal); /* ascii string */
|
||||
else
|
||||
m = s->challen; /* fixed length binary */
|
||||
if(m > *n)
|
||||
return toosmall(fss, m);
|
||||
*n = m;
|
||||
memmove(va, s->chal, m);
|
||||
fss->phase = SNeedResp;
|
||||
return RpcOk;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
p9response(Fsstate *fss, State *s)
|
||||
{
|
||||
char key[DESKEYLEN];
|
||||
uchar buf[8];
|
||||
ulong chal;
|
||||
char *pw;
|
||||
|
||||
pw = _str_findattr(s->key->privattr, "!password");
|
||||
if(pw == nil)
|
||||
return failure(fss, "vncresponse cannot happen");
|
||||
passtokey(key, pw);
|
||||
memset(buf, 0, 8);
|
||||
sprint((char*)buf, "%d", atoi(s->chal));
|
||||
if(encrypt(key, buf, 8) < 0)
|
||||
return failure(fss, "can't encrypt response");
|
||||
chal = (buf[0]<<24)+(buf[1]<<16)+(buf[2]<<8)+buf[3];
|
||||
s->resplen = snprint(s->resp, sizeof s->resp, "%.8lux", chal);
|
||||
return RpcOk;
|
||||
}
|
||||
|
||||
static uchar tab[256];
|
||||
|
||||
/* VNC reverses the bits of each byte before using as a des key */
|
||||
static void
|
||||
mktab(void)
|
||||
{
|
||||
int i, j, k;
|
||||
static int once;
|
||||
|
||||
if(once)
|
||||
return;
|
||||
once = 1;
|
||||
|
||||
for(i=0; i<256; i++) {
|
||||
j=i;
|
||||
tab[i] = 0;
|
||||
for(k=0; k<8; k++) {
|
||||
tab[i] = (tab[i]<<1) | (j&1);
|
||||
j >>= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
vncaddkey(Key *k)
|
||||
{
|
||||
uchar *p;
|
||||
char *s;
|
||||
|
||||
k->priv = emalloc(8+1);
|
||||
if(s = _str_findattr(k->privattr, "!password")){
|
||||
mktab();
|
||||
memset(k->priv, 0, 8+1);
|
||||
strncpy((char*)k->priv, s, 8);
|
||||
for(p=k->priv; *p; p++)
|
||||
*p = tab[*p];
|
||||
}else{
|
||||
werrstr("no key data");
|
||||
return -1;
|
||||
}
|
||||
return replacekey(k);
|
||||
}
|
||||
|
||||
static void
|
||||
vncclosekey(Key *k)
|
||||
{
|
||||
free(k->priv);
|
||||
}
|
||||
|
||||
static int
|
||||
vncresponse(Fsstate*, State *s)
|
||||
{
|
||||
DESstate des;
|
||||
|
||||
memmove(s->resp, s->chal, sizeof s->chal);
|
||||
setupDESstate(&des, s->key->priv, nil);
|
||||
desECBencrypt((uchar*)s->resp, s->challen, &des);
|
||||
s->resplen = s->challen;
|
||||
return RpcOk;
|
||||
}
|
||||
|
||||
static int
|
||||
p9crwrite(Fsstate *fss, void *va, uint n)
|
||||
{
|
||||
char tbuf[TICKETLEN+AUTHENTLEN];
|
||||
State *s;
|
||||
char *data = va;
|
||||
Authenticator a;
|
||||
char resp[Maxchal];
|
||||
int ret;
|
||||
|
||||
s = fss->ps;
|
||||
switch(fss->phase){
|
||||
default:
|
||||
return phaseerror(fss, "write");
|
||||
|
||||
case CNeedChal:
|
||||
if(n >= sizeof(s->chal))
|
||||
return failure(fss, Ebadarg);
|
||||
memset(s->chal, 0, sizeof s->chal);
|
||||
memmove(s->chal, data, n);
|
||||
s->challen = n;
|
||||
|
||||
if(s->astype == AuthChal)
|
||||
ret = p9response(fss, s);
|
||||
else
|
||||
ret = vncresponse(fss, s);
|
||||
if(ret != RpcOk)
|
||||
return ret;
|
||||
fss->phase = CHaveResp;
|
||||
return RpcOk;
|
||||
|
||||
case SNeedResp:
|
||||
/* send response to auth server and get ticket */
|
||||
if(n > sizeof(resp))
|
||||
return failure(fss, Ebadarg);
|
||||
memset(resp, 0, sizeof resp);
|
||||
memmove(resp, data, n);
|
||||
if(write(s->asfd, resp, s->challen) != s->challen)
|
||||
return failure(fss, Easproto);
|
||||
|
||||
/* get ticket plus authenticator from auth server */
|
||||
if(_asrdresp(s->asfd, tbuf, TICKETLEN+AUTHENTLEN) < 0)
|
||||
return failure(fss, nil);
|
||||
|
||||
/* check ticket */
|
||||
convM2T(tbuf, &s->t, s->key->priv);
|
||||
if(s->t.num != AuthTs
|
||||
|| memcmp(s->t.chal, s->tr.chal, sizeof(s->t.chal)) != 0)
|
||||
return failure(fss, Easproto);
|
||||
convM2A(tbuf+TICKETLEN, &a, s->t.key);
|
||||
if(a.num != AuthAc
|
||||
|| memcmp(a.chal, s->tr.chal, sizeof(a.chal)) != 0
|
||||
|| a.id != 0)
|
||||
return failure(fss, Easproto);
|
||||
|
||||
fss->haveai = 1;
|
||||
fss->ai.cuid = s->t.cuid;
|
||||
fss->ai.suid = s->t.suid;
|
||||
fss->ai.nsecret = 0;
|
||||
fss->ai.secret = nil;
|
||||
fss->phase = Established;
|
||||
return RpcOk;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
getchal(State *s, Fsstate *fss)
|
||||
{
|
||||
char trbuf[TICKREQLEN];
|
||||
int n;
|
||||
|
||||
safecpy(s->tr.hostid, _str_findattr(s->key->attr, "user"), sizeof(s->tr.hostid));
|
||||
safecpy(s->tr.authdom, _str_findattr(s->key->attr, "dom"), sizeof(s->tr.authdom));
|
||||
s->tr.type = s->astype;
|
||||
convTR2M(&s->tr, trbuf);
|
||||
|
||||
/* get challenge from auth server */
|
||||
s->asfd = _authdial(nil, _str_findattr(s->key->attr, "dom"));
|
||||
if(s->asfd < 0)
|
||||
return failure(fss, Easproto);
|
||||
if(write(s->asfd, trbuf, TICKREQLEN) != TICKREQLEN)
|
||||
return failure(fss, Easproto);
|
||||
n = _asrdresp(s->asfd, s->chal, s->challen);
|
||||
if(n <= 0){
|
||||
if(n == 0)
|
||||
werrstr("_asrdresp short read");
|
||||
return failure(fss, nil);
|
||||
}
|
||||
s->challen = n;
|
||||
fss->phase = SHaveChal;
|
||||
return RpcOk;
|
||||
}
|
||||
|
||||
Proto p9cr =
|
||||
{
|
||||
.name= "p9cr",
|
||||
.init= p9crinit,
|
||||
.write= p9crwrite,
|
||||
.read= p9crread,
|
||||
.close= p9crclose,
|
||||
.keyprompt= "user? !password?",
|
||||
};
|
||||
|
||||
Proto vnc =
|
||||
{
|
||||
.name= "vnc",
|
||||
.init= p9crinit,
|
||||
.write= p9crwrite,
|
||||
.read= p9crread,
|
||||
.close= p9crclose,
|
||||
.keyprompt= "!password?",
|
||||
.addkey= vncaddkey,
|
||||
};
|
||||
352
src/cmd/factotum/p9sk1.c
Normal file
352
src/cmd/factotum/p9sk1.c
Normal file
|
|
@ -0,0 +1,352 @@
|
|||
/*
|
||||
* p9sk1, p9sk2 - Plan 9 secret (private) key authentication.
|
||||
* p9sk2 is an incomplete flawed variant of p9sk1.
|
||||
*
|
||||
* Client protocol:
|
||||
* write challenge[challen] (p9sk1 only)
|
||||
* read tickreq[tickreqlen]
|
||||
* write ticket[ticketlen]
|
||||
* read authenticator[authentlen]
|
||||
*
|
||||
* Server protocol:
|
||||
* read challenge[challen] (p9sk1 only)
|
||||
* write tickreq[tickreqlen]
|
||||
* read ticket[ticketlen]
|
||||
* write authenticator[authentlen]
|
||||
*/
|
||||
|
||||
#include "std.h"
|
||||
#include "dat.h"
|
||||
|
||||
static int gettickets(Ticketreq*, char*, Key*);
|
||||
|
||||
#define max(a, b) ((a) > (b) ? (a) : (b))
|
||||
enum
|
||||
{
|
||||
MAXAUTH = max(TICKREQLEN, TICKETLEN+max(TICKETLEN, AUTHENTLEN))
|
||||
};
|
||||
|
||||
static int
|
||||
p9skclient(Conv *c)
|
||||
{
|
||||
char *user;
|
||||
char cchal[CHALLEN];
|
||||
uchar secret[8];
|
||||
char buf[MAXAUTH];
|
||||
int speakfor, ret;
|
||||
Attr *a;
|
||||
Authenticator au;
|
||||
Key *k;
|
||||
Ticket t;
|
||||
Ticketreq tr;
|
||||
|
||||
ret = -1;
|
||||
a = nil;
|
||||
k = nil;
|
||||
|
||||
/* p9sk1: send client challenge */
|
||||
if(c->proto == &p9sk1){
|
||||
c->state = "write challenge";
|
||||
memrandom(cchal, CHALLEN);
|
||||
if(convwrite(c, cchal, CHALLEN) < 0)
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* read ticket request */
|
||||
c->state = "read tickreq";
|
||||
if(convread(c, buf, TICKREQLEN) < 0)
|
||||
goto out;
|
||||
convM2TR(buf, &tr);
|
||||
|
||||
/* p9sk2: use server challenge as client challenge */
|
||||
if(c->proto == &p9sk2)
|
||||
memmove(cchal, tr.chal, CHALLEN);
|
||||
|
||||
/*
|
||||
* find a key.
|
||||
*
|
||||
* if the user is the factotum owner, any key will do.
|
||||
* if not, then if we have a speakfor key,
|
||||
* we will only vouch for the user's local identity.
|
||||
*
|
||||
* this logic is duplicated in p9any.c
|
||||
*/
|
||||
user = strfindattr(c->attr, "user");
|
||||
a = delattr(copyattr(c->attr), "role");
|
||||
a = addattr(a, "proto=p9sk1");
|
||||
|
||||
if(strcmp(c->sysuser, owner) == 0){
|
||||
speakfor = 0;
|
||||
a = addattr(a, "proto=p9sk1 user? dom=%q", tr.authdom);
|
||||
}else if(user==nil || strcmp(c->sysuser, user)==0){
|
||||
speakfor = 1;
|
||||
a = delattr(a, "user");
|
||||
a = addattr(a, "proto=p9sk1 user? dom=%q role=speakfor", tr.authdom);
|
||||
}else{
|
||||
werrstr("will not authenticate for %q as %q", c->sysuser, user);
|
||||
goto out;
|
||||
}
|
||||
|
||||
for(;;){
|
||||
c->state = "find key";
|
||||
k = keyfetch(c, "%A", a);
|
||||
if(k == nil)
|
||||
goto out;
|
||||
|
||||
/* relay ticket request to auth server, get tickets */
|
||||
strcpy(tr.hostid, strfindattr(k->attr, "user"));
|
||||
if(speakfor)
|
||||
strcpy(tr.uid, c->sysuser);
|
||||
else
|
||||
strcpy(tr.uid, tr.hostid);
|
||||
|
||||
c->state = "get tickets";
|
||||
if(gettickets(&tr, buf, k) < 0)
|
||||
goto out;
|
||||
|
||||
convM2T(buf, &t, k->priv);
|
||||
if(t.num == AuthTc)
|
||||
break;
|
||||
|
||||
/* we don't agree with the auth server about the key; try again */
|
||||
c->state = "replace key";
|
||||
if((k = keyreplace(c, k, "key mismatch with auth server")) == nil){
|
||||
werrstr("key mismatch with auth server");
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
/* send second ticket and authenticator to server */
|
||||
c->state = "write ticket+auth";
|
||||
memmove(buf, buf+TICKETLEN, TICKETLEN);
|
||||
au.num = AuthAc;
|
||||
memmove(au.chal, tr.chal, CHALLEN);
|
||||
au.id = 0;
|
||||
convA2M(&au, buf+TICKETLEN, t.key);
|
||||
if(convwrite(c, buf, TICKETLEN+AUTHENTLEN) < 0)
|
||||
goto out;
|
||||
|
||||
/* read authenticator from server */
|
||||
c->state = "read auth";
|
||||
if(convread(c, buf, AUTHENTLEN) < 0)
|
||||
goto out;
|
||||
convM2A(buf, &au, t.key);
|
||||
if(au.num != AuthAs || memcmp(au.chal, cchal, CHALLEN) != 0 || au.id != 0){
|
||||
werrstr("server lies through his teeth");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* success */
|
||||
c->attr = addcap(c->attr, c->sysuser, &t);
|
||||
des56to64((uchar*)t.key, secret);
|
||||
c->attr = addattr(c->attr, "secret=%.8H", secret);
|
||||
ret = 0;
|
||||
|
||||
out:
|
||||
freeattr(a);
|
||||
keyclose(k);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
p9skserver(Conv *c)
|
||||
{
|
||||
char cchal[CHALLEN], buf[MAXAUTH];
|
||||
uchar secret[8];
|
||||
int ret;
|
||||
Attr *a;
|
||||
Authenticator au;
|
||||
Key *k;
|
||||
Ticketreq tr;
|
||||
Ticket t;
|
||||
|
||||
ret = -1;
|
||||
|
||||
a = addattr(copyattr(c->attr), "user? dom?");
|
||||
a = addattr(a, "user? dom? proto=p9sk1");
|
||||
if((k = keyfetch(c, "%A", a)) == nil)
|
||||
goto out;
|
||||
|
||||
/* p9sk1: read client challenge */
|
||||
if(c->proto == &p9sk1){
|
||||
if(convread(c, cchal, CHALLEN) < 0)
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* send ticket request */
|
||||
memset(&tr, 0, sizeof tr);
|
||||
tr.type = AuthTreq;
|
||||
strcpy(tr.authid, strfindattr(k->attr, "user"));
|
||||
strcpy(tr.authdom, strfindattr(k->attr, "dom"));
|
||||
memrandom(tr.chal, sizeof tr.chal);
|
||||
convTR2M(&tr, buf);
|
||||
if(convwrite(c, buf, TICKREQLEN) < 0)
|
||||
goto out;
|
||||
|
||||
/* p9sk2: use server challenge as client challenge */
|
||||
if(c->proto == &p9sk2)
|
||||
memmove(cchal, tr.chal, sizeof tr.chal);
|
||||
|
||||
/* read ticket+authenticator */
|
||||
if(convread(c, buf, TICKETLEN+AUTHENTLEN) < 0)
|
||||
goto out;
|
||||
|
||||
convM2T(buf, &t, k->priv);
|
||||
if(t.num != AuthTs || memcmp(t.chal, tr.chal, CHALLEN) != 0){
|
||||
/* BUG badkey */
|
||||
werrstr("key mismatch with auth server");
|
||||
goto out;
|
||||
}
|
||||
|
||||
convM2A(buf+TICKETLEN, &au, t.key);
|
||||
if(au.num != AuthAc || memcmp(au.chal, tr.chal, CHALLEN) != 0 || au.id != 0){
|
||||
werrstr("client lies through his teeth");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* send authenticator */
|
||||
au.num = AuthAs;
|
||||
memmove(au.chal, cchal, CHALLEN);
|
||||
convA2M(&au, buf, t.key);
|
||||
if(convwrite(c, buf, AUTHENTLEN) < 0)
|
||||
goto out;
|
||||
|
||||
/* success */
|
||||
c->attr = addcap(c->attr, c->sysuser, &t);
|
||||
des56to64((uchar*)t.key, secret);
|
||||
c->attr = addattr(c->attr, "secret=%.8H", secret);
|
||||
ret = 0;
|
||||
|
||||
out:
|
||||
freeattr(a);
|
||||
keyclose(k);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
_asgetticket(int fd, char *trbuf, char *tbuf)
|
||||
{
|
||||
if(write(fd, trbuf, TICKREQLEN) < 0){
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
return _asrdresp(fd, tbuf, 2*TICKETLEN);
|
||||
}
|
||||
static int
|
||||
getastickets(Ticketreq *tr, char *buf)
|
||||
{
|
||||
int asfd;
|
||||
int ret;
|
||||
|
||||
if((asfd = xioauthdial(nil, tr->authdom)) < 0)
|
||||
return -1;
|
||||
convTR2M(tr, buf);
|
||||
ret = xioasgetticket(asfd, buf, buf);
|
||||
xioclose(asfd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
mktickets(Ticketreq *tr, char *buf, Key *k)
|
||||
{
|
||||
Ticket t;
|
||||
|
||||
if(strcmp(tr->authid, tr->hostid) != 0)
|
||||
return -1;
|
||||
|
||||
memset(&t, 0, sizeof t);
|
||||
memmove(t.chal, tr->chal, CHALLEN);
|
||||
strcpy(t.cuid, tr->uid);
|
||||
strcpy(t.suid, tr->uid);
|
||||
memrandom(t.key, DESKEYLEN);
|
||||
t.num = AuthTc;
|
||||
convT2M(&t, buf, k->priv);
|
||||
t.num = AuthTs;
|
||||
convT2M(&t, buf+TICKETLEN, k->priv);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
gettickets(Ticketreq *tr, char *buf, Key *k)
|
||||
{
|
||||
if(getastickets(tr, buf) == 0)
|
||||
return 0;
|
||||
if(mktickets(tr, buf, k) == 0)
|
||||
return 0;
|
||||
werrstr("gettickets: %r");
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int
|
||||
p9sk1check(Key *k)
|
||||
{
|
||||
char *user, *dom, *pass;
|
||||
Ticketreq tr;
|
||||
|
||||
user = strfindattr(k->attr, "user");
|
||||
dom = strfindattr(k->attr, "dom");
|
||||
if(user==nil || dom==nil){
|
||||
werrstr("need user and dom attributes");
|
||||
return -1;
|
||||
}
|
||||
if(strlen(user) >= sizeof tr.authid){
|
||||
werrstr("user name too long");
|
||||
return -1;
|
||||
}
|
||||
if(strlen(dom) >= sizeof tr.authdom){
|
||||
werrstr("auth dom name too long");
|
||||
return -1;
|
||||
}
|
||||
|
||||
k->priv = emalloc(DESKEYLEN);
|
||||
if(pass = strfindattr(k->privattr, "!password"))
|
||||
passtokey(k->priv, pass);
|
||||
else if(pass = strfindattr(k->privattr, "!hex")){
|
||||
if(hexparse(pass, k->priv, 7) < 0){
|
||||
werrstr("malformed !hex key data");
|
||||
return -1;
|
||||
}
|
||||
}else{
|
||||
werrstr("need !password or !hex attribute");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
p9sk1close(Key *k)
|
||||
{
|
||||
free(k->priv);
|
||||
k->priv = nil;
|
||||
}
|
||||
|
||||
static Role
|
||||
p9sk1roles[] =
|
||||
{
|
||||
"client", p9skclient,
|
||||
"server", p9skserver,
|
||||
0
|
||||
};
|
||||
|
||||
static Role
|
||||
p9sk2roles[] =
|
||||
{
|
||||
"client", p9skclient,
|
||||
"server", p9skserver,
|
||||
0
|
||||
};
|
||||
|
||||
Proto p9sk1 = {
|
||||
.name= "p9sk1",
|
||||
.roles= p9sk1roles,
|
||||
.checkkey= p9sk1check,
|
||||
.closekey= p9sk1close,
|
||||
.keyprompt= "user? dom? !password?",
|
||||
};
|
||||
|
||||
Proto p9sk2 = {
|
||||
.name= "p9sk2",
|
||||
.roles= p9sk2roles,
|
||||
};
|
||||
|
||||
100
src/cmd/factotum/pass.c
Normal file
100
src/cmd/factotum/pass.c
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
/*
|
||||
* This is just a repository for a password.
|
||||
* We don't want to encourage this, there's
|
||||
* no server side.
|
||||
*/
|
||||
|
||||
#include "dat.h"
|
||||
|
||||
typedef struct State State;
|
||||
struct State
|
||||
{
|
||||
Key *key;
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
HavePass,
|
||||
Maxphase,
|
||||
};
|
||||
|
||||
static char *phasenames[Maxphase] =
|
||||
{
|
||||
[HavePass] "HavePass",
|
||||
};
|
||||
|
||||
static int
|
||||
passinit(Proto *p, Fsstate *fss)
|
||||
{
|
||||
int ask;
|
||||
Key *k;
|
||||
State *s;
|
||||
|
||||
k = findkey(fss, Kuser, &ask, 0, fss->attr, "%s", p->keyprompt);
|
||||
if(k == nil){
|
||||
if(ask)
|
||||
return RpcNeedkey;
|
||||
return failure(fss, nil);
|
||||
}
|
||||
setattrs(fss->attr, k->attr);
|
||||
s = emalloc(sizeof(*s));
|
||||
s->key = k;
|
||||
fss->ps = s;
|
||||
return RpcOk;
|
||||
}
|
||||
|
||||
static void
|
||||
passclose(Fsstate *fss)
|
||||
{
|
||||
State *s;
|
||||
|
||||
s = fss->ps;
|
||||
if(s->key)
|
||||
closekey(s->key);
|
||||
free(s);
|
||||
}
|
||||
|
||||
static int
|
||||
passread(Fsstate *fss, void *va, uint *n)
|
||||
{
|
||||
int m;
|
||||
char buf[500];
|
||||
char *pass, *user;
|
||||
State *s;
|
||||
|
||||
s = fss->ps;
|
||||
switch(fss->phase){
|
||||
default:
|
||||
return phaseerror(fss, "read");
|
||||
|
||||
case HavePass:
|
||||
user = strfindattr(s->key->attr, "user");
|
||||
pass = strfindattr(s->key->privattr, "!password");
|
||||
if(user==nil || pass==nil)
|
||||
return failure(fss, "passread cannot happen");
|
||||
snprint(buf, sizeof buf, "%q %q", user, pass);
|
||||
m = strlen(buf);
|
||||
if(m > *n)
|
||||
return toosmall(fss, m);
|
||||
*n = m;
|
||||
memmove(va, buf, m);
|
||||
return RpcOk;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
passwrite(Fsstate *fss, void*, uint)
|
||||
{
|
||||
return phaseerror(fss, "write");
|
||||
}
|
||||
|
||||
Proto pass =
|
||||
{
|
||||
.name= "pass",
|
||||
.init= passinit,
|
||||
.write= passwrite,
|
||||
.read= passread,
|
||||
.close= passclose,
|
||||
.addkey= replacekey,
|
||||
.keyprompt= "user? !password?",
|
||||
};
|
||||
189
src/cmd/factotum/plan9.c
Normal file
189
src/cmd/factotum/plan9.c
Normal file
|
|
@ -0,0 +1,189 @@
|
|||
#include "std.h"
|
||||
#include "dat.h"
|
||||
#include <bio.h>
|
||||
|
||||
int
|
||||
memrandom(void *p, int n)
|
||||
{
|
||||
uchar *cp;
|
||||
|
||||
for(cp = (uchar*)p; n > 0; n--)
|
||||
*cp++ = fastrand();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* create a change uid capability
|
||||
*/
|
||||
static int caphashfd;
|
||||
|
||||
static char*
|
||||
mkcap(char *from, char *to)
|
||||
{
|
||||
uchar rand[20];
|
||||
char *cap;
|
||||
char *key;
|
||||
int nfrom, nto;
|
||||
uchar hash[SHA1dlen];
|
||||
|
||||
if(caphashfd < 0)
|
||||
return nil;
|
||||
|
||||
/* create the capability */
|
||||
nto = strlen(to);
|
||||
nfrom = strlen(from);
|
||||
cap = emalloc(nfrom+1+nto+1+sizeof(rand)*3+1);
|
||||
sprint(cap, "%s@%s", from, to);
|
||||
memrandom(rand, sizeof(rand));
|
||||
key = cap+nfrom+1+nto+1;
|
||||
enc64(key, sizeof(rand)*3, rand, sizeof(rand));
|
||||
|
||||
/* hash the capability */
|
||||
hmac_sha1((uchar*)cap, strlen(cap), (uchar*)key, strlen(key), hash, nil);
|
||||
|
||||
/* give the kernel the hash */
|
||||
key[-1] = '@';
|
||||
if(write(caphashfd, hash, SHA1dlen) < 0){
|
||||
free(cap);
|
||||
return nil;
|
||||
}
|
||||
|
||||
return cap;
|
||||
}
|
||||
|
||||
Attr*
|
||||
addcap(Attr *a, char *from, Ticket *t)
|
||||
{
|
||||
char *cap;
|
||||
|
||||
cap = mkcap(from, t->suid);
|
||||
return addattr(a, "cuid=%q suid=%q cap=%q", t->cuid, t->suid, cap);
|
||||
}
|
||||
|
||||
/* bind in the default network and cs */
|
||||
static int
|
||||
bindnetcs(void)
|
||||
{
|
||||
int srvfd;
|
||||
|
||||
if(access("/net/tcp", AEXIST) < 0)
|
||||
bind("#I", "/net", MBEFORE);
|
||||
|
||||
if(access("/net/cs", AEXIST) < 0){
|
||||
if((srvfd = open("#s/cs", ORDWR)) >= 0){
|
||||
/* mount closes srvfd on success */
|
||||
if(mount(srvfd, -1, "/net", MBEFORE, "") >= 0)
|
||||
return 0;
|
||||
close(srvfd);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
_authdial(char *net, char *authdom)
|
||||
{
|
||||
int vanilla;
|
||||
|
||||
vanilla = net==nil || strcmp(net, "/net")==0;
|
||||
|
||||
if(!vanilla || bindnetcs()>=0)
|
||||
return authdial(net, authdom);
|
||||
|
||||
/* use the auth sever passed to us as an arg */
|
||||
if(authaddr == nil)
|
||||
return -1;
|
||||
return dial(netmkaddr(authaddr, "tcp", "567"), 0, 0, 0);
|
||||
}
|
||||
|
||||
Key*
|
||||
plan9authkey(Attr *a)
|
||||
{
|
||||
char *dom;
|
||||
Key *k;
|
||||
|
||||
/*
|
||||
* The only important part of a is dom.
|
||||
* We don't care, for example, about user name.
|
||||
*/
|
||||
dom = strfindattr(a, "dom");
|
||||
if(dom)
|
||||
k = keylookup("proto=p9sk1 role=server user? dom=%q", dom);
|
||||
else
|
||||
k = keylookup("proto=p9sk1 role=server user? dom?");
|
||||
if(k == nil)
|
||||
werrstr("could not find plan 9 auth key dom %q", dom);
|
||||
return k;
|
||||
}
|
||||
|
||||
/*
|
||||
* prompt for a string with a possible default response
|
||||
*/
|
||||
char*
|
||||
readcons(char *prompt, char *def, int raw)
|
||||
{
|
||||
int fdin, fdout, ctl, n;
|
||||
char line[10];
|
||||
char *s;
|
||||
|
||||
fdin = open("/dev/cons", OREAD);
|
||||
if(fdin < 0)
|
||||
fdin = 0;
|
||||
fdout = open("/dev/cons", OWRITE);
|
||||
if(fdout < 0)
|
||||
fdout = 1;
|
||||
if(def != nil)
|
||||
fprint(fdout, "%s[%s]: ", prompt, def);
|
||||
else
|
||||
fprint(fdout, "%s: ", prompt);
|
||||
if(raw){
|
||||
ctl = open("/dev/consctl", OWRITE);
|
||||
if(ctl >= 0)
|
||||
write(ctl, "rawon", 5);
|
||||
} else
|
||||
ctl = -1;
|
||||
s = estrdup("");
|
||||
for(;;){
|
||||
n = read(fdin, line, 1);
|
||||
if(n == 0){
|
||||
Error:
|
||||
close(fdin);
|
||||
close(fdout);
|
||||
if(ctl >= 0)
|
||||
close(ctl);
|
||||
free(s);
|
||||
return nil;
|
||||
}
|
||||
if(n < 0)
|
||||
goto Error;
|
||||
if(line[0] == 0x7f)
|
||||
goto Error;
|
||||
if(n == 0 || line[0] == '\n' || line[0] == '\r'){
|
||||
if(raw){
|
||||
write(ctl, "rawoff", 6);
|
||||
write(fdout, "\n", 1);
|
||||
}
|
||||
close(ctl);
|
||||
close(fdin);
|
||||
close(fdout);
|
||||
if(*s == 0 && def != nil)
|
||||
s = estrappend(s, "%s", def);
|
||||
return s;
|
||||
}
|
||||
if(line[0] == '\b'){
|
||||
if(strlen(s) > 0)
|
||||
s[strlen(s)-1] = 0;
|
||||
} else if(line[0] == 0x15) { /* ^U: line kill */
|
||||
if(def != nil)
|
||||
fprint(fdout, "\n%s[%s]: ", prompt, def);
|
||||
else
|
||||
fprint(fdout, "\n%s: ", prompt);
|
||||
|
||||
s[0] = 0;
|
||||
} else {
|
||||
s = estrappend(s, "%c", line[0]);
|
||||
}
|
||||
}
|
||||
return nil; /* not reached */
|
||||
}
|
||||
0
src/cmd/factotum/privattr
Normal file
0
src/cmd/factotum/privattr
Normal file
22
src/cmd/factotum/proto.c
Normal file
22
src/cmd/factotum/proto.c
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
#include "std.h"
|
||||
#include "dat.h"
|
||||
|
||||
Proto *prototab[] = {
|
||||
&apop,
|
||||
&cram,
|
||||
&p9any,
|
||||
&p9sk1,
|
||||
&p9sk2,
|
||||
nil,
|
||||
};
|
||||
|
||||
Proto*
|
||||
protolookup(char *name)
|
||||
{
|
||||
int i;
|
||||
|
||||
for(i=0; prototab[i]; i++)
|
||||
if(strcmp(prototab[i]->name, name) == 0)
|
||||
return prototab[i];
|
||||
return nil;
|
||||
}
|
||||
315
src/cmd/factotum/rpc.c
Normal file
315
src/cmd/factotum/rpc.c
Normal file
|
|
@ -0,0 +1,315 @@
|
|||
#include "std.h"
|
||||
#include "dat.h"
|
||||
|
||||
/*
|
||||
* Factotum RPC
|
||||
*
|
||||
* Must be paired write/read cycles on /mnt/factotum/rpc.
|
||||
* The format of a request is verb, single space, data.
|
||||
* Data format is verb-dependent; in particular, it can be binary.
|
||||
* The format of a response is the same. The write only sets up
|
||||
* the RPC. The read tries to execute it. If the /mnt/factotum/key
|
||||
* file is open, we ask for new keys using that instead of returning
|
||||
* an error in the RPC. This means the read blocks.
|
||||
* Textual arguments are parsed with tokenize, so rc-style quoting
|
||||
* rules apply.
|
||||
*
|
||||
* Only authentication protocol messages go here. Configuration
|
||||
* is still via ctl (below).
|
||||
*
|
||||
* Request RPCs are:
|
||||
* start attrs - initializes protocol for authentication, can fail.
|
||||
* returns "ok read" or "ok write" on success.
|
||||
* read - execute protocol read
|
||||
* write - execute protocol write
|
||||
* authinfo - if the protocol is finished, return the AI if any
|
||||
* attr - return protocol information
|
||||
* Return values are:
|
||||
* error message - an error happened.
|
||||
* ok [data] - success, possible data is request dependent.
|
||||
* needkey attrs - request aborted, get me this key and try again
|
||||
* badkey attrs - request aborted, this key might be bad
|
||||
* done [haveai] - authentication is done [haveai: you can get an ai with authinfo]
|
||||
*/
|
||||
|
||||
char *rpcname[] =
|
||||
{
|
||||
"unknown",
|
||||
"authinfo",
|
||||
"attr",
|
||||
"read",
|
||||
"start",
|
||||
"write",
|
||||
};
|
||||
|
||||
static int
|
||||
classify(char *s)
|
||||
{
|
||||
int i;
|
||||
|
||||
for(i=1; i<nelem(rpcname); i++)
|
||||
if(strcmp(s, rpcname[i]) == 0)
|
||||
return i;
|
||||
return RpcUnknown;
|
||||
}
|
||||
|
||||
int
|
||||
rpcwrite(Conv *c, void *data, int count)
|
||||
{
|
||||
int op;
|
||||
uchar *p;
|
||||
|
||||
if(count >= MaxRpc){
|
||||
werrstr("rpc too large");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* cancel any current rpc */
|
||||
c->rpc.op = RpcUnknown;
|
||||
c->nreply = 0;
|
||||
|
||||
/* parse new rpc */
|
||||
memmove(c->rpcbuf, data, count);
|
||||
c->rpcbuf[count] = 0;
|
||||
if(p = (uchar*)strchr((char*)c->rpcbuf, ' ')){
|
||||
*p++ = '\0';
|
||||
c->rpc.data = p;
|
||||
c->rpc.count = count - (p - (uchar*)c->rpcbuf);
|
||||
}else{
|
||||
c->rpc.data = "";
|
||||
c->rpc.count = 0;
|
||||
}
|
||||
op = classify(c->rpcbuf);
|
||||
if(op == RpcUnknown){
|
||||
werrstr("bad rpc verb: %s", c->rpcbuf);
|
||||
return -1;
|
||||
}
|
||||
|
||||
c->rpc.op = op;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
convthread(void *v)
|
||||
{
|
||||
Conv *c;
|
||||
Attr *a;
|
||||
char *role, *proto;
|
||||
Proto *p;
|
||||
Role *r;
|
||||
|
||||
c = v;
|
||||
a = parseattr(c->rpc.data);
|
||||
if(a == nil){
|
||||
werrstr("empty attr");
|
||||
goto out;
|
||||
}
|
||||
c->attr = a;
|
||||
proto = strfindattr(a, "proto");
|
||||
role = strfindattr(a, "role");
|
||||
|
||||
if(proto == nil){
|
||||
werrstr("no proto in attrs");
|
||||
goto out;
|
||||
}
|
||||
if(role == nil){
|
||||
werrstr("no role in attrs");
|
||||
goto out;
|
||||
}
|
||||
|
||||
p = protolookup(proto);
|
||||
if(p == nil){
|
||||
werrstr("unknown proto %s", proto);
|
||||
goto out;
|
||||
}
|
||||
|
||||
c->proto = p;
|
||||
for(r=p->roles; r->name; r++){
|
||||
if(strcmp(r->name, role) != 0)
|
||||
continue;
|
||||
rpcrespond(c, "ok");
|
||||
c->active = 1;
|
||||
if((*r->fn)(c) == 0){
|
||||
c->done = 1;
|
||||
werrstr("protocol finished");
|
||||
}else
|
||||
werrstr("%s %s %s: %r", p->name, r->name, c->state);
|
||||
goto out;
|
||||
}
|
||||
werrstr("unknown role");
|
||||
|
||||
out:
|
||||
c->active = 0;
|
||||
c->state = 0;
|
||||
rerrstr(c->err, sizeof c->err);
|
||||
rpcrespond(c, "error %r");
|
||||
convclose(c);
|
||||
}
|
||||
|
||||
static uchar* convAI2M(uchar *p, int n, char *cuid, char *suid, char *cap, char *hex);
|
||||
|
||||
void
|
||||
rpcexec(Conv *c)
|
||||
{
|
||||
uchar *p;
|
||||
|
||||
switch(c->rpc.op){
|
||||
case RpcRead:
|
||||
if(c->rpc.count > 0){
|
||||
rpcrespond(c, "error read takes no parameters");
|
||||
break;
|
||||
}
|
||||
/* fall through */
|
||||
default:
|
||||
if(!c->active){
|
||||
if(c->done)
|
||||
rpcrespond(c, "done");
|
||||
else
|
||||
rpcrespond(c, "error %s", c->err);
|
||||
break;
|
||||
}
|
||||
nbsendp(c->rpcwait, 0);
|
||||
break;
|
||||
case RpcUnknown:
|
||||
break;
|
||||
case RpcAuthinfo:
|
||||
/* deprecated */
|
||||
if(c->active)
|
||||
rpcrespond(c, "error conversation still active");
|
||||
else if(!c->done)
|
||||
rpcrespond(c, "error conversation not successful");
|
||||
else{
|
||||
/* make up an auth info using the attr */
|
||||
p = convAI2M((uchar*)c->reply+3, sizeof c->reply-3,
|
||||
strfindattr(c->attr, "cuid"),
|
||||
strfindattr(c->attr, "suid"),
|
||||
strfindattr(c->attr, "cap"),
|
||||
strfindattr(c->attr, "secret"));
|
||||
if(p == nil)
|
||||
rpcrespond(c, "error %r");
|
||||
else
|
||||
rpcrespondn(c, "ok", c->reply+3, p-(uchar*)(c->reply+3));
|
||||
}
|
||||
break;
|
||||
case RpcAttr:
|
||||
rpcrespond(c, "ok %A", c->attr);
|
||||
break;
|
||||
case RpcStart:
|
||||
convreset(c);
|
||||
c->ref++;
|
||||
threadcreate(convthread, c, STACK);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
rpcrespond(Conv *c, char *fmt, ...)
|
||||
{
|
||||
va_list arg;
|
||||
|
||||
if(c->hangup)
|
||||
return;
|
||||
|
||||
if(fmt == nil)
|
||||
fmt = "";
|
||||
|
||||
va_start(arg, fmt);
|
||||
c->nreply = vsnprint(c->reply, sizeof c->reply, fmt, arg);
|
||||
va_end(arg);
|
||||
(*c->kickreply)(c);
|
||||
c->rpc.op = RpcUnknown;
|
||||
}
|
||||
|
||||
void
|
||||
rpcrespondn(Conv *c, char *verb, void *data, int count)
|
||||
{
|
||||
char *p;
|
||||
|
||||
if(c->hangup)
|
||||
return;
|
||||
|
||||
if(strlen(verb)+1+count > sizeof c->reply){
|
||||
print("RPC response too large; caller %#lux", getcallerpc(&c));
|
||||
return;
|
||||
}
|
||||
|
||||
strcpy(c->reply, verb);
|
||||
p = c->reply + strlen(c->reply);
|
||||
*p++ = ' ';
|
||||
memmove(p, data, count);
|
||||
c->nreply = count + (p - c->reply);
|
||||
(*c->kickreply)(c);
|
||||
c->rpc.op = RpcUnknown;
|
||||
}
|
||||
|
||||
/* deprecated */
|
||||
static uchar*
|
||||
pstring(uchar *p, uchar *e, char *s)
|
||||
{
|
||||
uint n;
|
||||
|
||||
if(p == nil)
|
||||
return nil;
|
||||
if(s == nil)
|
||||
s = "";
|
||||
n = strlen(s);
|
||||
if(p+n+BIT16SZ >= e)
|
||||
return nil;
|
||||
PBIT16(p, n);
|
||||
p += BIT16SZ;
|
||||
memmove(p, s, n);
|
||||
p += n;
|
||||
return p;
|
||||
}
|
||||
|
||||
static uchar*
|
||||
pcarray(uchar *p, uchar *e, uchar *s, uint n)
|
||||
{
|
||||
if(p == nil)
|
||||
return nil;
|
||||
if(s == nil){
|
||||
if(n > 0)
|
||||
sysfatal("pcarray");
|
||||
s = (uchar*)"";
|
||||
}
|
||||
if(p+n+BIT16SZ >= e)
|
||||
return nil;
|
||||
PBIT16(p, n);
|
||||
p += BIT16SZ;
|
||||
memmove(p, s, n);
|
||||
p += n;
|
||||
return p;
|
||||
}
|
||||
|
||||
static uchar*
|
||||
convAI2M(uchar *p, int n, char *cuid, char *suid, char *cap, char *hex)
|
||||
{
|
||||
uchar *e = p+n;
|
||||
uchar *secret;
|
||||
int nsecret;
|
||||
|
||||
if(cuid == nil)
|
||||
cuid = "";
|
||||
if(suid == nil)
|
||||
suid = "";
|
||||
if(cap == nil)
|
||||
cap = "";
|
||||
if(hex == nil)
|
||||
hex = "";
|
||||
nsecret = strlen(hex)/2;
|
||||
secret = emalloc(nsecret);
|
||||
if(hexparse(hex, secret, nsecret) < 0){
|
||||
werrstr("hexparse %s failed", hex); /* can't happen */
|
||||
free(secret);
|
||||
return nil;
|
||||
}
|
||||
p = pstring(p, e, cuid);
|
||||
p = pstring(p, e, suid);
|
||||
p = pstring(p, e, cap);
|
||||
p = pcarray(p, e, secret, nsecret);
|
||||
free(secret);
|
||||
if(p == nil)
|
||||
werrstr("authinfo too big");
|
||||
return p;
|
||||
}
|
||||
|
||||
135
src/cmd/factotum/ssh.c
Normal file
135
src/cmd/factotum/ssh.c
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
#include "dat.h"
|
||||
#include <mp.h>
|
||||
#include <libsec.h>
|
||||
|
||||
typedef struct Sshrsastate Sshrsastate;
|
||||
|
||||
enum {
|
||||
CReadpub,
|
||||
CWritechal,
|
||||
CReadresp,
|
||||
};
|
||||
struct State
|
||||
{
|
||||
RSApriv *priv;
|
||||
Key *k;
|
||||
mpint *resp;
|
||||
int phase;
|
||||
};
|
||||
|
||||
static RSApriv*
|
||||
readrsapriv(char *s)
|
||||
{
|
||||
RSApriv *priv;
|
||||
|
||||
priv = rsaprivalloc();
|
||||
|
||||
strtoul(s, &s, 10);
|
||||
if((priv->pub.ek=strtomp(s, &s, 16, nil)) == nil)
|
||||
goto Error;
|
||||
if((priv->dk=strtomp(s, &s, 16, nil)) == nil)
|
||||
goto Error;
|
||||
if((priv->pub.n=strtomp(s, &s, 16, nil)) == nil)
|
||||
goto Error;
|
||||
if((priv->p=strtomp(s, &s, 16, nil)) == nil)
|
||||
goto Error;
|
||||
if((priv->q=strtomp(s, &s, 16, nil)) == nil)
|
||||
goto Error;
|
||||
if((priv->kp=strtomp(s, &s, 16, nil)) == nil)
|
||||
goto Error;
|
||||
if((priv->kq=strtomp(s, &s, 16, nil)) == nil)
|
||||
goto Error;
|
||||
if((priv->c2=strtomp(s, &s, 16, nil)) == nil)
|
||||
goto Error;
|
||||
|
||||
return priv;
|
||||
|
||||
Error:
|
||||
rsaprivfree(priv);
|
||||
return nil;
|
||||
}
|
||||
|
||||
int
|
||||
sshinit(Fsstate *fss,
|
||||
sshrsaopen(Key *k, char*, int client)
|
||||
{
|
||||
Sshrsastate *s;
|
||||
|
||||
fmtinstall('B', mpconv);
|
||||
assert(client);
|
||||
s = emalloc(sizeof *s);
|
||||
s->priv = readrsapriv(s_to_c(k->data));
|
||||
s->k = k;
|
||||
if(s->priv == nil){
|
||||
agentlog("error parsing ssh key %s", k->file);
|
||||
free(s);
|
||||
return nil;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
int
|
||||
sshrsaread(void *va, void *buf, int n)
|
||||
{
|
||||
Sshrsastate *s;
|
||||
|
||||
s = va;
|
||||
switch(s->phase){
|
||||
case Readpub:
|
||||
s->phase = Done;
|
||||
return snprint(buf, n, "%B", s->priv->pub.n);
|
||||
case Readresp:
|
||||
s->phase = Done;
|
||||
return snprint(buf, n, "%B", s->resp);
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
sshrsawrite(void *va, void *vbuf, int n)
|
||||
{
|
||||
mpint *m;
|
||||
char *buf;
|
||||
Sshrsastate *s;
|
||||
|
||||
s = va;
|
||||
if((s->k->flags&Fconfirmuse) && confirm("ssh use") < 0)
|
||||
return -1;
|
||||
|
||||
buf = emalloc(n+1);
|
||||
memmove(buf, vbuf, n);
|
||||
buf[n] = '\0';
|
||||
m = strtomp(buf, nil, 16, nil);
|
||||
free(buf);
|
||||
if(m == nil){
|
||||
werrstr("bad bignum");
|
||||
return -1;
|
||||
}
|
||||
|
||||
agentlog("ssh use");
|
||||
m = rsadecrypt(s->priv, m, m);
|
||||
s->resp = m;
|
||||
s->phase = Readresp;
|
||||
return n;
|
||||
}
|
||||
|
||||
void
|
||||
sshrsaclose(void *v)
|
||||
{
|
||||
Sshrsastate *s;
|
||||
|
||||
s = v;
|
||||
rsaprivfree(s->priv);
|
||||
mpfree(s->resp);
|
||||
free(s);
|
||||
}
|
||||
|
||||
Proto sshrsa = {
|
||||
.name= "ssh-rsa",
|
||||
.perm= 0666,
|
||||
.open= sshrsaopen,
|
||||
.read= sshrsaread,
|
||||
.write= sshrsawrite,
|
||||
.close= sshrsaclose,
|
||||
};
|
||||
172
src/cmd/factotum/sshrsa.c
Normal file
172
src/cmd/factotum/sshrsa.c
Normal file
|
|
@ -0,0 +1,172 @@
|
|||
/*
|
||||
* SSH RSA authentication.
|
||||
*
|
||||
* Client protocol:
|
||||
* read public key
|
||||
* if you don't like it, read another, repeat
|
||||
* write challenge
|
||||
* read response
|
||||
* all numbers are hexadecimal biginits parsable with strtomp.
|
||||
*/
|
||||
|
||||
#include "dat.h"
|
||||
|
||||
enum {
|
||||
CHavePub,
|
||||
CHaveResp,
|
||||
|
||||
Maxphase,
|
||||
};
|
||||
|
||||
static char *phasenames[] = {
|
||||
[CHavePub] "CHavePub",
|
||||
[CHaveResp] "CHaveResp",
|
||||
};
|
||||
|
||||
struct State
|
||||
{
|
||||
RSApriv *priv;
|
||||
mpint *resp;
|
||||
int off;
|
||||
Key *key;
|
||||
};
|
||||
|
||||
static RSApriv*
|
||||
readrsapriv(Key *k)
|
||||
{
|
||||
char *a;
|
||||
RSApriv *priv;
|
||||
|
||||
priv = rsaprivalloc();
|
||||
|
||||
if((a=strfindattr(k->attr, "ek"))==nil || (priv->pub.ek=strtomp(a, nil, 16, nil))==nil)
|
||||
goto Error;
|
||||
if((a=strfindattr(k->attr, "n"))==nil || (priv->pub.n=strtomp(a, nil, 16, nil))==nil)
|
||||
goto Error;
|
||||
if((a=strfindattr(k->privattr, "!p"))==nil || (priv->p=strtomp(a, nil, 16, nil))==nil)
|
||||
goto Error;
|
||||
if((a=strfindattr(k->privattr, "!q"))==nil || (priv->q=strtomp(a, nil, 16, nil))==nil)
|
||||
goto Error;
|
||||
if((a=strfindattr(k->privattr, "!kp"))==nil || (priv->kp=strtomp(a, nil, 16, nil))==nil)
|
||||
goto Error;
|
||||
if((a=strfindattr(k->privattr, "!kq"))==nil || (priv->kq=strtomp(a, nil, 16, nil))==nil)
|
||||
goto Error;
|
||||
if((a=strfindattr(k->privattr, "!c2"))==nil || (priv->c2=strtomp(a, nil, 16, nil))==nil)
|
||||
goto Error;
|
||||
if((a=strfindattr(k->privattr, "!dk"))==nil || (priv->dk=strtomp(a, nil, 16, nil))==nil)
|
||||
goto Error;
|
||||
return priv;
|
||||
|
||||
Error:
|
||||
rsaprivfree(priv);
|
||||
return nil;
|
||||
}
|
||||
|
||||
static int
|
||||
sshrsainit(Proto*, Fsstate *fss)
|
||||
{
|
||||
int iscli;
|
||||
State *s;
|
||||
|
||||
if((iscli = isclient(strfindattr(fss->attr, "role"))) < 0)
|
||||
return failure(fss, nil);
|
||||
if(iscli==0)
|
||||
return failure(fss, "sshrsa server unimplemented");
|
||||
|
||||
s = emalloc(sizeof *s);
|
||||
fss->phasename = phasenames;
|
||||
fss->maxphase = Maxphase;
|
||||
fss->phase = CHavePub;
|
||||
fss->ps = s;
|
||||
return RpcOk;
|
||||
}
|
||||
|
||||
static int
|
||||
sshrsaread(Fsstate *fss, void *va, uint *n)
|
||||
{
|
||||
RSApriv *priv;
|
||||
State *s;
|
||||
|
||||
s = fss->ps;
|
||||
switch(fss->phase){
|
||||
default:
|
||||
return phaseerror(fss, "read");
|
||||
case CHavePub:
|
||||
if(s->key){
|
||||
closekey(s->key);
|
||||
s->key = nil;
|
||||
}
|
||||
if((s->key = findkey(fss, Kuser, nil, s->off, fss->attr, nil)) == nil)
|
||||
return failure(fss, nil);
|
||||
s->off++;
|
||||
priv = s->key->priv;
|
||||
*n = snprint(va, *n, "%B", priv->pub.n);
|
||||
return RpcOk;
|
||||
case CHaveResp:
|
||||
*n = snprint(va, *n, "%B", s->resp);
|
||||
fss->phase = Established;
|
||||
return RpcOk;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
sshrsawrite(Fsstate *fss, void *va, uint)
|
||||
{
|
||||
mpint *m;
|
||||
State *s;
|
||||
|
||||
s = fss->ps;
|
||||
switch(fss->phase){
|
||||
default:
|
||||
return phaseerror(fss, "write");
|
||||
case CHavePub:
|
||||
if(s->key == nil)
|
||||
return failure(fss, "no current key");
|
||||
m = strtomp(va, nil, 16, nil);
|
||||
m = rsadecrypt(s->key->priv, m, m);
|
||||
s->resp = m;
|
||||
fss->phase = CHaveResp;
|
||||
return RpcOk;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
sshrsaclose(Fsstate *fss)
|
||||
{
|
||||
State *s;
|
||||
|
||||
s = fss->ps;
|
||||
if(s->key)
|
||||
closekey(s->key);
|
||||
if(s->resp)
|
||||
mpfree(s->resp);
|
||||
free(s);
|
||||
}
|
||||
|
||||
static int
|
||||
sshrsaaddkey(Key *k)
|
||||
{
|
||||
fmtinstall('B', mpconv);
|
||||
|
||||
if((k->priv = readrsapriv(k)) == nil){
|
||||
werrstr("malformed key data");
|
||||
return -1;
|
||||
}
|
||||
return replacekey(k);
|
||||
}
|
||||
|
||||
static void
|
||||
sshrsaclosekey(Key *k)
|
||||
{
|
||||
rsaprivfree(k->priv);
|
||||
}
|
||||
|
||||
Proto sshrsa = {
|
||||
.name= "sshrsa",
|
||||
.init= sshrsainit,
|
||||
.write= sshrsawrite,
|
||||
.read= sshrsaread,
|
||||
.close= sshrsaclose,
|
||||
.addkey= sshrsaaddkey,
|
||||
.closekey= sshrsaclosekey,
|
||||
};
|
||||
10
src/cmd/factotum/std.h
Normal file
10
src/cmd/factotum/std.h
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
#include <u.h>
|
||||
#include <libc.h>
|
||||
#include <auth.h>
|
||||
#include <authsrv.h>
|
||||
#include <mp.h>
|
||||
#include <libsec.h>
|
||||
#include <thread.h>
|
||||
#include <fcall.h>
|
||||
#include <9p.h>
|
||||
|
||||
121
src/cmd/factotum/test.c
Normal file
121
src/cmd/factotum/test.c
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
#include <u.h>
|
||||
#include <libc.h>
|
||||
#include <auth.h>
|
||||
|
||||
typedef struct Test Test;
|
||||
|
||||
struct Test
|
||||
{
|
||||
char *name;
|
||||
int (*server)(Test*, AuthRpc*, int);
|
||||
int (*client)(Test*, int);
|
||||
};
|
||||
|
||||
int
|
||||
ai2status(AuthInfo *ai)
|
||||
{
|
||||
if(ai == nil)
|
||||
return -1;
|
||||
auth_freeAI(ai);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
proxyserver(Test *t, AuthRpc *rpc, int fd)
|
||||
{
|
||||
char buf[1024];
|
||||
|
||||
sprint(buf, "proto=%q role=server", t->name);
|
||||
return ai2status(fauth_proxy(fd, rpc, nil, buf));
|
||||
}
|
||||
|
||||
int
|
||||
proxyclient(Test *t, int fd)
|
||||
{
|
||||
return ai2status(auth_proxy(fd, auth_getkey, "proto=%q role=client", t->name));
|
||||
}
|
||||
|
||||
Test test[] =
|
||||
{
|
||||
"apop", proxyserver, proxyclient,
|
||||
"cram", proxyserver, proxyclient,
|
||||
"p9sk1", proxyserver, proxyclient,
|
||||
"p9sk2", proxyserver, proxyclient,
|
||||
"p9any", proxyserver, proxyclient,
|
||||
};
|
||||
|
||||
void
|
||||
usage(void)
|
||||
{
|
||||
fprint(2, "usage: test [name]...\n");
|
||||
exits("usage");
|
||||
}
|
||||
|
||||
void
|
||||
runtest(AuthRpc *srpc, Test *t)
|
||||
{
|
||||
int p[2], bad;
|
||||
Waitmsg *w;
|
||||
|
||||
if(pipe(p) < 0)
|
||||
sysfatal("pipe: %r");
|
||||
|
||||
print("%s...", t->name);
|
||||
|
||||
switch(fork()){
|
||||
case -1:
|
||||
sysfatal("fork: %r");
|
||||
|
||||
case 0:
|
||||
close(p[0]);
|
||||
if((*t->server)(t, srpc, p[1]) < 0){
|
||||
print("\n\tserver: %r");
|
||||
_exits("oops");
|
||||
}
|
||||
close(p[1]);
|
||||
_exits(nil);
|
||||
default:
|
||||
close(p[1]);
|
||||
if((*t->client)(t, p[0]) < 0){
|
||||
print("\n\tclient: %r");
|
||||
bad = 1;
|
||||
}
|
||||
close(p[0]);
|
||||
break;
|
||||
}
|
||||
w = wait();
|
||||
if(w->msg[0])
|
||||
bad = 1;
|
||||
print("\n");
|
||||
}
|
||||
|
||||
void
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
int i, j;
|
||||
int afd;
|
||||
AuthRpc *srpc;
|
||||
|
||||
ARGBEGIN{
|
||||
default:
|
||||
usage();
|
||||
}ARGEND
|
||||
|
||||
quotefmtinstall();
|
||||
afd = open("/n/kremvax/factotum/rpc", ORDWR);
|
||||
if(afd < 0)
|
||||
sysfatal("open /n/kremvax/factotum/rpc: %r");
|
||||
srpc = auth_allocrpc(afd);
|
||||
if(srpc == nil)
|
||||
sysfatal("auth_allocrpc: %r");
|
||||
|
||||
if(argc == 0)
|
||||
for(i=0; i<nelem(test); i++)
|
||||
runtest(srpc, &test[i]);
|
||||
else
|
||||
for(i=0; i<argc; i++)
|
||||
for(j=0; j<nelem(test); j++)
|
||||
if(strcmp(argv[i], test[j].name) == 0)
|
||||
runtest(srpc, &test[j]);
|
||||
exits(nil);
|
||||
}
|
||||
11
src/cmd/factotum/testsetup
Executable file
11
src/cmd/factotum/testsetup
Executable file
|
|
@ -0,0 +1,11 @@
|
|||
#!/bin/rc
|
||||
|
||||
slay 8.out|rc
|
||||
8.out $* -s fact.s -m /n/kremvax
|
||||
8.out $* -s fact.c
|
||||
ramfs -m /n/sid >[2]/dev/null
|
||||
auth/aescbc -d < /usr/rsc/lib/factotum.aes >/n/sid/all
|
||||
read -m /n/sid/all >/n/kremvax/factotum/ctl
|
||||
read -m /n/sid/all >/mnt/factotum/ctl
|
||||
unmount /n/sid
|
||||
|
||||
52
src/cmd/factotum/util.c
Normal file
52
src/cmd/factotum/util.c
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
#include "std.h"
|
||||
#include "dat.h"
|
||||
|
||||
static int
|
||||
unhex(char c)
|
||||
{
|
||||
if('0' <= c && c <= '9')
|
||||
return c-'0';
|
||||
if('a' <= c && c <= 'f')
|
||||
return c-'a'+10;
|
||||
if('A' <= c && c <= 'F')
|
||||
return c-'A'+10;
|
||||
abort();
|
||||
return -1;
|
||||
}
|
||||
|
||||
int
|
||||
hexparse(char *hex, uchar *dat, int ndat)
|
||||
{
|
||||
int i, n;
|
||||
|
||||
n = strlen(hex);
|
||||
if(n%2)
|
||||
return -1;
|
||||
n /= 2;
|
||||
if(n > ndat)
|
||||
return -1;
|
||||
if(hex[strspn(hex, "0123456789abcdefABCDEF")] != '\0')
|
||||
return -1;
|
||||
for(i=0; i<n; i++)
|
||||
dat[i] = (unhex(hex[2*i])<<4)|unhex(hex[2*i+1]);
|
||||
return n;
|
||||
}
|
||||
|
||||
char*
|
||||
estrappend(char *s, char *fmt, ...)
|
||||
{
|
||||
char *t;
|
||||
va_list arg;
|
||||
|
||||
va_start(arg, fmt);
|
||||
t = vsmprint(fmt, arg);
|
||||
if(t == nil)
|
||||
sysfatal("out of memory");
|
||||
va_end(arg);
|
||||
s = erealloc(s, strlen(s)+strlen(t)+1);
|
||||
strcat(s, t);
|
||||
free(t);
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
15
src/cmd/factotum/x.c
Normal file
15
src/cmd/factotum/x.c
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
#include <u.h>
|
||||
#include <libc.h>
|
||||
#include <auth.h>
|
||||
|
||||
void
|
||||
f(void*)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
main(void)
|
||||
{
|
||||
f(auth_challenge);
|
||||
f(auth_response);
|
||||
}
|
||||
165
src/cmd/factotum/xio.c
Normal file
165
src/cmd/factotum/xio.c
Normal file
|
|
@ -0,0 +1,165 @@
|
|||
#include "std.h"
|
||||
#include "dat.h"
|
||||
|
||||
static Ioproc *cache[5];
|
||||
static int ncache;
|
||||
|
||||
static Ioproc*
|
||||
xioproc(void)
|
||||
{
|
||||
Ioproc *c;
|
||||
int i;
|
||||
|
||||
for(i=0; i<ncache; i++){
|
||||
if(c = cache[i]){
|
||||
cache[i] = nil;
|
||||
return c;
|
||||
}
|
||||
}
|
||||
|
||||
return ioproc();
|
||||
}
|
||||
|
||||
static void
|
||||
closexioproc(Ioproc *io)
|
||||
{
|
||||
int i;
|
||||
|
||||
for(i=0; i<ncache; i++)
|
||||
if(cache[i] == nil){
|
||||
cache[i] = io;
|
||||
return;
|
||||
}
|
||||
|
||||
closeioproc(io);
|
||||
}
|
||||
|
||||
int
|
||||
xiodial(char *ds, char *local, char *dir, int *cfdp)
|
||||
{
|
||||
int fd;
|
||||
Ioproc *io;
|
||||
|
||||
if((io = xioproc()) == nil)
|
||||
return -1;
|
||||
fd = iodial(io, ds, local, dir, cfdp);
|
||||
closexioproc(io);
|
||||
return fd;
|
||||
}
|
||||
|
||||
void
|
||||
xioclose(int fd)
|
||||
{
|
||||
Ioproc *io;
|
||||
|
||||
if((io = xioproc()) == nil){
|
||||
close(fd);
|
||||
return;
|
||||
}
|
||||
|
||||
ioclose(io, fd);
|
||||
closexioproc(io);
|
||||
}
|
||||
|
||||
int
|
||||
xiowrite(int fd, void *v, int n)
|
||||
{
|
||||
int m;
|
||||
Ioproc *io;
|
||||
|
||||
if((io = xioproc()) == nil)
|
||||
return -1;
|
||||
m = iowrite(io, fd, v, n);
|
||||
closexioproc(io);
|
||||
if(m != n)
|
||||
return -1;
|
||||
return n;
|
||||
}
|
||||
|
||||
static long
|
||||
_ioauthdial(va_list *arg)
|
||||
{
|
||||
char *net;
|
||||
char *dom;
|
||||
int fd;
|
||||
|
||||
net = va_arg(*arg, char*);
|
||||
dom = va_arg(*arg, char*);
|
||||
fd = _authdial(net, dom);
|
||||
if(fd < 0)
|
||||
fprint(2, "authdial: %r");
|
||||
return fd;
|
||||
}
|
||||
|
||||
int
|
||||
xioauthdial(char *net, char *dom)
|
||||
{
|
||||
int fd;
|
||||
Ioproc *io;
|
||||
|
||||
if((io = xioproc()) == nil)
|
||||
return -1;
|
||||
fd = iocall(io, _ioauthdial, net, dom);
|
||||
closexioproc(io);
|
||||
return fd;
|
||||
}
|
||||
|
||||
static long
|
||||
_ioasrdresp(va_list *arg)
|
||||
{
|
||||
int fd;
|
||||
void *a;
|
||||
int n;
|
||||
|
||||
fd = va_arg(*arg, int);
|
||||
a = va_arg(*arg, void*);
|
||||
n = va_arg(*arg, int);
|
||||
|
||||
return _asrdresp(fd, a, n);
|
||||
}
|
||||
|
||||
int
|
||||
xioasrdresp(int fd, void *a, int n)
|
||||
{
|
||||
Ioproc *io;
|
||||
|
||||
if((io = xioproc()) == nil)
|
||||
return -1;
|
||||
|
||||
n = iocall(io, _ioasrdresp, fd, a, n);
|
||||
closexioproc(io);
|
||||
return n;
|
||||
}
|
||||
|
||||
static long
|
||||
_ioasgetticket(va_list *arg)
|
||||
{
|
||||
int asfd;
|
||||
char *trbuf;
|
||||
char *tbuf;
|
||||
|
||||
asfd = va_arg(*arg, int);
|
||||
trbuf = va_arg(*arg, char*);
|
||||
tbuf = va_arg(*arg, char*);
|
||||
|
||||
return _asgetticket(asfd, trbuf, tbuf);
|
||||
}
|
||||
|
||||
int
|
||||
xioasgetticket(int fd, char *trbuf, char *tbuf)
|
||||
{
|
||||
int n;
|
||||
Ioproc *io;
|
||||
|
||||
if((io = xioproc()) == nil)
|
||||
return -1;
|
||||
|
||||
n = iocall(io, _ioasgetticket, fd, trbuf, tbuf);
|
||||
closexioproc(io);
|
||||
if(n != 2*TICKETLEN)
|
||||
n = -1;
|
||||
else
|
||||
n = 0;
|
||||
return n;
|
||||
}
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue