mirror of
https://github.com/9fans/plan9port.git
synced 2025-01-15 11:20:03 +00:00
2277c5d7bb
Lots of new code imported.
352 lines
7 KiB
C
352 lines
7 KiB
C
/*
|
|
* 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,
|
|
};
|
|
|