u9fs/authp9any.c

542 lines
11 KiB
C

/*
* 4th Edition p9any/p9sk1 authentication based on auth9p1.c
* Nigel Roles (nigel@9fs.org) 2003
*/
#include <plan9.h>
#include <fcall.h>
#include <u9fs.h>
#include <stdlib.h> /* for random stuff */
typedef struct Ticket Ticket;
typedef struct Ticketreq Ticketreq;
typedef struct Authenticator Authenticator;
enum
{
DOMLEN= 48, /* length of an authentication domain name */
CHALLEN= 8 /* length of a challenge */
};
enum {
HaveProtos,
NeedProto,
NeedChal,
HaveTreq,
NeedTicket,
HaveAuth,
Established,
};
/* encryption numberings (anti-replay) */
enum
{
AuthTreq=1, /* ticket request */
AuthChal=2, /* challenge box request */
AuthPass=3, /* change password */
AuthOK=4, /* fixed length reply follows */
AuthErr=5, /* error follows */
AuthMod=6, /* modify user */
AuthApop=7, /* apop authentication for pop3 */
AuthOKvar=9, /* variable length reply follows */
AuthChap=10, /* chap authentication for ppp */
AuthMSchap=11, /* MS chap authentication for ppp */
AuthCram=12, /* CRAM verification for IMAP (RFC2195 & rfc2104) */
AuthHttp=13, /* http domain login */
AuthVNC=14, /* http domain login */
AuthTs=64, /* ticket encrypted with server's key */
AuthTc, /* ticket encrypted with client's key */
AuthAs, /* server generated authenticator */
AuthAc, /* client generated authenticator */
AuthTp, /* ticket encrypted with client's key for password change */
AuthHr /* http reply */
};
struct Ticketreq
{
char type;
char authid[NAMELEN]; /* server's encryption id */
char authdom[DOMLEN]; /* server's authentication domain */
char chal[CHALLEN]; /* challenge from server */
char hostid[NAMELEN]; /* host's encryption id */
char uid[NAMELEN]; /* uid of requesting user on host */
};
#define TICKREQLEN (3*NAMELEN+CHALLEN+DOMLEN+1)
struct Ticket
{
char num; /* replay protection */
char chal[CHALLEN]; /* server challenge */
char cuid[NAMELEN]; /* uid on client */
char suid[NAMELEN]; /* uid on server */
char key[DESKEYLEN]; /* nonce DES key */
};
#define TICKETLEN (CHALLEN+2*NAMELEN+DESKEYLEN+1)
struct Authenticator
{
char num; /* replay protection */
char chal[CHALLEN];
ulong id; /* authenticator id, ++'d with each auth */
};
#define AUTHENTLEN (CHALLEN+4+1)
extern int chatty9p;
static int convT2M(Ticket*, char*, char*);
static void convM2T(char*, Ticket*, char*);
static void convM2Tnoenc(char*, Ticket*);
static int convA2M(Authenticator*, char*, char*);
static void convM2A(char*, Authenticator*, char*);
static int convTR2M(Ticketreq*, char*);
static void convM2TR(char*, Ticketreq*);
static int passtokey(char*, char*);
/*
* destructively encrypt the buffer, which
* must be at least 8 characters long.
*/
static int
encrypt9p(void *key, void *vbuf, int n)
{
char ekey[128], *buf;
int i, r;
if(n < 8)
return 0;
key_setup(key, ekey);
buf = vbuf;
n--;
r = n % 7;
n /= 7;
for(i = 0; i < n; i++){
block_cipher(ekey, buf, 0);
buf += 7;
}
if(r)
block_cipher(ekey, buf - 7 + r, 0);
return 1;
}
/*
* destructively decrypt the buffer, which
* must be at least 8 characters long.
*/
static int
decrypt9p(void *key, void *vbuf, int n)
{
char ekey[128], *buf;
int i, r;
if(n < 8)
return 0;
key_setup(key, ekey);
buf = vbuf;
n--;
r = n % 7;
n /= 7;
buf += n * 7;
if(r)
block_cipher(ekey, buf - 7 + r, 1);
for(i = 0; i < n; i++){
buf -= 7;
block_cipher(ekey, buf, 1);
}
return 1;
}
#define CHAR(x) *p++ = f->x
#define SHORT(x) p[0] = f->x; p[1] = f->x>>8; p += 2
#define VLONG(q) p[0] = (q); p[1] = (q)>>8; p[2] = (q)>>16; p[3] = (q)>>24; p += 4
#define LONG(x) VLONG(f->x)
#define STRING(x,n) memmove(p, f->x, n); p += n
static int
convTR2M(Ticketreq *f, char *ap)
{
int n;
uchar *p;
p = (uchar*)ap;
CHAR(type);
STRING(authid, NAMELEN);
STRING(authdom, DOMLEN);
STRING(chal, CHALLEN);
STRING(hostid, NAMELEN);
STRING(uid, NAMELEN);
n = p - (uchar*)ap;
return n;
}
static int
convT2M(Ticket *f, char *ap, char *key)
{
int n;
uchar *p;
p = (uchar*)ap;
CHAR(num);
STRING(chal, CHALLEN);
STRING(cuid, NAMELEN);
STRING(suid, NAMELEN);
STRING(key, DESKEYLEN);
n = p - (uchar*)ap;
if(key)
encrypt9p(key, ap, n);
return n;
}
int
convA2M(Authenticator *f, char *ap, char *key)
{
int n;
uchar *p;
p = (uchar*)ap;
CHAR(num);
STRING(chal, CHALLEN);
LONG(id);
n = p - (uchar*)ap;
if(key)
encrypt9p(key, ap, n);
return n;
}
#undef CHAR
#undef SHORT
#undef VLONG
#undef LONG
#undef STRING
#define CHAR(x) f->x = *p++
#define SHORT(x) f->x = (p[0] | (p[1]<<8)); p += 2
#define VLONG(q) q = (p[0] | (p[1]<<8) | (p[2]<<16) | (p[3]<<24)); p += 4
#define LONG(x) VLONG(f->x)
#define STRING(x,n) memmove(f->x, p, n); p += n
void
convM2A(char *ap, Authenticator *f, char *key)
{
uchar *p;
if(key)
decrypt9p(key, ap, AUTHENTLEN);
p = (uchar*)ap;
CHAR(num);
STRING(chal, CHALLEN);
LONG(id);
USED(p);
}
void
convM2T(char *ap, Ticket *f, char *key)
{
uchar *p;
if(key)
decrypt9p(key, ap, TICKETLEN);
p = (uchar*)ap;
CHAR(num);
STRING(chal, CHALLEN);
STRING(cuid, NAMELEN);
f->cuid[NAMELEN-1] = 0;
STRING(suid, NAMELEN);
f->suid[NAMELEN-1] = 0;
STRING(key, DESKEYLEN);
USED(p);
}
#undef CHAR
#undef SHORT
#undef LONG
#undef VLONG
#undef STRING
static int
passtokey(char *key, char *p)
{
uchar buf[NAMELEN], *t;
int i, n;
n = strlen(p);
if(n >= NAMELEN)
n = NAMELEN-1;
memset(buf, ' ', 8);
t = buf;
strncpy((char*)t, p, n);
t[n] = 0;
memset(key, 0, DESKEYLEN);
for(;;){
for(i = 0; i < DESKEYLEN; i++)
key[i] = (t[i] >> i) + (t[i+1] << (8 - (i+1)));
if(n <= 8)
return 1;
n -= 8;
t += 8;
if(n < 8){
t -= 8 - n;
n = 8;
}
encrypt9p(key, t, 8);
}
return 1; /* not reached */
}
static char authkey[DESKEYLEN];
static char *authid;
static char *authdom;
static char *haveprotosmsg;
static char *needprotomsg;
static void
p9anyinit(void)
{
int n, fd;
char abuf[200];
char *af, *f[4];
af = autharg;
if(af == nil)
af = "/etc/u9fs.key";
if((fd = open(af, OREAD)) < 0)
sysfatal("can't open key file '%s'", af);
if((n = readn(fd, abuf, sizeof(abuf)-1)) < 0)
sysfatal("can't read key file '%s'", af);
close(fd);
if (n > 0 && abuf[n - 1] == '\n')
n--;
abuf[n] = '\0';
if(getfields(abuf, f, nelem(f), 0, "\n") != 3)
sysfatal("key file '%s' not exactly 3 lines", af);
passtokey(authkey, f[0]);
authid = strdup(f[1]);
authdom = strdup(f[2]);
haveprotosmsg = smprint("p9sk1@%s", authdom);
needprotomsg = smprint("p9sk1 %s", authdom);
if(haveprotosmsg == nil || needprotomsg == nil)
sysfatal("out of memory");
}
typedef struct AuthSession {
int state;
char *uname;
char *aname;
char cchal[CHALLEN];
Ticketreq tr;
Ticket t;
} AuthSession;
static char*
p9anyauth(Fcall *rx, Fcall *tx)
{
AuthSession *sp;
Fid *f;
char *ep;
sp = malloc(sizeof(AuthSession));
f = newauthfid(rx->afid, sp, &ep);
if (f == nil) {
free(sp);
return ep;
}
if (chatty9p)
fprint(2, "p9anyauth: afid %d\n", rx->afid);
sp->state = HaveProtos;
sp->uname = strdup(rx->uname);
sp->aname = strdup(rx->aname);
tx->aqid.type = QTAUTH;
tx->aqid.path = 1;
tx->aqid.vers = 0;
return nil;
}
static char *
p9anyattach(Fcall *rx, Fcall *tx)
{
AuthSession *sp;
Fid *f;
char *ep;
f = oldauthfid(rx->afid, (void **)&sp, &ep);
if (f == nil)
return ep;
if (chatty9p)
fprint(2, "p9anyattach: afid %d state %d\n", rx->afid, sp->state);
if (sp->state == Established && strcmp(rx->uname, sp->uname) == 0
&& strcmp(rx->aname, sp->aname) == 0){
rx->uname = sp->t.suid;
return nil;
}
return "authentication failed";
}
static int
readstr(Fcall *rx, Fcall *tx, char *s, int len)
{
if (rx->offset >= len)
return 0;
tx->count = len - rx->offset;
if (tx->count > rx->count)
tx->count = rx->count;
memcpy(tx->data, s + rx->offset, tx->count);
return tx->count;
}
static char *
p9anyread(Fcall *rx, Fcall *tx)
{
AuthSession *sp;
char *ep;
Fid *f;
f = oldauthfid(rx->fid, (void **)&sp, &ep);
if (f == nil)
return ep;
if (chatty9p)
fprint(2, "p9anyread: afid %d state %d\n", rx->fid, sp->state);
switch (sp->state) {
case HaveProtos:
readstr(rx, tx, haveprotosmsg, strlen(haveprotosmsg) + 1);
if (rx->offset + tx->count == strlen(haveprotosmsg) + 1)
sp->state = NeedProto;
return nil;
case HaveTreq:
if (rx->count != TICKREQLEN)
goto botch;
convTR2M(&sp->tr, tx->data);
tx->count = TICKREQLEN;
sp->state = NeedTicket;
return nil;
case HaveAuth: {
Authenticator a;
if (rx->count != AUTHENTLEN)
goto botch;
a.num = AuthAs;
memmove(a.chal, sp->cchal, CHALLEN);
a.id = 0;
convA2M(&a, (char*)tx->data, sp->t.key);
memset(sp->t.key, 0, sizeof(sp->t.key));
tx->count = rx->count;
sp->state = Established;
return nil;
}
default:
botch:
return "protocol botch";
}
}
static char *
p9anywrite(Fcall *rx, Fcall *tx)
{
AuthSession *sp;
char *ep;
Fid *f;
f = oldauthfid(rx->fid, (void **)&sp, &ep);
if (f == nil)
return ep;
if (chatty9p)
fprint(2, "p9anywrite: afid %d state %d\n", rx->fid, sp->state);
switch (sp->state) {
case NeedProto:
if (rx->count != strlen(needprotomsg) + 1)
return "protocol response wrong length";
if (memcmp(rx->data, needprotomsg, rx->count) != 0)
return "unacceptable protocol";
sp->state = NeedChal;
tx->count = rx->count;
return nil;
case NeedChal:
if (rx->count != CHALLEN)
goto botch;
memmove(sp->cchal, rx->data, CHALLEN);
sp->tr.type = AuthTreq;
safecpy(sp->tr.authid, authid, sizeof(sp->tr.authid));
safecpy(sp->tr.authdom, authdom, sizeof(sp->tr.authdom));
randombytes((uchar *)sp->tr.chal, CHALLEN);
safecpy(sp->tr.hostid, "", sizeof(sp->tr.hostid));
safecpy(sp->tr.uid, "", sizeof(sp->tr.uid));
tx->count = rx->count;
sp->state = HaveTreq;
return nil;
case NeedTicket: {
Authenticator a;
if (rx->count != TICKETLEN + AUTHENTLEN) {
fprint(2, "bad length in attach");
goto botch;
}
convM2T((char*)rx->data, &sp->t, authkey);
if (sp->t.num != AuthTs) {
fprint(2, "bad AuthTs in attach\n");
goto botch;
}
if (memcmp(sp->t.chal, sp->tr.chal, CHALLEN) != 0) {
fprint(2, "bad challenge in attach\n");
goto botch;
}
convM2A((char*)rx->data + TICKETLEN, &a, sp->t.key);
if (a.num != AuthAc) {
fprint(2, "bad AuthAs in attach\n");
goto botch;
}
if(memcmp(a.chal, sp->tr.chal, CHALLEN) != 0) {
fprint(2, "bad challenge in attach 2\n");
goto botch;
}
sp->state = HaveAuth;
tx->count = rx->count;
return nil;
}
default:
botch:
return "protocol botch";
}
}
static void
safefree(char *p)
{
if (p) {
memset(p, 0, strlen(p));
free(p);
}
}
static char *
p9anyclunk(Fcall *rx, Fcall *tx)
{
Fid *f;
AuthSession *sp;
char *ep;
f = oldauthfid(rx->fid, (void **)&sp, &ep);
if (f == nil)
return ep;
if (chatty9p)
fprint(2, "p9anyclunk: afid %d\n", rx->fid);
safefree(sp->uname);
safefree(sp->aname);
memset(sp, 0, sizeof(*sp));
free(sp);
return nil;
}
Auth authp9any = {
"p9any",
p9anyauth,
p9anyattach,
p9anyinit,
p9anyread,
p9anywrite,
p9anyclunk,
};