xref: /plan9/sys/src/cmd/cifs/auth.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 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
dmp(char * s,int seq,void * buf,int n)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 *
auth_plain(char * windom,char * keyp,uchar * chal,int len)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 *
auth_lm_and_ntlm(char * windom,char * keyp,uchar * chal,int len)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 *
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 	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 *
auth_ntlmv2(char * windom,char * keyp,uchar * chal,int len)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
autherr(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 *
getauth(char * name,char * windom,char * keyp,int secmode,uchar * chal,int len)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
genmac(uchar * buf,int len,int seq,uchar key[MACkeylen],uchar ours[MAClen])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
macsign(Pkt * p,int seq)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 }
417