moving to auth
This commit is contained in:
parent
7637c81af0
commit
0f8ec41b0a
@ -1 +0,0 @@
|
|||||||
key, delkey, wipe should be in ctl not rpc.
|
|
||||||
@ -1,348 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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?",
|
|
||||||
};
|
|
||||||
@ -1,231 +0,0 @@
|
|||||||
#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",
|
|
||||||
"disabled",
|
|
||||||
};
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
@ -1,424 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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**)(void*)&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?",
|
|
||||||
};
|
|
||||||
|
|
||||||
@ -1,139 +0,0 @@
|
|||||||
#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);
|
|
||||||
}
|
|
||||||
@ -1,254 +0,0 @@
|
|||||||
#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;
|
|
||||||
}
|
|
||||||
|
|
||||||
File diff suppressed because it is too large
Load Diff
@ -1,158 +0,0 @@
|
|||||||
#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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,227 +0,0 @@
|
|||||||
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 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*);
|
|
||||||
|
|
||||||
extern int extrafactotumdir;
|
|
||||||
|
|
||||||
int havesecstore(void);
|
|
||||||
int secstorefetch(void);
|
|
||||||
File diff suppressed because it is too large
Load Diff
@ -1,531 +0,0 @@
|
|||||||
#include "std.h"
|
|
||||||
#include "dat.h"
|
|
||||||
|
|
||||||
enum
|
|
||||||
{
|
|
||||||
Qroot,
|
|
||||||
Qfactotum,
|
|
||||||
Qrpc,
|
|
||||||
Qkeylist,
|
|
||||||
Qprotolist,
|
|
||||||
Qconfirm,
|
|
||||||
Qlog,
|
|
||||||
Qctl,
|
|
||||||
Qneedkey,
|
|
||||||
Qconv,
|
|
||||||
};
|
|
||||||
|
|
||||||
static int qtop;
|
|
||||||
|
|
||||||
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, qtop);
|
|
||||||
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 && fid->aux){
|
|
||||||
convhangup(fid->aux);
|
|
||||||
convclose(fid->aux);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static Channel *creq;
|
|
||||||
static Channel *cfid, *cfidr;
|
|
||||||
|
|
||||||
static void
|
|
||||||
fsreqthread(void *v)
|
|
||||||
{
|
|
||||||
Req *r;
|
|
||||||
|
|
||||||
USED(v);
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
r->fid->qid = mkqid(QTDIR, qtop);
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
fsstart(Srv *s)
|
|
||||||
{
|
|
||||||
USED(s);
|
|
||||||
|
|
||||||
if(extrafactotumdir)
|
|
||||||
qtop = Qroot;
|
|
||||||
else
|
|
||||||
qtop = Qfactotum;
|
|
||||||
creq = chancreate(sizeof(Req*), 0);
|
|
||||||
cfid = chancreate(sizeof(Fid*), 0);
|
|
||||||
cfidr = chancreate(sizeof(Fid*), 0);
|
|
||||||
proccreate(fsproc, nil, STACK);
|
|
||||||
}
|
|
||||||
|
|
||||||
Srv fs = {
|
|
||||||
.attach= fsattach,
|
|
||||||
.walk1= fswalk1,
|
|
||||||
.open= fssend,
|
|
||||||
.read= fssend,
|
|
||||||
.write= fssend,
|
|
||||||
.stat= fsstat,
|
|
||||||
.flush= fssend,
|
|
||||||
.destroyfid= fssendclunk,
|
|
||||||
.start= fsstart,
|
|
||||||
};
|
|
||||||
|
|
||||||
@ -1,3 +0,0 @@
|
|||||||
kill 8.out|rc
|
|
||||||
unmount /srv/factotum /mnt
|
|
||||||
8.out
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
kill 8.out|rc
|
|
||||||
unmount /mnt/factotum
|
|
||||||
8.out -m /mnt/factotum
|
|
||||||
cat /mnt/factotum/log &
|
|
||||||
unmount /factotum
|
|
||||||
bind 8.out /factotum
|
|
||||||
@ -1,190 +0,0 @@
|
|||||||
#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);
|
|
||||||
}
|
|
||||||
@ -1,121 +0,0 @@
|
|||||||
#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);
|
|
||||||
}
|
|
||||||
|
|
||||||
@ -1,185 +0,0 @@
|
|||||||
#include "std.h"
|
|
||||||
#include "dat.h"
|
|
||||||
#include <9pclient.h>
|
|
||||||
|
|
||||||
int extrafactotumdir;
|
|
||||||
int debug;
|
|
||||||
int trysecstore = 1;
|
|
||||||
char *factname = "factotum";
|
|
||||||
char *service = "factotum";
|
|
||||||
char *owner;
|
|
||||||
char *authaddr;
|
|
||||||
void gflag(char*);
|
|
||||||
|
|
||||||
void
|
|
||||||
usage(void)
|
|
||||||
{
|
|
||||||
fprint(2, "usage: factotum [-Dd] [-a authaddr] [-m mtpt] [-s service]\n");
|
|
||||||
fprint(2, " or factotum -g keypattern\n");
|
|
||||||
fprint(2, " or factotum -g 'badkeyattr\\nmsg\\nkeypattern'\n");
|
|
||||||
threadexitsall("usage");
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
threadmain(int argc, char *argv[])
|
|
||||||
{
|
|
||||||
char *mtpt;
|
|
||||||
char err[ERRMAX];
|
|
||||||
|
|
||||||
// mtpt = "/mnt";
|
|
||||||
mtpt = nil;
|
|
||||||
owner = getuser();
|
|
||||||
quotefmtinstall();
|
|
||||||
fmtinstall('A', attrfmt);
|
|
||||||
fmtinstall('H', encodefmt);
|
|
||||||
fmtinstall('N', attrnamefmt);
|
|
||||||
|
|
||||||
if(argc == 3 && strcmp(argv[1], "-g") == 0){
|
|
||||||
gflag(argv[2]);
|
|
||||||
threadexitsall(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;
|
|
||||||
case 'n':
|
|
||||||
trysecstore = 0;
|
|
||||||
break;
|
|
||||||
case 'x':
|
|
||||||
extrafactotumdir = 1;
|
|
||||||
break;
|
|
||||||
}ARGEND
|
|
||||||
|
|
||||||
if(argc != 0)
|
|
||||||
usage();
|
|
||||||
|
|
||||||
if(trysecstore && havesecstore()){
|
|
||||||
while(secstorefetch() < 0){
|
|
||||||
rerrstr(err, sizeof err);
|
|
||||||
if(strcmp(err, "cancel") == 0)
|
|
||||||
break;
|
|
||||||
fprint(2, "secstorefetch: %r\n");
|
|
||||||
fprint(2, "Enter an empty password to quit.\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 rv;
|
|
||||||
char buf[1024];
|
|
||||||
CFid *fid;
|
|
||||||
|
|
||||||
fid = nsopen("factotum", nil, "ctl", OWRITE);
|
|
||||||
if(fid == nil)
|
|
||||||
sysfatal("opening factotum/ctl: %r");
|
|
||||||
snprint(buf, sizeof buf, "key %A\n", attr);
|
|
||||||
rv = fswrite(fid, buf, strlen(buf));
|
|
||||||
fsclose(fid);
|
|
||||||
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/tty", ORDWR)) < 0)
|
|
||||||
sysfatal("open /dev/tty: %r");
|
|
||||||
|
|
||||||
nf = getfields(s, f, nelem(f), 0, "\n");
|
|
||||||
if(nf == 1){ /* needkey or old badkey */
|
|
||||||
fprint(fd, "\n");
|
|
||||||
askuser(fd, s);
|
|
||||||
threadexitsall(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]);
|
|
||||||
threadexitsall(nil);
|
|
||||||
}
|
|
||||||
usage();
|
|
||||||
}
|
|
||||||
@ -1,33 +0,0 @@
|
|||||||
<$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\
|
|
||||||
secstore.$O\
|
|
||||||
|
|
||||||
HFILES=dat.h
|
|
||||||
|
|
||||||
<$PLAN9/src/mkone
|
|
||||||
|
|
||||||
$O.test: test.$O
|
|
||||||
$LD -o $target $prereq
|
|
||||||
|
|
||||||
@ -1,270 +0,0 @@
|
|||||||
#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 AuthRpcMax;
|
|
||||||
}
|
|
||||||
|
|
||||||
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, *choice;
|
|
||||||
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 = keylookup("%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 */
|
|
||||||
choice = estrappend(nil, "%q %q", f[i], q);
|
|
||||||
if(convwrite(c, choice, strlen(choice)+1) < 0){
|
|
||||||
free(choice);
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
free(choice);
|
|
||||||
|
|
||||||
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,
|
|
||||||
};
|
|
||||||
|
|
||||||
@ -1,545 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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,
|
|
||||||
};
|
|
||||||
@ -1,352 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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,
|
|
||||||
};
|
|
||||||
|
|
||||||
@ -1,100 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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?",
|
|
||||||
};
|
|
||||||
@ -1,45 +0,0 @@
|
|||||||
#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;
|
|
||||||
}
|
|
||||||
|
|
||||||
Attr*
|
|
||||||
addcap(Attr *a, char *from, Ticket *t)
|
|
||||||
{
|
|
||||||
return addattr(a, "cuid=%q suid=%q cap=''", t->cuid, t->suid);
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
_authdial(char *net, char *authdom)
|
|
||||||
{
|
|
||||||
return authdial(net, authdom);
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
@ -1,22 +0,0 @@
|
|||||||
#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;
|
|
||||||
}
|
|
||||||
@ -1,315 +0,0 @@
|
|||||||
#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;
|
|
||||||
}
|
|
||||||
|
|
||||||
@ -1,644 +0,0 @@
|
|||||||
/*
|
|
||||||
* Various files from /sys/src/cmd/auth/secstore, just enough
|
|
||||||
* to download a file at boot time.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "std.h"
|
|
||||||
#include "dat.h"
|
|
||||||
#include <ip.h>
|
|
||||||
|
|
||||||
enum{ CHK = 16};
|
|
||||||
enum{ MAXFILESIZE = 10*1024*1024 };
|
|
||||||
|
|
||||||
enum{// PW status bits
|
|
||||||
Enabled = (1<<0),
|
|
||||||
STA = (1<<1), // extra SecurID step
|
|
||||||
};
|
|
||||||
|
|
||||||
static char testmess[] = "__secstore\tPAK\nC=%s\nm=0\n";
|
|
||||||
char *secstore;
|
|
||||||
|
|
||||||
int
|
|
||||||
secdial(void)
|
|
||||||
{
|
|
||||||
char *p;
|
|
||||||
|
|
||||||
p = secstore;
|
|
||||||
if(p == nil) /* else use the authserver */
|
|
||||||
p = getenv("secstore");
|
|
||||||
if(p == nil)
|
|
||||||
p = getenv("auth");
|
|
||||||
if(p == nil)
|
|
||||||
p = "secstore";
|
|
||||||
|
|
||||||
return dial(netmkaddr(p, "net", "secstore"), 0, 0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int
|
|
||||||
havesecstore(void)
|
|
||||||
{
|
|
||||||
int m, n, fd;
|
|
||||||
uchar buf[500];
|
|
||||||
|
|
||||||
n = snprint((char*)buf, sizeof buf, testmess, owner);
|
|
||||||
hnputs(buf, 0x8000+n-2);
|
|
||||||
|
|
||||||
fd = secdial();
|
|
||||||
if(fd < 0)
|
|
||||||
return 0;
|
|
||||||
if(write(fd, buf, n) != n || readn(fd, buf, 2) != 2){
|
|
||||||
close(fd);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
n = ((buf[0]&0x7f)<<8) + buf[1];
|
|
||||||
if(n+1 > sizeof buf){
|
|
||||||
werrstr("implausibly large count %d", n);
|
|
||||||
close(fd);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
m = readn(fd, buf, n);
|
|
||||||
close(fd);
|
|
||||||
if(m != n){
|
|
||||||
if(m >= 0)
|
|
||||||
werrstr("short read from secstore");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
buf[n] = 0;
|
|
||||||
if(strcmp((char*)buf, "!account expired") == 0){
|
|
||||||
werrstr("account expired");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return strcmp((char*)buf, "!account exists") == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// delimited, authenticated, encrypted connection
|
|
||||||
enum{ Maxmsg=4096 }; // messages > Maxmsg bytes are truncated
|
|
||||||
typedef struct SConn SConn;
|
|
||||||
|
|
||||||
extern SConn* newSConn(int); // arg is open file descriptor
|
|
||||||
struct SConn{
|
|
||||||
void *chan;
|
|
||||||
int secretlen;
|
|
||||||
int (*secret)(SConn*, uchar*, int);//
|
|
||||||
int (*read)(SConn*, uchar*, int); // <0 if error; errmess in buffer
|
|
||||||
int (*write)(SConn*, uchar*, int);
|
|
||||||
void (*free)(SConn*); // also closes file descriptor
|
|
||||||
};
|
|
||||||
// secret(s,b,dir) sets secret for digest, encrypt, using the secretlen
|
|
||||||
// bytes in b to form keys for the two directions;
|
|
||||||
// set dir=0 in client, dir=1 in server
|
|
||||||
|
|
||||||
// error convention: write !message in-band
|
|
||||||
#define readstr secstore_readstr
|
|
||||||
static void writerr(SConn*, char*);
|
|
||||||
static int readstr(SConn*, char*); // call with buf of size Maxmsg+1
|
|
||||||
// returns -1 upon error, with error message in buf
|
|
||||||
|
|
||||||
typedef struct ConnState {
|
|
||||||
uchar secret[SHA1dlen];
|
|
||||||
ulong seqno;
|
|
||||||
RC4state rc4;
|
|
||||||
} ConnState;
|
|
||||||
|
|
||||||
typedef struct SS{
|
|
||||||
int fd; // file descriptor for read/write of encrypted data
|
|
||||||
int alg; // if nonzero, "alg sha rc4_128"
|
|
||||||
ConnState in, out;
|
|
||||||
} SS;
|
|
||||||
|
|
||||||
static int
|
|
||||||
SC_secret(SConn *conn, uchar *sigma, int direction)
|
|
||||||
{
|
|
||||||
SS *ss = (SS*)(conn->chan);
|
|
||||||
int nsigma = conn->secretlen;
|
|
||||||
|
|
||||||
if(direction != 0){
|
|
||||||
hmac_sha1(sigma, nsigma, (uchar*)"one", 3, ss->out.secret, nil);
|
|
||||||
hmac_sha1(sigma, nsigma, (uchar*)"two", 3, ss->in.secret, nil);
|
|
||||||
}else{
|
|
||||||
hmac_sha1(sigma, nsigma, (uchar*)"two", 3, ss->out.secret, nil);
|
|
||||||
hmac_sha1(sigma, nsigma, (uchar*)"one", 3, ss->in.secret, nil);
|
|
||||||
}
|
|
||||||
setupRC4state(&ss->in.rc4, ss->in.secret, 16); // restrict to 128 bits
|
|
||||||
setupRC4state(&ss->out.rc4, ss->out.secret, 16);
|
|
||||||
ss->alg = 1;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
hash(uchar secret[SHA1dlen], uchar *data, int len, int seqno, uchar d[SHA1dlen])
|
|
||||||
{
|
|
||||||
DigestState sha;
|
|
||||||
uchar seq[4];
|
|
||||||
|
|
||||||
seq[0] = seqno>>24;
|
|
||||||
seq[1] = seqno>>16;
|
|
||||||
seq[2] = seqno>>8;
|
|
||||||
seq[3] = seqno;
|
|
||||||
memset(&sha, 0, sizeof sha);
|
|
||||||
sha1(secret, SHA1dlen, nil, &sha);
|
|
||||||
sha1(data, len, nil, &sha);
|
|
||||||
sha1(seq, 4, d, &sha);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
verify(uchar secret[SHA1dlen], uchar *data, int len, int seqno, uchar d[SHA1dlen])
|
|
||||||
{
|
|
||||||
DigestState sha;
|
|
||||||
uchar seq[4];
|
|
||||||
uchar digest[SHA1dlen];
|
|
||||||
|
|
||||||
seq[0] = seqno>>24;
|
|
||||||
seq[1] = seqno>>16;
|
|
||||||
seq[2] = seqno>>8;
|
|
||||||
seq[3] = seqno;
|
|
||||||
memset(&sha, 0, sizeof sha);
|
|
||||||
sha1(secret, SHA1dlen, nil, &sha);
|
|
||||||
sha1(data, len, nil, &sha);
|
|
||||||
sha1(seq, 4, digest, &sha);
|
|
||||||
return memcmp(d, digest, SHA1dlen);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
SC_read(SConn *conn, uchar *buf, int n)
|
|
||||||
{
|
|
||||||
SS *ss = (SS*)(conn->chan);
|
|
||||||
uchar count[2], digest[SHA1dlen];
|
|
||||||
int len, nr;
|
|
||||||
|
|
||||||
if(read(ss->fd, count, 2) != 2 || count[0]&0x80 == 0){
|
|
||||||
werrstr("!SC_read invalid count");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
len = (count[0]&0x7f)<<8 | count[1]; // SSL-style count; no pad
|
|
||||||
if(ss->alg){
|
|
||||||
len -= SHA1dlen;
|
|
||||||
if(len <= 0 || readn(ss->fd, digest, SHA1dlen) != SHA1dlen){
|
|
||||||
werrstr("!SC_read missing sha1");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if(len > n || readn(ss->fd, buf, len) != len){
|
|
||||||
werrstr("!SC_read missing data");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
rc4(&ss->in.rc4, digest, SHA1dlen);
|
|
||||||
rc4(&ss->in.rc4, buf, len);
|
|
||||||
if(verify(ss->in.secret, buf, len, ss->in.seqno, digest) != 0){
|
|
||||||
werrstr("!SC_read integrity check failed");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}else{
|
|
||||||
if(len <= 0 || len > n){
|
|
||||||
werrstr("!SC_read implausible record length");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if( (nr = readn(ss->fd, buf, len)) != len){
|
|
||||||
werrstr("!SC_read expected %d bytes, but got %d", len, nr);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ss->in.seqno++;
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
SC_write(SConn *conn, uchar *buf, int n)
|
|
||||||
{
|
|
||||||
SS *ss = (SS*)(conn->chan);
|
|
||||||
uchar count[2], digest[SHA1dlen], enc[Maxmsg+1];
|
|
||||||
int len;
|
|
||||||
|
|
||||||
if(n <= 0 || n > Maxmsg+1){
|
|
||||||
werrstr("!SC_write invalid n %d", n);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
len = n;
|
|
||||||
if(ss->alg)
|
|
||||||
len += SHA1dlen;
|
|
||||||
count[0] = 0x80 | len>>8;
|
|
||||||
count[1] = len;
|
|
||||||
if(write(ss->fd, count, 2) != 2){
|
|
||||||
werrstr("!SC_write invalid count");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if(ss->alg){
|
|
||||||
hash(ss->out.secret, buf, n, ss->out.seqno, digest);
|
|
||||||
rc4(&ss->out.rc4, digest, SHA1dlen);
|
|
||||||
memcpy(enc, buf, n);
|
|
||||||
rc4(&ss->out.rc4, enc, n);
|
|
||||||
if(write(ss->fd, digest, SHA1dlen) != SHA1dlen ||
|
|
||||||
write(ss->fd, enc, n) != n){
|
|
||||||
werrstr("!SC_write error on send");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}else{
|
|
||||||
if(write(ss->fd, buf, n) != n){
|
|
||||||
werrstr("!SC_write error on send");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ss->out.seqno++;
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
SC_free(SConn *conn)
|
|
||||||
{
|
|
||||||
SS *ss = (SS*)(conn->chan);
|
|
||||||
|
|
||||||
close(ss->fd);
|
|
||||||
free(ss);
|
|
||||||
free(conn);
|
|
||||||
}
|
|
||||||
|
|
||||||
SConn*
|
|
||||||
newSConn(int fd)
|
|
||||||
{
|
|
||||||
SS *ss;
|
|
||||||
SConn *conn;
|
|
||||||
|
|
||||||
if(fd < 0)
|
|
||||||
return nil;
|
|
||||||
ss = (SS*)emalloc(sizeof(*ss));
|
|
||||||
conn = (SConn*)emalloc(sizeof(*conn));
|
|
||||||
ss->fd = fd;
|
|
||||||
ss->alg = 0;
|
|
||||||
conn->chan = (void*)ss;
|
|
||||||
conn->secretlen = SHA1dlen;
|
|
||||||
conn->free = SC_free;
|
|
||||||
conn->secret = SC_secret;
|
|
||||||
conn->read = SC_read;
|
|
||||||
conn->write = SC_write;
|
|
||||||
return conn;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
writerr(SConn *conn, char *s)
|
|
||||||
{
|
|
||||||
char buf[Maxmsg];
|
|
||||||
|
|
||||||
snprint(buf, Maxmsg, "!%s", s);
|
|
||||||
conn->write(conn, (uchar*)buf, strlen(buf));
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
readstr(SConn *conn, char *s)
|
|
||||||
{
|
|
||||||
int n;
|
|
||||||
|
|
||||||
n = conn->read(conn, (uchar*)s, Maxmsg);
|
|
||||||
if(n >= 0){
|
|
||||||
s[n] = 0;
|
|
||||||
if(s[0] == '!'){
|
|
||||||
memmove(s, s+1, n);
|
|
||||||
n = -1;
|
|
||||||
}
|
|
||||||
}else{
|
|
||||||
strcpy(s, "read error");
|
|
||||||
}
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
getfile(SConn *conn, uchar *key, int nkey)
|
|
||||||
{
|
|
||||||
char *buf;
|
|
||||||
int nbuf, n, nr, len;
|
|
||||||
char s[Maxmsg+1], *gf, *p, *q;
|
|
||||||
uchar skey[SHA1dlen], ib[Maxmsg+CHK], *ibr, *ibw;
|
|
||||||
AESstate aes;
|
|
||||||
DigestState *sha;
|
|
||||||
|
|
||||||
gf = "factotum";
|
|
||||||
memset(&aes, 0, sizeof aes);
|
|
||||||
|
|
||||||
snprint(s, Maxmsg, "GET %s\n", gf);
|
|
||||||
conn->write(conn, (uchar*)s, strlen(s));
|
|
||||||
|
|
||||||
/* get file size */
|
|
||||||
s[0] = '\0';
|
|
||||||
if(readstr(conn, s) < 0){
|
|
||||||
werrstr("secstore: %r");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if((len = atoi(s)) < 0){
|
|
||||||
werrstr("secstore: remote file %s does not exist", gf);
|
|
||||||
return -1;
|
|
||||||
}else if(len > MAXFILESIZE){//assert
|
|
||||||
werrstr("secstore: implausible file size %d for %s", len, gf);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
ibr = ibw = ib;
|
|
||||||
buf = nil;
|
|
||||||
nbuf = 0;
|
|
||||||
for(nr=0; nr < len;){
|
|
||||||
if((n = conn->read(conn, ibw, Maxmsg)) <= 0){
|
|
||||||
werrstr("secstore: empty file chunk n=%d nr=%d len=%d: %r", n, nr, len);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
nr += n;
|
|
||||||
ibw += n;
|
|
||||||
if(!aes.setup){ /* first time, read 16 byte IV */
|
|
||||||
if(n < 16){
|
|
||||||
werrstr("secstore: no IV in file");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
sha = sha1((uchar*)"aescbc file", 11, nil, nil);
|
|
||||||
sha1(key, nkey, skey, sha);
|
|
||||||
setupAESstate(&aes, skey, AESbsize, ibr);
|
|
||||||
memset(skey, 0, sizeof skey);
|
|
||||||
ibr += AESbsize;
|
|
||||||
n -= AESbsize;
|
|
||||||
}
|
|
||||||
aesCBCdecrypt(ibw-n, n, &aes);
|
|
||||||
n = ibw-ibr-CHK;
|
|
||||||
if(n > 0){
|
|
||||||
buf = erealloc(buf, nbuf+n+1);
|
|
||||||
memmove(buf+nbuf, ibr, n);
|
|
||||||
nbuf += n;
|
|
||||||
ibr += n;
|
|
||||||
}
|
|
||||||
memmove(ib, ibr, ibw-ibr);
|
|
||||||
ibw = ib + (ibw-ibr);
|
|
||||||
ibr = ib;
|
|
||||||
}
|
|
||||||
n = ibw-ibr;
|
|
||||||
if((n != CHK) || (memcmp(ib, "XXXXXXXXXXXXXXXX", CHK) != 0)){
|
|
||||||
werrstr("secstore: decrypted file failed to authenticate!");
|
|
||||||
free(buf);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if(nbuf == 0){
|
|
||||||
werrstr("secstore got empty file");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
buf[nbuf] = '\0';
|
|
||||||
p = buf;
|
|
||||||
n = 0;
|
|
||||||
while(p){
|
|
||||||
if(q = strchr(p, '\n'))
|
|
||||||
*q++ = '\0';
|
|
||||||
n++;
|
|
||||||
if(ctlwrite(p) < 0)
|
|
||||||
fprint(2, "secstore(%s) line %d: %r\n", gf, n);
|
|
||||||
p = q;
|
|
||||||
}
|
|
||||||
free(buf);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static char VERSION[] = "secstore";
|
|
||||||
|
|
||||||
typedef struct PAKparams{
|
|
||||||
mpint *q, *p, *r, *g;
|
|
||||||
} PAKparams;
|
|
||||||
|
|
||||||
static PAKparams *pak;
|
|
||||||
|
|
||||||
// This group was generated by the seed EB7B6E35F7CD37B511D96C67D6688CC4DD440E1E.
|
|
||||||
static void
|
|
||||||
initPAKparams(void)
|
|
||||||
{
|
|
||||||
if(pak)
|
|
||||||
return;
|
|
||||||
pak = (PAKparams*)emalloc(sizeof(*pak));
|
|
||||||
pak->q = strtomp("E0F0EF284E10796C5A2A511E94748BA03C795C13", nil, 16, nil);
|
|
||||||
pak->p = strtomp("C41CFBE4D4846F67A3DF7DE9921A49D3B42DC33728427AB159CEC8CBBD"
|
|
||||||
"B12B5F0C244F1A734AEB9840804EA3C25036AD1B61AFF3ABBC247CD4B384224567A86"
|
|
||||||
"3A6F020E7EE9795554BCD08ABAD7321AF27E1E92E3DB1C6E7E94FAAE590AE9C48F96D9"
|
|
||||||
"3D178E809401ABE8A534A1EC44359733475A36A70C7B425125062B1142D", nil, 16, nil);
|
|
||||||
pak->r = strtomp("DF310F4E54A5FEC5D86D3E14863921E834113E060F90052AD332B3241CEF"
|
|
||||||
"2497EFA0303D6344F7C819691A0F9C4A773815AF8EAECFB7EC1D98F039F17A32A7E887"
|
|
||||||
"D97251A927D093F44A55577F4D70444AEBD06B9B45695EC23962B175F266895C67D21"
|
|
||||||
"C4656848614D888A4", nil, 16, nil);
|
|
||||||
pak->g = strtomp("2F1C308DC46B9A44B52DF7DACCE1208CCEF72F69C743ADD4D2327173444"
|
|
||||||
"ED6E65E074694246E07F9FD4AE26E0FDDD9F54F813C40CB9BCD4338EA6F242AB94CD41"
|
|
||||||
"0E676C290368A16B1A3594877437E516C53A6EEE5493A038A017E955E218E7819734E3E"
|
|
||||||
"2A6E0BAE08B14258F8C03CC1B30E0DDADFCF7CEDF0727684D3D255F1", nil, 16, nil);
|
|
||||||
}
|
|
||||||
|
|
||||||
// H = (sha(ver,C,sha(passphrase)))^r mod p,
|
|
||||||
// a hash function expensive to attack by brute force.
|
|
||||||
static void
|
|
||||||
longhash(char *ver, char *C, uchar *passwd, mpint *H)
|
|
||||||
{
|
|
||||||
uchar *Cp;
|
|
||||||
int i, n, nver, nC;
|
|
||||||
uchar buf[140], key[1];
|
|
||||||
|
|
||||||
nver = strlen(ver);
|
|
||||||
nC = strlen(C);
|
|
||||||
n = nver + nC + SHA1dlen;
|
|
||||||
Cp = (uchar*)emalloc(n);
|
|
||||||
memmove(Cp, ver, nver);
|
|
||||||
memmove(Cp+nver, C, nC);
|
|
||||||
memmove(Cp+nver+nC, passwd, SHA1dlen);
|
|
||||||
for(i = 0; i < 7; i++){
|
|
||||||
key[0] = 'A'+i;
|
|
||||||
hmac_sha1(Cp, n, key, sizeof key, buf+i*SHA1dlen, nil);
|
|
||||||
}
|
|
||||||
memset(Cp, 0, n);
|
|
||||||
free(Cp);
|
|
||||||
betomp(buf, sizeof buf, H);
|
|
||||||
mpmod(H, pak->p, H);
|
|
||||||
mpexp(H, pak->r, pak->p, H);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hi = H^-1 mod p
|
|
||||||
static char *
|
|
||||||
PAK_Hi(char *C, char *passphrase, mpint *H, mpint *Hi)
|
|
||||||
{
|
|
||||||
uchar passhash[SHA1dlen];
|
|
||||||
|
|
||||||
sha1((uchar *)passphrase, strlen(passphrase), passhash, nil);
|
|
||||||
initPAKparams();
|
|
||||||
longhash(VERSION, C, passhash, H);
|
|
||||||
mpinvert(H, pak->p, Hi);
|
|
||||||
return mptoa(Hi, 64, nil, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// another, faster, hash function for each party to
|
|
||||||
// confirm that the other has the right secrets.
|
|
||||||
static void
|
|
||||||
shorthash(char *mess, char *C, char *S, char *m, char *mu, char *sigma, char *Hi, uchar *digest)
|
|
||||||
{
|
|
||||||
SHA1state *state;
|
|
||||||
|
|
||||||
state = sha1((uchar*)mess, strlen(mess), 0, 0);
|
|
||||||
state = sha1((uchar*)C, strlen(C), 0, state);
|
|
||||||
state = sha1((uchar*)S, strlen(S), 0, state);
|
|
||||||
state = sha1((uchar*)m, strlen(m), 0, state);
|
|
||||||
state = sha1((uchar*)mu, strlen(mu), 0, state);
|
|
||||||
state = sha1((uchar*)sigma, strlen(sigma), 0, state);
|
|
||||||
state = sha1((uchar*)Hi, strlen(Hi), 0, state);
|
|
||||||
state = sha1((uchar*)mess, strlen(mess), 0, state);
|
|
||||||
state = sha1((uchar*)C, strlen(C), 0, state);
|
|
||||||
state = sha1((uchar*)S, strlen(S), 0, state);
|
|
||||||
state = sha1((uchar*)m, strlen(m), 0, state);
|
|
||||||
state = sha1((uchar*)mu, strlen(mu), 0, state);
|
|
||||||
state = sha1((uchar*)sigma, strlen(sigma), 0, state);
|
|
||||||
sha1((uchar*)Hi, strlen(Hi), digest, state);
|
|
||||||
}
|
|
||||||
|
|
||||||
// On input, conn provides an open channel to the server;
|
|
||||||
// C is the name this client calls itself;
|
|
||||||
// pass is the user's passphrase
|
|
||||||
// On output, session secret has been set in conn
|
|
||||||
// (unless return code is negative, which means failure).
|
|
||||||
// If pS is not nil, it is set to the (alloc'd) name the server calls itself.
|
|
||||||
static int
|
|
||||||
PAKclient(SConn *conn, char *C, char *pass, char **pS)
|
|
||||||
{
|
|
||||||
char *mess, *mess2, *eol, *S, *hexmu, *ks, *hexm, *hexsigma = nil, *hexHi;
|
|
||||||
char kc[2*SHA1dlen+1];
|
|
||||||
uchar digest[SHA1dlen];
|
|
||||||
int rc = -1, n;
|
|
||||||
mpint *x, *m = mpnew(0), *mu = mpnew(0), *sigma = mpnew(0);
|
|
||||||
mpint *H = mpnew(0), *Hi = mpnew(0);
|
|
||||||
|
|
||||||
hexHi = PAK_Hi(C, pass, H, Hi);
|
|
||||||
|
|
||||||
// random 1<=x<=q-1; send C, m=g**x H
|
|
||||||
x = mprand(164, genrandom, nil);
|
|
||||||
mpmod(x, pak->q, x);
|
|
||||||
if(mpcmp(x, mpzero) == 0)
|
|
||||||
mpassign(mpone, x);
|
|
||||||
mpexp(pak->g, x, pak->p, m);
|
|
||||||
mpmul(m, H, m);
|
|
||||||
mpmod(m, pak->p, m);
|
|
||||||
hexm = mptoa(m, 64, nil, 0);
|
|
||||||
mess = (char*)emalloc(2*Maxmsg+2);
|
|
||||||
mess2 = mess+Maxmsg+1;
|
|
||||||
snprint(mess, Maxmsg, "%s\tPAK\nC=%s\nm=%s\n", VERSION, C, hexm);
|
|
||||||
conn->write(conn, (uchar*)mess, strlen(mess));
|
|
||||||
|
|
||||||
// recv g**y, S, check hash1(g**xy)
|
|
||||||
if(readstr(conn, mess) < 0){
|
|
||||||
fprint(2, "error: %s\n", mess);
|
|
||||||
writerr(conn, "couldn't read g**y");
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
eol = strchr(mess, '\n');
|
|
||||||
if(strncmp("mu=", mess, 3) != 0 || !eol || strncmp("\nk=", eol, 3) != 0){
|
|
||||||
writerr(conn, "verifier syntax error");
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
hexmu = mess+3;
|
|
||||||
*eol = 0;
|
|
||||||
ks = eol+3;
|
|
||||||
eol = strchr(ks, '\n');
|
|
||||||
if(!eol || strncmp("\nS=", eol, 3) != 0){
|
|
||||||
writerr(conn, "verifier syntax error for secstore 1.0");
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
*eol = 0;
|
|
||||||
S = eol+3;
|
|
||||||
eol = strchr(S, '\n');
|
|
||||||
if(!eol){
|
|
||||||
writerr(conn, "verifier syntax error for secstore 1.0");
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
*eol = 0;
|
|
||||||
if(pS)
|
|
||||||
*pS = estrdup(S);
|
|
||||||
strtomp(hexmu, nil, 64, mu);
|
|
||||||
mpexp(mu, x, pak->p, sigma);
|
|
||||||
hexsigma = mptoa(sigma, 64, nil, 0);
|
|
||||||
shorthash("server", C, S, hexm, hexmu, hexsigma, hexHi, digest);
|
|
||||||
enc64(kc, sizeof kc, digest, SHA1dlen);
|
|
||||||
if(strcmp(ks, kc) != 0){
|
|
||||||
writerr(conn, "verifier didn't match");
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
|
|
||||||
// send hash2(g**xy)
|
|
||||||
shorthash("client", C, S, hexm, hexmu, hexsigma, hexHi, digest);
|
|
||||||
enc64(kc, sizeof kc, digest, SHA1dlen);
|
|
||||||
snprint(mess2, Maxmsg, "k'=%s\n", kc);
|
|
||||||
conn->write(conn, (uchar*)mess2, strlen(mess2));
|
|
||||||
|
|
||||||
// set session key
|
|
||||||
shorthash("session", C, S, hexm, hexmu, hexsigma, hexHi, digest);
|
|
||||||
memset(hexsigma, 0, strlen(hexsigma));
|
|
||||||
n = conn->secret(conn, digest, 0);
|
|
||||||
memset(digest, 0, SHA1dlen);
|
|
||||||
if(n < 0){//assert
|
|
||||||
writerr(conn, "can't set secret");
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
|
|
||||||
rc = 0;
|
|
||||||
done:
|
|
||||||
mpfree(x);
|
|
||||||
mpfree(sigma);
|
|
||||||
mpfree(mu);
|
|
||||||
mpfree(m);
|
|
||||||
mpfree(Hi);
|
|
||||||
mpfree(H);
|
|
||||||
free(hexsigma);
|
|
||||||
free(hexHi);
|
|
||||||
free(hexm);
|
|
||||||
free(mess);
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
secstorefetch(void)
|
|
||||||
{
|
|
||||||
int rv = -1, fd;
|
|
||||||
char s[Maxmsg+1];
|
|
||||||
SConn *conn;
|
|
||||||
char *pass, *sta;
|
|
||||||
|
|
||||||
sta = nil;
|
|
||||||
conn = nil;
|
|
||||||
pass = readcons("secstore password", nil, 1);
|
|
||||||
if(pass==nil || strlen(pass)==0){
|
|
||||||
werrstr("cancel");
|
|
||||||
goto Out;
|
|
||||||
}
|
|
||||||
if((fd = secdial()) < 0)
|
|
||||||
goto Out;
|
|
||||||
if((conn = newSConn(fd)) == nil)
|
|
||||||
goto Out;
|
|
||||||
if(PAKclient(conn, owner, pass, nil) < 0){
|
|
||||||
werrstr("password mistyped?");
|
|
||||||
goto Out;
|
|
||||||
}
|
|
||||||
if(readstr(conn, s) < 0)
|
|
||||||
goto Out;
|
|
||||||
if(strcmp(s, "STA") == 0){
|
|
||||||
sta = readcons("STA PIN+SecureID", nil, 1);
|
|
||||||
if(sta==nil || strlen(sta)==0){
|
|
||||||
werrstr("cancel");
|
|
||||||
goto Out;
|
|
||||||
}
|
|
||||||
if(strlen(sta) >= sizeof s - 3){
|
|
||||||
werrstr("STA response too long");
|
|
||||||
goto Out;
|
|
||||||
}
|
|
||||||
strcpy(s+3, sta);
|
|
||||||
conn->write(conn, (uchar*)s, strlen(s));
|
|
||||||
readstr(conn, s);
|
|
||||||
}
|
|
||||||
if(strcmp(s, "OK") !=0){
|
|
||||||
werrstr("%s", s);
|
|
||||||
goto Out;
|
|
||||||
}
|
|
||||||
if(getfile(conn, (uchar*)pass, strlen(pass)) < 0)
|
|
||||||
goto Out;
|
|
||||||
conn->write(conn, (uchar*)"BYE", 3);
|
|
||||||
rv = 0;
|
|
||||||
|
|
||||||
Out:
|
|
||||||
if(conn)
|
|
||||||
conn->free(conn);
|
|
||||||
if(pass)
|
|
||||||
free(pass);
|
|
||||||
if(sta)
|
|
||||||
free(sta);
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
|
|
||||||
@ -1,135 +0,0 @@
|
|||||||
#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,
|
|
||||||
};
|
|
||||||
@ -1,172 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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,
|
|
||||||
};
|
|
||||||
@ -1,10 +0,0 @@
|
|||||||
#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>
|
|
||||||
|
|
||||||
@ -1,121 +0,0 @@
|
|||||||
#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);
|
|
||||||
}
|
|
||||||
@ -1,11 +0,0 @@
|
|||||||
#!/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
|
|
||||||
|
|
||||||
@ -1,54 +0,0 @@
|
|||||||
#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;
|
|
||||||
int l;
|
|
||||||
va_list arg;
|
|
||||||
|
|
||||||
va_start(arg, fmt);
|
|
||||||
t = vsmprint(fmt, arg);
|
|
||||||
if(t == nil)
|
|
||||||
sysfatal("out of memory");
|
|
||||||
va_end(arg);
|
|
||||||
l = s ? strlen(s) : 0;
|
|
||||||
s = erealloc(s, l+strlen(t)+1);
|
|
||||||
strcpy(s+l, t);
|
|
||||||
free(t);
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@ -1,15 +0,0 @@
|
|||||||
#include <u.h>
|
|
||||||
#include <libc.h>
|
|
||||||
#include <auth.h>
|
|
||||||
|
|
||||||
void
|
|
||||||
f(void*)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
main(void)
|
|
||||||
{
|
|
||||||
f(auth_challenge);
|
|
||||||
f(auth_response);
|
|
||||||
}
|
|
||||||
@ -1,165 +0,0 @@
|
|||||||
#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\n");
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
@ -1,213 +0,0 @@
|
|||||||
#include <u.h>
|
|
||||||
#include <libc.h>
|
|
||||||
#include <mp.h>
|
|
||||||
#include <libsec.h>
|
|
||||||
#include "SConn.h"
|
|
||||||
|
|
||||||
extern int verbose;
|
|
||||||
|
|
||||||
typedef struct ConnState {
|
|
||||||
uchar secret[SHA1dlen];
|
|
||||||
ulong seqno;
|
|
||||||
RC4state rc4;
|
|
||||||
} ConnState;
|
|
||||||
|
|
||||||
typedef struct SS{
|
|
||||||
int fd; // file descriptor for read/write of encrypted data
|
|
||||||
int alg; // if nonzero, "alg sha rc4_128"
|
|
||||||
ConnState in, out;
|
|
||||||
} SS;
|
|
||||||
|
|
||||||
static int
|
|
||||||
SC_secret(SConn *conn, uchar *sigma, int direction)
|
|
||||||
{
|
|
||||||
SS *ss = (SS*)(conn->chan);
|
|
||||||
int nsigma = conn->secretlen;
|
|
||||||
|
|
||||||
if(direction != 0){
|
|
||||||
hmac_sha1(sigma, nsigma, (uchar*)"one", 3, ss->out.secret, nil);
|
|
||||||
hmac_sha1(sigma, nsigma, (uchar*)"two", 3, ss->in.secret, nil);
|
|
||||||
}else{
|
|
||||||
hmac_sha1(sigma, nsigma, (uchar*)"two", 3, ss->out.secret, nil);
|
|
||||||
hmac_sha1(sigma, nsigma, (uchar*)"one", 3, ss->in.secret, nil);
|
|
||||||
}
|
|
||||||
setupRC4state(&ss->in.rc4, ss->in.secret, 16); // restrict to 128 bits
|
|
||||||
setupRC4state(&ss->out.rc4, ss->out.secret, 16);
|
|
||||||
ss->alg = 1;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
hash(uchar secret[SHA1dlen], uchar *data, int len, int seqno, uchar d[SHA1dlen])
|
|
||||||
{
|
|
||||||
DigestState sha;
|
|
||||||
uchar seq[4];
|
|
||||||
|
|
||||||
seq[0] = seqno>>24;
|
|
||||||
seq[1] = seqno>>16;
|
|
||||||
seq[2] = seqno>>8;
|
|
||||||
seq[3] = seqno;
|
|
||||||
memset(&sha, 0, sizeof sha);
|
|
||||||
sha1(secret, SHA1dlen, nil, &sha);
|
|
||||||
sha1(data, len, nil, &sha);
|
|
||||||
sha1(seq, 4, d, &sha);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
verify(uchar secret[SHA1dlen], uchar *data, int len, int seqno, uchar d[SHA1dlen])
|
|
||||||
{
|
|
||||||
DigestState sha;
|
|
||||||
uchar seq[4];
|
|
||||||
uchar digest[SHA1dlen];
|
|
||||||
|
|
||||||
seq[0] = seqno>>24;
|
|
||||||
seq[1] = seqno>>16;
|
|
||||||
seq[2] = seqno>>8;
|
|
||||||
seq[3] = seqno;
|
|
||||||
memset(&sha, 0, sizeof sha);
|
|
||||||
sha1(secret, SHA1dlen, nil, &sha);
|
|
||||||
sha1(data, len, nil, &sha);
|
|
||||||
sha1(seq, 4, digest, &sha);
|
|
||||||
return memcmp(d, digest, SHA1dlen);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
SC_read(SConn *conn, uchar *buf, int n)
|
|
||||||
{
|
|
||||||
SS *ss = (SS*)(conn->chan);
|
|
||||||
uchar count[2], digest[SHA1dlen];
|
|
||||||
int len, nr;
|
|
||||||
|
|
||||||
if(read(ss->fd, count, 2) != 2 || (count[0]&0x80) == 0){
|
|
||||||
snprint((char*)buf,n,"!SC_read invalid count");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
len = (count[0]&0x7f)<<8 | count[1]; // SSL-style count; no pad
|
|
||||||
if(ss->alg){
|
|
||||||
len -= SHA1dlen;
|
|
||||||
if(len <= 0 || readn(ss->fd, digest, SHA1dlen) != SHA1dlen){
|
|
||||||
snprint((char*)buf,n,"!SC_read missing sha1");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if(len > n || readn(ss->fd, buf, len) != len){
|
|
||||||
snprint((char*)buf,n,"!SC_read missing data");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
rc4(&ss->in.rc4, digest, SHA1dlen);
|
|
||||||
rc4(&ss->in.rc4, buf, len);
|
|
||||||
if(verify(ss->in.secret, buf, len, ss->in.seqno, digest) != 0){
|
|
||||||
snprint((char*)buf,n,"!SC_read integrity check failed");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}else{
|
|
||||||
if(len <= 0 || len > n){
|
|
||||||
snprint((char*)buf,n,"!SC_read implausible record length");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if( (nr = readn(ss->fd, buf, len)) != len){
|
|
||||||
snprint((char*)buf,n,"!SC_read expected %d bytes, but got %d", len, nr);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ss->in.seqno++;
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
SC_write(SConn *conn, uchar *buf, int n)
|
|
||||||
{
|
|
||||||
SS *ss = (SS*)(conn->chan);
|
|
||||||
uchar count[2], digest[SHA1dlen], enc[Maxmsg+1];
|
|
||||||
int len;
|
|
||||||
|
|
||||||
if(n <= 0 || n > Maxmsg+1){
|
|
||||||
werrstr("!SC_write invalid n %d", n);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
len = n;
|
|
||||||
if(ss->alg)
|
|
||||||
len += SHA1dlen;
|
|
||||||
count[0] = 0x80 | len>>8;
|
|
||||||
count[1] = len;
|
|
||||||
if(write(ss->fd, count, 2) != 2){
|
|
||||||
werrstr("!SC_write invalid count");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if(ss->alg){
|
|
||||||
hash(ss->out.secret, buf, n, ss->out.seqno, digest);
|
|
||||||
rc4(&ss->out.rc4, digest, SHA1dlen);
|
|
||||||
memcpy(enc, buf, n);
|
|
||||||
rc4(&ss->out.rc4, enc, n);
|
|
||||||
if(write(ss->fd, digest, SHA1dlen) != SHA1dlen ||
|
|
||||||
write(ss->fd, enc, n) != n){
|
|
||||||
werrstr("!SC_write error on send");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}else{
|
|
||||||
if(write(ss->fd, buf, n) != n){
|
|
||||||
werrstr("!SC_write error on send");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ss->out.seqno++;
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
SC_free(SConn *conn)
|
|
||||||
{
|
|
||||||
SS *ss = (SS*)(conn->chan);
|
|
||||||
|
|
||||||
close(ss->fd);
|
|
||||||
free(ss);
|
|
||||||
free(conn);
|
|
||||||
}
|
|
||||||
|
|
||||||
SConn*
|
|
||||||
newSConn(int fd)
|
|
||||||
{
|
|
||||||
SS *ss;
|
|
||||||
SConn *conn;
|
|
||||||
|
|
||||||
if(fd < 0)
|
|
||||||
return nil;
|
|
||||||
ss = (SS*)emalloc(sizeof(*ss));
|
|
||||||
conn = (SConn*)emalloc(sizeof(*conn));
|
|
||||||
ss->fd = fd;
|
|
||||||
ss->alg = 0;
|
|
||||||
conn->chan = (void*)ss;
|
|
||||||
conn->secretlen = SHA1dlen;
|
|
||||||
conn->free = SC_free;
|
|
||||||
conn->secret = SC_secret;
|
|
||||||
conn->read = SC_read;
|
|
||||||
conn->write = SC_write;
|
|
||||||
return conn;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
writerr(SConn *conn, char *s)
|
|
||||||
{
|
|
||||||
char buf[Maxmsg];
|
|
||||||
|
|
||||||
snprint(buf, Maxmsg, "!%s", s);
|
|
||||||
conn->write(conn, (uchar*)buf, strlen(buf));
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
readstr(SConn *conn, char *s)
|
|
||||||
{
|
|
||||||
int n;
|
|
||||||
|
|
||||||
n = conn->read(conn, (uchar*)s, Maxmsg);
|
|
||||||
if(n >= 0){
|
|
||||||
s[n] = 0;
|
|
||||||
if(s[0] == '!'){
|
|
||||||
memmove(s, s+1, n);
|
|
||||||
n = -1;
|
|
||||||
}
|
|
||||||
}else{
|
|
||||||
strcpy(s, "read error");
|
|
||||||
}
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
|
|
||||||
@ -1,26 +0,0 @@
|
|||||||
// delimited, authenticated, encrypted connection
|
|
||||||
enum{ Maxmsg=4096 }; // messages > Maxmsg bytes are truncated
|
|
||||||
typedef struct SConn SConn;
|
|
||||||
|
|
||||||
extern SConn* newSConn(int); // arg is open file descriptor
|
|
||||||
struct SConn{
|
|
||||||
void *chan;
|
|
||||||
int secretlen;
|
|
||||||
int (*secret)(SConn*, uchar*, int);//
|
|
||||||
int (*read)(SConn*, uchar*, int); // <0 if error; errmess in buffer
|
|
||||||
int (*write)(SConn*, uchar*, int);
|
|
||||||
void (*free)(SConn*); // also closes file descriptor
|
|
||||||
};
|
|
||||||
// secret(s,b,dir) sets secret for digest, encrypt, using the secretlen
|
|
||||||
// bytes in b to form keys for the two directions;
|
|
||||||
// set dir=0 in client, dir=1 in server
|
|
||||||
|
|
||||||
// error convention: write !message in-band
|
|
||||||
extern void writerr(SConn*, char*);
|
|
||||||
extern int readstr(SConn*, char*); // call with buf of size Maxmsg+1
|
|
||||||
// returns -1 upon error, with error message in buf
|
|
||||||
|
|
||||||
extern void *emalloc(ulong); /* dies on failure; clears memory */
|
|
||||||
extern void *erealloc(void *, ulong);
|
|
||||||
extern char *estrdup(char *);
|
|
||||||
|
|
||||||
@ -1,156 +0,0 @@
|
|||||||
/* encrypt file by writing
|
|
||||||
v2hdr,
|
|
||||||
16byte initialization vector,
|
|
||||||
AES-CBC(key, random | file),
|
|
||||||
HMAC_SHA1(md5(key), AES-CBC(random | file))
|
|
||||||
*/
|
|
||||||
#include <u.h>
|
|
||||||
#include <libc.h>
|
|
||||||
#include <bio.h>
|
|
||||||
#include <mp.h>
|
|
||||||
#include <libsec.h>
|
|
||||||
|
|
||||||
extern char* getpassm(char*);
|
|
||||||
|
|
||||||
enum{ CHK = 16, BUF = 4096 };
|
|
||||||
|
|
||||||
uchar v2hdr[AESbsize+1] = "AES CBC SHA1 2\n";
|
|
||||||
Biobuf bin;
|
|
||||||
Biobuf bout;
|
|
||||||
|
|
||||||
void
|
|
||||||
safewrite(uchar *buf, int n)
|
|
||||||
{
|
|
||||||
int i = Bwrite(&bout, buf, n);
|
|
||||||
|
|
||||||
if(i == n)
|
|
||||||
return;
|
|
||||||
fprint(2, "write error\n");
|
|
||||||
exits("write error");
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
saferead(uchar *buf, int n)
|
|
||||||
{
|
|
||||||
int i = Bread(&bin, buf, n);
|
|
||||||
|
|
||||||
if(i == n)
|
|
||||||
return;
|
|
||||||
fprint(2, "read error\n");
|
|
||||||
exits("read error");
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
main(int argc, char **argv)
|
|
||||||
{
|
|
||||||
int encrypt = 0; /* 0=decrypt, 1=encrypt */
|
|
||||||
int n, nkey, pass_stdin = 0;
|
|
||||||
char *pass;
|
|
||||||
uchar key[AESmaxkey], key2[SHA1dlen];
|
|
||||||
uchar buf[BUF+SHA1dlen]; /* assumption: CHK <= SHA1dlen */
|
|
||||||
AESstate aes;
|
|
||||||
DigestState *dstate;
|
|
||||||
|
|
||||||
ARGBEGIN{
|
|
||||||
case 'e':
|
|
||||||
encrypt = 1;
|
|
||||||
break;
|
|
||||||
case 'i':
|
|
||||||
pass_stdin = 1;
|
|
||||||
break;
|
|
||||||
}ARGEND;
|
|
||||||
if(argc!=0){
|
|
||||||
fprint(2,"usage: %s -d < cipher.aes > clear.txt\n", argv0);
|
|
||||||
fprint(2," or: %s -e < clear.txt > cipher.aes\n", argv0);
|
|
||||||
exits("usage");
|
|
||||||
}
|
|
||||||
Binit(&bin, 0, OREAD);
|
|
||||||
Binit(&bout, 1, OWRITE);
|
|
||||||
|
|
||||||
if(pass_stdin){
|
|
||||||
n = readn(3, buf, (sizeof buf)-1);
|
|
||||||
if(n < 1)
|
|
||||||
exits("usage: echo password |[3=1] auth/aescbc -i ...");
|
|
||||||
buf[n] = 0;
|
|
||||||
while(buf[n-1] == '\n')
|
|
||||||
buf[--n] = 0;
|
|
||||||
}else{
|
|
||||||
pass = readcons("aescbc key", nil, 1);
|
|
||||||
n = strlen(pass);
|
|
||||||
if(n >= BUF)
|
|
||||||
exits("key too long");
|
|
||||||
strcpy((char*)buf, pass);
|
|
||||||
memset(pass, 0, n);
|
|
||||||
free(pass);
|
|
||||||
}
|
|
||||||
if(n <= 0){
|
|
||||||
fprint(2,"no key\n");
|
|
||||||
exits("key");
|
|
||||||
}
|
|
||||||
dstate = sha1((uchar*)"aescbc file", 11, nil, nil);
|
|
||||||
sha1(buf, n, key2, dstate);
|
|
||||||
memcpy(key, key2, 16);
|
|
||||||
nkey = 16;
|
|
||||||
md5(key, nkey, key2, 0); /* so even if HMAC_SHA1 is broken, encryption key is protected */
|
|
||||||
|
|
||||||
if(encrypt){
|
|
||||||
safewrite(v2hdr, AESbsize);
|
|
||||||
genrandom(buf,2*AESbsize); /* CBC is semantically secure if IV is unpredictable. */
|
|
||||||
setupAESstate(&aes, key, nkey, buf); /* use first AESbsize bytes as IV */
|
|
||||||
aesCBCencrypt(buf+AESbsize, AESbsize, &aes); /* use second AESbsize bytes as initial plaintext */
|
|
||||||
safewrite(buf, 2*AESbsize);
|
|
||||||
dstate = hmac_sha1(buf+AESbsize, AESbsize, key2, MD5dlen, 0, 0);
|
|
||||||
while(1){
|
|
||||||
n = Bread(&bin, buf, BUF);
|
|
||||||
if(n < 0){
|
|
||||||
fprint(2,"read error\n");
|
|
||||||
exits("read error");
|
|
||||||
}
|
|
||||||
aesCBCencrypt(buf, n, &aes);
|
|
||||||
safewrite(buf, n);
|
|
||||||
dstate = hmac_sha1(buf, n, key2, MD5dlen, 0, dstate);
|
|
||||||
if(n < BUF)
|
|
||||||
break; /* EOF */
|
|
||||||
}
|
|
||||||
hmac_sha1(0, 0, key2, MD5dlen, buf, dstate);
|
|
||||||
safewrite(buf, SHA1dlen);
|
|
||||||
}else{ /* decrypt */
|
|
||||||
saferead(buf, AESbsize);
|
|
||||||
if(memcmp(buf, v2hdr, AESbsize) == 0){
|
|
||||||
saferead(buf, 2*AESbsize); /* read IV and random initial plaintext */
|
|
||||||
setupAESstate(&aes, key, nkey, buf);
|
|
||||||
dstate = hmac_sha1(buf+AESbsize, AESbsize, key2, MD5dlen, 0, 0);
|
|
||||||
aesCBCdecrypt(buf+AESbsize, AESbsize, &aes);
|
|
||||||
saferead(buf, SHA1dlen);
|
|
||||||
while((n = Bread(&bin, buf+SHA1dlen, BUF)) > 0){
|
|
||||||
dstate = hmac_sha1(buf, n, key2, MD5dlen, 0, dstate);
|
|
||||||
aesCBCdecrypt(buf, n, &aes);
|
|
||||||
safewrite(buf, n);
|
|
||||||
memmove(buf, buf+n, SHA1dlen); /* these bytes are not yet decrypted */
|
|
||||||
}
|
|
||||||
hmac_sha1(0, 0, key2, MD5dlen, buf+SHA1dlen, dstate);
|
|
||||||
if(memcmp(buf, buf+SHA1dlen, SHA1dlen) != 0){
|
|
||||||
fprint(2,"decrypted file failed to authenticate\n");
|
|
||||||
exits("decrypted file failed to authenticate");
|
|
||||||
}
|
|
||||||
}else{ /* compatibility with past mistake */
|
|
||||||
// if file was encrypted with bad aescbc use this:
|
|
||||||
// memset(key, 0, AESmaxkey);
|
|
||||||
// else assume we're decrypting secstore files
|
|
||||||
setupAESstate(&aes, key, AESbsize, buf);
|
|
||||||
saferead(buf, CHK);
|
|
||||||
aesCBCdecrypt(buf, CHK, &aes);
|
|
||||||
while((n = Bread(&bin, buf+CHK, BUF)) > 0){
|
|
||||||
aesCBCdecrypt(buf+CHK, n, &aes);
|
|
||||||
safewrite(buf, n);
|
|
||||||
memmove(buf, buf+n, CHK);
|
|
||||||
}
|
|
||||||
if(memcmp(buf, "XXXXXXXXXXXXXXXX", CHK) != 0){
|
|
||||||
fprint(2,"decrypted file failed to authenticate\n");
|
|
||||||
exits("decrypted file failed to authenticate");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
exits("");
|
|
||||||
return 1; /* gcc */
|
|
||||||
}
|
|
||||||
@ -1,87 +0,0 @@
|
|||||||
#include <u.h>
|
|
||||||
#include <libc.h>
|
|
||||||
#include <mp.h>
|
|
||||||
#include <libsec.h>
|
|
||||||
#include "SConn.h"
|
|
||||||
|
|
||||||
static long
|
|
||||||
ls(char *p, Dir **dirbuf)
|
|
||||||
{
|
|
||||||
int fd;
|
|
||||||
long n;
|
|
||||||
Dir *db;
|
|
||||||
|
|
||||||
if((db = dirstat(p)) == nil ||
|
|
||||||
!(db->qid.type & QTDIR) ||
|
|
||||||
(fd = open(p, OREAD)) < 0 )
|
|
||||||
return -1;
|
|
||||||
free(db);
|
|
||||||
n = dirreadall(fd, dirbuf);
|
|
||||||
close(fd);
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
|
|
||||||
static uchar*
|
|
||||||
sha1file(char *pfx, char *nm)
|
|
||||||
{
|
|
||||||
int n, fd, len;
|
|
||||||
char *tmp;
|
|
||||||
uchar buf[8192];
|
|
||||||
static uchar digest[SHA1dlen];
|
|
||||||
DigestState *s;
|
|
||||||
|
|
||||||
len = strlen(pfx)+1+strlen(nm)+1;
|
|
||||||
tmp = emalloc(len);
|
|
||||||
snprint(tmp, len, "%s/%s", pfx, nm);
|
|
||||||
if((fd = open(tmp, OREAD)) < 0){
|
|
||||||
free(tmp);
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
free(tmp);
|
|
||||||
s = nil;
|
|
||||||
while((n = read(fd, buf, sizeof buf)) > 0)
|
|
||||||
s = sha1(buf, n, nil, s);
|
|
||||||
close(fd);
|
|
||||||
sha1(nil, 0, digest, s);
|
|
||||||
return digest;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
compare(Dir *a, Dir *b)
|
|
||||||
{
|
|
||||||
return strcmp(a->name, b->name);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* list the (name mtime size sum) of regular, readable files in path */
|
|
||||||
char *
|
|
||||||
dirls(char *path)
|
|
||||||
{
|
|
||||||
char *list, *date, dig[30], buf[128];
|
|
||||||
int m, nmwid, lenwid;
|
|
||||||
long i, n, ndir, len;
|
|
||||||
Dir *dirbuf;
|
|
||||||
|
|
||||||
if(path==nil || (ndir = ls(path, &dirbuf)) < 0)
|
|
||||||
return nil;
|
|
||||||
|
|
||||||
qsort(dirbuf, ndir, sizeof dirbuf[0], (int (*)(const void *, const void *))compare);
|
|
||||||
for(nmwid=lenwid=i=0; i<ndir; i++){
|
|
||||||
if((m = strlen(dirbuf[i].name)) > nmwid)
|
|
||||||
nmwid = m;
|
|
||||||
snprint(buf, sizeof(buf), "%ulld", dirbuf[i].length);
|
|
||||||
if((m = strlen(buf)) > lenwid)
|
|
||||||
lenwid = m;
|
|
||||||
}
|
|
||||||
for(list=nil, len=0, i=0; i<ndir; i++){
|
|
||||||
date = ctime(dirbuf[i].mtime);
|
|
||||||
date[28] = 0; // trim newline
|
|
||||||
n = snprint(buf, sizeof buf, "%*ulld %s", lenwid, dirbuf[i].length, date+4);
|
|
||||||
n += enc64(dig, sizeof dig, sha1file(path, dirbuf[i].name), SHA1dlen);
|
|
||||||
n += nmwid+3+strlen(dirbuf[i].name);
|
|
||||||
list = erealloc(list, len+n+1);
|
|
||||||
len += snprint(list+len, n+1, "%-*s\t%s %s\n", nmwid, dirbuf[i].name, buf, dig);
|
|
||||||
}
|
|
||||||
free(dirbuf);
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
|
|
||||||
@ -1,27 +0,0 @@
|
|||||||
<$PLAN9/src/mkhdr
|
|
||||||
|
|
||||||
BIN=$PLAN9/bin
|
|
||||||
#CFLAGS=-Fw
|
|
||||||
HFILES =\
|
|
||||||
SConn.h\
|
|
||||||
secstore.h\
|
|
||||||
|
|
||||||
OFILES =\
|
|
||||||
pak.$O\
|
|
||||||
password.$O\
|
|
||||||
SConn.$O\
|
|
||||||
util.$O\
|
|
||||||
|
|
||||||
|
|
||||||
TARG=aescbc secstore secstored secuser
|
|
||||||
|
|
||||||
<$PLAN9/src/mkmany
|
|
||||||
|
|
||||||
$O.aescbc: aescbc.$O util.$O
|
|
||||||
$LD -o $target $prereq $LDFLAGS
|
|
||||||
|
|
||||||
$O.secstored: secstored.$O dirls.$O secureidcheck.$O $OFILES
|
|
||||||
$LD -o $target $prereq
|
|
||||||
|
|
||||||
$O.secuser: secuser.$O $OFILES
|
|
||||||
$LD -o $target $prereq
|
|
||||||
@ -1,344 +0,0 @@
|
|||||||
// PAK is an encrypted key exchange protocol designed by Philip MacKenzie et al.
|
|
||||||
// It is patented and use outside Plan 9 requires you get a license.
|
|
||||||
// (All other EKE protocols are patented as well, by Lucent or others.)
|
|
||||||
#include <u.h>
|
|
||||||
#include <libc.h>
|
|
||||||
#include <mp.h>
|
|
||||||
#include <libsec.h>
|
|
||||||
#include "SConn.h"
|
|
||||||
#include "secstore.h"
|
|
||||||
|
|
||||||
extern int verbose;
|
|
||||||
|
|
||||||
char VERSION[] = "secstore";
|
|
||||||
static char *feedback[] = {"alpha","bravo","charlie","delta","echo","foxtrot","golf","hotel"};
|
|
||||||
|
|
||||||
typedef struct PAKparams{
|
|
||||||
mpint *q, *p, *r, *g;
|
|
||||||
} PAKparams;
|
|
||||||
|
|
||||||
static PAKparams *pak;
|
|
||||||
|
|
||||||
// from seed EB7B6E35F7CD37B511D96C67D6688CC4DD440E1E
|
|
||||||
static void
|
|
||||||
initPAKparams(void)
|
|
||||||
{
|
|
||||||
if(pak)
|
|
||||||
return;
|
|
||||||
pak = (PAKparams*)emalloc(sizeof(*pak));
|
|
||||||
pak->q = strtomp("E0F0EF284E10796C5A2A511E94748BA03C795C13", nil, 16, nil);
|
|
||||||
pak->p = strtomp("C41CFBE4D4846F67A3DF7DE9921A49D3B42DC33728427AB159CEC8CBB"
|
|
||||||
"DB12B5F0C244F1A734AEB9840804EA3C25036AD1B61AFF3ABBC247CD4B384224567A86"
|
|
||||||
"3A6F020E7EE9795554BCD08ABAD7321AF27E1E92E3DB1C6E7E94FAAE590AE9C48F96D9"
|
|
||||||
"3D178E809401ABE8A534A1EC44359733475A36A70C7B425125062B1142D",
|
|
||||||
nil, 16, nil);
|
|
||||||
pak->r = strtomp("DF310F4E54A5FEC5D86D3E14863921E834113E060F90052AD332B3241"
|
|
||||||
"CEF2497EFA0303D6344F7C819691A0F9C4A773815AF8EAECFB7EC1D98F039F17A32A7E"
|
|
||||||
"887D97251A927D093F44A55577F4D70444AEBD06B9B45695EC23962B175F266895C67D"
|
|
||||||
"21C4656848614D888A4", nil, 16, nil);
|
|
||||||
pak->g = strtomp("2F1C308DC46B9A44B52DF7DACCE1208CCEF72F69C743ADD4D23271734"
|
|
||||||
"44ED6E65E074694246E07F9FD4AE26E0FDDD9F54F813C40CB9BCD4338EA6F242AB94CD"
|
|
||||||
"410E676C290368A16B1A3594877437E516C53A6EEE5493A038A017E955E218E7819734"
|
|
||||||
"E3E2A6E0BAE08B14258F8C03CC1B30E0DDADFCF7CEDF0727684D3D255F1",
|
|
||||||
nil, 16, nil);
|
|
||||||
}
|
|
||||||
|
|
||||||
// H = (sha(ver,C,sha(passphrase)))^r mod p,
|
|
||||||
// a hash function expensive to attack by brute force.
|
|
||||||
static void
|
|
||||||
longhash(char *ver, char *C, uchar *passwd, mpint *H)
|
|
||||||
{
|
|
||||||
uchar *Cp;
|
|
||||||
int i, n, nver, nC;
|
|
||||||
uchar buf[140], key[1];
|
|
||||||
|
|
||||||
nver = strlen(ver);
|
|
||||||
nC = strlen(C);
|
|
||||||
n = nver + nC + SHA1dlen;
|
|
||||||
Cp = (uchar*)emalloc(n);
|
|
||||||
memmove(Cp, ver, nver);
|
|
||||||
memmove(Cp+nver, C, nC);
|
|
||||||
memmove(Cp+nver+nC, passwd, SHA1dlen);
|
|
||||||
for(i = 0; i < 7; i++){
|
|
||||||
key[0] = 'A'+i;
|
|
||||||
hmac_sha1(Cp, n, key, sizeof key, buf+i*SHA1dlen, nil);
|
|
||||||
}
|
|
||||||
memset(Cp, 0, n);
|
|
||||||
free(Cp);
|
|
||||||
betomp(buf, sizeof buf, H);
|
|
||||||
mpmod(H, pak->p, H);
|
|
||||||
mpexp(H, pak->r, pak->p, H);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hi = H^-1 mod p
|
|
||||||
char *
|
|
||||||
PAK_Hi(char *C, char *passphrase, mpint *H, mpint *Hi)
|
|
||||||
{
|
|
||||||
uchar passhash[SHA1dlen];
|
|
||||||
|
|
||||||
sha1((uchar *)passphrase, strlen(passphrase), passhash, nil);
|
|
||||||
initPAKparams();
|
|
||||||
longhash(VERSION, C, passhash, H);
|
|
||||||
mpinvert(H, pak->p, Hi);
|
|
||||||
return mptoa(Hi, 64, nil, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// another, faster, hash function for each party to
|
|
||||||
// confirm that the other has the right secrets.
|
|
||||||
static void
|
|
||||||
shorthash(char *mess, char *C, char *S, char *m, char *mu, char *sigma, char *Hi, uchar *digest)
|
|
||||||
{
|
|
||||||
SHA1state *state;
|
|
||||||
|
|
||||||
state = sha1((uchar*)mess, strlen(mess), 0, 0);
|
|
||||||
state = sha1((uchar*)C, strlen(C), 0, state);
|
|
||||||
state = sha1((uchar*)S, strlen(S), 0, state);
|
|
||||||
state = sha1((uchar*)m, strlen(m), 0, state);
|
|
||||||
state = sha1((uchar*)mu, strlen(mu), 0, state);
|
|
||||||
state = sha1((uchar*)sigma, strlen(sigma), 0, state);
|
|
||||||
state = sha1((uchar*)Hi, strlen(Hi), 0, state);
|
|
||||||
state = sha1((uchar*)mess, strlen(mess), 0, state);
|
|
||||||
state = sha1((uchar*)C, strlen(C), 0, state);
|
|
||||||
state = sha1((uchar*)S, strlen(S), 0, state);
|
|
||||||
state = sha1((uchar*)m, strlen(m), 0, state);
|
|
||||||
state = sha1((uchar*)mu, strlen(mu), 0, state);
|
|
||||||
state = sha1((uchar*)sigma, strlen(sigma), 0, state);
|
|
||||||
sha1((uchar*)Hi, strlen(Hi), digest, state);
|
|
||||||
}
|
|
||||||
|
|
||||||
// On input, conn provides an open channel to the server;
|
|
||||||
// C is the name this client calls itself;
|
|
||||||
// pass is the user's passphrase
|
|
||||||
// On output, session secret has been set in conn
|
|
||||||
// (unless return code is negative, which means failure).
|
|
||||||
// If pS is not nil, it is set to the (alloc'd) name the server calls itself.
|
|
||||||
int
|
|
||||||
PAKclient(SConn *conn, char *C, char *pass, char **pS)
|
|
||||||
{
|
|
||||||
char *mess, *mess2, *eol, *S, *hexmu, *ks, *hexm, *hexsigma = nil, *hexHi;
|
|
||||||
char kc[2*SHA1dlen+1];
|
|
||||||
uchar digest[SHA1dlen];
|
|
||||||
int rc = -1, n;
|
|
||||||
mpint *x, *m = mpnew(0), *mu = mpnew(0), *sigma = mpnew(0);
|
|
||||||
mpint *H = mpnew(0), *Hi = mpnew(0);
|
|
||||||
|
|
||||||
hexHi = PAK_Hi(C, pass, H, Hi);
|
|
||||||
if(verbose)
|
|
||||||
fprint(2,"%s\n", feedback[H->p[0]&0x7]); // provide a clue to catch typos
|
|
||||||
|
|
||||||
// random 1<=x<=q-1; send C, m=g**x H
|
|
||||||
x = mprand(240, genrandom, nil);
|
|
||||||
mpmod(x, pak->q, x);
|
|
||||||
if(mpcmp(x, mpzero) == 0)
|
|
||||||
mpassign(mpone, x);
|
|
||||||
mpexp(pak->g, x, pak->p, m);
|
|
||||||
mpmul(m, H, m);
|
|
||||||
mpmod(m, pak->p, m);
|
|
||||||
hexm = mptoa(m, 64, nil, 0);
|
|
||||||
mess = (char*)emalloc(2*Maxmsg+2);
|
|
||||||
mess2 = mess+Maxmsg+1;
|
|
||||||
snprint(mess, Maxmsg, "%s\tPAK\nC=%s\nm=%s\n", VERSION, C, hexm);
|
|
||||||
conn->write(conn, (uchar*)mess, strlen(mess));
|
|
||||||
|
|
||||||
// recv g**y, S, check hash1(g**xy)
|
|
||||||
if(readstr(conn, mess) < 0){
|
|
||||||
fprint(2, "error: %s\n", mess);
|
|
||||||
writerr(conn, "couldn't read g**y");
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
eol = strchr(mess, '\n');
|
|
||||||
if(strncmp("mu=", mess, 3) != 0 || !eol || strncmp("\nk=", eol, 3) != 0){
|
|
||||||
writerr(conn, "verifier syntax error");
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
hexmu = mess+3;
|
|
||||||
*eol = 0;
|
|
||||||
ks = eol+3;
|
|
||||||
eol = strchr(ks, '\n');
|
|
||||||
if(!eol || strncmp("\nS=", eol, 3) != 0){
|
|
||||||
writerr(conn, "verifier syntax error for secstore 1.0");
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
*eol = 0;
|
|
||||||
S = eol+3;
|
|
||||||
eol = strchr(S, '\n');
|
|
||||||
if(!eol){
|
|
||||||
writerr(conn, "verifier syntax error for secstore 1.0");
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
*eol = 0;
|
|
||||||
if(pS)
|
|
||||||
*pS = estrdup(S);
|
|
||||||
strtomp(hexmu, nil, 64, mu);
|
|
||||||
mpexp(mu, x, pak->p, sigma);
|
|
||||||
hexsigma = mptoa(sigma, 64, nil, 0);
|
|
||||||
shorthash("server", C, S, hexm, hexmu, hexsigma, hexHi, digest);
|
|
||||||
enc64(kc, sizeof kc, digest, SHA1dlen);
|
|
||||||
if(strcmp(ks, kc) != 0){
|
|
||||||
writerr(conn, "verifier didn't match");
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
|
|
||||||
// send hash2(g**xy)
|
|
||||||
shorthash("client", C, S, hexm, hexmu, hexsigma, hexHi, digest);
|
|
||||||
enc64(kc, sizeof kc, digest, SHA1dlen);
|
|
||||||
snprint(mess2, Maxmsg, "k'=%s\n", kc);
|
|
||||||
conn->write(conn, (uchar*)mess2, strlen(mess2));
|
|
||||||
|
|
||||||
// set session key
|
|
||||||
shorthash("session", C, S, hexm, hexmu, hexsigma, hexHi, digest);
|
|
||||||
memset(hexsigma, 0, strlen(hexsigma));
|
|
||||||
n = conn->secret(conn, digest, 0);
|
|
||||||
memset(digest, 0, SHA1dlen);
|
|
||||||
if(n < 0){
|
|
||||||
writerr(conn, "can't set secret");
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
|
|
||||||
rc = 0;
|
|
||||||
done:
|
|
||||||
mpfree(x);
|
|
||||||
mpfree(sigma);
|
|
||||||
mpfree(mu);
|
|
||||||
mpfree(m);
|
|
||||||
mpfree(Hi);
|
|
||||||
mpfree(H);
|
|
||||||
free(hexsigma);
|
|
||||||
free(hexHi);
|
|
||||||
free(hexm);
|
|
||||||
free(mess);
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
// On input,
|
|
||||||
// mess contains first message;
|
|
||||||
// name is name this server should call itself.
|
|
||||||
// On output, session secret has been set in conn;
|
|
||||||
// if pw!=nil, then *pw points to PW struct for authenticated user.
|
|
||||||
// returns -1 if error
|
|
||||||
int
|
|
||||||
PAKserver(SConn *conn, char *S, char *mess, PW **pwp)
|
|
||||||
{
|
|
||||||
int rc = -1, n;
|
|
||||||
char mess2[Maxmsg+1], *eol;
|
|
||||||
char *C, ks[41], *kc, *hexm, *hexmu = nil, *hexsigma = nil, *hexHi = nil;
|
|
||||||
uchar digest[SHA1dlen];
|
|
||||||
mpint *H = mpnew(0), *Hi = mpnew(0);
|
|
||||||
mpint *y = nil, *m = mpnew(0), *mu = mpnew(0), *sigma = mpnew(0);
|
|
||||||
PW *pw = nil;
|
|
||||||
|
|
||||||
// secstore version and algorithm
|
|
||||||
snprint(mess2,Maxmsg,"%s\tPAK\n", VERSION);
|
|
||||||
n = strlen(mess2);
|
|
||||||
if(strncmp(mess,mess2,n) != 0){
|
|
||||||
writerr(conn, "protocol should start with ver alg");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
mess += n;
|
|
||||||
initPAKparams();
|
|
||||||
|
|
||||||
// parse first message into C, m
|
|
||||||
eol = strchr(mess, '\n');
|
|
||||||
if(strncmp("C=", mess, 2) != 0 || !eol){
|
|
||||||
fprint(2,"mess[1]=%s\n", mess);
|
|
||||||
writerr(conn, "PAK version mismatch");
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
C = mess+2;
|
|
||||||
*eol = 0;
|
|
||||||
hexm = eol+3;
|
|
||||||
eol = strchr(hexm, '\n');
|
|
||||||
if(strncmp("m=", hexm-2, 2) != 0 || !eol){
|
|
||||||
writerr(conn, "PAK version mismatch");
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
*eol = 0;
|
|
||||||
strtomp(hexm, nil, 64, m);
|
|
||||||
mpmod(m, pak->p, m);
|
|
||||||
|
|
||||||
// lookup client
|
|
||||||
if((pw = getPW(C,0)) == nil) {
|
|
||||||
snprint(mess2, sizeof mess2, "%r");
|
|
||||||
writerr(conn, mess2);
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
if(mpcmp(m, mpzero) == 0) {
|
|
||||||
writerr(conn, "account exists");
|
|
||||||
freePW(pw);
|
|
||||||
pw = nil;
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
hexHi = mptoa(pw->Hi, 64, nil, 0);
|
|
||||||
|
|
||||||
// random y, mu=g**y, sigma=g**xy
|
|
||||||
y = mprand(240, genrandom, nil);
|
|
||||||
mpmod(y, pak->q, y);
|
|
||||||
if(mpcmp(y, mpzero) == 0){
|
|
||||||
mpassign(mpone, y);
|
|
||||||
}
|
|
||||||
mpexp(pak->g, y, pak->p, mu);
|
|
||||||
mpmul(m, pw->Hi, m);
|
|
||||||
mpmod(m, pak->p, m);
|
|
||||||
mpexp(m, y, pak->p, sigma);
|
|
||||||
|
|
||||||
// send g**y, hash1(g**xy)
|
|
||||||
hexmu = mptoa(mu, 64, nil, 0);
|
|
||||||
hexsigma = mptoa(sigma, 64, nil, 0);
|
|
||||||
shorthash("server", C, S, hexm, hexmu, hexsigma, hexHi, digest);
|
|
||||||
enc64(ks, sizeof ks, digest, SHA1dlen);
|
|
||||||
snprint(mess2, sizeof mess2, "mu=%s\nk=%s\nS=%s\n", hexmu, ks, S);
|
|
||||||
conn->write(conn, (uchar*)mess2, strlen(mess2));
|
|
||||||
|
|
||||||
// recv hash2(g**xy)
|
|
||||||
if(readstr(conn, mess2) < 0){
|
|
||||||
writerr(conn, "couldn't read verifier");
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
eol = strchr(mess2, '\n');
|
|
||||||
if(strncmp("k'=", mess2, 3) != 0 || !eol){
|
|
||||||
writerr(conn, "verifier syntax error");
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
kc = mess2+3;
|
|
||||||
*eol = 0;
|
|
||||||
shorthash("client", C, S, hexm, hexmu, hexsigma, hexHi, digest);
|
|
||||||
enc64(ks, sizeof ks, digest, SHA1dlen);
|
|
||||||
if(strcmp(ks, kc) != 0) {
|
|
||||||
rc = -2;
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
|
|
||||||
// set session key
|
|
||||||
shorthash("session", C, S, hexm, hexmu, hexsigma, hexHi, digest);
|
|
||||||
n = conn->secret(conn, digest, 1);
|
|
||||||
if(n < 0){
|
|
||||||
writerr(conn, "can't set secret");
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
|
|
||||||
rc = 0;
|
|
||||||
done:
|
|
||||||
if(rc<0 && pw){
|
|
||||||
pw->failed++;
|
|
||||||
putPW(pw);
|
|
||||||
}
|
|
||||||
if(rc==0 && pw && pw->failed>0){
|
|
||||||
pw->failed = 0;
|
|
||||||
putPW(pw);
|
|
||||||
}
|
|
||||||
if(pwp)
|
|
||||||
*pwp = pw;
|
|
||||||
else
|
|
||||||
freePW(pw);
|
|
||||||
free(hexsigma);
|
|
||||||
free(hexHi);
|
|
||||||
free(hexmu);
|
|
||||||
mpfree(y);
|
|
||||||
mpfree(sigma);
|
|
||||||
mpfree(mu);
|
|
||||||
mpfree(m);
|
|
||||||
mpfree(Hi);
|
|
||||||
mpfree(H);
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
@ -1,136 +0,0 @@
|
|||||||
/* password.c */
|
|
||||||
#include <u.h>
|
|
||||||
#include <libc.h>
|
|
||||||
#include <bio.h>
|
|
||||||
#include <mp.h>
|
|
||||||
#include <libsec.h>
|
|
||||||
#include "SConn.h"
|
|
||||||
#include "secstore.h"
|
|
||||||
|
|
||||||
static Biobuf*
|
|
||||||
openPW(char *id, int mode)
|
|
||||||
{
|
|
||||||
Biobuf *b;
|
|
||||||
int nfn = strlen(SECSTORE_DIR)+strlen(id)+20;
|
|
||||||
char *fn = emalloc(nfn);
|
|
||||||
|
|
||||||
snprint(fn, nfn, "%s/who/%s", SECSTORE_DIR, id);
|
|
||||||
b = Bopen(fn, mode);
|
|
||||||
free(fn);
|
|
||||||
return b;
|
|
||||||
}
|
|
||||||
|
|
||||||
static ulong
|
|
||||||
mtimePW(char *id)
|
|
||||||
{
|
|
||||||
Dir *d;
|
|
||||||
int nfn = strlen(SECSTORE_DIR)+strlen(id)+20;
|
|
||||||
char *fn = emalloc(nfn);
|
|
||||||
ulong mt;
|
|
||||||
|
|
||||||
snprint(fn, nfn, "%s/who/%s", SECSTORE_DIR, id);
|
|
||||||
d = dirstat(fn);
|
|
||||||
free(fn);
|
|
||||||
mt = d->mtime;
|
|
||||||
free(d);
|
|
||||||
return mt;
|
|
||||||
}
|
|
||||||
|
|
||||||
PW *
|
|
||||||
getPW(char *id, int dead_or_alive)
|
|
||||||
{
|
|
||||||
uint now = time(0);
|
|
||||||
Biobuf *bin;
|
|
||||||
PW *pw;
|
|
||||||
char *f1, *f2; // fields 1, 2 = attribute, value
|
|
||||||
|
|
||||||
if((bin = openPW(id, OREAD)) == 0){
|
|
||||||
id = "FICTITIOUS";
|
|
||||||
if((bin = openPW(id, OREAD)) == 0){
|
|
||||||
werrstr("account does not exist");
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pw = emalloc(sizeof(*pw));
|
|
||||||
pw->id = estrdup(id);
|
|
||||||
pw->status |= Enabled;
|
|
||||||
while( (f1 = Brdline(bin, '\n')) != 0){
|
|
||||||
f1[Blinelen(bin)-1] = 0;
|
|
||||||
for(f2 = f1; *f2 && (*f2!=' ') && (*f2!='\t'); f2++){}
|
|
||||||
if(*f2)
|
|
||||||
for(*f2++ = 0; *f2 && (*f2==' ' || *f2=='\t'); f2++){}
|
|
||||||
if(strcmp(f1, "exp") == 0){
|
|
||||||
pw->expire = strtoul(f2, 0, 10);
|
|
||||||
}else if(strcmp(f1, "DISABLED") == 0){
|
|
||||||
pw->status &= ~Enabled;
|
|
||||||
}else if(strcmp(f1, "STA") == 0){
|
|
||||||
pw->status |= STA;
|
|
||||||
}else if(strcmp(f1, "failed") == 0){
|
|
||||||
pw->failed = strtoul(f2, 0, 10);
|
|
||||||
}else if(strcmp(f1, "other") == 0){
|
|
||||||
pw->other = estrdup(f2);
|
|
||||||
}else if(strcmp(f1, "PAK-Hi") == 0){
|
|
||||||
pw->Hi = strtomp(f2, nil, 64, nil);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Bterm(bin);
|
|
||||||
if(dead_or_alive)
|
|
||||||
return pw; // return PW entry for editing, whether currently valid or not
|
|
||||||
if(pw->expire <= now){
|
|
||||||
werrstr("account expired");
|
|
||||||
freePW(pw);
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
if((pw->status & Enabled) == 0){
|
|
||||||
werrstr("account disabled");
|
|
||||||
freePW(pw);
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
if(pw->failed < 10)
|
|
||||||
return pw; // success
|
|
||||||
if(now < mtimePW(id)+300){
|
|
||||||
werrstr("too many failures; try again in five minutes");
|
|
||||||
freePW(pw);
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
pw->failed = 0;
|
|
||||||
putPW(pw); // reset failed-login-counter after five minutes
|
|
||||||
return pw;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
putPW(PW *pw)
|
|
||||||
{
|
|
||||||
Biobuf *bout;
|
|
||||||
char *hexHi;
|
|
||||||
|
|
||||||
if((bout = openPW(pw->id, OWRITE|OTRUNC)) ==0){
|
|
||||||
werrstr("can't open PW file");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
Bprint(bout, "exp %lud\n", pw->expire);
|
|
||||||
if(!(pw->status & Enabled))
|
|
||||||
Bprint(bout, "DISABLED\n");
|
|
||||||
if(pw->status & STA)
|
|
||||||
Bprint(bout, "STA\n");
|
|
||||||
if(pw->failed)
|
|
||||||
Bprint(bout, "failed\t%d\n", pw->failed);
|
|
||||||
if(pw->other)
|
|
||||||
Bprint(bout,"other\t%s\n", pw->other);
|
|
||||||
hexHi = mptoa(pw->Hi, 64, nil, 0);
|
|
||||||
Bprint(bout, "PAK-Hi\t%s\n", hexHi);
|
|
||||||
free(hexHi);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
freePW(PW *pw)
|
|
||||||
{
|
|
||||||
if(pw == nil)
|
|
||||||
return;
|
|
||||||
free(pw->id);
|
|
||||||
free(pw->other);
|
|
||||||
mpfree(pw->Hi);
|
|
||||||
free(pw);
|
|
||||||
}
|
|
||||||
|
|
||||||
@ -1,9 +0,0 @@
|
|||||||
SConn.c 2004/1225
|
|
||||||
SConn.h 2004/1225
|
|
||||||
aescbc.c 2004/1225
|
|
||||||
dirls.c 2004/1225
|
|
||||||
pak.c 2004/1225
|
|
||||||
password.c 2004/1225
|
|
||||||
secstore.c 2004/1225
|
|
||||||
secstore.h 2004/1225
|
|
||||||
util.c 2004/1225
|
|
||||||
@ -1,35 +0,0 @@
|
|||||||
#include <u.h>
|
|
||||||
#include <libc.h>
|
|
||||||
#include <ip.h>
|
|
||||||
|
|
||||||
int verbose = 1;
|
|
||||||
static char testmess[] = "__secstore\tPAK\nC=%s\nm=0\n";
|
|
||||||
|
|
||||||
void
|
|
||||||
main(int argc, char **argv)
|
|
||||||
{
|
|
||||||
int n, m, fd;
|
|
||||||
uchar buf[500];
|
|
||||||
|
|
||||||
if(argc != 2)
|
|
||||||
exits("usage: secacct userid");
|
|
||||||
|
|
||||||
n = snprint((char*)buf, sizeof buf, testmess, argv[1]);
|
|
||||||
hnputs(buf, 0x8000+n-2);
|
|
||||||
|
|
||||||
fd = dial("tcp!ruble.cs.bell-labs.com!5356", 0, 0, 0);
|
|
||||||
if(fd < 0)
|
|
||||||
exits("cannot dial ruble");
|
|
||||||
if(write(fd, buf, n) != n || readn(fd, buf, 2) != 2)
|
|
||||||
exits("cannot exchange first round");
|
|
||||||
n = ((buf[0]&0x7f)<<8) + buf[1];
|
|
||||||
if(n+1 > sizeof buf)
|
|
||||||
exits("implausibly large count");
|
|
||||||
m = readn(fd, buf, n);
|
|
||||||
close(fd);
|
|
||||||
if(m != n)
|
|
||||||
fprint(2,"short read from secstore\n");
|
|
||||||
buf[m] = 0;
|
|
||||||
print("%s\n", (char*)buf);
|
|
||||||
exits(0);
|
|
||||||
}
|
|
||||||
@ -1,28 +0,0 @@
|
|||||||
#include <u.h>
|
|
||||||
#include <libc.h>
|
|
||||||
#include <bio.h>
|
|
||||||
#include <ndb.h>
|
|
||||||
|
|
||||||
extern char* secureidcheck(char *user, char *response);
|
|
||||||
Ndb *db;
|
|
||||||
|
|
||||||
void
|
|
||||||
main(int argc, char **argv)
|
|
||||||
{
|
|
||||||
Ndb *db2;
|
|
||||||
|
|
||||||
if(argc!=2){
|
|
||||||
fprint(2,"usage %s pinsecurid\n", argv[0]);
|
|
||||||
exits("usage");
|
|
||||||
}
|
|
||||||
db = ndbopen("/lib/ndb/auth");
|
|
||||||
if(db == 0)
|
|
||||||
syslog(0, "secstore", "no /lib/ndb/auth");
|
|
||||||
db2 = ndbopen(0);
|
|
||||||
if(db2 == 0)
|
|
||||||
syslog(0, "secstore", "no /lib/ndb/local");
|
|
||||||
db = ndbcat(db, db2);
|
|
||||||
print("user=%s\n", getenv("user"));
|
|
||||||
print("%s\n", secureidcheck(getenv("user"), argv[1]));
|
|
||||||
exits(0);
|
|
||||||
}
|
|
||||||
@ -1,585 +0,0 @@
|
|||||||
/* network login client */
|
|
||||||
#include <u.h>
|
|
||||||
#include <libc.h>
|
|
||||||
#include <mp.h>
|
|
||||||
#include <libsec.h>
|
|
||||||
#include <authsrv.h>
|
|
||||||
#include "SConn.h"
|
|
||||||
#include "secstore.h"
|
|
||||||
enum{ CHK = 16, MAXFILES = 100 };
|
|
||||||
|
|
||||||
typedef struct AuthConn{
|
|
||||||
SConn *conn;
|
|
||||||
char pass[64];
|
|
||||||
int passlen;
|
|
||||||
} AuthConn;
|
|
||||||
|
|
||||||
int verbose;
|
|
||||||
Nvrsafe nvr;
|
|
||||||
char *SECSTORE_DIR;
|
|
||||||
|
|
||||||
void
|
|
||||||
usage(void)
|
|
||||||
{
|
|
||||||
fprint(2, "usage: secstore [-cin] [-g getfile] [-p putfile] [-r rmfile] [-s tcp!server!5356] [-u user] [-v]\n");
|
|
||||||
exits("usage");
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
getfile(SConn *conn, char *gf, uchar **buf, ulong *buflen, uchar *key, int nkey)
|
|
||||||
{
|
|
||||||
int fd = -1;
|
|
||||||
int i, n, nr, nw, len;
|
|
||||||
char s[Maxmsg+1];
|
|
||||||
uchar skey[SHA1dlen], ib[Maxmsg+CHK], *ibr, *ibw, *bufw, *bufe;
|
|
||||||
AESstate aes;
|
|
||||||
DigestState *sha;
|
|
||||||
|
|
||||||
if(strchr(gf, '/')){
|
|
||||||
fprint(2, "simple filenames, not paths like %s\n", gf);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
memset(&aes, 0, sizeof aes);
|
|
||||||
|
|
||||||
snprint(s, Maxmsg, "GET %s\n", gf);
|
|
||||||
conn->write(conn, (uchar*)s, strlen(s));
|
|
||||||
|
|
||||||
/* get file size */
|
|
||||||
s[0] = '\0';
|
|
||||||
bufw = bufe = nil;
|
|
||||||
if(readstr(conn, s) < 0){
|
|
||||||
fprint(2, "remote: %s\n", s);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
len = atoi(s);
|
|
||||||
if(len == -1){
|
|
||||||
fprint(2, "remote file %s does not exist\n", gf);
|
|
||||||
return -1;
|
|
||||||
}else if(len == -3){
|
|
||||||
fprint(2, "implausible filesize for %s\n", gf);
|
|
||||||
return -1;
|
|
||||||
}else if(len < 0){
|
|
||||||
fprint(2, "GET refused for %s\n", gf);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if(buf != nil){
|
|
||||||
*buflen = len - AESbsize - CHK;
|
|
||||||
*buf = bufw = emalloc(len);
|
|
||||||
bufe = bufw + len;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* directory listing */
|
|
||||||
if(strcmp(gf,".")==0){
|
|
||||||
if(buf != nil)
|
|
||||||
*buflen = len;
|
|
||||||
for(i=0; i < len; i += n){
|
|
||||||
if((n = conn->read(conn, (uchar*)s, Maxmsg)) <= 0){
|
|
||||||
fprint(2, "empty file chunk\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if(buf == nil)
|
|
||||||
write(1, s, n);
|
|
||||||
else
|
|
||||||
memmove((*buf)+i, s, n);
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* conn is already encrypted against wiretappers,
|
|
||||||
but gf is also encrypted against server breakin. */
|
|
||||||
if(buf == nil && (fd =create(gf, OWRITE, 0600)) < 0){
|
|
||||||
fprint(2, "can't open %s: %r\n", gf);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
ibr = ibw = ib;
|
|
||||||
for(nr=0; nr < len;){
|
|
||||||
if((n = conn->read(conn, ibw, Maxmsg)) <= 0){
|
|
||||||
fprint(2, "empty file chunk n=%d nr=%d len=%d: %r\n", n, nr, len);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
nr += n;
|
|
||||||
ibw += n;
|
|
||||||
if(!aes.setup){ /* first time, read 16 byte IV */
|
|
||||||
if(n < AESbsize){
|
|
||||||
fprint(2, "no IV in file\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
sha = sha1((uchar*)"aescbc file", 11, nil, nil);
|
|
||||||
sha1(key, nkey, skey, sha);
|
|
||||||
setupAESstate(&aes, skey, AESbsize, ibr);
|
|
||||||
memset(skey, 0, sizeof skey);
|
|
||||||
ibr += AESbsize;
|
|
||||||
n -= AESbsize;
|
|
||||||
}
|
|
||||||
aesCBCdecrypt(ibw-n, n, &aes);
|
|
||||||
n = ibw-ibr-CHK;
|
|
||||||
if(n > 0){
|
|
||||||
if(buf == nil){
|
|
||||||
nw = write(fd, ibr, n);
|
|
||||||
if(nw != n){
|
|
||||||
fprint(2, "write error on %s", gf);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}else{
|
|
||||||
assert(bufw+n <= bufe);
|
|
||||||
memmove(bufw, ibr, n);
|
|
||||||
bufw += n;
|
|
||||||
}
|
|
||||||
ibr += n;
|
|
||||||
}
|
|
||||||
memmove(ib, ibr, ibw-ibr);
|
|
||||||
ibw = ib + (ibw-ibr);
|
|
||||||
ibr = ib;
|
|
||||||
}
|
|
||||||
if(buf == nil)
|
|
||||||
close(fd);
|
|
||||||
n = ibw-ibr;
|
|
||||||
if((n != CHK) || (memcmp(ib, "XXXXXXXXXXXXXXXX", CHK) != 0)){
|
|
||||||
fprint(2,"decrypted file failed to authenticate!\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// This sends a file to the secstore disk that can, in an emergency, be
|
|
||||||
// decrypted by the program aescbc.c.
|
|
||||||
static int
|
|
||||||
putfile(SConn *conn, char *pf, uchar *buf, ulong len, uchar *key, int nkey)
|
|
||||||
{
|
|
||||||
int i, n, fd, ivo, bufi, done;
|
|
||||||
char s[Maxmsg];
|
|
||||||
uchar skey[SHA1dlen], b[CHK+Maxmsg], IV[AESbsize];
|
|
||||||
AESstate aes;
|
|
||||||
DigestState *sha;
|
|
||||||
|
|
||||||
/* create initialization vector */
|
|
||||||
srand(time(0)); /* doesn't need to be unpredictable */
|
|
||||||
for(i=0; i<AESbsize; i++)
|
|
||||||
IV[i] = 0xff & rand();
|
|
||||||
sha = sha1((uchar*)"aescbc file", 11, nil, nil);
|
|
||||||
sha1(key, nkey, skey, sha);
|
|
||||||
setupAESstate(&aes, skey, AESbsize, IV);
|
|
||||||
memset(skey, 0, sizeof skey);
|
|
||||||
|
|
||||||
snprint(s, Maxmsg, "PUT %s\n", pf);
|
|
||||||
conn->write(conn, (uchar*)s, strlen(s));
|
|
||||||
|
|
||||||
if(buf == nil){
|
|
||||||
/* get file size */
|
|
||||||
if((fd = open(pf, OREAD)) < 0){
|
|
||||||
fprint(2, "can't open %s: %r\n", pf);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
len = seek(fd, 0, 2);
|
|
||||||
seek(fd, 0, 0);
|
|
||||||
} else {
|
|
||||||
fd = -1;
|
|
||||||
}
|
|
||||||
if(len > MAXFILESIZE){
|
|
||||||
fprint(2, "implausible filesize %ld for %s\n", len, pf);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* send file size */
|
|
||||||
snprint(s, Maxmsg, "%ld", len+AESbsize+CHK);
|
|
||||||
conn->write(conn, (uchar*)s, strlen(s));
|
|
||||||
|
|
||||||
/* send IV and file+XXXXX in Maxmsg chunks */
|
|
||||||
ivo = AESbsize;
|
|
||||||
bufi = 0;
|
|
||||||
memcpy(b, IV, ivo);
|
|
||||||
for(done = 0; !done; ){
|
|
||||||
if(buf == nil){
|
|
||||||
n = read(fd, b+ivo, Maxmsg-ivo);
|
|
||||||
if(n < 0){
|
|
||||||
fprint(2, "read error on %s: %r\n", pf);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}else{
|
|
||||||
if((n = len - bufi) > Maxmsg-ivo)
|
|
||||||
n = Maxmsg-ivo;
|
|
||||||
memcpy(b+ivo, buf+bufi, n);
|
|
||||||
bufi += n;
|
|
||||||
}
|
|
||||||
n += ivo;
|
|
||||||
ivo = 0;
|
|
||||||
if(n < Maxmsg){ /* EOF on input; append XX... */
|
|
||||||
memset(b+n, 'X', CHK);
|
|
||||||
n += CHK; // might push n>Maxmsg
|
|
||||||
done = 1;
|
|
||||||
}
|
|
||||||
aesCBCencrypt(b, n, &aes);
|
|
||||||
if(n > Maxmsg){
|
|
||||||
assert(done==1);
|
|
||||||
conn->write(conn, b, Maxmsg);
|
|
||||||
n -= Maxmsg;
|
|
||||||
memmove(b, b+Maxmsg, n);
|
|
||||||
}
|
|
||||||
conn->write(conn, b, n);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(buf == nil)
|
|
||||||
close(fd);
|
|
||||||
fprint(2, "saved %ld bytes\n", len);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
removefile(SConn *conn, char *rf)
|
|
||||||
{
|
|
||||||
char buf[Maxmsg];
|
|
||||||
|
|
||||||
if(strchr(rf, '/')){
|
|
||||||
fprint(2, "simple filenames, not paths like %s\n", rf);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
snprint(buf, Maxmsg, "RM %s\n", rf);
|
|
||||||
conn->write(conn, (uchar*)buf, strlen(buf));
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
cmd(AuthConn *c, char **gf, int *Gflag, char **pf, char **rf)
|
|
||||||
{
|
|
||||||
ulong len;
|
|
||||||
int rv = -1;
|
|
||||||
uchar *memfile, *memcur, *memnext;
|
|
||||||
|
|
||||||
while(*gf != nil){
|
|
||||||
if(verbose)
|
|
||||||
fprint(2, "get %s\n", *gf);
|
|
||||||
if(getfile(c->conn, *gf, *Gflag ? &memfile : nil, &len, (uchar*)c->pass, c->passlen) < 0)
|
|
||||||
goto Out;
|
|
||||||
if(*Gflag){
|
|
||||||
// write one line at a time, as required by /mnt/factotum/ctl
|
|
||||||
memcur = memfile;
|
|
||||||
while(len>0){
|
|
||||||
memnext = (uchar*)strchr((char*)memcur, '\n');
|
|
||||||
if(memnext){
|
|
||||||
write(1, memcur, memnext-memcur+1);
|
|
||||||
len -= memnext-memcur+1;
|
|
||||||
memcur = memnext+1;
|
|
||||||
}else{
|
|
||||||
write(1, memcur, len);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
free(memfile);
|
|
||||||
}
|
|
||||||
gf++;
|
|
||||||
Gflag++;
|
|
||||||
}
|
|
||||||
while(*pf != nil){
|
|
||||||
if(verbose)
|
|
||||||
fprint(2, "put %s\n", *pf);
|
|
||||||
if(putfile(c->conn, *pf, nil, 0, (uchar*)c->pass, c->passlen) < 0)
|
|
||||||
goto Out;
|
|
||||||
pf++;
|
|
||||||
}
|
|
||||||
while(*rf != nil){
|
|
||||||
if(verbose)
|
|
||||||
fprint(2, "rm %s\n", *rf);
|
|
||||||
if(removefile(c->conn, *rf) < 0)
|
|
||||||
goto Out;
|
|
||||||
rf++;
|
|
||||||
}
|
|
||||||
|
|
||||||
c->conn->write(c->conn, (uchar*)"BYE", 3);
|
|
||||||
rv = 0;
|
|
||||||
|
|
||||||
Out:
|
|
||||||
c->conn->free(c->conn);
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
chpasswd(AuthConn *c, char *id)
|
|
||||||
{
|
|
||||||
ulong len;
|
|
||||||
int rv = -1, newpasslen = 0;
|
|
||||||
mpint *H, *Hi;
|
|
||||||
uchar *memfile;
|
|
||||||
char *newpass, *passck;
|
|
||||||
char *list, *cur, *next, *hexHi;
|
|
||||||
char *f[8], prompt[128];
|
|
||||||
|
|
||||||
H = mpnew(0);
|
|
||||||
Hi = mpnew(0);
|
|
||||||
// changing our password is vulnerable to connection failure
|
|
||||||
for(;;){
|
|
||||||
snprint(prompt, sizeof(prompt), "new password for %s: ", id);
|
|
||||||
newpass = readcons(prompt, nil, 1);
|
|
||||||
if(newpass == nil)
|
|
||||||
goto Out;
|
|
||||||
if(strlen(newpass) >= 7)
|
|
||||||
break;
|
|
||||||
else if(strlen(newpass) == 0){
|
|
||||||
fprint(2, "!password change aborted\n");
|
|
||||||
goto Out;
|
|
||||||
}
|
|
||||||
print("!password must be at least 7 characters\n");
|
|
||||||
}
|
|
||||||
newpasslen = strlen(newpass);
|
|
||||||
snprint(prompt, sizeof(prompt), "retype password: ");
|
|
||||||
passck = readcons(prompt, nil, 1);
|
|
||||||
if(passck == nil){
|
|
||||||
fprint(2, "readcons failed\n");
|
|
||||||
goto Out;
|
|
||||||
}
|
|
||||||
if(strcmp(passck, newpass) != 0){
|
|
||||||
fprint(2, "passwords didn't match\n");
|
|
||||||
goto Out;
|
|
||||||
}
|
|
||||||
|
|
||||||
c->conn->write(c->conn, (uchar*)"CHPASS", strlen("CHPASS"));
|
|
||||||
hexHi = PAK_Hi(id, newpass, H, Hi);
|
|
||||||
c->conn->write(c->conn, (uchar*)hexHi, strlen(hexHi));
|
|
||||||
free(hexHi);
|
|
||||||
mpfree(H);
|
|
||||||
mpfree(Hi);
|
|
||||||
|
|
||||||
if(getfile(c->conn, ".", (uchar **)(void*)&list, &len, nil, 0) < 0){
|
|
||||||
fprint(2, "directory listing failed.\n");
|
|
||||||
goto Out;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Loop over files and reencrypt them; try to keep going after error */
|
|
||||||
for(cur=list; (next=strchr(cur, '\n')) != nil; cur=next+1){
|
|
||||||
*next = '\0';
|
|
||||||
if(tokenize(cur, f, nelem(f))< 1)
|
|
||||||
break;
|
|
||||||
fprint(2, "reencrypting '%s'\n", f[0]);
|
|
||||||
if(getfile(c->conn, f[0], &memfile, &len, (uchar*)c->pass, c->passlen) < 0){
|
|
||||||
fprint(2, "getfile of '%s' failed\n", f[0]);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if(putfile(c->conn, f[0], memfile, len, (uchar*)newpass, newpasslen) < 0)
|
|
||||||
fprint(2, "putfile of '%s' failed\n", f[0]);
|
|
||||||
free(memfile);
|
|
||||||
}
|
|
||||||
free(list);
|
|
||||||
c->conn->write(c->conn, (uchar*)"BYE", 3);
|
|
||||||
rv = 0;
|
|
||||||
|
|
||||||
Out:
|
|
||||||
if(newpass != nil){
|
|
||||||
memset(newpass, 0, newpasslen);
|
|
||||||
free(newpass);
|
|
||||||
}
|
|
||||||
c->conn->free(c->conn);
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
|
|
||||||
static AuthConn*
|
|
||||||
login(char *id, char *dest, int pass_stdin, int pass_nvram)
|
|
||||||
{
|
|
||||||
AuthConn *c;
|
|
||||||
int fd, n, ntry = 0;
|
|
||||||
char *S, *PINSTA = nil, *nl, s[Maxmsg+1], *pass;
|
|
||||||
|
|
||||||
if(dest == nil){
|
|
||||||
fprint(2, "tried to login with nil dest\n");
|
|
||||||
exits("nil dest");
|
|
||||||
}
|
|
||||||
c = emalloc(sizeof(*c));
|
|
||||||
if(pass_nvram){
|
|
||||||
/* if(readnvram(&nvr, 0) < 0) */
|
|
||||||
exits("readnvram: %r");
|
|
||||||
strecpy(c->pass, c->pass+sizeof c->pass, nvr.config);
|
|
||||||
}
|
|
||||||
if(pass_stdin){
|
|
||||||
n = readn(0, s, Maxmsg-2); // so len(PINSTA)<Maxmsg-3
|
|
||||||
if(n < 1)
|
|
||||||
exits("no password on standard input");
|
|
||||||
s[n] = 0;
|
|
||||||
nl = strchr(s, '\n');
|
|
||||||
if(nl){
|
|
||||||
*nl++ = 0;
|
|
||||||
PINSTA = estrdup(nl);
|
|
||||||
nl = strchr(PINSTA, '\n');
|
|
||||||
if(nl)
|
|
||||||
*nl = 0;
|
|
||||||
}
|
|
||||||
strecpy(c->pass, c->pass+sizeof c->pass, s);
|
|
||||||
}
|
|
||||||
while(1){
|
|
||||||
if(verbose)
|
|
||||||
fprint(2, "dialing %s\n", dest);
|
|
||||||
if((fd = dial(dest, nil, nil, nil)) < 0){
|
|
||||||
fprint(2, "can't dial %s\n", dest);
|
|
||||||
free(c);
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
if((c->conn = newSConn(fd)) == nil){
|
|
||||||
free(c);
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
ntry++;
|
|
||||||
if(!pass_stdin && !pass_nvram){
|
|
||||||
pass = readcons("secstore password", nil, 1);
|
|
||||||
if(pass == nil)
|
|
||||||
pass = estrdup("");
|
|
||||||
if(strlen(pass) >= sizeof c->pass){
|
|
||||||
fprint(2, "password too long, skipping secstore login\n");
|
|
||||||
exits("password too long");
|
|
||||||
}
|
|
||||||
strcpy(c->pass, pass);
|
|
||||||
memset(pass, 0, strlen(pass));
|
|
||||||
free(pass);
|
|
||||||
}
|
|
||||||
if(c->pass[0]==0){
|
|
||||||
fprint(2, "null password, skipping secstore login\n");
|
|
||||||
exits("no password");
|
|
||||||
}
|
|
||||||
if(PAKclient(c->conn, id, c->pass, &S) >= 0)
|
|
||||||
break;
|
|
||||||
c->conn->free(c->conn);
|
|
||||||
if(pass_stdin)
|
|
||||||
exits("invalid password on standard input");
|
|
||||||
if(pass_nvram)
|
|
||||||
exits("invalid password in nvram");
|
|
||||||
// and let user try retyping the password
|
|
||||||
if(ntry==3)
|
|
||||||
fprint(2, "Enter an empty password to quit.\n");
|
|
||||||
}
|
|
||||||
c->passlen = strlen(c->pass);
|
|
||||||
fprint(2, "server: %s\n", S);
|
|
||||||
free(S);
|
|
||||||
if(readstr(c->conn, s) < 0){
|
|
||||||
c->conn->free(c->conn);
|
|
||||||
free(c);
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
if(strcmp(s, "STA") == 0){
|
|
||||||
long sn;
|
|
||||||
if(pass_stdin){
|
|
||||||
if(PINSTA)
|
|
||||||
strncpy(s+3, PINSTA, (sizeof s)-3);
|
|
||||||
else
|
|
||||||
exits("missing PIN+SecureID on standard input");
|
|
||||||
free(PINSTA);
|
|
||||||
}else{
|
|
||||||
pass = readcons("STA PIN+SecureID", nil, 1);
|
|
||||||
if(pass == nil)
|
|
||||||
pass = estrdup("");
|
|
||||||
strncpy(s+3, pass, (sizeof s)-4);
|
|
||||||
memset(pass, 0, strlen(pass));
|
|
||||||
free(pass);
|
|
||||||
}
|
|
||||||
sn = strlen(s+3);
|
|
||||||
if(verbose)
|
|
||||||
fprint(2, "%ld\n", sn);
|
|
||||||
c->conn->write(c->conn, (uchar*)s, sn+3);
|
|
||||||
readstr(c->conn, s);
|
|
||||||
}
|
|
||||||
if(strcmp(s, "OK") != 0){
|
|
||||||
fprint(2, "%s\n", s);
|
|
||||||
c->conn->free(c->conn);
|
|
||||||
free(c);
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
return c;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
main(int argc, char **argv)
|
|
||||||
{
|
|
||||||
int chpass = 0, pass_stdin = 0, pass_nvram = 0, rc;
|
|
||||||
int ngfile = 0, npfile = 0, nrfile = 0, Gflag[MAXFILES+1];
|
|
||||||
char *gfile[MAXFILES], *pfile[MAXFILES], *rfile[MAXFILES];
|
|
||||||
char *serve, *tcpserve, *user;
|
|
||||||
AuthConn *c;
|
|
||||||
|
|
||||||
serve = "$auth";
|
|
||||||
user = getuser();
|
|
||||||
memset(Gflag, 0, sizeof Gflag);
|
|
||||||
fmtinstall('B', mpfmt);
|
|
||||||
fmtinstall('H', encodefmt);
|
|
||||||
|
|
||||||
ARGBEGIN{
|
|
||||||
case 'c':
|
|
||||||
chpass = 1;
|
|
||||||
break;
|
|
||||||
case 'G':
|
|
||||||
Gflag[ngfile]++;
|
|
||||||
/* fall through */
|
|
||||||
case 'g':
|
|
||||||
if(ngfile >= MAXFILES)
|
|
||||||
exits("too many gfiles");
|
|
||||||
gfile[ngfile++] = ARGF();
|
|
||||||
if(gfile[ngfile-1] == nil)
|
|
||||||
usage();
|
|
||||||
break;
|
|
||||||
case 'i':
|
|
||||||
pass_stdin = 1;
|
|
||||||
break;
|
|
||||||
case 'n':
|
|
||||||
pass_nvram = 1;
|
|
||||||
break;
|
|
||||||
case 'p':
|
|
||||||
if(npfile >= MAXFILES)
|
|
||||||
exits("too many pfiles");
|
|
||||||
pfile[npfile++] = ARGF();
|
|
||||||
if(pfile[npfile-1] == nil)
|
|
||||||
usage();
|
|
||||||
break;
|
|
||||||
case 'r':
|
|
||||||
if(nrfile >= MAXFILES)
|
|
||||||
exits("too many rfiles");
|
|
||||||
rfile[nrfile++] = ARGF();
|
|
||||||
if(rfile[nrfile-1] == nil)
|
|
||||||
usage();
|
|
||||||
break;
|
|
||||||
case 's':
|
|
||||||
serve = EARGF(usage());
|
|
||||||
break;
|
|
||||||
case 'u':
|
|
||||||
user = EARGF(usage());
|
|
||||||
break;
|
|
||||||
case 'v':
|
|
||||||
verbose++;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
usage();
|
|
||||||
break;
|
|
||||||
}ARGEND;
|
|
||||||
gfile[ngfile] = nil;
|
|
||||||
pfile[npfile] = nil;
|
|
||||||
rfile[nrfile] = nil;
|
|
||||||
|
|
||||||
if(argc!=0 || user==nil)
|
|
||||||
usage();
|
|
||||||
|
|
||||||
if(chpass && (ngfile || npfile || nrfile)){
|
|
||||||
fprint(2, "Get, put, and remove invalid with password change.\n");
|
|
||||||
exits("usage");
|
|
||||||
}
|
|
||||||
|
|
||||||
rc = strlen(serve)+sizeof("tcp!!99990");
|
|
||||||
tcpserve = emalloc(rc);
|
|
||||||
if(strchr(serve,'!'))
|
|
||||||
strcpy(tcpserve, serve);
|
|
||||||
else
|
|
||||||
snprint(tcpserve, rc, "tcp!%s!5356", serve);
|
|
||||||
c = login(user, tcpserve, pass_stdin, pass_nvram);
|
|
||||||
free(tcpserve);
|
|
||||||
if(c == nil){
|
|
||||||
fprint(2, "secstore authentication failed\n");
|
|
||||||
exits("secstore authentication failed");
|
|
||||||
}
|
|
||||||
if(chpass)
|
|
||||||
rc = chpasswd(c, user);
|
|
||||||
else
|
|
||||||
rc = cmd(c, gfile, Gflag, pfile, rfile);
|
|
||||||
if(rc < 0){
|
|
||||||
fprint(2, "secstore cmd failed\n");
|
|
||||||
exits("secstore cmd failed");
|
|
||||||
}
|
|
||||||
exits("");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@ -1,31 +0,0 @@
|
|||||||
enum{ MAXFILESIZE = 10*1024*1024 };
|
|
||||||
|
|
||||||
enum{// PW status bits
|
|
||||||
Enabled = (1<<0),
|
|
||||||
STA = (1<<1), // extra SecurID step
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef struct PW {
|
|
||||||
char *id; // user id
|
|
||||||
ulong expire; // expiration time (epoch seconds)
|
|
||||||
ushort status; // Enabled, STA, ...
|
|
||||||
ushort failed; // number of failed login attempts
|
|
||||||
char *other; // other information, e.g. sponsor
|
|
||||||
mpint *Hi; // H(passphrase)^-1 mod p
|
|
||||||
} PW;
|
|
||||||
|
|
||||||
PW *getPW(char *, int);
|
|
||||||
int putPW(PW *);
|
|
||||||
void freePW(PW *);
|
|
||||||
|
|
||||||
// *client: SConn, client name, passphrase
|
|
||||||
// *server: SConn, (partial) 1st msg, PW entry
|
|
||||||
// *setpass: Username, hashed passphrase, PW entry
|
|
||||||
int PAKclient(SConn *, char *, char *, char **);
|
|
||||||
int PAKserver(SConn *, char *, char *, PW **);
|
|
||||||
char *PAK_Hi(char *, char *, mpint *, mpint *);
|
|
||||||
|
|
||||||
#define LOG "secstore"
|
|
||||||
|
|
||||||
extern char *SECSTORE_DIR;
|
|
||||||
|
|
||||||
@ -1,420 +0,0 @@
|
|||||||
#include <u.h>
|
|
||||||
#include <libc.h>
|
|
||||||
#include <bio.h>
|
|
||||||
#include <ndb.h>
|
|
||||||
#include <mp.h>
|
|
||||||
#include <libsec.h>
|
|
||||||
#include "SConn.h"
|
|
||||||
#include "secstore.h"
|
|
||||||
|
|
||||||
char *SECSTORE_DIR;
|
|
||||||
char* secureidcheck(char *, char *); // from /sys/src/cmd/auth/
|
|
||||||
extern char* dirls(char *path);
|
|
||||||
|
|
||||||
int verbose;
|
|
||||||
Ndb *db;
|
|
||||||
|
|
||||||
static void
|
|
||||||
usage(void)
|
|
||||||
{
|
|
||||||
fprint(2, "usage: secstored [-R] [-S servername] [-s tcp!*!5356] [-v] [-x netmtpt]\n");
|
|
||||||
exits("usage");
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
getdir(SConn *conn, char *id)
|
|
||||||
{
|
|
||||||
char *ls, *s;
|
|
||||||
uchar *msg;
|
|
||||||
int n, len;
|
|
||||||
|
|
||||||
s = emalloc(Maxmsg);
|
|
||||||
snprint(s, Maxmsg, "%s/store/%s", SECSTORE_DIR, id);
|
|
||||||
|
|
||||||
if((ls = dirls(s)) == nil)
|
|
||||||
len = 0;
|
|
||||||
else
|
|
||||||
len = strlen(ls);
|
|
||||||
|
|
||||||
/* send file size */
|
|
||||||
snprint(s, Maxmsg, "%d", len);
|
|
||||||
conn->write(conn, (uchar*)s, strlen(s));
|
|
||||||
|
|
||||||
/* send directory listing in Maxmsg chunks */
|
|
||||||
n = Maxmsg;
|
|
||||||
msg = (uchar*)ls;
|
|
||||||
while(len > 0){
|
|
||||||
if(len < Maxmsg)
|
|
||||||
n = len;
|
|
||||||
conn->write(conn, msg, n);
|
|
||||||
msg += n;
|
|
||||||
len -= n;
|
|
||||||
}
|
|
||||||
free(s);
|
|
||||||
free(ls);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
char *
|
|
||||||
validatefile(char *f)
|
|
||||||
{
|
|
||||||
char *nl;
|
|
||||||
|
|
||||||
if(f==nil || *f==0)
|
|
||||||
return nil;
|
|
||||||
if(nl = strchr(f, '\n'))
|
|
||||||
*nl = 0;
|
|
||||||
if(strchr(f,'/') != nil || strcmp(f,"..")==0 || strlen(f) >= 300){
|
|
||||||
syslog(0, LOG, "no slashes allowed: %s\n", f);
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
return f;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
getfile(SConn *conn, char *id, char *gf)
|
|
||||||
{
|
|
||||||
int n, gd, len;
|
|
||||||
ulong mode;
|
|
||||||
char *s;
|
|
||||||
Dir *st;
|
|
||||||
|
|
||||||
if(strcmp(gf,".")==0)
|
|
||||||
return getdir(conn, id);
|
|
||||||
|
|
||||||
/* send file size */
|
|
||||||
s = emalloc(Maxmsg);
|
|
||||||
snprint(s, Maxmsg, "%s/store/%s/%s", SECSTORE_DIR, id, gf);
|
|
||||||
gd = open(s, OREAD);
|
|
||||||
if(gd < 0){
|
|
||||||
syslog(0, LOG, "can't open %s: %r\n", s);
|
|
||||||
free(s);
|
|
||||||
conn->write(conn, (uchar*)"-1", 2);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
st = dirfstat(gd);
|
|
||||||
if(st == nil){
|
|
||||||
syslog(0, LOG, "can't stat %s: %r\n", s);
|
|
||||||
free(s);
|
|
||||||
conn->write(conn, (uchar*)"-1", 2);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
mode = st->mode;
|
|
||||||
len = st->length;
|
|
||||||
free(st);
|
|
||||||
if(mode & DMDIR) {
|
|
||||||
syslog(0, LOG, "%s should be a plain file, not a directory\n", s);
|
|
||||||
free(s);
|
|
||||||
conn->write(conn, (uchar*)"-1", 2);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if(len < 0 || len > MAXFILESIZE){
|
|
||||||
syslog(0, LOG, "implausible filesize %d for %s\n", len, gf);
|
|
||||||
free(s);
|
|
||||||
conn->write(conn, (uchar*)"-3", 2);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
snprint(s, Maxmsg, "%d", len);
|
|
||||||
conn->write(conn, (uchar*)s, strlen(s));
|
|
||||||
|
|
||||||
/* send file in Maxmsg chunks */
|
|
||||||
while(len > 0){
|
|
||||||
n = read(gd, s, Maxmsg);
|
|
||||||
if(n <= 0){
|
|
||||||
syslog(0, LOG, "read error on %s: %r\n", gf);
|
|
||||||
free(s);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
conn->write(conn, (uchar*)s, n);
|
|
||||||
len -= n;
|
|
||||||
}
|
|
||||||
close(gd);
|
|
||||||
free(s);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
putfile(SConn *conn, char *id, char *pf)
|
|
||||||
{
|
|
||||||
int n, nw, pd;
|
|
||||||
long len;
|
|
||||||
char s[Maxmsg+1];
|
|
||||||
|
|
||||||
/* get file size */
|
|
||||||
n = readstr(conn, s);
|
|
||||||
if(n < 0){
|
|
||||||
syslog(0, LOG, "remote: %s: %r\n", s);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
len = atoi(s);
|
|
||||||
if(len == -1){
|
|
||||||
syslog(0, LOG, "remote file %s does not exist\n", pf);
|
|
||||||
return -1;
|
|
||||||
}else if(len < 0 || len > MAXFILESIZE){
|
|
||||||
syslog(0, LOG, "implausible filesize %ld for %s\n", len, pf);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* get file in Maxmsg chunks */
|
|
||||||
if(strchr(pf,'/') != nil || strcmp(pf,"..")==0){
|
|
||||||
syslog(0, LOG, "no slashes allowed: %s\n", pf);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
snprint(s, Maxmsg, "%s/store/%s/%s", SECSTORE_DIR, id, pf);
|
|
||||||
pd = create(s, OWRITE, 0660);
|
|
||||||
if(pd < 0){
|
|
||||||
syslog(0, LOG, "can't open %s: %r\n", s);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
while(len > 0){
|
|
||||||
n = conn->read(conn, (uchar*)s, Maxmsg);
|
|
||||||
if(n <= 0){
|
|
||||||
syslog(0, LOG, "empty file chunk\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
nw = write(pd, s, n);
|
|
||||||
if(nw != n){
|
|
||||||
syslog(0, LOG, "write error on %s: %r", pf);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
len -= n;
|
|
||||||
}
|
|
||||||
close(pd);
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
removefile(SConn *conn, char *id, char *f)
|
|
||||||
{
|
|
||||||
Dir *d;
|
|
||||||
char buf[Maxmsg];
|
|
||||||
|
|
||||||
snprint(buf, Maxmsg, "%s/store/%s/%s", SECSTORE_DIR, id, f);
|
|
||||||
|
|
||||||
if((d = dirstat(buf)) == nil){
|
|
||||||
snprint(buf, sizeof buf, "remove failed: %r");
|
|
||||||
writerr(conn, buf);
|
|
||||||
return -1;
|
|
||||||
}else if(d->mode & DMDIR){
|
|
||||||
snprint(buf, sizeof buf, "can't remove a directory");
|
|
||||||
writerr(conn, buf);
|
|
||||||
free(d);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
free(d);
|
|
||||||
if(remove(buf) < 0){
|
|
||||||
snprint(buf, sizeof buf, "remove failed: %r");
|
|
||||||
writerr(conn, buf);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* given line directory from accept, returns ipaddr!port */
|
|
||||||
static char*
|
|
||||||
remoteIP(char *ldir)
|
|
||||||
{
|
|
||||||
int fd, n;
|
|
||||||
char rp[100], ap[500];
|
|
||||||
|
|
||||||
snprint(rp, sizeof rp, "%s/remote", ldir);
|
|
||||||
fd = open(rp, OREAD);
|
|
||||||
if(fd < 0)
|
|
||||||
return strdup("?!?");
|
|
||||||
n = read(fd, ap, sizeof ap);
|
|
||||||
if(n <= 0 || n == sizeof ap){
|
|
||||||
fprint(2, "error %d reading %s: %r\n", n, rp);
|
|
||||||
return strdup("?!?");
|
|
||||||
}
|
|
||||||
close(fd);
|
|
||||||
ap[n--] = 0;
|
|
||||||
if(ap[n] == '\n')
|
|
||||||
ap[n] = 0;
|
|
||||||
return strdup(ap);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
dologin(int fd, char *S, int forceSTA)
|
|
||||||
{
|
|
||||||
int i, n, rv;
|
|
||||||
char *file, *mess;
|
|
||||||
char msg[Maxmsg+1];
|
|
||||||
PW *pw;
|
|
||||||
SConn *conn;
|
|
||||||
|
|
||||||
pw = nil;
|
|
||||||
rv = -1;
|
|
||||||
|
|
||||||
// collect the first message
|
|
||||||
if((conn = newSConn(fd)) == nil)
|
|
||||||
return -1;
|
|
||||||
if(readstr(conn, msg) < 0){
|
|
||||||
fprint(2, "remote: %s: %r\n", msg);
|
|
||||||
writerr(conn, "can't read your first message");
|
|
||||||
goto Out;
|
|
||||||
}
|
|
||||||
|
|
||||||
// authenticate
|
|
||||||
if(PAKserver(conn, S, msg, &pw) < 0){
|
|
||||||
if(pw != nil)
|
|
||||||
syslog(0, LOG, "secstore denied for %s", pw->id);
|
|
||||||
goto Out;
|
|
||||||
}
|
|
||||||
if((forceSTA || pw->status&STA) != 0){
|
|
||||||
conn->write(conn, (uchar*)"STA", 3);
|
|
||||||
if(readstr(conn, msg) < 10 || strncmp(msg, "STA", 3) != 0){
|
|
||||||
syslog(0, LOG, "no STA from %s", pw->id);
|
|
||||||
goto Out;
|
|
||||||
}
|
|
||||||
mess = secureidcheck(pw->id, msg+3);
|
|
||||||
if(mess != nil){
|
|
||||||
syslog(0, LOG, "secureidcheck denied %s because %s", pw->id, mess);
|
|
||||||
goto Out;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
conn->write(conn, (uchar*)"OK", 2);
|
|
||||||
syslog(0, LOG, "AUTH %s", pw->id);
|
|
||||||
|
|
||||||
// perform operations as asked
|
|
||||||
while((n = readstr(conn, msg)) > 0){
|
|
||||||
syslog(0, LOG, "[%s] %s", pw->id, msg);
|
|
||||||
|
|
||||||
if(strncmp(msg, "GET ", 4) == 0){
|
|
||||||
file = validatefile(msg+4);
|
|
||||||
if(file==nil || getfile(conn, pw->id, file) < 0)
|
|
||||||
goto Err;
|
|
||||||
|
|
||||||
}else if(strncmp(msg, "PUT ", 4) == 0){
|
|
||||||
file = validatefile(msg+4);
|
|
||||||
if(file==nil || putfile(conn, pw->id, file) < 0){
|
|
||||||
syslog(0, LOG, "failed PUT %s/%s", pw->id, file);
|
|
||||||
goto Err;
|
|
||||||
}
|
|
||||||
|
|
||||||
}else if(strncmp(msg, "RM ", 3) == 0){
|
|
||||||
file = validatefile(msg+3);
|
|
||||||
if(file==nil || removefile(conn, pw->id, file) < 0){
|
|
||||||
syslog(0, LOG, "failed RM %s/%s", pw->id, file);
|
|
||||||
goto Err;
|
|
||||||
}
|
|
||||||
|
|
||||||
}else if(strncmp(msg, "CHPASS", 6) == 0){
|
|
||||||
if(readstr(conn, msg) < 0){
|
|
||||||
syslog(0, LOG, "protocol botch CHPASS for %s", pw->id);
|
|
||||||
writerr(conn, "protocol botch while setting PAK");
|
|
||||||
goto Out;
|
|
||||||
}
|
|
||||||
pw->Hi = strtomp(msg, nil, 64, pw->Hi);
|
|
||||||
for(i=0; i < 4 && putPW(pw) < 0; i++)
|
|
||||||
syslog(0, LOG, "password change failed for %s (%d): %r", pw->id, i);
|
|
||||||
if(i==4)
|
|
||||||
goto Out;
|
|
||||||
|
|
||||||
}else if(strncmp(msg, "BYE", 3) == 0){
|
|
||||||
rv = 0;
|
|
||||||
break;
|
|
||||||
|
|
||||||
}else{
|
|
||||||
writerr(conn, "unrecognized operation");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
if(n <= 0)
|
|
||||||
syslog(0, LOG, "%s closed connection without saying goodbye\n", pw->id);
|
|
||||||
|
|
||||||
Out:
|
|
||||||
freePW(pw);
|
|
||||||
conn->free(conn);
|
|
||||||
return rv;
|
|
||||||
Err:
|
|
||||||
writerr(conn, "operation failed");
|
|
||||||
goto Out;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
main(int argc, char **argv)
|
|
||||||
{
|
|
||||||
int afd, dfd, lcfd, forceSTA = 0;
|
|
||||||
char adir[40], ldir[40], *remote;
|
|
||||||
char *serve = "tcp!*!5356", *p, aserve[128];
|
|
||||||
char *S = "secstore";
|
|
||||||
char *dbpath;
|
|
||||||
Ndb *db2;
|
|
||||||
|
|
||||||
S = sysname();
|
|
||||||
SECSTORE_DIR = unsharp("#9/secstore");
|
|
||||||
// setnetmtpt(net, sizeof(net), nil);
|
|
||||||
ARGBEGIN{
|
|
||||||
case 'R':
|
|
||||||
forceSTA = 1;
|
|
||||||
break;
|
|
||||||
case 's':
|
|
||||||
serve = EARGF(usage());
|
|
||||||
break;
|
|
||||||
case 'S':
|
|
||||||
S = EARGF(usage());
|
|
||||||
break;
|
|
||||||
case 'x':
|
|
||||||
p = ARGF();
|
|
||||||
if(p == nil)
|
|
||||||
usage();
|
|
||||||
USED(p);
|
|
||||||
// setnetmtpt(net, sizeof(net), p);
|
|
||||||
break;
|
|
||||||
case 'v':
|
|
||||||
verbose++;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
usage();
|
|
||||||
}ARGEND;
|
|
||||||
|
|
||||||
if(!verbose)
|
|
||||||
switch(rfork(RFNOTEG|RFPROC|RFFDG)) {
|
|
||||||
case -1:
|
|
||||||
sysfatal("fork: %r");
|
|
||||||
case 0:
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
exits(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
snprint(aserve, sizeof aserve, "%s", serve);
|
|
||||||
afd = announce(aserve, adir);
|
|
||||||
if(afd < 0)
|
|
||||||
sysfatal("%s: %r\n", aserve);
|
|
||||||
syslog(0, LOG, "ANNOUNCE %s", aserve);
|
|
||||||
for(;;){
|
|
||||||
if((lcfd = listen(adir, ldir)) < 0)
|
|
||||||
exits("can't listen");
|
|
||||||
switch(fork()){
|
|
||||||
case -1:
|
|
||||||
fprint(2, "secstore forking: %r\n");
|
|
||||||
close(lcfd);
|
|
||||||
break;
|
|
||||||
case 0:
|
|
||||||
// "/lib/ndb/common.radius does not exist" if db set before fork
|
|
||||||
db = ndbopen(dbpath=unsharp("#9/ndb/auth"));
|
|
||||||
if(db == 0)
|
|
||||||
syslog(0, LOG, "no ndb/auth");
|
|
||||||
db2 = ndbopen(0);
|
|
||||||
if(db2 == 0)
|
|
||||||
syslog(0, LOG, "no ndb/local");
|
|
||||||
db = ndbcat(db, db2);
|
|
||||||
if((dfd = accept(lcfd, ldir)) < 0)
|
|
||||||
exits("can't accept");
|
|
||||||
alarm(30*60*1000); // 30 min
|
|
||||||
remote = remoteIP(ldir);
|
|
||||||
syslog(0, LOG, "secstore from %s", remote);
|
|
||||||
free(remote);
|
|
||||||
dologin(dfd, S, forceSTA);
|
|
||||||
exits(nil);
|
|
||||||
default:
|
|
||||||
close(lcfd);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@ -1,446 +0,0 @@
|
|||||||
/* RFC2138 */
|
|
||||||
#include <u.h>
|
|
||||||
#include <libc.h>
|
|
||||||
#include <ip.h>
|
|
||||||
#include <ctype.h>
|
|
||||||
#include <mp.h>
|
|
||||||
#include <libsec.h>
|
|
||||||
#include <bio.h>
|
|
||||||
#include <ndb.h>
|
|
||||||
#define AUTHLOG "auth"
|
|
||||||
|
|
||||||
enum{ R_AccessRequest=1, /* Packet code */
|
|
||||||
R_AccessAccept=2,
|
|
||||||
R_AccessReject=3,
|
|
||||||
R_AccessChallenge=11,
|
|
||||||
R_UserName=1,
|
|
||||||
R_UserPassword=2,
|
|
||||||
R_NASIPAddress=4,
|
|
||||||
R_ReplyMessage=18,
|
|
||||||
R_State=24,
|
|
||||||
R_NASIdentifier=32
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef struct Secret{
|
|
||||||
uchar *s;
|
|
||||||
int len;
|
|
||||||
} Secret;
|
|
||||||
|
|
||||||
typedef struct Attribute{
|
|
||||||
struct Attribute *next;
|
|
||||||
uchar type;
|
|
||||||
uchar len; // number of bytes in value
|
|
||||||
uchar val[256];
|
|
||||||
} Attribute;
|
|
||||||
|
|
||||||
typedef struct Packet{
|
|
||||||
uchar code, ID;
|
|
||||||
uchar authenticator[16];
|
|
||||||
Attribute first;
|
|
||||||
} Packet;
|
|
||||||
|
|
||||||
// assumes pass is at most 16 chars
|
|
||||||
void
|
|
||||||
hide(Secret *shared, uchar *auth, Secret *pass, uchar *x)
|
|
||||||
{
|
|
||||||
DigestState *M;
|
|
||||||
int i, n = pass->len;
|
|
||||||
|
|
||||||
M = md5(shared->s, shared->len, nil, nil);
|
|
||||||
md5(auth, 16, x, M);
|
|
||||||
if(n > 16)
|
|
||||||
n = 16;
|
|
||||||
for(i = 0; i < n; i++)
|
|
||||||
x[i] ^= (pass->s)[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
authcmp(Secret *shared, uchar *buf, int m, uchar *auth)
|
|
||||||
{
|
|
||||||
DigestState *M;
|
|
||||||
uchar x[16];
|
|
||||||
|
|
||||||
M = md5(buf, 4, nil, nil); // Code+ID+Length
|
|
||||||
M = md5(auth, 16, nil, M); // RequestAuth
|
|
||||||
M = md5(buf+20, m-20, nil, M); // Attributes
|
|
||||||
md5(shared->s, shared->len, x, M);
|
|
||||||
return memcmp(x, buf+4, 16);
|
|
||||||
}
|
|
||||||
|
|
||||||
Packet*
|
|
||||||
newRequest(uchar *auth)
|
|
||||||
{
|
|
||||||
static uchar ID = 0;
|
|
||||||
Packet *p;
|
|
||||||
|
|
||||||
p = (Packet*)malloc(sizeof(*p));
|
|
||||||
if(p == nil)
|
|
||||||
return nil;
|
|
||||||
p->code = R_AccessRequest;
|
|
||||||
p->ID = ++ID;
|
|
||||||
memmove(p->authenticator, auth, 16);
|
|
||||||
p->first.next = nil;
|
|
||||||
p->first.type = 0;
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
freePacket(Packet *p)
|
|
||||||
{
|
|
||||||
Attribute *a, *x;
|
|
||||||
|
|
||||||
if(!p)
|
|
||||||
return;
|
|
||||||
a = p->first.next;
|
|
||||||
while(a){
|
|
||||||
x = a;
|
|
||||||
a = a->next;
|
|
||||||
free(x);
|
|
||||||
}
|
|
||||||
free(p);
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
ding(void *v, char *msg)
|
|
||||||
{
|
|
||||||
USED(v);
|
|
||||||
/* syslog(0, AUTHLOG, "ding %s", msg); */
|
|
||||||
if(strstr(msg, "alarm"))
|
|
||||||
return 1;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
Packet *
|
|
||||||
rpc(char *dest, Secret *shared, Packet *req)
|
|
||||||
{
|
|
||||||
uchar buf[4096], buf2[4096], *b, *e;
|
|
||||||
Packet *resp;
|
|
||||||
Attribute *a;
|
|
||||||
int m, n, fd, try;
|
|
||||||
|
|
||||||
// marshal request
|
|
||||||
e = buf + sizeof buf;
|
|
||||||
buf[0] = req->code;
|
|
||||||
buf[1] = req->ID;
|
|
||||||
memmove(buf+4, req->authenticator, 16);
|
|
||||||
b = buf+20;
|
|
||||||
for(a = &req->first; a; a = a->next){
|
|
||||||
if(b + 2 + a->len > e)
|
|
||||||
return nil;
|
|
||||||
*b++ = a->type;
|
|
||||||
*b++ = 2 + a->len;
|
|
||||||
memmove(b, a->val, a->len);
|
|
||||||
b += a->len;
|
|
||||||
}
|
|
||||||
n = b-buf;
|
|
||||||
buf[2] = n>>8;
|
|
||||||
buf[3] = n;
|
|
||||||
|
|
||||||
// send request, wait for reply
|
|
||||||
fd = dial(dest, 0, 0, 0);
|
|
||||||
if(fd < 0){
|
|
||||||
syslog(0, AUTHLOG, "%s: rpc can't get udp channel", dest);
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
atnotify(ding, 1);
|
|
||||||
m = -1;
|
|
||||||
for(try = 0; try < 2; try++){
|
|
||||||
alarm(4000);
|
|
||||||
m = write(fd, buf, n);
|
|
||||||
if(m != n){
|
|
||||||
syslog(0, AUTHLOG, "%s: rpc write err %d %d: %r", dest, m, n);
|
|
||||||
m = -1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
m = read(fd, buf2, sizeof buf2);
|
|
||||||
alarm(0);
|
|
||||||
if(m < 0){
|
|
||||||
syslog(0, AUTHLOG, "%s rpc read err %d: %r", dest, m);
|
|
||||||
break; // failure
|
|
||||||
}
|
|
||||||
if(m == 0 || buf2[1] != buf[1]){ // need matching ID
|
|
||||||
syslog(0, AUTHLOG, "%s unmatched reply %d", dest, m);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if(authcmp(shared, buf2, m, buf+4) == 0)
|
|
||||||
break;
|
|
||||||
syslog(0, AUTHLOG, "%s bad rpc chksum", dest);
|
|
||||||
}
|
|
||||||
close(fd);
|
|
||||||
if(m <= 0)
|
|
||||||
return nil;
|
|
||||||
|
|
||||||
// unmarshal reply
|
|
||||||
b = buf2;
|
|
||||||
e = buf2+m;
|
|
||||||
resp = (Packet*)malloc(sizeof(*resp));
|
|
||||||
if(resp == nil)
|
|
||||||
return nil;
|
|
||||||
resp->code = *b++;
|
|
||||||
resp->ID = *b++;
|
|
||||||
n = *b++;
|
|
||||||
n = (n<<8) | *b++;
|
|
||||||
if(m != n){
|
|
||||||
syslog(0, AUTHLOG, "rpc got %d bytes, length said %d", m, n);
|
|
||||||
if(m > n)
|
|
||||||
e = buf2+n;
|
|
||||||
}
|
|
||||||
memmove(resp->authenticator, b, 16);
|
|
||||||
b += 16;
|
|
||||||
a = &resp->first;
|
|
||||||
a->type = 0;
|
|
||||||
while(1){
|
|
||||||
if(b >= e){
|
|
||||||
a->next = nil;
|
|
||||||
break; // exit loop
|
|
||||||
}
|
|
||||||
a->type = *b++;
|
|
||||||
a->len = (*b++) - 2;
|
|
||||||
if(b + a->len > e){ // corrupt packet
|
|
||||||
a->next = nil;
|
|
||||||
freePacket(resp);
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
memmove(a->val, b, a->len);
|
|
||||||
b += a->len;
|
|
||||||
if(b < e){ // any more attributes?
|
|
||||||
a->next = (Attribute*)malloc(sizeof(*a));
|
|
||||||
if(a->next == nil){
|
|
||||||
free(req);
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
a = a->next;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return resp;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
setAttribute(Packet *p, uchar type, uchar *s, int n)
|
|
||||||
{
|
|
||||||
Attribute *a;
|
|
||||||
|
|
||||||
a = &p->first;
|
|
||||||
if(a->type != 0){
|
|
||||||
a = (Attribute*)malloc(sizeof(*a));
|
|
||||||
if(a == nil)
|
|
||||||
return -1;
|
|
||||||
a->next = p->first.next;
|
|
||||||
p->first.next = a;
|
|
||||||
}
|
|
||||||
a->type = type;
|
|
||||||
a->len = n;
|
|
||||||
if(a->len > 253 ) // RFC2138, section 5
|
|
||||||
a->len = 253;
|
|
||||||
memmove(a->val, s, a->len);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* return a reply message attribute string */
|
|
||||||
char*
|
|
||||||
replymsg(Packet *p)
|
|
||||||
{
|
|
||||||
Attribute *a;
|
|
||||||
static char buf[255];
|
|
||||||
|
|
||||||
for(a = &p->first; a; a = a->next){
|
|
||||||
if(a->type == R_ReplyMessage){
|
|
||||||
if(a->len >= sizeof buf)
|
|
||||||
a->len = sizeof(buf)-1;
|
|
||||||
memmove(buf, a->val, a->len);
|
|
||||||
buf[a->len] = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return buf;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* for convenience while debugging */
|
|
||||||
char *replymess;
|
|
||||||
Attribute *stateattr;
|
|
||||||
|
|
||||||
void
|
|
||||||
logPacket(Packet *p)
|
|
||||||
{
|
|
||||||
Attribute *a;
|
|
||||||
char buf[255];
|
|
||||||
char pbuf[4*1024];
|
|
||||||
uchar *au = p->authenticator;
|
|
||||||
int i;
|
|
||||||
char *np, *e;
|
|
||||||
|
|
||||||
e = pbuf + sizeof(pbuf);
|
|
||||||
|
|
||||||
np = seprint(pbuf, e, "Packet ID=%d auth=%x %x %x... ", p->ID, au[0], au[1], au[2]);
|
|
||||||
switch(p->code){
|
|
||||||
case R_AccessRequest:
|
|
||||||
np = seprint(np, e, "request\n");
|
|
||||||
break;
|
|
||||||
case R_AccessAccept:
|
|
||||||
np = seprint(np, e, "accept\n");
|
|
||||||
break;
|
|
||||||
case R_AccessReject:
|
|
||||||
np = seprint(np, e, "reject\n");
|
|
||||||
break;
|
|
||||||
case R_AccessChallenge:
|
|
||||||
np = seprint(np, e, "challenge\n");
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
np = seprint(np, e, "code=%d\n", p->code);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
replymess = "0000000";
|
|
||||||
for(a = &p->first; a; a = a->next){
|
|
||||||
if(a->len > 253 )
|
|
||||||
a->len = 253;
|
|
||||||
memmove(buf, a->val, a->len);
|
|
||||||
np = seprint(np, e, " [%d]", a->type);
|
|
||||||
for(i = 0; i<a->len; i++)
|
|
||||||
if(isprint(a->val[i]))
|
|
||||||
np = seprint(np, e, "%c", a->val[i]);
|
|
||||||
else
|
|
||||||
np = seprint(np, e, "\\%o", a->val[i]);
|
|
||||||
np = seprint(np, e, "\n");
|
|
||||||
buf[a->len] = 0;
|
|
||||||
if(a->type == R_ReplyMessage)
|
|
||||||
replymess = strdup(buf);
|
|
||||||
else if(a->type == R_State)
|
|
||||||
stateattr = a;
|
|
||||||
}
|
|
||||||
|
|
||||||
syslog(0, AUTHLOG, "%s", pbuf);
|
|
||||||
}
|
|
||||||
|
|
||||||
static uchar*
|
|
||||||
getipv4addr(void)
|
|
||||||
{
|
|
||||||
Ipifc *nifc;
|
|
||||||
Iplifc *lifc;
|
|
||||||
static Ipifc *ifc;
|
|
||||||
|
|
||||||
ifc = readipifc("/net", ifc, -1);
|
|
||||||
for(nifc = ifc; nifc; nifc = nifc->next)
|
|
||||||
for(lifc = nifc->lifc; lifc; lifc = lifc->next)
|
|
||||||
if(ipcmp(lifc->ip, IPnoaddr) != 0 && ipcmp(lifc->ip, v4prefix) != 0)
|
|
||||||
return lifc->ip;
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
extern Ndb *db;
|
|
||||||
|
|
||||||
/* returns 0 on success, error message on failure */
|
|
||||||
char*
|
|
||||||
secureidcheck(char *user, char *response)
|
|
||||||
{
|
|
||||||
Packet *req = nil, *resp = nil;
|
|
||||||
ulong u[4];
|
|
||||||
uchar x[16];
|
|
||||||
char *radiussecret;
|
|
||||||
char ruser[ 64];
|
|
||||||
char dest[3*IPaddrlen+20];
|
|
||||||
Secret shared, pass;
|
|
||||||
char *rv = "authentication failed";
|
|
||||||
Ndbs s;
|
|
||||||
Ndbtuple *t, *nt, *tt;
|
|
||||||
uchar *ip;
|
|
||||||
static Ndb *netdb;
|
|
||||||
|
|
||||||
if(netdb == nil)
|
|
||||||
netdb = ndbopen(0);
|
|
||||||
|
|
||||||
/* bad responses make them disable the fob, avoid silly checks */
|
|
||||||
if(strlen(response) < 4 || strpbrk(response,"abcdefABCDEF") != nil)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
/* get radius secret */
|
|
||||||
radiussecret = ndbgetvalue(db, &s, "radius", "lra-radius", "secret", &t);
|
|
||||||
if(radiussecret == nil){
|
|
||||||
syslog(0, AUTHLOG, "secureidcheck: nil radius secret: %r");
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* translate user name if we have to */
|
|
||||||
strcpy(ruser, user);
|
|
||||||
for(nt = t; nt; nt = nt->entry){
|
|
||||||
if(strcmp(nt->attr, "uid") == 0 && strcmp(nt->val, user) == 0)
|
|
||||||
for(tt = nt->line; tt != nt; tt = tt->line)
|
|
||||||
if(strcmp(tt->attr, "rid") == 0){
|
|
||||||
strcpy(ruser, tt->val);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ndbfree(t);
|
|
||||||
|
|
||||||
u[0] = fastrand();
|
|
||||||
u[1] = fastrand();
|
|
||||||
u[2] = fastrand();
|
|
||||||
u[3] = fastrand();
|
|
||||||
req = newRequest((uchar*)u);
|
|
||||||
if(req == nil)
|
|
||||||
goto out;
|
|
||||||
shared.s = (uchar*)radiussecret;
|
|
||||||
shared.len = strlen(radiussecret);
|
|
||||||
ip = getipv4addr();
|
|
||||||
if(ip == nil){
|
|
||||||
syslog(0, AUTHLOG, "no interfaces: %r\n");
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
if(setAttribute(req, R_NASIPAddress, ip + IPv4off, 4) < 0)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
if(setAttribute(req, R_UserName, (uchar*)ruser, strlen(ruser)) < 0)
|
|
||||||
goto out;
|
|
||||||
pass.s = (uchar*)response;
|
|
||||||
pass.len = strlen(response);
|
|
||||||
hide(&shared, req->authenticator, &pass, x);
|
|
||||||
if(setAttribute(req, R_UserPassword, x, 16) < 0)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
t = ndbsearch(netdb, &s, "sys", "lra-radius");
|
|
||||||
if(t == nil){
|
|
||||||
syslog(0, AUTHLOG, "secureidcheck: nil radius sys search: %r\n");
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
for(nt = t; nt; nt = nt->entry){
|
|
||||||
if(strcmp(nt->attr, "ip") != 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
snprint(dest,sizeof dest,"udp!%s!oradius", nt->val);
|
|
||||||
resp = rpc(dest, &shared, req);
|
|
||||||
if(resp == nil){
|
|
||||||
syslog(0, AUTHLOG, "%s nil response", dest);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if(resp->ID != req->ID){
|
|
||||||
syslog(0, AUTHLOG, "%s mismatched ID req=%d resp=%d",
|
|
||||||
dest, req->ID, resp->ID);
|
|
||||||
freePacket(resp);
|
|
||||||
resp = nil;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch(resp->code){
|
|
||||||
case R_AccessAccept:
|
|
||||||
syslog(0, AUTHLOG, "%s accepted ruser=%s", dest, ruser);
|
|
||||||
rv = nil;
|
|
||||||
break;
|
|
||||||
case R_AccessReject:
|
|
||||||
syslog(0, AUTHLOG, "%s rejected ruser=%s %s", dest, ruser, replymsg(resp));
|
|
||||||
rv = "secureid failed";
|
|
||||||
break;
|
|
||||||
case R_AccessChallenge:
|
|
||||||
syslog(0, AUTHLOG, "%s challenge ruser=%s %s", dest, ruser, replymsg(resp));
|
|
||||||
rv = "secureid out of sync";
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
syslog(0, AUTHLOG, "%s code=%d ruser=%s %s", dest, resp->code, ruser, replymsg(resp));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break; // we have a proper reply, no need to ask again
|
|
||||||
}
|
|
||||||
ndbfree(t);
|
|
||||||
free(radiussecret);
|
|
||||||
out:
|
|
||||||
freePacket(req);
|
|
||||||
freePacket(resp);
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
@ -1,244 +0,0 @@
|
|||||||
#include <u.h>
|
|
||||||
#include <libc.h>
|
|
||||||
#include <mp.h>
|
|
||||||
#include <libsec.h>
|
|
||||||
#include "SConn.h"
|
|
||||||
#include "secstore.h"
|
|
||||||
|
|
||||||
int verbose;
|
|
||||||
|
|
||||||
static void userinput(char *, int);
|
|
||||||
char *SECSTORE_DIR;
|
|
||||||
|
|
||||||
static void
|
|
||||||
ensure_exists(char *f, ulong perm)
|
|
||||||
{
|
|
||||||
int fd;
|
|
||||||
|
|
||||||
if(access(f, AEXIST) >= 0)
|
|
||||||
return;
|
|
||||||
if(verbose)
|
|
||||||
fprint(2,"first time setup for secstore: create %s %lo\n", f, perm);
|
|
||||||
fd = create(f, OREAD, perm);
|
|
||||||
if(fd < 0){
|
|
||||||
fprint(2, "unable to create %s\n", f);
|
|
||||||
exits("secstored directories");
|
|
||||||
}
|
|
||||||
close(fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int
|
|
||||||
main(int argc, char **argv)
|
|
||||||
{
|
|
||||||
int isnew;
|
|
||||||
char *id, buf[Maxmsg], home[Maxmsg], prompt[100], *hexHi;
|
|
||||||
char *pass, *passck;
|
|
||||||
long expsecs;
|
|
||||||
mpint *H = mpnew(0), *Hi = mpnew(0);
|
|
||||||
PW *pw;
|
|
||||||
Tm *tm;
|
|
||||||
|
|
||||||
SECSTORE_DIR = unsharp("#9/secstore");
|
|
||||||
|
|
||||||
ARGBEGIN{
|
|
||||||
case 'v':
|
|
||||||
verbose++;
|
|
||||||
break;
|
|
||||||
}ARGEND;
|
|
||||||
if(argc!=1){
|
|
||||||
print("usage: secuser [-v] <user>\n");
|
|
||||||
exits("usage");
|
|
||||||
}
|
|
||||||
|
|
||||||
ensure_exists(SECSTORE_DIR, DMDIR|0755L);
|
|
||||||
snprint(home, sizeof(home), "%s/who", SECSTORE_DIR);
|
|
||||||
ensure_exists(home, DMDIR|0755L);
|
|
||||||
snprint(home, sizeof(home), "%s/store", SECSTORE_DIR);
|
|
||||||
ensure_exists(home, DMDIR|0700L);
|
|
||||||
|
|
||||||
id = argv[0];
|
|
||||||
if(verbose)
|
|
||||||
fprint(2,"secuser %s\n", id);
|
|
||||||
if((pw = getPW(id,1)) == nil){
|
|
||||||
isnew = 1;
|
|
||||||
print("new account (because %s/%s %r)\n", SECSTORE_DIR, id);
|
|
||||||
pw = emalloc(sizeof(*pw));
|
|
||||||
pw->id = estrdup(id);
|
|
||||||
snprint(home, sizeof(home), "%s/store/%s", SECSTORE_DIR, id);
|
|
||||||
if(access(home, AEXIST) == 0){
|
|
||||||
print("new user, but directory %s already exists\n", home);
|
|
||||||
exits(home);
|
|
||||||
}
|
|
||||||
}else{
|
|
||||||
isnew = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* get main password for id */
|
|
||||||
for(;;){
|
|
||||||
if(isnew)
|
|
||||||
snprint(prompt, sizeof(prompt), "%s password", id);
|
|
||||||
else
|
|
||||||
snprint(prompt, sizeof(prompt), "%s password [default = don't change]", id);
|
|
||||||
pass = readcons(prompt, nil, 1);
|
|
||||||
if(pass == nil){
|
|
||||||
print("getpass failed\n");
|
|
||||||
exits("getpass failed");
|
|
||||||
}
|
|
||||||
if(verbose)
|
|
||||||
print("%ld characters\n", strlen(pass));
|
|
||||||
if(pass[0] == '\0' && isnew == 0)
|
|
||||||
break;
|
|
||||||
if(strlen(pass) >= 7)
|
|
||||||
break;
|
|
||||||
print("password must be at least 7 characters\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
if(pass[0] != '\0'){
|
|
||||||
snprint(prompt, sizeof(prompt), "retype password");
|
|
||||||
if(verbose)
|
|
||||||
print("confirming...\n");
|
|
||||||
passck = readcons(prompt, nil, 1);
|
|
||||||
if(passck == nil){
|
|
||||||
print("getpass failed\n");
|
|
||||||
exits("getpass failed");
|
|
||||||
}
|
|
||||||
if(strcmp(pass, passck) != 0){
|
|
||||||
print("passwords didn't match\n");
|
|
||||||
exits("no match");
|
|
||||||
}
|
|
||||||
memset(passck, 0, strlen(passck));
|
|
||||||
free(passck);
|
|
||||||
hexHi = PAK_Hi(id, pass, H, Hi);
|
|
||||||
memset(pass, 0, strlen(pass));
|
|
||||||
free(pass);
|
|
||||||
free(hexHi);
|
|
||||||
mpfree(H);
|
|
||||||
pw->Hi = Hi;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* get expiration time (midnight of date specified) */
|
|
||||||
if(isnew)
|
|
||||||
expsecs = time(0) + 365*24*60*60;
|
|
||||||
else
|
|
||||||
expsecs = pw->expire;
|
|
||||||
|
|
||||||
for(;;){
|
|
||||||
tm = localtime(expsecs);
|
|
||||||
print("expires [DDMMYYYY, default = %2.2d%2.2d%4.4d]: ",
|
|
||||||
tm->mday, tm->mon, tm->year+1900);
|
|
||||||
userinput(buf, sizeof(buf));
|
|
||||||
if(strlen(buf) == 0)
|
|
||||||
break;
|
|
||||||
if(strlen(buf) != 8){
|
|
||||||
print("!bad date format: %s\n", buf);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
tm->mday = (buf[0]-'0')*10 + (buf[1]-'0');
|
|
||||||
if(tm->mday > 31 || tm->mday < 1){
|
|
||||||
print("!bad day of month: %d\n", tm->mday);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
tm->mon = (buf[2]-'0')*10 + (buf[3]-'0') - 1;
|
|
||||||
if(tm->mon > 11 || tm->mday < 0){
|
|
||||||
print("!bad month: %d\n", tm->mon + 1);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
tm->year = atoi(buf+4) - 1900;
|
|
||||||
if(tm->year < 70){
|
|
||||||
print("!bad year: %d\n", tm->year + 1900);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
tm->sec = 59;
|
|
||||||
tm->min = 59;
|
|
||||||
tm->hour = 23;
|
|
||||||
tm->yday = 0;
|
|
||||||
expsecs = tm2sec(tm);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
pw->expire = expsecs;
|
|
||||||
|
|
||||||
/* failed logins */
|
|
||||||
if(pw->failed != 0 )
|
|
||||||
print("clearing %d failed login attempts\n", pw->failed);
|
|
||||||
pw->failed = 0;
|
|
||||||
|
|
||||||
/* status bits */
|
|
||||||
if(isnew)
|
|
||||||
pw->status = Enabled;
|
|
||||||
for(;;){
|
|
||||||
print("Enabled or Disabled [default %s]: ",
|
|
||||||
(pw->status & Enabled) ? "Enabled" : "Disabled" );
|
|
||||||
userinput(buf, sizeof(buf));
|
|
||||||
if(strlen(buf) == 0)
|
|
||||||
break;
|
|
||||||
if(buf[0]=='E' || buf[0]=='e'){
|
|
||||||
pw->status |= Enabled;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if(buf[0]=='D' || buf[0]=='d'){
|
|
||||||
pw->status = pw->status & ~Enabled;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for(;;){
|
|
||||||
print("require STA? [default %s]: ",
|
|
||||||
(pw->status & STA) ? "yes" : "no" );
|
|
||||||
userinput(buf, sizeof(buf));
|
|
||||||
if(strlen(buf) == 0)
|
|
||||||
break;
|
|
||||||
if(buf[0]=='Y' || buf[0]=='y'){
|
|
||||||
pw->status |= STA;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if(buf[0]=='N' || buf[0]=='n'){
|
|
||||||
pw->status = pw->status & ~STA;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* free form field */
|
|
||||||
if(isnew)
|
|
||||||
pw->other = nil;
|
|
||||||
print("comments [default = %s]: ", (pw->other == nil) ? "" : pw->other);
|
|
||||||
userinput(buf, 72); /* 72 comes from password.h */
|
|
||||||
if(buf[0])
|
|
||||||
if((pw->other = strdup(buf)) == nil)
|
|
||||||
sysfatal("strdup");
|
|
||||||
|
|
||||||
syslog(0, LOG, "CHANGELOGIN for '%s'", pw->id);
|
|
||||||
if(putPW(pw) < 0){
|
|
||||||
print("error writing entry: %r\n");
|
|
||||||
exits("can't write password file");
|
|
||||||
}else{
|
|
||||||
print("change written\n");
|
|
||||||
if(isnew && create(home, OREAD, DMDIR | 0775L) < 0){
|
|
||||||
print("unable to create %s: %r\n", home);
|
|
||||||
exits(home);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
exits("");
|
|
||||||
return 1; /* keep other compilers happy */
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void
|
|
||||||
userinput(char *buf, int blen)
|
|
||||||
{
|
|
||||||
int n;
|
|
||||||
|
|
||||||
while(1){
|
|
||||||
n = read(0, buf, blen);
|
|
||||||
if(n<=0)
|
|
||||||
exits("read error");
|
|
||||||
if(buf[n-1]=='\n'){
|
|
||||||
buf[n-1] = '\0';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
buf += n; blen -= n;
|
|
||||||
if(blen<=0)
|
|
||||||
exits("input too large");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@ -1,28 +0,0 @@
|
|||||||
#include <u.h>
|
|
||||||
#include <libc.h>
|
|
||||||
|
|
||||||
void *
|
|
||||||
emalloc(ulong n)
|
|
||||||
{
|
|
||||||
void *p = malloc(n);
|
|
||||||
if(p == nil)
|
|
||||||
sysfatal("emalloc");
|
|
||||||
memset(p, 0, n);
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
|
|
||||||
void *
|
|
||||||
erealloc(void *p, ulong n)
|
|
||||||
{
|
|
||||||
if ((p = realloc(p, n)) == nil)
|
|
||||||
sysfatal("erealloc");
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
|
|
||||||
char *
|
|
||||||
estrdup(char *s)
|
|
||||||
{
|
|
||||||
if ((s = strdup(s)) == nil)
|
|
||||||
sysfatal("estrdup");
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
Loading…
Reference in New Issue
Block a user