diff options
author | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
---|---|---|
committer | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
commit | e5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch) | |
tree | d8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/cmd/auth/factotum/chap.c |
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/cmd/auth/factotum/chap.c')
-rwxr-xr-x | sys/src/cmd/auth/factotum/chap.c | 452 |
1 files changed, 452 insertions, 0 deletions
diff --git a/sys/src/cmd/auth/factotum/chap.c b/sys/src/cmd/auth/factotum/chap.c new file mode 100755 index 000000000..b941e0cf3 --- /dev/null +++ b/sys/src/cmd/auth/factotum/chap.c @@ -0,0 +1,452 @@ +/* + * CHAP, MSCHAP + * + * The client does not authenticate the server, hence no CAI + * + * Client protocol: + * write Chapchal + * read response Chapreply or MSchaprely structure + * + * Server protocol: + * read challenge: 8 bytes binary + * write user: utf8 + * write response: Chapreply or MSchapreply structure + */ + +#include <ctype.h> +#include "dat.h" + +enum { + ChapChallen = 8, + ChapResplen = 16, + MSchapResplen = 24, +}; + +static int dochal(State*); +static int doreply(State*, void*, int); +static void doLMchap(char *, uchar [ChapChallen], uchar [MSchapResplen]); +static void doNTchap(char *, uchar [ChapChallen], uchar [MSchapResplen]); +static void dochap(char *, int, char [ChapChallen], uchar [ChapResplen]); + + +struct State +{ + char *protoname; + int astype; + int asfd; + Key *key; + Ticket t; + Ticketreq tr; + char chal[ChapChallen]; + MSchapreply mcr; + char cr[ChapResplen]; + char err[ERRMAX]; + char user[64]; + uchar secret[16]; /* for mschap */ + int nsecret; +}; + +enum +{ + CNeedChal, + CHaveResp, + + SHaveChal, + SNeedUser, + SNeedResp, + SHaveZero, + SHaveCAI, + + Maxphase +}; + +static char *phasenames[Maxphase] = +{ +[CNeedChal] "CNeedChal", +[CHaveResp] "CHaveResp", + +[SHaveChal] "SHaveChal", +[SNeedUser] "SNeedUser", +[SNeedResp] "SNeedResp", +[SHaveZero] "SHaveZero", +[SHaveCAI] "SHaveCAI", +}; + +static int +chapinit(Proto *p, Fsstate *fss) +{ + int iscli, ret; + State *s; + + if((iscli = isclient(_strfindattr(fss->attr, "role"))) < 0) + return failure(fss, nil); + + s = emalloc(sizeof *s); + fss->phasename = phasenames; + fss->maxphase = Maxphase; + s->asfd = -1; + if(p == &chap){ + s->astype = AuthChap; + s->protoname = "chap"; + }else{ + s->astype = AuthMSchap; + s->protoname = "mschap"; + } + + if(iscli) + fss->phase = CNeedChal; + else{ + if((ret = findp9authkey(&s->key, fss)) != RpcOk){ + free(s); + return ret; + } + if(dochal(s) < 0){ + free(s); + return failure(fss, nil); + } + fss->phase = SHaveChal; + } + + fss->ps = s; + return RpcOk; +} + +static void +chapclose(Fsstate *fss) +{ + State *s; + + s = fss->ps; + if(s->asfd >= 0){ + close(s->asfd); + s->asfd = -1; + } + free(s); +} + + +static int +chapwrite(Fsstate *fss, void *va, uint n) +{ + int ret, nreply; + char *a, *v; + void *reply; + Key *k; + Keyinfo ki; + State *s; + Chapreply cr; + MSchapreply mcr; + OChapreply ocr; + OMSchapreply omcr; + + s = fss->ps; + a = va; + switch(fss->phase){ + default: + return phaseerror(fss, "write"); + + case CNeedChal: + ret = findkey(&k, mkkeyinfo(&ki, fss, nil), "%s", fss->proto->keyprompt); + if(ret != RpcOk) + return ret; + v = _strfindattr(k->privattr, "!password"); + if(v == nil) + return failure(fss, "key has no password"); + setattrs(fss->attr, k->attr); + switch(s->astype){ + default: + abort(); + case AuthMSchap: + doLMchap(v, (uchar *)a, (uchar *)s->mcr.LMresp); + doNTchap(v, (uchar *)a, (uchar *)s->mcr.NTresp); + break; + case AuthChap: + dochap(v, *a, a+1, (uchar *)s->cr); + break; + } + closekey(k); + fss->phase = CHaveResp; + return RpcOk; + + case SNeedUser: + if(n >= sizeof s->user) + return failure(fss, "user name too long"); + memmove(s->user, va, n); + s->user[n] = '\0'; + fss->phase = SNeedResp; + return RpcOk; + + case SNeedResp: + switch(s->astype){ + default: + return failure(fss, "chap internal botch"); + case AuthChap: + if(n != sizeof(Chapreply)) + return failure(fss, "did not get Chapreply"); + memmove(&cr, va, sizeof cr); + ocr.id = cr.id; + memmove(ocr.resp, cr.resp, sizeof ocr.resp); + memset(omcr.uid, 0, sizeof(omcr.uid)); + strecpy(ocr.uid, ocr.uid+sizeof ocr.uid, s->user); + reply = &ocr; + nreply = sizeof ocr; + break; + case AuthMSchap: + if(n != sizeof(MSchapreply)) + return failure(fss, "did not get MSchapreply"); + memmove(&mcr, va, sizeof mcr); + memmove(omcr.LMresp, mcr.LMresp, sizeof omcr.LMresp); + memmove(omcr.NTresp, mcr.NTresp, sizeof omcr.NTresp); + memset(omcr.uid, 0, sizeof(omcr.uid)); + strecpy(omcr.uid, omcr.uid+sizeof omcr.uid, s->user); + reply = &omcr; + nreply = sizeof omcr; + break; + } + if(doreply(s, reply, nreply) < 0) + return failure(fss, nil); + fss->phase = Established; + fss->ai.cuid = s->t.cuid; + fss->ai.suid = s->t.suid; + fss->ai.secret = s->secret; + fss->ai.nsecret = s->nsecret; + fss->haveai = 1; + return RpcOk; + } +} + +static int +chapread(Fsstate *fss, void *va, uint *n) +{ + State *s; + + s = fss->ps; + switch(fss->phase){ + default: + return phaseerror(fss, "read"); + + case CHaveResp: + switch(s->astype){ + default: + phaseerror(fss, "write"); + break; + case AuthMSchap: + if(*n > sizeof(MSchapreply)) + *n = sizeof(MSchapreply); + memmove(va, &s->mcr, *n); + break; + case AuthChap: + if(*n > ChapResplen) + *n = ChapResplen; + memmove(va, s->cr, ChapResplen); + break; + } + fss->phase = Established; + fss->haveai = 0; + return RpcOk; + + case SHaveChal: + if(*n > sizeof s->chal) + *n = sizeof s->chal; + memmove(va, s->chal, *n); + fss->phase = SNeedUser; + return RpcOk; + } +} + +static int +dochal(State *s) +{ + char *dom, *user; + char trbuf[TICKREQLEN]; + + s->asfd = -1; + + /* send request to authentication server and get challenge */ + if((dom = _strfindattr(s->key->attr, "dom")) == nil + || (user = _strfindattr(s->key->attr, "user")) == nil){ + werrstr("chap/dochal cannot happen"); + goto err; + } + s->asfd = _authdial(nil, dom); + if(s->asfd < 0) + goto err; + + memset(&s->tr, 0, sizeof(s->tr)); + s->tr.type = s->astype; + safecpy(s->tr.authdom, dom, sizeof s->tr.authdom); + safecpy(s->tr.hostid, user, sizeof(s->tr.hostid)); + convTR2M(&s->tr, trbuf); + + if(write(s->asfd, trbuf, TICKREQLEN) != TICKREQLEN) + goto err; + + /* readn, not _asrdresp. needs to match auth.srv.c. */ + if(readn(s->asfd, s->chal, sizeof s->chal) != sizeof s->chal) + goto err; + return 0; + +err: + if(s->asfd >= 0) + close(s->asfd); + s->asfd = -1; + return -1; +} + +static int +doreply(State *s, void *reply, int nreply) +{ + char ticket[TICKETLEN+AUTHENTLEN]; + int n; + Authenticator a; + + if((n=write(s->asfd, reply, nreply)) != nreply){ + if(n >= 0) + werrstr("short write to auth server"); + goto err; + } + + if(_asrdresp(s->asfd, ticket, TICKETLEN+AUTHENTLEN) < 0){ + /* leave connection open so we can try again */ + return -1; + } + s->nsecret = readn(s->asfd, s->secret, sizeof s->secret); + if(s->nsecret < 0) + s->nsecret = 0; + close(s->asfd); + s->asfd = -1; + convM2T(ticket, &s->t, s->key->priv); + if(s->t.num != AuthTs + || memcmp(s->t.chal, s->tr.chal, sizeof(s->t.chal)) != 0){ + if(s->key->successes == 0) + disablekey(s->key); + werrstr(Easproto); + return -1; + } + s->key->successes++; + convM2A(ticket+TICKETLEN, &a, s->t.key); + if(a.num != AuthAc + || memcmp(a.chal, s->tr.chal, sizeof(a.chal)) != 0 + || a.id != 0){ + werrstr(Easproto); + return -1; + } + + return 0; +err: + if(s->asfd >= 0) + close(s->asfd); + s->asfd = -1; + return -1; +} + +Proto chap = { +.name= "chap", +.init= chapinit, +.write= chapwrite, +.read= chapread, +.close= chapclose, +.addkey= replacekey, +.keyprompt= "!password?" +}; + +Proto mschap = { +.name= "mschap", +.init= chapinit, +.write= chapwrite, +.read= chapread, +.close= chapclose, +.addkey= replacekey, +.keyprompt= "!password?" +}; + +static void +hash(uchar pass[16], uchar c8[ChapChallen], uchar p24[MSchapResplen]) +{ + int i; + uchar p21[21]; + ulong schedule[32]; + + memset(p21, 0, sizeof p21 ); + memmove(p21, pass, 16); + + for(i=0; i<3; i++) { + key_setup(p21+i*7, schedule); + memmove(p24+i*8, c8, 8); + block_cipher(schedule, p24+i*8, 0); + } +} + +static void +doNTchap(char *pass, uchar chal[ChapChallen], uchar reply[MSchapResplen]) +{ + Rune r; + int i, n; + uchar digest[MD4dlen]; + uchar *w, unipass[256]; + + // Standard says unlimited length, experience says 128 max + if ((n = strlen(pass)) > 128) + n = 128; + + for(i=0, w=unipass; i < n; i++) { + pass += chartorune(&r, pass); + *w++ = r & 0xff; + *w++ = r >> 8; + } + + memset(digest, 0, sizeof digest); + md4(unipass, w-unipass, digest, nil); + memset(unipass, 0, sizeof unipass); + hash(digest, chal, reply); +} + +static void +doLMchap(char *pass, uchar chal[ChapChallen], uchar reply[MSchapResplen]) +{ + int i; + ulong schedule[32]; + uchar p14[15], p16[16]; + uchar s8[8] = {0x4b, 0x47, 0x53, 0x21, 0x40, 0x23, 0x24, 0x25}; + int n = strlen(pass); + + if(n > 14){ + // let prudent people avoid the LM vulnerability + // and protect the loop below from buffer overflow + memset(reply, 0, MSchapResplen); + return; + } + + // Spec says space padded, experience says otherwise + memset(p14, 0, sizeof p14 -1); + p14[sizeof p14 - 1] = '\0'; + + // NT4 requires uppercase, Win XP doesn't care + for (i = 0; pass[i]; i++) + p14[i] = islower(pass[i])? toupper(pass[i]): pass[i]; + + for(i=0; i<2; i++) { + key_setup(p14+i*7, schedule); + memmove(p16+i*8, s8, 8); + block_cipher(schedule, p16+i*8, 0); + } + + memset(p14, 0, sizeof p14); + hash(p16, chal, reply); +} + +static void +dochap(char *pass, int id, char chal[ChapChallen], uchar resp[ChapResplen]) +{ + char buf[1+ChapChallen+MAXNAMELEN+1]; + int n = strlen(pass); + + *buf = id; + if (n > MAXNAMELEN) + n = MAXNAMELEN-1; + memset(buf, 0, sizeof buf); + strncpy(buf+1, pass, n); + memmove(buf+1+n, chal, ChapChallen); + md5((uchar*)buf, 1+n+ChapChallen, resp, nil); +} + |