370 lines
6.7 KiB
C
370 lines
6.7 KiB
C
/*
|
|
* p9cr - 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"
|
|
|
|
/* shared with auth dialing routines */
|
|
typedef struct ServerState ServerState;
|
|
struct ServerState
|
|
{
|
|
int asfd;
|
|
Key *k;
|
|
Ticketreq tr;
|
|
Ticket t;
|
|
char *dom;
|
|
char *hostid;
|
|
};
|
|
|
|
enum
|
|
{
|
|
MAXCHAL = 64,
|
|
MAXRESP = 64,
|
|
};
|
|
|
|
extern Proto p9cr, vnc;
|
|
static int p9response(char*, uchar*, uchar*);
|
|
// static int vncresponse(char*, uchar*, uchar*);
|
|
static int p9crchal(ServerState *s, int, char*, uchar*, int);
|
|
static int p9crresp(ServerState*, uchar*, int);
|
|
|
|
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 *pw, *res, *user;
|
|
int astype, challen, resplen, ntry, ret;
|
|
Attr *attr;
|
|
Key *k;
|
|
uchar chal[MAXCHAL+1], resp[MAXRESP];
|
|
int (*response)(char*, uchar*, uchar*);
|
|
|
|
k = nil;
|
|
res = nil;
|
|
ret = -1;
|
|
attr = c->attr;
|
|
|
|
if(c->proto == &p9cr){
|
|
astype = AuthChal;
|
|
challen = NETCHLEN;
|
|
response = p9response;
|
|
attr = _mkattr(AttrNameval, "proto", "p9sk1", _delattr(_copyattr(attr), "proto"));
|
|
}else if(c->proto == &vnc){
|
|
astype = AuthVNC;
|
|
challen = MAXCHAL;
|
|
// response = vncresponse;
|
|
}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;
|
|
}
|
|
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(convread(c, chal, challen) < 0)
|
|
goto out;
|
|
chal[challen] = 0;
|
|
|
|
if((resplen = (*response)(pw, chal, resp)) < 0)
|
|
goto out;
|
|
|
|
if(convwrite(c, resp, resplen) < 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);
|
|
if(c->attr != attr)
|
|
freeattr(attr);
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
p9crserver(Conv *c)
|
|
{
|
|
uchar chal[MAXCHAL], *resp, *resp1;
|
|
char *user;
|
|
ServerState s;
|
|
int astype, ret, challen, resplen;
|
|
Attr *a;
|
|
|
|
ret = -1;
|
|
user = nil;
|
|
resp = nil;
|
|
memset(&s, 0, sizeof s);
|
|
s.asfd = -1;
|
|
|
|
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";
|
|
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;
|
|
}
|
|
|
|
for(;;){
|
|
c->state = "read user";
|
|
if(convreadm(c, &user) < 0)
|
|
goto out;
|
|
|
|
c->state = "authchal";
|
|
if(p9crchal(&s, astype, user, chal, challen) < 0)
|
|
goto out;
|
|
|
|
c->state = "write challenge";
|
|
if(convwrite(c, chal, challen) < 0)
|
|
goto out;
|
|
|
|
c->state = "read response";
|
|
if((resplen = convreadm(c, (char**)(void*)&resp)) < 0)
|
|
goto out;
|
|
if(c->proto == &p9cr){
|
|
if(resplen > NETCHLEN){
|
|
convprint(c, "bad response too long");
|
|
goto out;
|
|
}
|
|
resp1 = emalloc(NETCHLEN);
|
|
memset(resp1, 0, NETCHLEN);
|
|
memmove(resp1, resp, resplen);
|
|
free(resp);
|
|
resp = resp1;
|
|
resplen = NETCHLEN;
|
|
}
|
|
|
|
c->state = "authwrite";
|
|
switch(p9crresp(&s, resp, resplen)){
|
|
case -1:
|
|
fprint(2, "p9crresp: %r\n");
|
|
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
|
|
p9crchal(ServerState *s, int astype, char *user, uchar *chal, int challen)
|
|
{
|
|
char trbuf[TICKREQLEN];
|
|
Ticketreq tr;
|
|
int n;
|
|
|
|
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);
|
|
|
|
if(strlen(user) >= sizeof tr.uid){
|
|
werrstr("user name too long");
|
|
return -1;
|
|
}
|
|
strcpy(tr.uid, user);
|
|
convTR2M(&tr, trbuf);
|
|
|
|
if(xiowrite(s->asfd, trbuf, TICKREQLEN) != TICKREQLEN)
|
|
return -1;
|
|
|
|
if((n=xioasrdresp(s->asfd, chal, challen)) <= 0)
|
|
return -1;
|
|
return n;
|
|
}
|
|
|
|
static int
|
|
p9crresp(ServerState *s, uchar *resp, int resplen)
|
|
{
|
|
char tabuf[TICKETLEN+AUTHENTLEN];
|
|
Authenticator a;
|
|
Ticket t;
|
|
Ticketreq tr;
|
|
|
|
if(xiowrite(s->asfd, resp, resplen) != resplen)
|
|
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 int
|
|
p9response(char *pw, uchar *chal, uchar *resp)
|
|
{
|
|
char key[DESKEYLEN];
|
|
uchar buf[8];
|
|
ulong x;
|
|
|
|
passtokey(key, pw);
|
|
memset(buf, 0, 8);
|
|
snprint((char*)buf, sizeof buf, "%d", atoi((char*)chal));
|
|
if(encrypt(key, buf, 8) < 0){
|
|
werrstr("can't encrypt response");
|
|
return -1;
|
|
}
|
|
x = (buf[0]<<24)+(buf[1]<<16)+(buf[2]<<8)+buf[3];
|
|
return snprint((char*)resp, MAXRESP, "%.8lux", x);
|
|
}
|
|
|
|
/*
|
|
static int
|
|
vncresponse(char *pw, uchar *chal, uchar *resp)
|
|
{
|
|
DESstate des;
|
|
|
|
memmove(resp, chal, MAXCHAL);
|
|
setupDESstate(&des, 0, nil); // XXX put key in for 0
|
|
desECBencrypt(resp, MAXCHAL, &des);
|
|
return MAXCHAL;
|
|
}
|
|
*/
|
|
|
|
static Role
|
|
p9crroles[] =
|
|
{
|
|
"client", p9crclient,
|
|
"server", p9crserver,
|
|
0
|
|
};
|
|
|
|
Proto p9cr = {
|
|
"p9cr",
|
|
p9crroles,
|
|
"user? !password?",
|
|
p9crcheck,
|
|
nil
|
|
};
|
|
|
|
/* still need to implement vnc key generator */
|
|
Proto vnc = {
|
|
"vnc",
|
|
p9crroles,
|
|
"user? !password?",
|
|
p9crcheck,
|
|
nil
|
|
};
|