--------------------- PatchSet 492 Date: 2000/08/03 12:41:29 Author: kinkie Branch: ntlm Tag: (none) Log: First version, almost-working-but-not-quite-there, of the NTLM/NTLMSSP authentication module for Squid. Members: ntlm_auth_modules/NTLMSSP/Makefile.in:1.1->1.1.2.1 ntlm_auth_modules/NTLMSSP/libntlmssp.c:1.1->1.1.2.1 ntlm_auth_modules/NTLMSSP/ntlm.h:1.1->1.1.2.1 ntlm_auth_modules/NTLMSSP/ntlm_auth.c:1.1->1.1.2.1 --- /dev/null Wed Feb 14 00:44:43 2007 +++ squid/ntlm_auth_modules/NTLMSSP/Makefile.in Wed Feb 14 00:45:14 2007 @@ -0,0 +1,84 @@ +# +# Makefile for the Squid Object Cache server +# +# $Id: Makefile.in,v 1.1.2.1 2000/08/03 12:41:29 kinkie Exp $ +# +# Uncomment and customize the following to suit your needs: +# + +prefix = @prefix@ +exec_prefix = @exec_prefix@ +exec_suffix = @exec_suffix@ +top_srcdir = @top_srcdir@ +bindir = @bindir@ +srcdir = @srcdir@ +VPATH = @srcdir@ + +# Gotta love the DOS legacy +# +NTLM_AUTH_EXE = ntlm_auth$(exec_suffix) + +CC = @CC@ +MAKEDEPEND = @MAKEDEPEND@ +INSTALL = @INSTALL@ +INSTALL_BIN = @INSTALL_PROGRAM@ +CRYPTLIB = @CRYPTLIB@ +AC_CFLAGS = @CFLAGS@ +LDFLAGS = @LDFLAGS@ +XTRA_LIBS = @XTRA_LIBS@ +XTRA_OBJS = @XTRA_OBJS@ +MV = @MV@ +RM = @RM@ +SHELL = /bin/sh + + +INCLUDE = -I. -I../../include -I$(srcdir)/smbval -I$(top_srcdir)/include +CFLAGS = $(AC_CFLAGS) $(INCLUDE) $(DEFINES) +AUTH_LIBS = -L../../lib -lmiscutil $(CRYPTLIB) $(XTRA_LIBS) + +PROGS = $(NTLM_AUTH_EXE) +OBJS = ntlm_auth.o libntlmssp.o + +all: $(NTLM_AUTH_EXE) + +$(OBJS): $(top_srcdir)/include/version.h ntlm.h + +$(NTLM_AUTH_EXE): $(OBJS) smbval/smbvalid.a + $(CC) $(LDFLAGS) $^ -o $@ $(AUTH_LIBS) + +smbval/smbvalid.a: + cd smbval; $(MAKE) + +install-mkdirs: + -@if test ! -d $(prefix); then \ + echo "mkdir $(prefix)"; \ + mkdir $(prefix); \ + fi + -@if test ! -d $(bindir); then \ + echo "mkdir $(bindir)"; \ + mkdir $(bindir); \ + fi + +install: all install-mkdirs + @for f in $(PROGS); do \ + if test -f $(bindir)/$$f; then \ + echo $(MV) $(bindir)/$$f $(bindir)/-$$f; \ + $(MV) $(bindir)/$$f $(bindir)/-$$f; \ + fi; \ + echo $(INSTALL_BIN) $$f $(bindir); \ + $(INSTALL_BIN) $$f $(bindir); \ + if test -f $(bindir)/-$$f; then \ + echo $(RM) -f $(bindir)/-$$f; \ + $(RM) -f $(bindir)/-$$f; \ + fi; \ + done + +clean: + -rm -rf *.o *pure_* core $(PROGS) + cd smbval; make clean + +distclean: clean + -rm -f Makefile + +depend: + $(MAKEDEPEND) -I../include -I. -fMakefile *.c --- /dev/null Wed Feb 14 00:44:43 2007 +++ squid/ntlm_auth_modules/NTLMSSP/libntlmssp.c Wed Feb 14 00:45:14 2007 @@ -0,0 +1,287 @@ +/* + * (C) 2000 Francesco Chemolli + * Distributed freely under the terms of the GNU General Public License, + * version 2. See the file COPYING for licensing details + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. + */ + + +#include "ntlm.h" +#include "util.h" /* from Squid */ +#include "valid.h" + +#ifndef OLD +#include "smblib-priv.h" /* from samba */ +#endif + +#if HAVE_STRING_H +#include +#endif +#if HAVE_STDLIB_H +#include +#endif + +static ntlmssp *model; + +#define lstring_zero(s) s.str=NULL; s.l=-1; + +/* instantiates an empty ntlmssp, with fields initalized to 0/NULL + * it will be left to user's care to free it + * Returns NULL in case of failure + */ +ntlmssp * create_ntlmssp () { + ntlmssp *rv; + debug("create_ntlmssp: entering\n"); + if (!model) { + debug("create_ntlmssp: building model\n"); + model=malloc(sizeof(ntlmssp)); + if (!model) { + fprintf(stderr,"ntlmssp: malloc failed for model\n"); + return NULL; + } + model->type=0; + model->flags=0; + lstring_zero(model->domain); + lstring_zero(model->workstation); + lstring_zero(model->user); + lstring_zero(model->sessionkey); + lstring_zero(model->lm); + lstring_zero(model->nt); + } + rv=malloc(sizeof(ntlmssp)); + if (!rv) { + fprintf(stderr,"ntlmssp: malloc failed for result\n"); + return NULL; + } + memcpy(rv,model,sizeof(ntlmssp)); + return rv; +} + +/* Frees the ntlmssp and all it references. */ +void drop_ntlmssp(ntlmssp *nt) { + debug("dropping ntlmssp\n"); + if (nt->domain.str) free(nt->domain.str); + if (nt->workstation.str) free(nt->workstation.str); + if (nt->user.str) free(nt->user.str); + if (nt->sessionkey.str) free(nt->sessionkey.str); + if (nt->lm.str) free(nt->lm.str); + if (nt->nt.str) free(nt->nt.str); + free(nt); +} + +/* fetches a string from the authentication packet. + * We'll forego Unicode for now, hopefully it will work nonetheless + */ +static lstring fetch_string (char * packet, int32_t length, strhdr *str) { + int16_t l; /* length */ + int32_t o; /* offset */ + lstring rv; + + l = SSWAP(str->len); + o = WSWAP(str->offset); + debug("fetch_string(l=%d,o=%d)\n",l,o); + + if (l < 0 || l > MAX_FIELD_LENGTH || o+l > length) { + debug("ntlmssp: insane data (l: %d, o: %d)\n", l,o); + rv.l=-1; + rv.str=NULL; + return rv; + } + + rv.l=l; + rv.str=malloc(l+1); + memcpy(rv.str,packet+o,l); + rv.str[l]='\0'; /* add a dummy just in case, it shouldn't */ + /* be needed anyways. */ + debug("fetch_string got '%s'\n",rv.str); + return rv; +} + +/* decodes a base64-encoded ntlm challenge. returns NULL upon failure, + * it's up to the user to free it when needed. + */ +ntlmssp *decode_ntlmssp_auth (char *encoded) { + ntlmssp *rv; + char *decoded; + int32_t length; + struct ntlmhdr *hdr; + struct ntlm_negotiate *n; + struct ntlm_challenge *c; + struct ntlm_authenticate *a; + + debug("decode_ntlmssp_auth(%s)\n",encoded); + decoded=base64_decode(encoded); /* base64_decode returns static storage! */ + length=strlen(encoded)/4*3; /* can be useful for bounds-checking */ + + if (!decoded) { + fprintf(stderr,"ntlmssp: base64-decode failed\n"); + return NULL; + } + hdr=(ntlmhdr *)decoded; + if (memcmp(hdr->signature, "NTLMSSP", 8) != 0) { + fprintf(stderr,"ntlmssp: not a NTLMSSP signature\n"); + return NULL; + } + + rv=create_ntlmssp(); + if (!rv) + return NULL; + + rv->type=WSWAP(hdr->type); + switch (rv->type) { + case NTLM_NEGOTIATE: + n=(struct ntlm_negotiate *) decoded; + rv->domain=fetch_string(decoded,length,&n->domain); + rv->workstation=fetch_string(decoded,length,&n->workstation); + rv->flags=n->flags; + debug("decode_ntlmssp_auth: negotiation request\n\tdomain: '%s'\n" + "\tworkstation: '%s'\n\tflags: %d\n",rv->domain.str, + rv->workstation.str, rv->flags); + break; + case NTLM_CHALLENGE: + c=(struct ntlm_challenge *) decoded; +/* rv->target=fetch_string(decoded,length,&c->target); */ + rv->flags=c->flags; + rv->challenge.str=malloc(9); /* 8+1 */ + rv->challenge.l=8; + memcpy(rv->challenge.str,&c->challenge,8); + rv->challenge.str[8]='\0'; + break; + case NTLM_AUTHENTICATE: + a=(struct ntlm_authenticate *) decoded; + rv->lm=fetch_string(decoded,length,&a->lmresponse); + rv->nt=fetch_string(decoded,length,&a->ntresponse); + rv->domain=fetch_string(decoded,length,&a->domain); + rv->user=fetch_string(decoded,length,&a->user); + rv->workstation=fetch_string(decoded,length,&a->workstation); + rv->sessionkey=fetch_string(decoded,length,&a->sessionkey); + rv->flags=a->flags; + debug("decode_ntlmssp_auth: authentication request\n" + "\tlmresponse: '%s'\n\tntresponse: '%s'\n\tdomain: '%s'\n" + "\tuser: '%s'\n\tworkstation: '%s'\n\tsession key: '%s'\n" + "\tflags: %d\n", + rv->lm.str, rv->nt.str, rv->domain.str, rv->user.str, + rv->workstation.str, rv->sessionkey.str,rv->flags); + break; + default: + drop_ntlmssp(rv); + fprintf(stderr,"ntlmssp: garbled packet type\n"); + return NULL; + } + return rv; +} + + +/* Adds something to the payload. The caller must guarrantee that + * there is enough space in the payload string to accommodate the + * added value. + * payload_length and hdr will be modified as a side-effect. + */ +void add_to_payload (char *payload, int *payload_length, struct strhdr *hdr, + char *toadd, int toadd_length, int base_offset) { + int l=*payload_length; + memcpy(payload+l,toadd,toadd_length); + hdr->len=toadd_length; + hdr->maxlen=toadd_length; + hdr->offset=l+base_offset; /* 48 is the base offset of the payload */ + *payload_length+=toadd_length; +} + + +static char challenge[8]; +#ifdef OLD +static void *handle=NULL; + +/* connects if necessary. Returns the handle (or NULL if + the connection failed) */ +static void* init_challenge() { + if (handle!=NULL) + return handle; + handle=NTLM_Connect("supervisor","tlc1","GCSINT",challenge); + return (handle!=NULL); +} +#else /* OLD */ +SMB_Handle_Type handle=NULL; + +/* returns 0 on success, > 0 on failure */ +static int init_challenge() { + char *SMB_Prots[] = { + "LANMAN1.0", + "LM1.2X002", + "Samba", + NULL}; + + handle=SMB_Connect_Server(NULL,DOMAIN_CONTROLLER,NTLM_DOMAIN); + if (handle==NULL) { /* couldn't connect */ + SMB_Discon(handle,0); + handle=NULL; + return 1; + } + if (SMB_Negotiate(handle, SMB_Prots) < 0) { /* An error */ + SMB_Discon(handle,0); + return 2; + } + if (handle -> Security == 0) { /* share-level security, unuseable */ + SMB_Discon(handle,0); + handle=NULL; + return 3; + } + memcpy(challenge, handle->Encrypt_Key, 8); + return 0; +} + +#endif /* OLD */ + +/* FIX a lot of stuff, like checks for memory etc. */ +char* make_challenge() { + struct ntlm_challenge ch; + int pl=0; + + if (init_challenge() > 0) + return NULL; + memset(&ch, 0, sizeof(struct ntlm_challenge)); + memcpy(ch.hdr.signature, "NTLMSSP", 8); + ch.hdr.type = WSWAP(NTLM_CHALLENGE); +#ifdef USE_MOD_NTLM_STUFF + ch.flags = WSWAP(0x8201); + ch.len=WSWAP(sizeof(struct ntlm_challenge)); +#else /* use previous squid conventions. */ + add_to_payload(ch.pad,&pl,&ch.target,NTLM_DOMAIN,strlen(NTLM_DOMAIN), + NTLM_CHALLENGE_HEADER_OFFSET); + ch.flags=WSWAP(0x18206); + ch.unknown[6] = SSWAP(0x003a); +#endif +#ifdef USE_STATIC_CHALLENGE + memcpy(ch.challenge,NTLM_STATIC_CHALLENGE,8); +#else + memcpy(ch.challenge,challenge,8); +#endif + + return base64_encode_bin((char *)&ch,48+pl); +} + +/* FIXME: we're assuming that we are connected and haven't been dropped off */ +int ntlm_check_auth(ntlmssp *got) { + int rv; + if (init_challenge() > 0) + return NULL; + debug("ntlm_check_auth entered\n"); +#ifdef OLD + rv=NTLM_Auth(handle,got->user.str, + (got->nt.l==24?got->nt.str:got->lm.str),1); +#else + debug("ntlm_check_auth: using %s challenge\n",(got->nt.l==24?"nt":"lm")); + rv=SMB_Logon_Server(handle,got->user.str, + (got->nt.l==24?got->nt.str:got->lm.str),1); +#endif + debug("\tresult is %d\n",rv); + return (rv==NTV_NO_ERROR); +} --- /dev/null Wed Feb 14 00:44:43 2007 +++ squid/ntlm_auth_modules/NTLMSSP/ntlm.h Wed Feb 14 00:45:14 2007 @@ -0,0 +1,167 @@ +/* + * (C) 2000 Francesco Chemolli , + * inspired by previous work by Andy Doran + * + * Distributed freely under the terms of the GNU General Public License, + * version 2. See the file COPYING for licensing details + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. + */ + +#ifndef _NTLM_H_ +#define _NTLM_H_ + +#define NTLM_STATIC_CHALLENGE "deadbeef" + +#if 0 +#define NTLM_DOMAIN "GCSINT" /* must be upper-case */ +#define DOMAIN_CONTROLLER "supervisor" +#else +#define NTLM_DOMAIN "DC" /* must be upper-case */ +#define DOMAIN_CONTROLLER "C0DC01" +#endif + +#define DEBUG +#define USE_MOD_NTLM_STUFF + +#include + +/* All of this cruft is little endian */ +#ifdef WORDS_BIGENDIAN +#define SSWAP(x) (bswap16((x))) +#define WSWAP(x) (bswap32((x))) +#else +#define SSWAP(x) (x) +#define WSWAP(x) (x) +#endif + +#ifdef __GNUC__ /* this is really a gcc-ism */ +#ifdef DEBUG +#include +static char* __foo; +#define debug(X...) fprintf(stderr,"ntlm-auth(%s:%d): ",\ + ((__foo=strrchr(__FILE__,'/'))==NULL?__FILE__:__foo+1),\ + __LINE__);\ + fprintf(stderr,X) +#else /* DEBUG */ +#define debug(X...) /* */ +#endif /* DEBUG */ +#else /* __GNUC__ */ +#define debug(char *format, ...) {} /* Too lazy to write va_args stuff */ +#endif + +#define MAX_FIELD_LENGTH 1024 /* max length of an NTLMSSP field */ + +/* NTLM request types that we know about */ +#define NTLM_NEGOTIATE 1 +#define NTLM_CHALLENGE 2 +#define NTLM_CHALLENGE_HEADER_OFFSET 48 +#define NTLM_AUTHENTICATE 3 + +/* Header proceeding each request */ +typedef struct ntlmhdr { + char signature[8]; /* NTLMSSP */ + int32_t type; /* One of NTLM_* from above */ +} ntlmhdr; + +/* String header. String data resides at the end of the request */ +typedef struct strhdr { + int16_t len; /* Length in bytes */ + int16_t maxlen; /* Allocated space in bytes */ + int32_t offset; /* Offset from start of request */ +} strhdr; + +/* Negotiation request sent by client */ +struct ntlm_negotiate { + ntlmhdr hdr; /* NTLM header */ + int32_t flags; /* Request flags */ + strhdr domain; /* Domain we wish to authenticate in */ + strhdr workstation; /* Client workstation name */ + char pad[256]; /* String data */ +}; + +#ifndef USE_MOD_NTLM_STUFF +/* Challenge request sent by server. */ +struct ntlm_challenge { + ntlmhdr hdr; /* NTLM header */ + strhdr target; /* Authentication target (domain/server ...) */ + int32_t flags; /* Request flags */ + u_char challenge[8]; /* Challenge string */ + int16_t unknown[8]; /* Some sort of context data */ + char pad[256]; /* String data */ +}; +#else /* USE_MOD_NTLM_STUFF */ +struct ntlm_challenge { + ntlmhdr hdr; + u_char pad1[4]; + u_int32_t len; + u_int32_t flags; + u_char challenge[8]; + u_char pad2[8]; +}; +#endif /* USE_MOD_NTLM_STUFF */ + +/* Authentication request sent by client in response to challenge */ +struct ntlm_authenticate { + ntlmhdr hdr; /* NTLM header */ + strhdr lmresponse; /* LANMAN challenge response */ + strhdr ntresponse; /* NT challenge response */ + strhdr domain; /* Domain to authenticate against */ + strhdr user; /* Username */ + strhdr workstation; /* Workstation name */ + strhdr sessionkey; /* Session key for server's use */ + int32_t flags; /* Request flags */ + char pad[256*6]; /* String data */ +}; + +typedef struct { + int32_t l; /* length, -1 if not initialized */ + char *str; /* the string. NULL if not initialized */ +} lstring; + +typedef struct { + int32_t type; /* NTLM_* */ + int32_t flags; + lstring domain; + lstring workstation; + lstring user; + lstring sessionkey; + lstring challenge; + lstring target; + lstring lm; + lstring nt; +} ntlmssp; + +#define NONCE_LEN 8 + +typedef struct ntlm_config_struct { + unsigned int ntlm_on; + unsigned int ntlm_authoritative; + char *ntlm_domain; + char *ntlm_server; + char *ntlm_backup; +} ntlm_config; + +typedef struct ntlm_connection_struct { + void *handle; + char *nonce; /* we call it 'challenge' */ + char *user; + char *domain; + char *password; +} ntlm_connection; + + +ntlmssp * create_ntlmssp (); +void drop_ntlmssp(ntlmssp *nt); +ntlmssp *decode_ntlmssp_auth (char *encoded); +char* make_challenge(); +int ntlm_check_auth(ntlmssp *got); + +#endif /* _NTLM_H_ */ --- /dev/null Wed Feb 14 00:44:43 2007 +++ squid/ntlm_auth_modules/NTLMSSP/ntlm_auth.c Wed Feb 14 00:45:14 2007 @@ -0,0 +1,90 @@ +/* + * (C) 2000 Francesco Chemolli + * Distributed freely under the terms of the GNU General Public License, + * version 2. See the file COPYING for licensing details + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. + */ + + +#include "config.h" + +#include "ntlm.h" +#include "util.h" +#include + +#if HAVE_STDIO_H +#include +#endif +#if HAVE_STDLIB_H +#include +#endif +#if HAVE_STRING_H +#include +#endif + + +int main(int argc, char *argv[]) { + char buf[10240]; + ntlmssp *got; + char *ch; + + setbuf(stdout,NULL); + setbuf(stderr,NULL); + + while (1) { + if(fgets(buf, 10240, stdin) == NULL) exit(0); + ch=strchr(buf,'\n'); + if (ch) + *ch='\0'; /* terminate the string at newline. */ + debug("ntlm authenticator. Got '%s' from cache\n",buf); + if (strncmp(buf,"RESET",5)==0) { + debug("ntlm authenticator resetting, sending OK\n"); + printf("RESET OK\n"); /* This should read "RESET OK" */ + continue; + } + got=decode_ntlmssp_auth(buf); + if (!got) { + debug("couldn't decode challenge. Just returning ERR\n"); + printf("ERR\n"); + } + debug("challenge type is %d\n",got->type); + switch(got->type) { + case NTLM_NEGOTIATE: + ch=make_challenge(); + debug("got challenge ('%s')\n",ch); + if (ch==NULL) { + fprintf(stderr,"Couldn't get a valid challenge!\n"); + printf("ERR\n"); + } + debug("sending: 'CH %s'\n",ch); + printf ("CH %s\n",ch); + break; + case NTLM_CHALLENGE: + /* huh? we shouldn't be getting those */ + fprintf(stderr,"Huh? We should be issuing challenges, " + "not getting them. Returning ERR\n"); + printf("ERR\n"); + break; + case NTLM_AUTHENTICATE: + debug("Got an authentication request\n"); + if (ntlm_check_auth(got)) { + debug("Returning 'OK %s\\%s'\n",got->domain.str, got->user.str); + printf("OK %s\\%s\n",got->domain.str, got->user.str); + } else { + debug("Returning 'ERR'\n"); + printf("ERR\n"); + } + break; + } + drop_ntlmssp(got); + } + return 0; +}