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 DEF_AUTH 	"ntlmv2"
19
 
20
static enum {
21
	MACkeylen	= 40,	/* MAC key len */
22
	MAClen		= 8,	/* signature length */
23
	MACoff		= 14,	/* sign. offset from start of SMB (not netbios) pkt */
24
	Bliplen		= 8,	/* size of LMv2 client nonce */
25
};
26
 
27
static void
28
dmp(char *s, int seq, void *buf, int n)
29
{
30
	int i;
31
	char *p = buf;
32
 
33
	print("%s %3d      ", s, seq);
34
	while(n > 0){
35
		for(i = 0; i < 16 && n > 0; i++, n--)
36
			print("%02x ", *p++ & 0xff);
37
		if(n > 0)
38
			print("\n");
39
	}
40
	print("\n");
41
}
42
 
43
static Auth *
44
auth_plain(char *windom, char *keyp, uchar *chal, int len)
45
{
46
	UserPasswd *up;
47
	static Auth *ap;
48
 
49
	USED(chal, len);
50
 
51
	up = auth_getuserpasswd(auth_getkey, "windom=%s proto=pass service=cifs %s",
52
		windom, keyp);
53
	if(! up)
54
		sysfatal("cannot get key - %r");
55
 
56
	ap = emalloc9p(sizeof(Auth));
57
	memset(ap, 0, sizeof(ap));
58
	ap->user = estrdup9p(up->user);
59
	ap->windom = estrdup9p(windom);
60
 
61
	ap->resp[0] = estrdup9p(up->passwd);
62
	ap->len[0] = strlen(up->passwd);
63
	memset(up->passwd, 0, strlen(up->passwd));
64
	free(up);
65
 
66
	return ap;
67
}
68
 
69
static Auth *
70
auth_lm_and_ntlm(char *windom, char *keyp, uchar *chal, int len)
71
{
72
	int err;
73
	char user[64];
74
	Auth *ap;
75
	MSchapreply mcr;
76
 
77
	err = auth_respond(chal, len, user, sizeof user, &mcr, sizeof mcr,
78
		auth_getkey, "windom=%s proto=mschap role=client service=cifs %s",
79
		windom, keyp);
80
	if(err == -1)
81
		sysfatal("cannot get key - %r");
82
 
83
	ap = emalloc9p(sizeof(Auth));
84
	memset(ap, 0, sizeof(ap));
85
	ap->user = estrdup9p(user);
86
	ap->windom = estrdup9p(windom);
87
 
88
	/* LM response */
89
	ap->len[0] = sizeof(mcr.LMresp);
90
	ap->resp[0] = emalloc9p(ap->len[0]);
91
	memcpy(ap->resp[0], mcr.LMresp, ap->len[0]);
92
 
93
	/* NTLM response */
94
	ap->len[1] = sizeof(mcr.NTresp);
95
	ap->resp[1] = emalloc9p(ap->len[1]);
96
	memcpy(ap->resp[1], mcr.NTresp, ap->len[1]);
97
 
98
	return ap;
99
}
100
 
101
/*
102
 * NTLM response only, the LM response is a just
103
 * copy of the NTLM one. we do this because the lm
104
 * response is easily reversed - Google for l0pht
105
 * 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
	nttime = time(nil);	/* nt time now */
166
	nttime += 11644473600LL;
167
	nttime *= 10000000LL;
168
	*p++ = nttime;
169
	*p++ = nttime >> 8;
170
	*p++ = nttime >> 16;
171
	*p++ = nttime >> 24;
172
	*p++ = nttime >> 32;
173
	*p++ = nttime >> 40;
174
	*p++ = nttime >> 48;
175
	*p++ = nttime >> 56;
176
 
177
	genrandom(p, 8);
178
	p += 8;			/* client nonce */
179
	*p++ = 0x6f;
180
	*p++ = 0;
181
	*p++ = 0x6e;
182
	*p++ = 0;		/* unknown data */
183
 
184
	*p++ = Bdomain;
185
	*p++ = 0;		/* name type */
186
 
187
	n = utflen(windom) * 2;
188
	*p++ = n;
189
	*p++ = n >> 8;		/* name length */
190
 
191
	d = windom;
192
	while(*d && p-blob < (len-8)){
193
		d += chartorune(&r, d);
194
		r = toupperrune(r);
195
		*p++ = r;
196
		*p++ = r >> 8;
197
	}
198
 
199
	*p++ = 0;
200
	*p++ = Beof;		/* name type */
201
 
202
	*p++ = 0;
203
	*p++ = 0;		/* name length */
204
 
205
	*p++ = 0x65;
206
	*p++ = 0;
207
	*p++ = 0;
208
	*p++ = 0;		/* unknown data */
209
	return p - blob;
210
}
211
 
212
static Auth *
213
auth_ntlmv2(char *windom, char *keyp, uchar *chal, int len)
214
{
215
	int i, n;
216
	Rune r;
217
	char *p, *u;
218
	uchar v1hash[MD5dlen], blip[Bliplen], blob[1024], v2hash[MD5dlen];
219
	uchar c, lm_hmac[MD5dlen], nt_hmac[MD5dlen], nt_sesskey[MD5dlen],
220
		lm_sesskey[MD5dlen];
221
	DigestState *ds;
222
	UserPasswd *up;
223
	static Auth *ap;
224
 
225
	up = auth_getuserpasswd(auth_getkey, "windom=%s proto=pass  service=cifs-ntlmv2 %s",
226
		windom, keyp);
227
	if(!up)
228
		sysfatal("cannot get key - %r");
229
 
230
	ap = emalloc9p(sizeof(Auth));
231
	memset(ap, 0, sizeof(ap));
232
 
233
	/* Standard says unlimited length, experience says 128 max */
234
	if((n = strlen(up->passwd)) > 128)
235
		n = 128;
236
 
237
	ds = md4(nil, 0, nil, nil);
238
	for(i=0, p=up->passwd; i < n; i++) {
239
		p += chartorune(&r, p);
240
		c = r;
241
		md4(&c, 1, nil, ds);
242
		c = r >> 8;
243
		md4(&c, 1, nil, ds);
244
	}
245
	md4(nil, 0, v1hash, ds);
246
 
247
	/*
248
	 * Some documentation insists that the username must be forced to
249
	 * uppercase, but the domain name should not be. Other shows both
250
	 * being forced to uppercase. I am pretty sure this is irrevevant as the
251
	 * domain name passed from the remote server always seems to be in
252
	 * uppercase already.
253
	 */
254
        ds = hmac_t64(nil, 0, v1hash, MD5dlen, nil, nil);
255
	u = up->user;
256
	while(*u){
257
		u += chartorune(&r, u);
258
		r = toupperrune(r);
259
		c = r;
260
        	hmac_t64(&c, 1, v1hash, MD5dlen, nil, ds);
261
		c = r >> 8;
262
        	hmac_t64(&c, 1, v1hash, MD5dlen, nil, ds);
263
	}
264
	u = windom;
265
 
266
	while(*u){
267
		u += chartorune(&r, u);
268
		c = r;
269
        	hmac_t64(&c, 1, v1hash, MD5dlen, nil, ds);
270
		c = r >> 8;
271
        	hmac_t64(&c, 1, v1hash, MD5dlen, nil, ds);
272
	}
273
        hmac_t64(nil, 0, v1hash, MD5dlen, v2hash, ds);
274
	ap->user = estrdup9p(up->user);
275
	ap->windom = estrdup9p(windom);
276
 
277
	/* LM v2 */
278
 
279
	genrandom(blip, Bliplen);
280
        ds = hmac_t64(chal, len, v2hash, MD5dlen, nil, nil);
281
	hmac_t64(blip, Bliplen, v2hash, MD5dlen, lm_hmac, ds);
282
	ap->len[0] = MD5dlen+Bliplen;
283
	ap->resp[0] = emalloc9p(ap->len[0]);
284
	memcpy(ap->resp[0], lm_hmac, MD5dlen);
285
	memcpy(ap->resp[0]+MD5dlen, blip, Bliplen);
286
 
287
	/* LM v2 session key */
288
	hmac_t64(lm_hmac, MD5dlen, v2hash, MD5dlen, lm_sesskey, nil);
289
 
290
	/* LM v2 MAC key */
291
	ap->mackey[0] = emalloc9p(MACkeylen);
292
	memcpy(ap->mackey[0], lm_sesskey, MD5dlen);
293
	memcpy(ap->mackey[0]+MD5dlen, ap->resp[0], MACkeylen-MD5dlen);
294
 
295
	/* NTLM v2 */
296
	n = ntv2_blob(blob, sizeof(blob), windom);
297
        ds = hmac_t64(chal, len, v2hash, MD5dlen, nil, nil);
298
	hmac_t64(blob, n, v2hash, MD5dlen, nt_hmac, ds);
299
	ap->len[1] = MD5dlen+n;
300
	ap->resp[1] = emalloc9p(ap->len[1]);
301
	memcpy(ap->resp[1], nt_hmac, MD5dlen);
302
	memcpy(ap->resp[1]+MD5dlen, blob, n);
303
 
304
	/*
305
	 * v2hash definitely OK by
306
	 * the time we get here.
307
	 */
308
	/* NTLM v2 session key */
309
	hmac_t64(nt_hmac, MD5dlen, v2hash, MD5dlen, nt_sesskey, nil);
310
 
311
	/* NTLM v2 MAC key */
312
	ap->mackey[1] = emalloc9p(MACkeylen);
313
	memcpy(ap->mackey[1], nt_sesskey, MD5dlen);
314
	memcpy(ap->mackey[1]+MD5dlen, ap->resp[1], MACkeylen-MD5dlen);
315
	free(up);
316
 
317
	return ap;
318
}
319
 
320
struct {
321
	char	*name;
322
	Auth	*(*func)(char *, char *, uchar *, int);
323
} methods[] = {
324
	{ "plain",	auth_plain },
325
	{ "lm+ntlm",	auth_lm_and_ntlm },
326
	{ "ntlm",	auth_ntlm },
327
	{ "ntlmv2",	auth_ntlmv2 },
328
//	{ "kerberos",	auth_kerberos },
329
};
330
 
331
void
332
autherr(void)
333
{
334
	int i;
335
 
336
	fprint(2, "supported auth methods:\t");
337
	for(i = 0; i < nelem(methods); i++)
338
		fprint(2, "%s ", methods[i].name);
339
	fprint(2, "\n");
340
	exits("usage");
341
}
342
 
343
Auth *
344
getauth(char *name, char *windom, char *keyp, int secmode, uchar *chal, int len)
345
{
346
	int i;
347
	Auth *ap;
348
 
349
	if(name == nil){
350
		name = DEF_AUTH;
351
		if((secmode & SECMODE_PW_ENCRYPT) == 0)
352
			sysfatal("plaintext authentication required, use '-a plain'");
353
	}
354
 
355
	ap = nil;
356
	for(i = 0; i < nelem(methods); i++)
357
		if(strcmp(methods[i].name, name) == 0){
358
			ap = methods[i].func(windom, keyp, chal, len);
359
			break;
360
		}
361
 
362
	if(! ap){
363
		fprint(2, "%s: %s - unknown auth method\n", argv0, name);
364
		autherr();	/* never returns */
365
	}
366
	return ap;
367
}
368
 
369
static int
370
genmac(uchar *buf, int len, int seq, uchar key[MACkeylen], uchar ours[MAClen])
371
{
372
	DigestState *ds;
373
	uchar *sig, digest[MD5dlen], theirs[MAClen];
374
 
375
	sig = buf+MACoff;
376
	memcpy(theirs, sig, MAClen);
377
 
378
	memset(sig, 0, MAClen);
379
	sig[0] = seq;
380
	sig[1] = seq >> 8;
381
	sig[2] = seq >> 16;
382
	sig[3] = seq >> 24;
383
 
384
	ds = md5(key, MACkeylen, nil, nil);
385
	md5(buf, len, digest, ds);
386
	memcpy(ours, digest, MAClen);
387
 
388
	return memcmp(theirs, ours, MAClen);
389
}
390
 
391
int
392
macsign(Pkt *p, int seq)
393
{
394
	int rc, len;
395
	uchar *sig, *buf, mac[MAClen];
396
 
397
	sig = p->buf + NBHDRLEN + MACoff;
398
	buf = p->buf + NBHDRLEN;
399
	len = (p->pos - p->buf) - NBHDRLEN;
400
 
401
#ifdef DEBUG_MAC
402
	if(seq & 1)
403
		dmp("rx", seq, sig, MAClen);
404
#endif
405
	rc = 0;
406
	if(! p->s->seqrun)
407
		memcpy(mac, "BSRSPYL ", 8);	/* no idea, ask MS */
408
	else
409
		rc = genmac(buf, len, seq, p->s->auth->mackey[0], mac);
410
#ifdef DEBUG_MAC
411
	if(!(seq & 1))
412
		dmp("tx", seq, mac, MAClen);
413
#endif
414
	memcpy(sig, mac, MAClen);
415
	return rc;
416
}