> surely you need to use the "user" attribute.  if you don't please send
> a copy of chap that doesn't give compiler warnings.

The user attribute is available at the end of CR protocol, upon 'attr'
rpc request.

The attached chap.c compiles cleanly here.

- yk
/*
 * 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"

extern Proto chap, mschap;

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, *res;
        Attr *attr;
        Key *k;
        Chapreply cr;
        MSchapreply mscr;
        DigestState *ds;

        ret = -1;
        chal = nil;
        res = 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(strfindattr(k->attr, "user") == nil){
                werrstr("key has no user (cannot happen?)");
                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 = {
        "chap",
        chaproles,
        "user? !password?",
        chapcheck
};

Proto mschap = {
        "mschap",
        chaproles,
        "user? !password?",
        chapcheck
};

Reply via email to