9fans - fans of the OS Plan 9 from Bell Labs
 help / color / mirror / Atom feed
* Re: [9fans] nfactotum: mschap fix
       [not found] <CAG3N4d_mEkM2CeEQ-WhqKmK2_WbJz6X_N2JQkaR6QMRtThkw_w@mail.gmail.c>
@ 2012-03-30 15:53 ` erik quanstrom
  2012-03-30 16:11   ` yaroslav
  0 siblings, 1 reply; 4+ messages in thread
From: erik quanstrom @ 2012-03-30 15:53 UTC (permalink / raw)
  To: yarikos, 9fans

> /n/sources/contrib/quanstro/root/sys/src/cmd/auth/factotum/chap.c:153,166
> - /sys/src/cmd/auth/factotum/chap.c:154,163
>        c->state = "read challenge";
>        if((nchal = convreadm(c, (char**)(void*)&chal)) < 0)
>                goto out;
> -       if(astype == AuthMSchap && nchal != MSchallen)
> -       c->state = "write user";
>        if((user = strfindattr(k->attr, "user")) == nil){
>                werrstr("key has no user (cannot happen?)");
>                goto out;
>        }
> -       if(convprint(c, "%s", user) < 0)
> -               goto out;

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

- erik



^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [9fans] nfactotum: mschap fix
  2012-03-30 15:53 ` [9fans] nfactotum: mschap fix erik quanstrom
@ 2012-03-30 16:11   ` yaroslav
  0 siblings, 0 replies; 4+ messages in thread
From: yaroslav @ 2012-03-30 16:11 UTC (permalink / raw)
  To: quanstro; +Cc: 9fans

[-- Attachment #1: Type: text/plain, Size: 271 bytes --]

> 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

[-- Attachment #2: chap.c --]
[-- Type: text/plain, Size: 8337 bytes --]

/*
 * 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
};


^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [9fans] nfactotum: mschap fix
  2012-03-30 15:24 Yaroslav
@ 2012-03-30 16:37 ` Lucio De Re
  0 siblings, 0 replies; 4+ messages in thread
From: Lucio De Re @ 2012-03-30 16:37 UTC (permalink / raw)
  To: 9fans

> -       if(astype == AuthMSchap && nchal != MSchallen)
> -       c->state = "write user";

Who slipped up with the indentation?  I hope it was not intentional.

++L




^ permalink raw reply	[flat|nested] 4+ messages in thread

* [9fans] nfactotum: mschap fix
@ 2012-03-30 15:24 Yaroslav
  2012-03-30 16:37 ` Lucio De Re
  0 siblings, 1 reply; 4+ messages in thread
From: Yaroslav @ 2012-03-30 15:24 UTC (permalink / raw)
  To: 9fans

Forwarding it to the list so people don't bother to debug themselves.
It's tested with cifs and a local change which enables upas/fs to do
IMAP with NTLM auth.

---------- Forwarded message ----------
From: Yaroslav <yarikos@gmail.com>
Date: 2012/3/30
Subject: Re: [9fans] SSHv2
To: erik quanstrom <quanstro@quanstro.net>


2012/3/30 erik quanstrom <quanstro@quanstro.net>:
> On Fri Mar 30 09:48:50 EDT 2012, yarikos@gmail.com wrote:
>> 2012/3/30 erik quanstrom <quanstro@quanstro.net>:
>> > what's the basis for this claim?  it might be broken, since we don't use it
>> > much, but it's not missing.
>>
>> Yes, sorry, I've meant it's broken; details follow.
>
> try pulling a new copy.
>
> - erik

There are phases mismatch between factotum's chapconv() and libauth's
auth_respond(). I've checked plan9port - it has identical problem.
Patch which fixes it follows:

term% diff -c /n/sources/contrib/quanstro/root/sys/src/cmd/auth/factotum/chap.c
/sys/src/cmd/auth/factotum/chap.c
/n/sources/contrib/quanstro/root/sys/src/cmd/auth/factotum/chap.c:120,125
- /sys/src/cmd/auth/factotum/chap.c:120,126

       ret = -1;
       chal = nil;
+       res = nil;
       k = nil;
       attr = c->attr;

/n/sources/contrib/quanstro/root/sys/src/cmd/auth/factotum/chap.c:153,166
- /sys/src/cmd/auth/factotum/chap.c:154,163
       c->state = "read challenge";
       if((nchal = convreadm(c, (char**)(void*)&chal)) < 0)
               goto out;
-       if(astype == AuthMSchap && nchal != MSchallen)
-       c->state = "write user";
       if((user = strfindattr(k->attr, "user")) == nil){
               werrstr("key has no user (cannot happen?)");
               goto out;
       }
-       if(convprint(c, "%s", user) < 0)
-               goto out;

       c->state = "write response";
       if((pw = strfindattr(k->privattr, "!password")) == nil){


-- 
- Yaroslav



^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2012-03-30 16:37 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
     [not found] <CAG3N4d_mEkM2CeEQ-WhqKmK2_WbJz6X_N2JQkaR6QMRtThkw_w@mail.gmail.c>
2012-03-30 15:53 ` [9fans] nfactotum: mschap fix erik quanstrom
2012-03-30 16:11   ` yaroslav
2012-03-30 15:24 Yaroslav
2012-03-30 16:37 ` Lucio De Re

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).