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
 * Beware the LM hash is easy to crack (google for l0phtCrack)
3
 * and though NTLM is more secure it is still breakable.
4
 * Ntlmv2 is better and seen as good enough by the Windows community.
5
 * For real security use Kerberos.
6
 */
7
#include <u.h>
8
#include <libc.h>
9
#include <mp.h>
10
#include <auth.h>
11
#include <libsec.h>
12
#include <ctype.h>
13
#include <fcall.h>
14
#include <thread.h>
15
#include <9p.h>
16
#include "cifs.h"
17
 
18
#define NTLMV2_TEST	1
19
#define DEF_AUTH 	"ntlmv2"
20
 
21
static enum {
22
	MACkeylen	= 40,	/* MAC key len */
23
	MAClen		= 8,	/* signature length */
24
	MACoff		= 14,	/* sign. offset from start of SMB (not netbios) pkt */
25
	Bliplen		= 8,	/* size of LMv2 client nonce */
26
};
27
 
28
static void
29
dmp(char *s, int seq, void *buf, int n)
30
{
31
	int i;
32
	char *p = buf;
33
 
34
	print("%s %3d      ", s, seq);
35
	while(n > 0){
36
		for(i = 0; i < 16 && n > 0; i++, n--)
37
			print("%02x ", *p++ & 0xff);
38
		if(n > 0)
39
			print("\n");
40
	}
41
	print("\n");
42
}
43
 
44
static Auth *
45
auth_plain(char *windom, char *keyp, uchar *chal, int len)
46
{
47
	UserPasswd *up;
48
	static Auth *ap;
49
 
50
	USED(chal, len);
51
 
52
	up = auth_getuserpasswd(auth_getkey, "windom=%s proto=pass service=cifs %s",
53
		windom, keyp);
54
	if(! up)
55
		sysfatal("cannot get key - %r");
56
 
57
	ap = emalloc9p(sizeof(Auth));
58
	memset(ap, 0, sizeof(ap));
59
	ap->user = estrdup9p(up->user);
60
	ap->windom = estrdup9p(windom);
61
 
62
	ap->resp[0] = estrdup9p(up->passwd);
63
	ap->len[0] = strlen(up->passwd);
64
	memset(up->passwd, 0, strlen(up->passwd));
65
	free(up);
66
 
67
	return ap;
68
}
69
 
70
static Auth *
71
auth_lm_and_ntlm(char *windom, char *keyp, uchar *chal, int len)
72
{
73
	int err;
74
	Auth *ap;
75
	char user[64];
76
	MSchapreply mcr;
77
 
78
	err = auth_respond(chal, len, user, sizeof user, &mcr, sizeof mcr,
79
		auth_getkey, "windom=%s proto=mschap role=client service=cifs %s",
80
		windom, keyp);
81
	if(err == -1)
82
		sysfatal("cannot get key - %r");
83
 
84
	ap = emalloc9p(sizeof(Auth));
85
	memset(ap, 0, sizeof(ap));
86
	ap->user = estrdup9p(user);
87
	ap->windom = estrdup9p(windom);
88
 
89
	/* LM response */
90
	ap->len[0] = sizeof(mcr.LMresp);
91
	ap->resp[0] = emalloc9p(ap->len[0]);
92
	memcpy(ap->resp[0], mcr.LMresp, ap->len[0]);
93
 
94
	/* NTLM response */
95
	ap->len[1] = sizeof(mcr.NTresp);
96
	ap->resp[1] = emalloc9p(ap->len[1]);
97
	memcpy(ap->resp[1], mcr.NTresp, ap->len[1]);
98
 
99
	return ap;
100
}
101
 
102
/*
103
 * NTLM response only, the LM response is a just
104
 * copy of the NTLM one.  We do this because the lm
105
 * response is easily reversed - Google for l0pht for more info.
106
 */
107
static Auth *
108
auth_ntlm(char *windom, char *keyp, uchar *chal, int len)
109
{
110
	Auth *ap;
111
 
112
	if((ap = auth_lm_and_ntlm(windom, keyp, chal, len)) == nil)
113
		return nil;
114
 
115
	free(ap->resp[0]);
116
	ap->len[0] = ap->len[1];
117
	ap->resp[0] = emalloc9p(ap->len[0]);
118
	memcpy(ap->resp[0], ap->resp[1], ap->len[0]);
119
	return ap;
120
}
121
 
122
/*
123
 * This is not really nescessary as all fields hmac_md5'ed
124
 * in the ntlmv2 protocol are less than 64 bytes long, however
125
 * I still do this for completeness.
126
 */
127
static DigestState *
128
hmac_t64(uchar *data, ulong dlen, uchar *key, ulong klen, uchar *digest,
129
	DigestState *state)
130
{
131
	if(klen > 64)
132
		klen = 64;
133
	return hmac_md5(data, dlen, key, klen, digest, state);
134
}
135
 
136
 
137
static int
138
ntv2_blob(uchar *blob, int len, char *windom)
139
{
140
	int n;
141
	uvlong nttime;
142
	Rune r;
143
	char *d;
144
	uchar *p;
145
	enum {			/* name types */
146
		Beof,		/* end of name list */
147
		Bnetbios,	/* Netbios machine name */
148
		Bdomain,	/* Windows Domain name (NT) */
149
		Bdnsfqdn,	/* DNS Fully Qualified Domain Name */
150
		Bdnsname,	/* DNS machine name (win2k) */
151
	};
152
 
153
	p = blob;
154
	*p++ = 1;		/* response type */
155
	*p++ = 1;		/* max response type understood by client */
156
 
157
	*p++ = 0;
158
	*p++ = 0;		/* 2 bytes reserved */
159
 
160
	*p++ = 0;
161
	*p++ = 0;
162
	*p++ = 0;
163
	*p++ = 0;		/* 4 bytes unknown */
164
 
165
#ifdef NTLMV2_TEST
166
	*p++ = 0xf0;
167
	*p++ = 0x20;
168
	*p++ = 0xd0;
169
	*p++ = 0xb6;
170
	*p++ = 0xc2;
171
	*p++ = 0x92;
172
	*p++ = 0xbe;
173
	*p++ = 0x01;
174
#else
175
	nttime = time(nil);	/* nt time now */
176
	nttime = nttime + 11644473600LL;
177
	nttime = nttime * 10000000LL;
178
	*p++ = nttime & 0xff;
179
	*p++ = (nttime >> 8) & 0xff;
180
	*p++ = (nttime >> 16) & 0xff;
181
	*p++ = (nttime >> 24) & 0xff;
182
	*p++ = (nttime >> 32) & 0xff;
183
	*p++ = (nttime >> 40) & 0xff;
184
	*p++ = (nttime >> 48) & 0xff;
185
	*p++ = (nttime >> 56) & 0xff;
186
#endif
187
#ifdef NTLMV2_TEST
188
	*p++ = 0x05;
189
	*p++ = 0x83;
190
	*p++ = 0x32;
191
	*p++ = 0xec;
192
	*p++ = 0xfa;
193
	*p++ = 0xe4;
194
	*p++ = 0xf3;
195
	*p++ = 0x6d;
196
#else
197
	genrandom(p, 8);
198
	p += 8;			/* client nonce */
199
#endif
200
	*p++ = 0x6f;
201
	*p++ = 0;
202
	*p++ = 0x6e;
203
	*p++ = 0;		/* unknown data */
204
 
205
	*p++ = Bdomain;
206
	*p++ = 0;		/* name type */
207
 
208
	n = utflen(windom) * 2;
209
	*p++ = n;
210
	*p++ = n >> 8;		/* name length */
211
 
212
	d = windom;
213
	while(*d && p - blob < len - 8){
214
		d += chartorune(&r, d);
215
		r = toupperrune(r);
216
		*p++ = r;
217
		*p++ = r >> 8;
218
	}
219
 
220
	*p++ = 0;
221
	*p++ = Beof;		/* name type */
222
 
223
	*p++ = 0;
224
	*p++ = 0;		/* name length */
225
 
226
	*p++ = 0x65;
227
	*p++ = 0;
228
	*p++ = 0;
229
	*p++ = 0;		/* unknown data */
230
	return p - blob;
231
}
232
 
233
static Auth *
234
auth_ntlmv2(char *windom, char *keyp, uchar *chal, int len)
235
{
236
	int i, n;
237
	Rune r;
238
	char *p, *u;
239
	uchar c, lm_hmac[MD5dlen], nt_hmac[MD5dlen], nt_sesskey[MD5dlen];
240
	uchar lm_sesskey[MD5dlen];
241
	uchar v1hash[MD5dlen], blip[Bliplen], blob[1024], v2hash[MD5dlen];
242
	DigestState *ds;
243
	UserPasswd *up;
244
	static Auth *ap;
245
 
246
	up = auth_getuserpasswd(auth_getkey, "windom=%s proto=pass service=cifs-ntlmv2 %s",
247
		windom, keyp);
248
	if(! up)
249
		sysfatal("cannot get key - %r");
250
 
251
#ifdef NTLMV2_TEST
252
{
253
	static uchar srvchal[] = { 0x52, 0xaa, 0xc8, 0xe8, 0x2c, 0x06, 0x7f, 0xa1 };
254
	up->user = "ADMINISTRATOR";
255
	windom = "rocknroll";
256
	chal = srvchal;
257
}
258
#endif
259
	ap = emalloc9p(sizeof(Auth));
260
	memset(ap, 0, sizeof(ap));
261
 
262
	/* Standard says unlimited length, experience says 128 max */
263
	if((n = strlen(up->passwd)) > 128)
264
		n = 128;
265
 
266
	ds = md4(nil, 0, nil, nil);
267
	for(i = 0, p = up->passwd; i < n; i++) {
268
		p += chartorune(&r, p);
269
		c = r;
270
		md4(&c, 1, nil, ds);
271
		c = r >> 8;
272
		md4(&c, 1, nil, ds);
273
	}
274
	md4(nil, 0, v1hash, ds);
275
 
276
#ifdef NTLMV2_TEST
277
{
278
	uchar v1[] = {
279
		0x0c, 0xb6, 0x94, 0x88, 0x05, 0xf7, 0x97, 0xbf,
280
		0x2a, 0x82, 0x80, 0x79, 0x73, 0xb8, 0x95, 0x37
281
	;
282
	memcpy(v1hash, v1, sizeof(v1));
283
}
284
#endif
285
	/*
286
	 * Some documentation insists that the username must be forced to
287
	 * uppercase, but the domain name should not be. Other shows both
288
	 * being forced to uppercase.  I am pretty sure this is irrevevant as
289
	 * the domain name passed from the remote server always seems to be in
290
	 * uppercase already.
291
	 */
292
        ds = hmac_t64(nil, 0, v1hash, MD5dlen, nil, nil);
293
	u = up->user;
294
	while(*u){
295
		u += chartorune(&r, u);
296
		r = toupperrune(r);
297
		c = r & 0xff;
298
        	hmac_t64(&c, 1, v1hash, MD5dlen, nil, ds);
299
		c = r >> 8;
300
        	hmac_t64(&c, 1, v1hash, MD5dlen, nil, ds);
301
	}
302
	u = windom;
303
 
304
	while(*u){
305
		u += chartorune(&r, u);
306
		c = r;
307
        	hmac_t64(&c, 1, v1hash, MD5dlen, nil, ds);
308
		c = r >> 8;
309
        	hmac_t64(&c, 1, v1hash, MD5dlen, nil, ds);
310
	}
311
        hmac_t64(nil, 0, v1hash, MD5dlen, v2hash, ds);
312
#ifdef NTLMV2_TEST
313
	print("want:               40 e1 b3 24...\n");
314
	dmp("v2hash==kr", 0, v2hash, MD5dlen);
315
#endif
316
	ap->user = estrdup9p(up->user);
317
	ap->windom = estrdup9p(windom);
318
 
319
	/* LM v2 */
320
 
321
	genrandom(blip, Bliplen);
322
#ifdef NTLMV2_TEST
323
{
324
	uchar t[] = { 0x05, 0x83, 0x32, 0xec, 0xfa, 0xe4, 0xf3, 0x6d };
325
	memcpy(blip, t, 8);
326
}
327
#endif
328
        ds = hmac_t64(chal, len, v2hash, MD5dlen, nil, nil);
329
	hmac_t64(blip, Bliplen, v2hash, MD5dlen, lm_hmac, ds);
330
	ap->len[0] = MD5dlen+Bliplen;
331
	ap->resp[0] = emalloc9p(ap->len[0]);
332
	memcpy(ap->resp[0], lm_hmac, MD5dlen);
333
	memcpy(ap->resp[0]+MD5dlen, blip, Bliplen);
334
#ifdef NTLMV2_TEST
335
	print("want:               38 6b ae...\n");
336
	dmp("lmv2 resp ", 0, lm_hmac, MD5dlen);
337
#endif
338
 
339
	/* LM v2 session key */
340
	hmac_t64(lm_hmac, MD5dlen, v2hash, MD5dlen, lm_sesskey, nil);
341
 
342
	/* LM v2 MAC key */
343
	ap->mackey[0] = emalloc9p(MACkeylen);
344
	memcpy(ap->mackey[0], lm_sesskey, MD5dlen);
345
	memcpy(ap->mackey[0]+MD5dlen, ap->resp[0], MACkeylen-MD5dlen);
346
 
347
	/* NTLM v2 */
348
	n = ntv2_blob(blob, sizeof(blob), windom);
349
        ds = hmac_t64(chal, len, v2hash, MD5dlen, nil, nil);
350
	hmac_t64(blob, n, v2hash, MD5dlen, nt_hmac, ds);
351
	ap->len[1] = MD5dlen+n;
352
	ap->resp[1] = emalloc9p(ap->len[1]);
353
	memcpy(ap->resp[1], nt_hmac, MD5dlen);
354
	memcpy(ap->resp[1]+MD5dlen, blob, n);
355
#ifdef NTLMV2_TEST
356
	print("want:               1a ad 55...\n");
357
	dmp("ntv2 resp ", 0, nt_hmac, MD5dlen);
358
#endif
359
 
360
	/* NTLM v2 session key */
361
	hmac_t64(nt_hmac, MD5dlen, v2hash, MD5dlen, nt_sesskey, nil);
362
 
363
	/* NTLM v2 MAC key */
364
	ap->mackey[1] = emalloc9p(MACkeylen);
365
	memcpy(ap->mackey[1], nt_sesskey, MD5dlen);
366
	memcpy(ap->mackey[1]+MD5dlen, ap->resp[1], MACkeylen-MD5dlen);
367
	free(up);
368
 
369
	return ap;
370
}
371
 
372
struct {
373
	char	*name;
374
	Auth	*(*func)(char *, char *, uchar *, int);
375
} methods[] = {
376
	{ "plain",	auth_plain },
377
	{ "lm+ntlm",	auth_lm_and_ntlm },
378
	{ "ntlm",	auth_ntlm },
379
	{ "ntlmv2",	auth_ntlmv2 },
380
//	{ "kerberos",	auth_kerberos },
381
};
382
 
383
void
384
autherr(void)
385
{
386
	int i;
387
 
388
	fprint(2, "supported auth methods:\t");
389
	for(i = 0; i < nelem(methods); i++)
390
		fprint(2, "%s ", methods[i].name);
391
	fprint(2, "\n");
392
	exits("usage");
393
}
394
 
395
Auth *
396
getauth(char *name, char *windom, char *keyp, int secmode, uchar *chal, int len)
397
{
398
	int i;
399
	Auth *ap;
400
 
401
	if(name == nil){
402
		name = DEF_AUTH;
403
		if((secmode & SECMODE_PW_ENCRYPT) == 0)
404
			sysfatal("plaintext authentication required, use '-a plain'");
405
	}
406
 
407
	ap = nil;
408
	for(i = 0; i < nelem(methods); i++)
409
		if(strcmp(methods[i].name, name) == 0){
410
			ap = methods[i].func(windom, keyp, chal, len);
411
			break;
412
		}
413
 
414
	if(! ap){
415
		fprint(2, "%s: %s - unknown auth method\n", argv0, name);
416
		autherr();		/* never returns */
417
	}
418
	return ap;
419
}
420
 
421
static int
422
genmac(uchar *buf, int len, int seq, uchar key[MACkeylen], uchar mine[MAClen])
423
{
424
	DigestState *ds;
425
	uchar *sig, digest[MD5dlen], their[MAClen];
426
 
427
	sig = buf+MACoff;
428
	memcpy(their, sig, MAClen);
429
	memset(sig, 0, MAClen);
430
	sig[0] = seq;
431
	sig[1] = seq >> 8;
432
	sig[2] = seq >> 16;
433
	sig[3] = seq >> 24;
434
 
435
	ds = md5(key, MACkeylen, nil, nil);
436
	md5(buf, len, nil, ds);
437
	md5(nil, 0, digest, ds);
438
	memcpy(mine, digest, MAClen);
439
 
440
	return memcmp(their, mine, MAClen);
441
}
442
 
443
int
444
macsign(Pkt *p)
445
{
446
	int i, len;
447
	uchar *sig, *buf, mac[MAClen], zeros[MACkeylen];
448
 
449
	sig = p->buf + NBHDRLEN + MACoff;
450
	buf = p->buf + NBHDRLEN;
451
	len = (p->pos - p->buf) - NBHDRLEN;
452
 
453
	for(i = -3; i < 4; i++){
454
		memset(zeros, 0, sizeof(zeros));
455
		if(genmac(buf, len, p->seq+i, zeros, mac) == 0){
456
			dmp("got", 0, buf, len);
457
			dmp("Zero OK", p->seq, mac, MAClen);
458
			return 0;
459
		}
460
 
461
		if(genmac(buf, len, p->seq+i, p->s->auth->mackey[0], mac) == 0){
462
			dmp("got", 0, buf, len);
463
			dmp("LM-hash OK", p->seq, mac, MAClen);
464
			return 0;
465
		}
466
 
467
		if(genmac(buf, len, p->seq+i, p->s->auth->mackey[1], mac) == 0){
468
			dmp("got", 0, buf, len);
469
			dmp("NT-hash OK", p->seq, mac, MAClen);
470
			return 0;
471
		}
472
	}
473
	genmac(buf, len, p->seq, p->s->auth->mackey[0], mac);
474
 
475
	memcpy(sig, mac, MAClen);
476
	return -1;
477
}