xref: /plan9/sys/src/cmd/cifs/auth-testcase.c (revision 671dfc474d1a5bcbeda8be1356d2abfa05b91489)
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
dmp(char * s,int seq,void * buf,int n)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 *
auth_plain(char * windom,char * keyp,uchar * chal,int len)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 *
auth_lm_and_ntlm(char * windom,char * keyp,uchar * chal,int len)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 *
auth_ntlm(char * windom,char * keyp,uchar * chal,int len)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 *
hmac_t64(uchar * data,ulong dlen,uchar * key,ulong klen,uchar * digest,DigestState * state)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
ntv2_blob(uchar * blob,int len,char * windom)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 *
auth_ntlmv2(char * windom,char * keyp,uchar * chal,int len)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 }
478