|
@@ -4,7 +4,8 @@
|
|
|
* The client does not authenticate the server, hence no CAI
|
|
|
*
|
|
|
* Client protocol:
|
|
|
- * unimplemented
|
|
|
+ * write Chapchal
|
|
|
+ * read response Chapreply or MSchaprely structure
|
|
|
*
|
|
|
* Server protocol:
|
|
|
* read challenge: 8 bytes binary
|
|
@@ -12,14 +13,21 @@
|
|
|
* 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
|
|
|
{
|
|
@@ -30,7 +38,9 @@ struct State
|
|
|
Ticket t;
|
|
|
Ticketreq tr;
|
|
|
char chal[ChapChallen];
|
|
|
- char err[ERRMAX];
|
|
|
+ MSchapreply mcr;
|
|
|
+ char cr[ChapResplen];
|
|
|
+ char err[ERRMAX];
|
|
|
char user[64];
|
|
|
uchar secret[16]; /* for mschap */
|
|
|
int nsecret;
|
|
@@ -38,6 +48,9 @@ struct State
|
|
|
|
|
|
enum
|
|
|
{
|
|
|
+ CNeedChal,
|
|
|
+ CHaveResp,
|
|
|
+
|
|
|
SHaveChal,
|
|
|
SNeedUser,
|
|
|
SNeedResp,
|
|
@@ -49,6 +62,9 @@ enum
|
|
|
|
|
|
static char *phasenames[Maxphase] =
|
|
|
{
|
|
|
+[CNeedChal] "CNeedChal",
|
|
|
+[CHaveResp] "CHaveResp",
|
|
|
+
|
|
|
[SHaveChal] "SHaveChal",
|
|
|
[SNeedUser] "SNeedUser",
|
|
|
[SNeedResp] "SNeedResp",
|
|
@@ -64,8 +80,6 @@ chapinit(Proto *p, Fsstate *fss)
|
|
|
|
|
|
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;
|
|
@@ -78,15 +92,21 @@ chapinit(Proto *p, Fsstate *fss)
|
|
|
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);
|
|
|
+
|
|
|
+ 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->phase = SHaveChal;
|
|
|
+
|
|
|
fss->ps = s;
|
|
|
return RpcOk;
|
|
|
}
|
|
@@ -104,11 +124,26 @@ chapclose(Fsstate *fss)
|
|
|
free(s);
|
|
|
}
|
|
|
|
|
|
+void
|
|
|
+dmpkey(char *s, void *v, int n)
|
|
|
+{
|
|
|
+ int i;
|
|
|
+ char *p = v;
|
|
|
+
|
|
|
+ print("%s", s);
|
|
|
+ for (i = 0; i < n; i++)
|
|
|
+ print("%02x ", *p++);
|
|
|
+ print("\n");
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
static int
|
|
|
chapwrite(Fsstate *fss, void *va, uint n)
|
|
|
{
|
|
|
- int nreply;
|
|
|
+ int ret, nreply;
|
|
|
+ char *a, *v;
|
|
|
void *reply;
|
|
|
+ Key *k;
|
|
|
State *s;
|
|
|
Chapreply cr;
|
|
|
MSchapreply mcr;
|
|
@@ -116,10 +151,34 @@ chapwrite(Fsstate *fss, void *va, uint n)
|
|
|
OMSchapreply omcr;
|
|
|
|
|
|
s = fss->ps;
|
|
|
+ a = va;
|
|
|
switch(fss->phase){
|
|
|
default:
|
|
|
return phaseerror(fss, "write");
|
|
|
|
|
|
+ case CNeedChal:
|
|
|
+ ret = findkey(&k, fss, fss->sysuser, 0, 0, fss->attr, "%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");
|
|
@@ -151,6 +210,9 @@ chapwrite(Fsstate *fss, void *va, uint n)
|
|
|
strecpy(omcr.uid, omcr.uid+sizeof omcr.uid, s->user);
|
|
|
reply = &omcr;
|
|
|
nreply = sizeof omcr;
|
|
|
+print("%s\n", omcr.uid);
|
|
|
+dmpkey("LM ", omcr.LMresp, 24);
|
|
|
+dmpkey("NT ", omcr.NTresp, 24);
|
|
|
break;
|
|
|
}
|
|
|
if(doreply(s, reply, nreply) < 0)
|
|
@@ -175,6 +237,25 @@ chapread(Fsstate *fss, void *va, uint *n)
|
|
|
default:
|
|
|
return phaseerror(fss, "read");
|
|
|
|
|
|
+ case CHaveResp:
|
|
|
+ switch(s->astype){
|
|
|
+ default:
|
|
|
+ abort();
|
|
|
+ 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;
|
|
@@ -273,6 +354,8 @@ Proto chap = {
|
|
|
.write= chapwrite,
|
|
|
.read= chapread,
|
|
|
.close= chapclose,
|
|
|
+.addkey= replacekey,
|
|
|
+.keyprompt= "!password?"
|
|
|
};
|
|
|
|
|
|
Proto mschap = {
|
|
@@ -281,5 +364,86 @@ Proto mschap = {
|
|
|
.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])
|
|
|
+{
|
|
|
+ int i, n;
|
|
|
+ uchar *w, unipass[256];
|
|
|
+ uchar digest[MD4dlen];
|
|
|
+
|
|
|
+ Rune r;
|
|
|
+
|
|
|
+ // Standard says unlimited length, experience says 128 max
|
|
|
+ if ((n = strlen(pass)) > 128)
|
|
|
+ n = 128;
|
|
|
+
|
|
|
+ memset(unipass, 0, sizeof unipass);
|
|
|
+ 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);
|
|
|
+ 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};
|
|
|
+
|
|
|
+ // 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);
|
|
|
+ }
|
|
|
+
|
|
|
+ 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;
|
|
|
+ strcpy(buf+1, pass);
|
|
|
+ memmove(buf+1+n, chal, ChapChallen);
|
|
|
+ md5((uchar*)buf, 1+n+ChapChallen, resp, nil);
|
|
|
+}
|
|
|
+
|