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