summaryrefslogtreecommitdiff
path: root/sys/src/cmd/auth/factotum/chap.c
diff options
context:
space:
mode:
authorTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
committerTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
commite5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch)
treed8d51eac403f07814b9e936eed0c9a79195e2450 /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-xsys/src/cmd/auth/factotum/chap.c452
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);
+}
+