summaryrefslogtreecommitdiff
path: root/sys/src/cmd/cifs/auth-testcase.c
diff options
context:
space:
mode:
authorTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
committerTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
commite5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch)
treed8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/cmd/cifs/auth-testcase.c
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/cmd/cifs/auth-testcase.c')
-rwxr-xr-xsys/src/cmd/cifs/auth-testcase.c477
1 files changed, 477 insertions, 0 deletions
diff --git a/sys/src/cmd/cifs/auth-testcase.c b/sys/src/cmd/cifs/auth-testcase.c
new file mode 100755
index 000000000..0bc74a773
--- /dev/null
+++ b/sys/src/cmd/cifs/auth-testcase.c
@@ -0,0 +1,477 @@
+/*
+ * Beware the LM hash is easy to crack (google for l0phtCrack)
+ * and though NTLM is more secure it is still breakable.
+ * Ntlmv2 is better and seen as good enough by the Windows community.
+ * For real security use Kerberos.
+ */
+#include <u.h>
+#include <libc.h>
+#include <mp.h>
+#include <auth.h>
+#include <libsec.h>
+#include <ctype.h>
+#include <fcall.h>
+#include <thread.h>
+#include <9p.h>
+#include "cifs.h"
+
+#define NTLMV2_TEST 1
+#define DEF_AUTH "ntlmv2"
+
+static enum {
+ MACkeylen = 40, /* MAC key len */
+ MAClen = 8, /* signature length */
+ MACoff = 14, /* sign. offset from start of SMB (not netbios) pkt */
+ Bliplen = 8, /* size of LMv2 client nonce */
+};
+
+static void
+dmp(char *s, int seq, void *buf, int n)
+{
+ int i;
+ char *p = buf;
+
+ print("%s %3d ", s, seq);
+ while(n > 0){
+ for(i = 0; i < 16 && n > 0; i++, n--)
+ print("%02x ", *p++ & 0xff);
+ if(n > 0)
+ print("\n");
+ }
+ print("\n");
+}
+
+static Auth *
+auth_plain(char *windom, char *keyp, uchar *chal, int len)
+{
+ UserPasswd *up;
+ static Auth *ap;
+
+ USED(chal, len);
+
+ up = auth_getuserpasswd(auth_getkey, "windom=%s proto=pass service=cifs %s",
+ windom, keyp);
+ if(! up)
+ sysfatal("cannot get key - %r");
+
+ ap = emalloc9p(sizeof(Auth));
+ memset(ap, 0, sizeof(ap));
+ ap->user = estrdup9p(up->user);
+ ap->windom = estrdup9p(windom);
+
+ ap->resp[0] = estrdup9p(up->passwd);
+ ap->len[0] = strlen(up->passwd);
+ memset(up->passwd, 0, strlen(up->passwd));
+ free(up);
+
+ return ap;
+}
+
+static Auth *
+auth_lm_and_ntlm(char *windom, char *keyp, uchar *chal, int len)
+{
+ int err;
+ Auth *ap;
+ char user[64];
+ MSchapreply mcr;
+
+ err = auth_respond(chal, len, user, sizeof user, &mcr, sizeof mcr,
+ auth_getkey, "windom=%s proto=mschap role=client service=cifs %s",
+ windom, keyp);
+ if(err == -1)
+ sysfatal("cannot get key - %r");
+
+ ap = emalloc9p(sizeof(Auth));
+ memset(ap, 0, sizeof(ap));
+ ap->user = estrdup9p(user);
+ ap->windom = estrdup9p(windom);
+
+ /* LM response */
+ ap->len[0] = sizeof(mcr.LMresp);
+ ap->resp[0] = emalloc9p(ap->len[0]);
+ memcpy(ap->resp[0], mcr.LMresp, ap->len[0]);
+
+ /* NTLM response */
+ ap->len[1] = sizeof(mcr.NTresp);
+ ap->resp[1] = emalloc9p(ap->len[1]);
+ memcpy(ap->resp[1], mcr.NTresp, ap->len[1]);
+
+ return ap;
+}
+
+/*
+ * NTLM response only, the LM response is a just
+ * copy of the NTLM one. We do this because the lm
+ * response is easily reversed - Google for l0pht for more info.
+ */
+static Auth *
+auth_ntlm(char *windom, char *keyp, uchar *chal, int len)
+{
+ Auth *ap;
+
+ if((ap = auth_lm_and_ntlm(windom, keyp, chal, len)) == nil)
+ return nil;
+
+ free(ap->resp[0]);
+ ap->len[0] = ap->len[1];
+ ap->resp[0] = emalloc9p(ap->len[0]);
+ memcpy(ap->resp[0], ap->resp[1], ap->len[0]);
+ return ap;
+}
+
+/*
+ * This is not really nescessary as all fields hmac_md5'ed
+ * in the ntlmv2 protocol are less than 64 bytes long, however
+ * I still do this for completeness.
+ */
+static DigestState *
+hmac_t64(uchar *data, ulong dlen, uchar *key, ulong klen, uchar *digest,
+ DigestState *state)
+{
+ if(klen > 64)
+ klen = 64;
+ return hmac_md5(data, dlen, key, klen, digest, state);
+}
+
+
+static int
+ntv2_blob(uchar *blob, int len, char *windom)
+{
+ int n;
+ uvlong nttime;
+ Rune r;
+ char *d;
+ uchar *p;
+ enum { /* name types */
+ Beof, /* end of name list */
+ Bnetbios, /* Netbios machine name */
+ Bdomain, /* Windows Domain name (NT) */
+ Bdnsfqdn, /* DNS Fully Qualified Domain Name */
+ Bdnsname, /* DNS machine name (win2k) */
+ };
+
+ p = blob;
+ *p++ = 1; /* response type */
+ *p++ = 1; /* max response type understood by client */
+
+ *p++ = 0;
+ *p++ = 0; /* 2 bytes reserved */
+
+ *p++ = 0;
+ *p++ = 0;
+ *p++ = 0;
+ *p++ = 0; /* 4 bytes unknown */
+
+#ifdef NTLMV2_TEST
+ *p++ = 0xf0;
+ *p++ = 0x20;
+ *p++ = 0xd0;
+ *p++ = 0xb6;
+ *p++ = 0xc2;
+ *p++ = 0x92;
+ *p++ = 0xbe;
+ *p++ = 0x01;
+#else
+ nttime = time(nil); /* nt time now */
+ nttime = nttime + 11644473600LL;
+ nttime = nttime * 10000000LL;
+ *p++ = nttime & 0xff;
+ *p++ = (nttime >> 8) & 0xff;
+ *p++ = (nttime >> 16) & 0xff;
+ *p++ = (nttime >> 24) & 0xff;
+ *p++ = (nttime >> 32) & 0xff;
+ *p++ = (nttime >> 40) & 0xff;
+ *p++ = (nttime >> 48) & 0xff;
+ *p++ = (nttime >> 56) & 0xff;
+#endif
+#ifdef NTLMV2_TEST
+ *p++ = 0x05;
+ *p++ = 0x83;
+ *p++ = 0x32;
+ *p++ = 0xec;
+ *p++ = 0xfa;
+ *p++ = 0xe4;
+ *p++ = 0xf3;
+ *p++ = 0x6d;
+#else
+ genrandom(p, 8);
+ p += 8; /* client nonce */
+#endif
+ *p++ = 0x6f;
+ *p++ = 0;
+ *p++ = 0x6e;
+ *p++ = 0; /* unknown data */
+
+ *p++ = Bdomain;
+ *p++ = 0; /* name type */
+
+ n = utflen(windom) * 2;
+ *p++ = n;
+ *p++ = n >> 8; /* name length */
+
+ d = windom;
+ while(*d && p - blob < len - 8){
+ d += chartorune(&r, d);
+ r = toupperrune(r);
+ *p++ = r;
+ *p++ = r >> 8;
+ }
+
+ *p++ = 0;
+ *p++ = Beof; /* name type */
+
+ *p++ = 0;
+ *p++ = 0; /* name length */
+
+ *p++ = 0x65;
+ *p++ = 0;
+ *p++ = 0;
+ *p++ = 0; /* unknown data */
+ return p - blob;
+}
+
+static Auth *
+auth_ntlmv2(char *windom, char *keyp, uchar *chal, int len)
+{
+ int i, n;
+ Rune r;
+ char *p, *u;
+ uchar c, lm_hmac[MD5dlen], nt_hmac[MD5dlen], nt_sesskey[MD5dlen];
+ uchar lm_sesskey[MD5dlen];
+ uchar v1hash[MD5dlen], blip[Bliplen], blob[1024], v2hash[MD5dlen];
+ DigestState *ds;
+ UserPasswd *up;
+ static Auth *ap;
+
+ up = auth_getuserpasswd(auth_getkey, "windom=%s proto=pass service=cifs-ntlmv2 %s",
+ windom, keyp);
+ if(! up)
+ sysfatal("cannot get key - %r");
+
+#ifdef NTLMV2_TEST
+{
+ static uchar srvchal[] = { 0x52, 0xaa, 0xc8, 0xe8, 0x2c, 0x06, 0x7f, 0xa1 };
+ up->user = "ADMINISTRATOR";
+ windom = "rocknroll";
+ chal = srvchal;
+}
+#endif
+ ap = emalloc9p(sizeof(Auth));
+ memset(ap, 0, sizeof(ap));
+
+ /* Standard says unlimited length, experience says 128 max */
+ if((n = strlen(up->passwd)) > 128)
+ n = 128;
+
+ ds = md4(nil, 0, nil, nil);
+ for(i = 0, p = up->passwd; i < n; i++) {
+ p += chartorune(&r, p);
+ c = r;
+ md4(&c, 1, nil, ds);
+ c = r >> 8;
+ md4(&c, 1, nil, ds);
+ }
+ md4(nil, 0, v1hash, ds);
+
+#ifdef NTLMV2_TEST
+{
+ uchar v1[] = {
+ 0x0c, 0xb6, 0x94, 0x88, 0x05, 0xf7, 0x97, 0xbf,
+ 0x2a, 0x82, 0x80, 0x79, 0x73, 0xb8, 0x95, 0x37
+ ;
+ memcpy(v1hash, v1, sizeof(v1));
+}
+#endif
+ /*
+ * Some documentation insists that the username must be forced to
+ * uppercase, but the domain name should not be. Other shows both
+ * being forced to uppercase. I am pretty sure this is irrevevant as
+ * the domain name passed from the remote server always seems to be in
+ * uppercase already.
+ */
+ ds = hmac_t64(nil, 0, v1hash, MD5dlen, nil, nil);
+ u = up->user;
+ while(*u){
+ u += chartorune(&r, u);
+ r = toupperrune(r);
+ c = r & 0xff;
+ hmac_t64(&c, 1, v1hash, MD5dlen, nil, ds);
+ c = r >> 8;
+ hmac_t64(&c, 1, v1hash, MD5dlen, nil, ds);
+ }
+ u = windom;
+
+ while(*u){
+ u += chartorune(&r, u);
+ c = r;
+ hmac_t64(&c, 1, v1hash, MD5dlen, nil, ds);
+ c = r >> 8;
+ hmac_t64(&c, 1, v1hash, MD5dlen, nil, ds);
+ }
+ hmac_t64(nil, 0, v1hash, MD5dlen, v2hash, ds);
+#ifdef NTLMV2_TEST
+ print("want: 40 e1 b3 24...\n");
+ dmp("v2hash==kr", 0, v2hash, MD5dlen);
+#endif
+ ap->user = estrdup9p(up->user);
+ ap->windom = estrdup9p(windom);
+
+ /* LM v2 */
+
+ genrandom(blip, Bliplen);
+#ifdef NTLMV2_TEST
+{
+ uchar t[] = { 0x05, 0x83, 0x32, 0xec, 0xfa, 0xe4, 0xf3, 0x6d };
+ memcpy(blip, t, 8);
+}
+#endif
+ ds = hmac_t64(chal, len, v2hash, MD5dlen, nil, nil);
+ hmac_t64(blip, Bliplen, v2hash, MD5dlen, lm_hmac, ds);
+ ap->len[0] = MD5dlen+Bliplen;
+ ap->resp[0] = emalloc9p(ap->len[0]);
+ memcpy(ap->resp[0], lm_hmac, MD5dlen);
+ memcpy(ap->resp[0]+MD5dlen, blip, Bliplen);
+#ifdef NTLMV2_TEST
+ print("want: 38 6b ae...\n");
+ dmp("lmv2 resp ", 0, lm_hmac, MD5dlen);
+#endif
+
+ /* LM v2 session key */
+ hmac_t64(lm_hmac, MD5dlen, v2hash, MD5dlen, lm_sesskey, nil);
+
+ /* LM v2 MAC key */
+ ap->mackey[0] = emalloc9p(MACkeylen);
+ memcpy(ap->mackey[0], lm_sesskey, MD5dlen);
+ memcpy(ap->mackey[0]+MD5dlen, ap->resp[0], MACkeylen-MD5dlen);
+
+ /* NTLM v2 */
+ n = ntv2_blob(blob, sizeof(blob), windom);
+ ds = hmac_t64(chal, len, v2hash, MD5dlen, nil, nil);
+ hmac_t64(blob, n, v2hash, MD5dlen, nt_hmac, ds);
+ ap->len[1] = MD5dlen+n;
+ ap->resp[1] = emalloc9p(ap->len[1]);
+ memcpy(ap->resp[1], nt_hmac, MD5dlen);
+ memcpy(ap->resp[1]+MD5dlen, blob, n);
+#ifdef NTLMV2_TEST
+ print("want: 1a ad 55...\n");
+ dmp("ntv2 resp ", 0, nt_hmac, MD5dlen);
+#endif
+
+ /* NTLM v2 session key */
+ hmac_t64(nt_hmac, MD5dlen, v2hash, MD5dlen, nt_sesskey, nil);
+
+ /* NTLM v2 MAC key */
+ ap->mackey[1] = emalloc9p(MACkeylen);
+ memcpy(ap->mackey[1], nt_sesskey, MD5dlen);
+ memcpy(ap->mackey[1]+MD5dlen, ap->resp[1], MACkeylen-MD5dlen);
+ free(up);
+
+ return ap;
+}
+
+struct {
+ char *name;
+ Auth *(*func)(char *, char *, uchar *, int);
+} methods[] = {
+ { "plain", auth_plain },
+ { "lm+ntlm", auth_lm_and_ntlm },
+ { "ntlm", auth_ntlm },
+ { "ntlmv2", auth_ntlmv2 },
+// { "kerberos", auth_kerberos },
+};
+
+void
+autherr(void)
+{
+ int i;
+
+ fprint(2, "supported auth methods:\t");
+ for(i = 0; i < nelem(methods); i++)
+ fprint(2, "%s ", methods[i].name);
+ fprint(2, "\n");
+ exits("usage");
+}
+
+Auth *
+getauth(char *name, char *windom, char *keyp, int secmode, uchar *chal, int len)
+{
+ int i;
+ Auth *ap;
+
+ if(name == nil){
+ name = DEF_AUTH;
+ if((secmode & SECMODE_PW_ENCRYPT) == 0)
+ sysfatal("plaintext authentication required, use '-a plain'");
+ }
+
+ ap = nil;
+ for(i = 0; i < nelem(methods); i++)
+ if(strcmp(methods[i].name, name) == 0){
+ ap = methods[i].func(windom, keyp, chal, len);
+ break;
+ }
+
+ if(! ap){
+ fprint(2, "%s: %s - unknown auth method\n", argv0, name);
+ autherr(); /* never returns */
+ }
+ return ap;
+}
+
+static int
+genmac(uchar *buf, int len, int seq, uchar key[MACkeylen], uchar mine[MAClen])
+{
+ DigestState *ds;
+ uchar *sig, digest[MD5dlen], their[MAClen];
+
+ sig = buf+MACoff;
+ memcpy(their, sig, MAClen);
+ memset(sig, 0, MAClen);
+ sig[0] = seq;
+ sig[1] = seq >> 8;
+ sig[2] = seq >> 16;
+ sig[3] = seq >> 24;
+
+ ds = md5(key, MACkeylen, nil, nil);
+ md5(buf, len, nil, ds);
+ md5(nil, 0, digest, ds);
+ memcpy(mine, digest, MAClen);
+
+ return memcmp(their, mine, MAClen);
+}
+
+int
+macsign(Pkt *p)
+{
+ int i, len;
+ uchar *sig, *buf, mac[MAClen], zeros[MACkeylen];
+
+ sig = p->buf + NBHDRLEN + MACoff;
+ buf = p->buf + NBHDRLEN;
+ len = (p->pos - p->buf) - NBHDRLEN;
+
+ for(i = -3; i < 4; i++){
+ memset(zeros, 0, sizeof(zeros));
+ if(genmac(buf, len, p->seq+i, zeros, mac) == 0){
+ dmp("got", 0, buf, len);
+ dmp("Zero OK", p->seq, mac, MAClen);
+ return 0;
+ }
+
+ if(genmac(buf, len, p->seq+i, p->s->auth->mackey[0], mac) == 0){
+ dmp("got", 0, buf, len);
+ dmp("LM-hash OK", p->seq, mac, MAClen);
+ return 0;
+ }
+
+ if(genmac(buf, len, p->seq+i, p->s->auth->mackey[1], mac) == 0){
+ dmp("got", 0, buf, len);
+ dmp("NT-hash OK", p->seq, mac, MAClen);
+ return 0;
+ }
+ }
+ genmac(buf, len, p->seq, p->s->auth->mackey[0], mac);
+
+ memcpy(sig, mac, MAClen);
+ return -1;
+}