Subversion Repositories planix.SVN

Rev

Rev 2 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 1
/*
2
 * CHAP, MSCHAP
3
 * 
4
 * The client does not authenticate the server, hence no CAI
5
 *
6
 * Client protocol:
7
 *	write Chapchal 
8
 *	read response Chapreply or MSchaprely structure
9
 *
10
 * Server protocol:
11
 *	read challenge: 8 bytes binary
12
 *	write user: utf8
13
 *	write response: Chapreply or MSchapreply structure
14
 */
15
 
16
#include <ctype.h>
17
#include "dat.h"
18
 
19
enum {
20
	ChapChallen = 8,
21
	ChapResplen = 16,
22
	MSchapResplen = 24,
23
};
24
 
25
static int dochal(State*);
26
static int doreply(State*, void*, int);
27
static void doLMchap(char *, uchar [ChapChallen], uchar [MSchapResplen]);
28
static void doNTchap(char *, uchar [ChapChallen], uchar [MSchapResplen]);
29
static void dochap(char *, int, char [ChapChallen], uchar [ChapResplen]);
30
 
31
 
32
struct State
33
{
34
	char *protoname;
35
	int astype;
36
	int asfd;
37
	Key *key;
38
	Ticket	t;
39
	Ticketreq	tr;
40
	char chal[ChapChallen];
41
	MSchapreply mcr;
42
	char cr[ChapResplen];
43
	char err[ERRMAX];
44
	char user[64];
45
	uchar secret[16];	/* for mschap */
46
	int nsecret;
47
};
48
 
49
enum
50
{
51
	CNeedChal,
52
	CHaveResp,
53
 
54
	SHaveChal,
55
	SNeedUser,
56
	SNeedResp,
57
	SHaveZero,
58
	SHaveCAI,
59
 
60
	Maxphase
61
};
62
 
63
static char *phasenames[Maxphase] =
64
{
65
[CNeedChal]	"CNeedChal",
66
[CHaveResp]	"CHaveResp",
67
 
68
[SHaveChal]	"SHaveChal",
69
[SNeedUser]	"SNeedUser",
70
[SNeedResp]	"SNeedResp",
71
[SHaveZero]	"SHaveZero",
72
[SHaveCAI]	"SHaveCAI",
73
};
74
 
75
static int
76
chapinit(Proto *p, Fsstate *fss)
77
{
78
	int iscli, ret;
79
	State *s;
80
 
81
	if((iscli = isclient(_strfindattr(fss->attr, "role"))) < 0)
82
		return failure(fss, nil);
83
 
84
	s = emalloc(sizeof *s);
85
	fss->phasename = phasenames;
86
	fss->maxphase = Maxphase;
87
	s->asfd = -1;
88
	if(p == &chap){
89
		s->astype = AuthChap;
90
		s->protoname = "chap";
91
	}else{
92
		s->astype = AuthMSchap;
93
		s->protoname = "mschap";
94
	}
95
 
96
	if(iscli)
97
		fss->phase = CNeedChal;
98
	else{
99
		if((ret = findp9authkey(&s->key, fss)) != RpcOk){
100
			free(s);
101
			return ret;
102
		}
103
		if(dochal(s) < 0){
104
			free(s);
105
			return failure(fss, nil);
106
		}
107
		fss->phase = SHaveChal;
108
	}
109
 
110
	fss->ps = s;
111
	return RpcOk;
112
}
113
 
114
static void
115
chapclose(Fsstate *fss)
116
{
117
	State *s;
118
 
119
	s = fss->ps;
120
	if(s->asfd >= 0){
121
		close(s->asfd);
122
		s->asfd = -1;
123
	}
124
	free(s);
125
}
126
 
127
 
128
static int
129
chapwrite(Fsstate *fss, void *va, uint n)
130
{
131
	int ret, nreply;
132
	char *a, *v;
133
	void *reply;
134
	Key *k;
135
	Keyinfo ki;
136
	State *s;
137
	Chapreply cr;
138
	MSchapreply mcr;
139
	OChapreply ocr;
140
	OMSchapreply omcr;
141
 
142
	s = fss->ps;
143
	a = va;
144
	switch(fss->phase){
145
	default:
146
		return phaseerror(fss, "write");
147
 
148
	case CNeedChal:
149
		ret = findkey(&k, mkkeyinfo(&ki, fss, nil), "%s", fss->proto->keyprompt);
150
		if(ret != RpcOk)
151
			return ret;
152
		v = _strfindattr(k->privattr, "!password");
153
		if(v == nil)
154
			return failure(fss, "key has no password");
155
		setattrs(fss->attr, k->attr);
156
		switch(s->astype){
157
		default:
158
			abort();
159
		case AuthMSchap:
160
			doLMchap(v, (uchar *)a, (uchar *)s->mcr.LMresp);
161
			doNTchap(v, (uchar *)a, (uchar *)s->mcr.NTresp);
162
			break;
163
		case AuthChap:
164
			dochap(v, *a, a+1, (uchar *)s->cr);
165
			break;
166
		}
167
		closekey(k);
168
		fss->phase = CHaveResp;
169
		return RpcOk;
170
 
171
	case SNeedUser:
172
		if(n >= sizeof s->user)
173
			return failure(fss, "user name too long");
174
		memmove(s->user, va, n);
175
		s->user[n] = '\0';
176
		fss->phase = SNeedResp;
177
		return RpcOk;
178
 
179
	case SNeedResp:
180
		switch(s->astype){
181
		default:
182
			return failure(fss, "chap internal botch");
183
		case AuthChap:
184
			if(n != sizeof(Chapreply))
185
				return failure(fss, "did not get Chapreply");
186
			memmove(&cr, va, sizeof cr);
187
			ocr.id = cr.id;
188
			memmove(ocr.resp, cr.resp, sizeof ocr.resp);
189
			memset(omcr.uid, 0, sizeof(omcr.uid));
190
			strecpy(ocr.uid, ocr.uid+sizeof ocr.uid, s->user);
191
			reply = &ocr;
192
			nreply = sizeof ocr;
193
			break;
194
		case AuthMSchap:
195
			if(n != sizeof(MSchapreply))
196
				return failure(fss, "did not get MSchapreply");
197
			memmove(&mcr, va, sizeof mcr);
198
			memmove(omcr.LMresp, mcr.LMresp, sizeof omcr.LMresp);
199
			memmove(omcr.NTresp, mcr.NTresp, sizeof omcr.NTresp);
200
			memset(omcr.uid, 0, sizeof(omcr.uid));
201
			strecpy(omcr.uid, omcr.uid+sizeof omcr.uid, s->user);
202
			reply = &omcr;
203
			nreply = sizeof omcr;
204
			break;
205
		}
206
		if(doreply(s, reply, nreply) < 0)
207
			return failure(fss, nil);
208
		fss->phase = Established;
209
		fss->ai.cuid = s->t.cuid;
210
		fss->ai.suid = s->t.suid;
211
		fss->ai.secret = s->secret;
212
		fss->ai.nsecret = s->nsecret;
213
		fss->haveai = 1;
214
		return RpcOk;
215
	}
216
}
217
 
218
static int
219
chapread(Fsstate *fss, void *va, uint *n)
220
{
221
	State *s;
222
 
223
	s = fss->ps;
224
	switch(fss->phase){
225
	default:
226
		return phaseerror(fss, "read");
227
 
228
	case CHaveResp:
229
		switch(s->astype){
230
		default:
231
			phaseerror(fss, "write");
232
			break;
233
		case AuthMSchap:
234
			if(*n > sizeof(MSchapreply))
235
				*n = sizeof(MSchapreply);
236
			memmove(va, &s->mcr, *n);
237
			break;
238
		case AuthChap:
239
			if(*n > ChapResplen)
240
				*n = ChapResplen;
241
			memmove(va, s->cr, ChapResplen);
242
			break;
243
		}
244
		fss->phase = Established;
245
		fss->haveai = 0;
246
		return RpcOk;
247
 
248
	case SHaveChal:
249
		if(*n > sizeof s->chal)
250
			*n = sizeof s->chal;
251
		memmove(va, s->chal, *n);
252
		fss->phase = SNeedUser;
253
		return RpcOk;
254
	}
255
}
256
 
257
static int
258
dochal(State *s)
259
{
260
	char *dom, *user;
261
	char trbuf[TICKREQLEN];
262
 
263
	s->asfd = -1;
264
 
265
	/* send request to authentication server and get challenge */
266
	if((dom = _strfindattr(s->key->attr, "dom")) == nil
267
	|| (user = _strfindattr(s->key->attr, "user")) == nil){
268
		werrstr("chap/dochal cannot happen");
269
		goto err;
270
	}
271
	s->asfd = _authdial(nil, dom);
272
	if(s->asfd < 0)
273
		goto err;
274
 
275
	memset(&s->tr, 0, sizeof(s->tr));
276
	s->tr.type = s->astype;
277
	safecpy(s->tr.authdom, dom, sizeof s->tr.authdom);
278
	safecpy(s->tr.hostid, user, sizeof(s->tr.hostid));
279
	convTR2M(&s->tr, trbuf);
280
 
281
	if(write(s->asfd, trbuf, TICKREQLEN) != TICKREQLEN)
282
		goto err;
283
 
284
	/* readn, not _asrdresp.  needs to match auth.srv.c. */
285
	if(readn(s->asfd, s->chal, sizeof s->chal) != sizeof s->chal)
286
		goto err;
287
	return 0;
288
 
289
err:
290
	if(s->asfd >= 0)
291
		close(s->asfd);
292
	s->asfd = -1;
293
	return -1;
294
}
295
 
296
static int
297
doreply(State *s, void *reply, int nreply)
298
{
299
	char ticket[TICKETLEN+AUTHENTLEN];
300
	int n;
301
	Authenticator a;
302
 
303
	if((n=write(s->asfd, reply, nreply)) != nreply){
304
		if(n >= 0)
305
			werrstr("short write to auth server");
306
		goto err;
307
	}
308
 
309
	if(_asrdresp(s->asfd, ticket, TICKETLEN+AUTHENTLEN) < 0){
310
		/* leave connection open so we can try again */
311
		return -1;
312
	}
313
	s->nsecret = readn(s->asfd, s->secret, sizeof s->secret);
314
	if(s->nsecret < 0)
315
		s->nsecret = 0;
316
	close(s->asfd);
317
	s->asfd = -1;
318
	convM2T(ticket, &s->t, s->key->priv);
319
	if(s->t.num != AuthTs
320
	|| memcmp(s->t.chal, s->tr.chal, sizeof(s->t.chal)) != 0){
321
		if(s->key->successes == 0)
322
			disablekey(s->key);
323
		werrstr(Easproto);
324
		return -1;
325
	}
326
	s->key->successes++;
327
	convM2A(ticket+TICKETLEN, &a, s->t.key);
328
	if(a.num != AuthAc
329
	|| memcmp(a.chal, s->tr.chal, sizeof(a.chal)) != 0
330
	|| a.id != 0){
331
		werrstr(Easproto);
332
		return -1;
333
	}
334
 
335
	return 0;
336
err:
337
	if(s->asfd >= 0)
338
		close(s->asfd);
339
	s->asfd = -1;
340
	return -1;
341
}
342
 
343
Proto chap = {
344
.name=	"chap",
345
.init=	chapinit,
346
.write=	chapwrite,
347
.read=	chapread,
348
.close=	chapclose,
349
.addkey= replacekey,
350
.keyprompt= "!password?"
351
};
352
 
353
Proto mschap = {
354
.name=	"mschap",
355
.init=	chapinit,
356
.write=	chapwrite,
357
.read=	chapread,
358
.close=	chapclose,
359
.addkey= replacekey,
360
.keyprompt= "!password?"
361
};
362
 
363
static void
364
hash(uchar pass[16], uchar c8[ChapChallen], uchar p24[MSchapResplen])
365
{
366
	int i;
367
	uchar p21[21];
368
	ulong schedule[32];
369
 
370
	memset(p21, 0, sizeof p21 );
371
	memmove(p21, pass, 16);
372
 
373
	for(i=0; i<3; i++) {
374
		key_setup(p21+i*7, schedule);
375
		memmove(p24+i*8, c8, 8);
376
		block_cipher(schedule, p24+i*8, 0);
377
	}
378
}
379
 
380
static void
381
doNTchap(char *pass, uchar chal[ChapChallen], uchar reply[MSchapResplen])
382
{
383
	Rune r;
384
	int i, n;
385
	uchar digest[MD4dlen];
386
	uchar *w, unipass[256];
387
 
388
	// Standard says unlimited length, experience says 128 max
389
	if ((n = strlen(pass)) > 128)
390
		n = 128;
391
 
392
	for(i=0, w=unipass; i < n; i++) {
393
		pass += chartorune(&r, pass);
394
		*w++ = r & 0xff;
395
		*w++ = r >> 8;
396
	}
397
 
398
	memset(digest, 0, sizeof digest);
399
	md4(unipass, w-unipass, digest, nil);
400
	memset(unipass, 0, sizeof unipass);
401
	hash(digest, chal, reply);
402
}
403
 
404
static void
405
doLMchap(char *pass, uchar chal[ChapChallen], uchar reply[MSchapResplen])
406
{
407
	int i;
408
	ulong schedule[32];
409
	uchar p14[15], p16[16];
410
	uchar s8[8] = {0x4b, 0x47, 0x53, 0x21, 0x40, 0x23, 0x24, 0x25};
411
	int n = strlen(pass);
412
 
413
	if(n > 14){
414
		// let prudent people avoid the LM vulnerability
415
		//   and protect the loop below from buffer overflow
416
		memset(reply, 0, MSchapResplen);
417
		return;
418
	}
419
 
420
	// Spec says space padded, experience says otherwise
421
	memset(p14, 0, sizeof p14 -1);
422
	p14[sizeof p14 - 1] = '\0';
423
 
424
	// NT4 requires uppercase, Win XP doesn't care
425
	for (i = 0; pass[i]; i++)
426
		p14[i] = islower(pass[i])? toupper(pass[i]): pass[i];
427
 
428
	for(i=0; i<2; i++) {
429
		key_setup(p14+i*7, schedule);
430
		memmove(p16+i*8, s8, 8);
431
		block_cipher(schedule, p16+i*8, 0);
432
	}
433
 
434
	memset(p14, 0, sizeof p14);
435
	hash(p16, chal, reply);
436
}
437
 
438
static void
439
dochap(char *pass, int id, char chal[ChapChallen], uchar resp[ChapResplen])
440
{
441
	char buf[1+ChapChallen+MAXNAMELEN+1];
442
	int n = strlen(pass);
443
 
444
	*buf = id;
445
	if (n > MAXNAMELEN)
446
		n = MAXNAMELEN-1;
447
	memset(buf, 0, sizeof buf);
448
	strncpy(buf+1, pass, n);
449
	memmove(buf+1+n, chal, ChapChallen);
450
	md5((uchar*)buf, 1+n+ChapChallen, resp, nil);
451
}
452