/* * CHAP, MSCHAP * * The client does not authenticate the server, hence no CAI * * Client protocol: * unimplemented * * Server protocol: * read challenge: 8 bytes binary * write user: utf8 * write response: Chapreply or MSchapreply structure */ #include "dat.h" enum { ChapChallen = 8, }; static int dochal(State*); static int doreply(State*, void*, int); struct State { char *protoname; int astype; int asfd; Key *key; Ticket t; Ticketreq tr; char chal[ChapChallen]; char err[ERRMAX]; char user[64]; uchar secret[16]; /* for mschap */ int nsecret; }; enum { SHaveChal, SNeedUser, SNeedResp, SHaveZero, SHaveCAI, Maxphase }; static char *phasenames[Maxphase] = { [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); if(iscli) return failure(fss, "%s client not supported", p->name); 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((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 nreply; void *reply; State *s; Chapreply cr; MSchapreply mcr; OChapreply ocr; OMSchapreply omcr; s = fss->ps; switch(fss->phase){ default: return phaseerror(fss, "write"); 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); 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); 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 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){ werrstr(Easproto); return -1; } 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, }; Proto mschap = { .name= "mschap", .init= chapinit, .write= chapwrite, .read= chapread, .close= chapclose, };