* This code gets the username and returns it. No validation is done.
* and by the way: it is a complete patch-up. Use the "real thing" NTLMSSP
* if you can.
- */
+ *
+ * Revised by Guido Serassio: <guido.serassio@acmeconsulting.it>
+ *
+ * - Added negotiation of UNICODE char support
+ * - More detailed debugging info
+ *
+*/
#include "config.h"
#include "ntlmauth.h"
#include "squid_endian.h"
-#include "ntlm.h"
#include "util.h"
#include <ctype.h>
#if HAVE_PWD_H
#include <pwd.h>
#endif
-
+#if HAVE_GETOPT_H
+#include <getopt.h>
+#endif
+#include "ntlm.h"
#define ERR "ERR\n"
#define OK "OK\n"
-#if 0
-#define NTLM_STATIC_CHALLENGE "deadbeef"
-#endif
-static char *authenticate_ntlm_domain = "LIFELESSWKS";
+#define BUFFER_SIZE 10240
+
+char *authenticate_ntlm_domain = "WORKGROUP";
+int debug_enabled = 0;
+int NTLM_packet_debug_enabled = 0;
/* NTLM authentication by ad@interlude.eu.org - 07/1999 */
/* XXX this is not done cleanly... */
+void
+hex_dump(void *data, int size)
+{
+ /* dumps size bytes of *data to stdout. Looks like:
+ * [0000] 75 6E 6B 6E 6F 77 6E 20
+ * 30 FF 00 00 00 00 39 00 unknown 0.....9.
+ * (in a single line of course)
+ */
+
+ if (!data)
+ return;
+
+ if (debug_enabled) {
+ unsigned char *p = data;
+ unsigned char c;
+ int n;
+ char bytestr[4] =
+ {0};
+ char addrstr[10] =
+ {0};
+ char hexstr[16 * 3 + 5] =
+ {0};
+ char charstr[16 * 1 + 5] =
+ {0};
+ for (n = 1; n <= size; n++) {
+ if (n % 16 == 1) {
+ /* store address for this line */
+ snprintf(addrstr, sizeof(addrstr), "%.4x",
+ ((unsigned int) p - (unsigned int) data));
+ }
+ c = *p;
+ if (isalnum(c) == 0) {
+ c = '.';
+ }
+ /* store hex str (for left side) */
+ snprintf(bytestr, sizeof(bytestr), "%02X ", *p);
+ strncat(hexstr, bytestr, sizeof(hexstr) - strlen(hexstr) - 1);
+
+ /* store char str (for right side) */
+ snprintf(bytestr, sizeof(bytestr), "%c", c);
+ strncat(charstr, bytestr, sizeof(charstr) - strlen(charstr) - 1);
+
+ if (n % 16 == 0) {
+ /* line completed */
+ fprintf(stderr, "[%4.4s] %-50.50s %s\n", addrstr, hexstr, charstr);
+ hexstr[0] = 0;
+ charstr[0] = 0;
+ } else if (n % 8 == 0) {
+ /* half line: add whitespaces */
+ strncat(hexstr, " ", sizeof(hexstr) - strlen(hexstr) - 1);
+ strncat(charstr, " ", sizeof(charstr) - strlen(charstr) - 1);
+ }
+ p++; /* next byte */
+ }
+
+ if (strlen(hexstr) > 0) {
+ /* print rest of buffer if not empty */
+ fprintf(stderr, "[%4.4s] %-50.50s %s\n", addrstr, hexstr, charstr);
+ }
+ }
+}
+
+
/* makes a null-terminated string lower-case. Changes CONTENTS! */
static void
lc(char *string)
* challenge strings can be guarenteed to be poor at best.
*/
void
-ntlmMakeChallenge(struct ntlm_challenge *chal)
+ntlmMakeChallenge(struct ntlm_challenge *chal, int32_t flags)
{
-#ifndef NTLM_STATIC_CHALLENGE
static unsigned hash;
int r;
-#endif
char *d;
int i;
+ debug("ntlmMakeChallenge: flg %08x\n", flags);
+
memset(chal, 0, sizeof(*chal));
memcpy(chal->hdr.signature, "NTLMSSP", 8);
- chal->flags = htole32(0x00018206);
+ chal->flags = htole32(CHALLENGE_TARGET_IS_DOMAIN |
+ NEGOTIATE_ALWAYS_SIGN |
+ NEGOTIATE_USE_NTLM |
+ NEGOTIATE_REQUEST_TARGET |
+ (NEGOTIATE_UNICODE & flags ? NEGOTIATE_UNICODE : NEGOTIATE_ASCII)
+ );
chal->hdr.type = htole32(NTLM_CHALLENGE);
chal->unknown[6] = htole16(0x003a);
chal->target.maxlen = htole16(i);
chal->target.len = chal->target.maxlen;
-#ifdef NTLM_STATIC_CHALLENGE
- memcpy(chal->challenge, NTLM_STATIC_CHALLENGE, 8);
-#else
r = (int) rand();
r = (hash ^ r) + r;
}
hash = r;
-#endif
}
/*
fprintf(stderr, "ntlmGetString: insane: l:%d o:%d\n", l, o);
return (NULL);
}
- if ((flags & 2) == 0) {
+ if ((flags & NEGOTIATE_ASCII) == 0) {
/* UNICODE string */
s = (u_short *) ((char *) hdr + o);
d = buf;
for (l >>= 1; l; s++, l--) {
c = le16toh(*s);
- if (c > 254 || c == '\0' || !isprint(c)) {
+ if (c > 254 || c == '\0') {
fprintf(stderr, "ntlmGetString: bad uni: %04x\n", c);
return (NULL);
}
*d++ = c;
- fprintf(stderr, "ntlmGetString: conv: '%c'\n", c);
}
*d = 0;
} else {
- /* ASCII string */
+ /* ASCII/OEM string */
sc = (char *) hdr + o;
d = buf;
for (; l; l--) {
- if (*sc == '\0' || !isprint((int)(unsigned char)*sc)) {
+ if (*sc == '\0' || !isprint((int) (unsigned char) *sc)) {
fprintf(stderr, "ntlmGetString: bad ascii: %04x\n", *sc);
return (NULL);
}
}
origbuf = buf;
if (ntlmCheckHeader(&auth->hdr, NTLM_AUTHENTICATE)) {
-
fprintf(stderr, "ntlmDecodeAuth: header check fails\n");
return -1;
}
-/* only on when you need to debug
- * fprintf(stderr,"ntlmDecodeAuth: size of %d\n", size);
- * fprintf(stderr,"ntlmDecodeAuth: flg %08x\n", auth->flags);
- * fprintf(stderr,"ntlmDecodeAuth: usr o(%d) l(%d)\n", auth->user.offset, auth->user.len);
- */
- if ((p = ntlmGetString(&auth->hdr, &auth->domain, 2)) == NULL)
+ debug("ntlmDecodeAuth: size of %d\n", size);
+ debug("ntlmDecodeAuth: flg %08x\n", auth->flags);
+ debug("ntlmDecodeAuth: usr o(%d) l(%d)\n", auth->user.offset, auth->user.len);
+
+ if ((p = ntlmGetString(&auth->hdr, &auth->domain, auth->flags)) == NULL)
p = authenticate_ntlm_domain;
-// fprintf(stderr,"ntlmDecodeAuth: Domain '%s'.\n",p);
+
+ debug("ntlmDecodeAuth: Domain '%s'.\n", p);
+
if ((s = strlen(p) + 1) >= size)
return 1;
strcpy(buf, p);
-// fprintf(stdout,"ntlmDecodeAuth: Domain '%s'.\n",buf);
+
+ debug("ntlmDecodeAuth: Domain '%s'.\n", buf);
size -= s;
buf += (s - 1);
*buf++ = '\\'; /* Using \ is more consistent with MS-proxy */
- p = ntlmGetString(&auth->hdr, &auth->user, 2);
+ p = ntlmGetString(&auth->hdr, &auth->user, auth->flags);
if ((s = strlen(p) + 1) >= size)
return 1;
while (*p)
*buf++ = '\0';
size -= s;
-// fprintf(stderr, "ntlmDecodeAuth: user: %s%s\n",origbuf, p);
+ debug("ntlmDecodeAuth: user: %s%s\n", origbuf, p);
return 0;
}
+/*
+ * options:
+ * -d enable debugging.
+ * -v enable verbose NTLM packet debugging.
+ * -l if specified, changes behavior on failures to last-ditch.
+ */
+char *my_program_name = NULL;
+
+void
+usage()
+{
+ fprintf(stderr,
+ "Usage: %s [-d] [-v] [-h]\n"
+ " -d enable debugging.\n"
+ " -v enable verbose NTLM packet debugging.\n"
+ " -h this message\n\n",
+ my_program_name);
+}
+
+
+void
+process_options(int argc, char *argv[])
+{
+ int opt, had_error = 0;
+
+ opterr = 0;
+ while (-1 != (opt = getopt(argc, argv, "hdv"))) {
+ switch (opt) {
+ case 'd':
+ debug_enabled = 1;
+ break;
+ case 'v':
+ debug_enabled = 1;
+ NTLM_packet_debug_enabled = 1;
+ break;
+ case 'h':
+ usage();
+ exit(0);
+ case '?':
+ opt = optopt;
+ /* fall thru to default */
+ default:
+ fprintf(stderr, "unknown option: -%c. Exiting\n", opt);
+ usage();
+ had_error = 1;
+ }
+ }
+ if (had_error)
+ exit(1);
+}
+
+
int
-main()
+main(int argc, char *argv[])
{
- char buf[256];
- char user[256], *p, *cleartext;
+ char buf[BUFFER_SIZE];
+ char user[256], *p, *decoded = NULL;
struct ntlm_challenge chal;
+ struct ntlm_negotiate *nego;
+ char helper_command[3];
int len;
char *data = NULL;
setbuf(stdout, NULL);
- while (fgets(buf, 256, stdin) != NULL) {
+ setbuf(stderr, NULL);
+
+ my_program_name = argv[0];
+
+ process_options(argc, argv);
+
+ debug("%s build " __DATE__ ", " __TIME__ " starting up...\n", my_program_name);
+
+ while (fgets(buf, BUFFER_SIZE, stdin) != NULL) {
user[0] = '\0'; /*no usercode */
if ((p = strchr(buf, '\n')) != NULL)
*p = '\0'; /* strip \n */
-#if defined(NTLMHELPPROTOCOLV3) || !defined(NTLMHELPPROTOCOLV2)
+ if ((strlen(buf) > 3) && NTLM_packet_debug_enabled) {
+ decoded = base64_decode(buf + 3);
+ strncpy(helper_command, buf, 2);
+ helper_command[2] = '\0';
+ debug("Got '%s' from Squid with data:\n", helper_command);
+ hex_dump(decoded, ((strlen(buf) - 3) * 3) / 4);
+ } else
+ debug("Got '%s' from Squid\n", buf);
+
if (strncasecmp(buf, "YR", 2) == 0) {
- ntlmMakeChallenge(&chal);
+ if (strlen(buf) > 3) {
+ nego = (struct ntlm_negotiate *) decoded;
+ ntlmMakeChallenge(&chal, nego->flags);
+ } else
+ ntlmMakeChallenge(&chal, NEGOTIATE_ASCII);
len =
sizeof(chal) - sizeof(chal.pad) +
le16toh(chal.target.maxlen);
data = (char *) base64_encode_bin((char *) &chal, len);
- printf("TT %s\n", data);
+ if (NTLM_packet_debug_enabled) {
+ printf("TT %s\n", data);
+ decoded = base64_decode(data);
+ debug("sending 'TT' to squid with data:\n");
+ hex_dump(decoded, (strlen(data) * 3) / 4);
+ } else
+ SEND2("TT %s", data);
} else if (strncasecmp(buf, "KK ", 3) == 0) {
- cleartext = (char *) uudecode(buf + 3);
- if (!ntlmCheckHeader((ntlmhdr *) cleartext, NTLM_AUTHENTICATE)) {
- if (!ntlmDecodeAuth((struct ntlm_authenticate *) cleartext, user, 256)) {
- lc(user);
- printf("AF %s\n", user);
- } else {
- lc(user);
- printf("NA invalid credentials%s\n", user);
- }
- } else {
- lc(user);
- printf("BH wrong packet type!%s\n", user);
- }
- }
-#endif
-#ifdef NTLMHELPPROTOCOLV2
-/* V2 of the protocol */
- if (strncasecmp(buf, "RESET", 5) == 0) {
- printf("RESET OK\n");
- } else {
- cleartext = (char *) uudecode(buf);
- if (!ntlmCheckHeader((struct ntlmhdr *) cleartext, NTLM_NEGOTIATE)) {
- ntlmMakeChallenge(&chal);
- len =
- sizeof(chal) - sizeof(chal.pad) +
- le16toh(chal.target.maxlen);
- data = (char *) base64_encode_bin((char *) &chal, len);
- printf("CH %s\n", data);
- } else if (!ntlmCheckHeader
- ((struct ntlmhdr *) cleartext, NTLM_AUTHENTICATE)) {
- if (!ntlmDecodeAuth
- ((struct ntlm_authenticate *) cleartext, user, 256)) {
+ if (!ntlmCheckHeader((ntlmhdr *) decoded, NTLM_AUTHENTICATE)) {
+ if (!ntlmDecodeAuth((struct ntlm_authenticate *) decoded, user, 256)) {
lc(user);
- printf("OK %s\n", user);
+ SEND2("AF %s", user);
} else {
lc(user);
- printf("ERR %s\n", user);
+ SEND2("NA invalid credentials, user=%s", user);
}
} else {
lc(user);
- printf("ERR %s\n", user);
+ SEND2("BH wrong packet type! user=%s", user);
}
}
-#endif /*v2 */
}
exit(0);
}
/*
- * $Id: ntlm.h,v 1.6 2003/08/05 21:40:02 robertc Exp $
+ * $Id: ntlm.h,v 1.7 2006/08/14 17:13:21 serassio Exp $
*
* AUTHOR: Andrew Doran <ad@interlude.eu.org>
*
#include "squid_endian.h"
/* NTLM request types that we know about */
-#define NTLM_NEGOTIATE 1
-#define NTLM_CHALLENGE 2
-#define NTLM_AUTHENTICATE 3
#define NTLM_ANY 0
/* Negotiation request sent by client */
};
char *ntlmGetString(ntlmhdr * hdr, strhdr * str, int flags);
-void ntlmMakeChallenge(struct ntlm_challenge *chal);
+void ntlmMakeChallenge(struct ntlm_challenge *chal, int32_t flags);
int ntlmCheckHeader(ntlmhdr * hdr, int type);
int ntlmCheckNegotiation(struct ntlm_negotiate *neg);
int ntlmAuthenticate(struct ntlm_authenticate *neg);
+#define safe_free(x) if (x) { free(x); x = NULL; }
+
+#undef debug
+
+/************* CONFIGURATION ***************/
+/*
+ * define this if you want debugging
+ */
+#ifndef DEBUG
+#define DEBUG
+#endif
+
+#define FAIL_DEBUG 0
+
+/************* END CONFIGURATION ***************/
+
+#include <sys/types.h>
+
+extern int debug_enabled;
+#if FAIL_DEBUG
+extern int fail_debug_enabled;
+#endif
+
+/* Debugging stuff */
+
+#ifdef __GNUC__ /* this is really a gcc-ism */
+#ifdef DEBUG
+#include <stdio.h>
+#include <unistd.h>
+static char *__foo;
+#define debug(X...) if (debug_enabled) { \
+ fprintf(stderr,"ntlm-auth[%d](%s:%d): ", getpid(), \
+ ((__foo=strrchr(__FILE__,'/'))==NULL?__FILE__:__foo+1),\
+ __LINE__);\
+ fprintf(stderr,X); }
+#else /* DEBUG */
+#define debug(X...) /* */
+#endif /* DEBUG */
+#else /* __GNUC__ */
+static void
+debug(char *format,...)
+{
+#ifdef DEBUG
+#ifdef _SQUID_MSWIN_
+#if FAIL_DEBUG
+ if (debug_enabled || fail_debug_enabled) {
+#else
+ if (debug_enabled) {
+#endif
+ va_list args;
+
+ va_start(args, format);
+ fprintf(stderr, "ntlm-auth[%d]: ", getpid());
+ vfprintf(stderr, format, args);
+ va_end(args);
+#if FAIL_DEBUG
+ fail_debug_enabled = 0;
+#endif
+ }
+#endif /* _SQUID_MSWIN_ */
+#endif /* DEBUG */
+}
+#endif /* __GNUC__ */
+
+
+/* A couple of harmless helper macros */
+#define SEND(X) debug("sending '%s' to squid\n",X); printf(X "\n");
+#ifdef __GNUC__
+#define SEND2(X,Y...) debug("sending '" X "' to squid\n",Y); printf(X "\n",Y);
+#else
+/* no gcc, no debugging. varargs macros are a gcc extension */
+#define SEND2(X,Y) debug("sending '" X "' to squid\n",Y); printf(X "\n",Y);
+#endif
+
#endif /* _NTLM_H_ */