---------------------
PatchSet 4512 
Date: 2002/07/16 13:22:02
Author: serassio
Branch: cygwin
Tag: (none) 
Log:
Added WIN32 native NTLM authenticathor

Members: 
	helpers/ntlm_auth/Makefile.am:1.2.12.1->1.2.12.2 
	helpers/ntlm_auth/NTLMSSP-WIN32/.cvsignore:1.1->1.1.8.1 
	helpers/ntlm_auth/NTLMSSP-WIN32/Makefile.am:1.1->1.1.8.1 
	helpers/ntlm_auth/NTLMSSP-WIN32/libntlmssp.c:1.1->1.1.8.1 
	helpers/ntlm_auth/NTLMSSP-WIN32/ntlm.h:1.1->1.1.8.1 
	helpers/ntlm_auth/NTLMSSP-WIN32/ntlm_auth.c:1.1->1.1.8.1 
	helpers/ntlm_auth/NTLMSSP-WIN32/readme.txt:1.1->1.1.8.1 
	include/sspwin32.h:1.1->1.1.6.1 
	lib/Makefile.am:1.2.6.4->1.2.6.5 
	lib/sspwin32.c:1.1->1.1.6.1 

Index: squid/helpers/ntlm_auth/Makefile.am
===================================================================
RCS file: /cvsroot/squid-sf//squid/helpers/ntlm_auth/Makefile.am,v
retrieving revision 1.2.12.1
retrieving revision 1.2.12.2
diff -u -r1.2.12.1 -r1.2.12.2
--- squid/helpers/ntlm_auth/Makefile.am	29 Jun 2002 21:14:51 -0000	1.2.12.1
+++ squid/helpers/ntlm_auth/Makefile.am	16 Jul 2002 13:22:02 -0000	1.2.12.2
@@ -1,7 +1,7 @@
 #  Makefile for storage modules in the Squid Object Cache server
 #
-#  $Id: Makefile.am,v 1.2.12.1 2002/06/29 21:14:51 serassio Exp $
+#  $Id: Makefile.am,v 1.2.12.2 2002/07/16 13:22:02 serassio Exp $
 #
 
-DIST_SUBDIRS	= fakeauth no_check SMB winbind
+DIST_SUBDIRS	= fakeauth no_check SMB winbind NTLMSSP-WIN32
 SUBDIRS		= @NTLM_AUTH_HELPERS@
--- /dev/null	Wed Feb 14 01:00:00 2007
+++ squid/helpers/ntlm_auth/NTLMSSP-WIN32/.cvsignore	Wed Feb 14 01:00:03 2007
@@ -0,0 +1,2 @@
+.cvsignore
+Makefile.in
--- /dev/null	Wed Feb 14 01:00:00 2007
+++ squid/helpers/ntlm_auth/NTLMSSP-WIN32/Makefile.am	Wed Feb 14 01:00:03 2007
@@ -0,0 +1,14 @@
+#
+#  Makefile for the Squid Object Cache server
+#
+#  $Id: Makefile.am,v 1.1.8.1 2002/07/16 13:22:02 serassio Exp $
+#
+
+libexec_PROGRAMS = ntlm_auth
+
+ntlm_auth_SOURCES = libntlmssp.c ntlm_auth.c ntlm.h
+
+INCLUDES      = -I. -I$(top_srcdir)/include -I$(top_srcdir)/src
+
+LDADD		= -L$(top_builddir)/lib -lntlmauth -lsspwin32 -lnetapi32 \
+		  -ladvapi32 -lmiscutil $(CRYPTLIB) $(XTRA_LIBS)
--- /dev/null	Wed Feb 14 01:00:00 2007
+++ squid/helpers/ntlm_auth/NTLMSSP-WIN32/libntlmssp.c	Wed Feb 14 01:00:03 2007
@@ -0,0 +1,382 @@
+/*
+ * (C) 2002 Guido Serassio <serassio@libero.it>
+ * Based on previous work of Francesco Chemolli and Robert Collins
+ * 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.
+ */
+
+typedef unsigned char uchar;
+
+#include "squid.h"
+#include "ntlm.h"
+#include <assert.h>
+#include <lm.h>
+#include <ntsecapi.h>
+
+#ifdef DEBUG
+#define debug_dump_ntlmssp_flags dump_ntlmssp_flags
+#else /* DEBUG */
+#define debug_dump_ntlmssp_flags(X)	/* empty */
+#endif /* DEBUG */
+
+#define ENCODED_PASS_LEN 24
+static char challenge[NONCE_LEN];
+static char lmencoded_empty_pass[ENCODED_PASS_LEN],
+	ntencoded_empty_pass[ENCODED_PASS_LEN];
+
+/* returns 1 on success, 0 on failure */
+int
+Valid_Group(char *UserName, char *Group)
+{
+    int result = FALSE;
+    WCHAR wszUserName[256];	// Unicode user name
+    WCHAR wszGroup[256];	// Unicode Group
+
+    LPLOCALGROUP_USERS_INFO_0 pBuf = NULL;
+    LPLOCALGROUP_USERS_INFO_0 pTmpBuf;
+    DWORD dwLevel = 0;
+    DWORD dwFlags = LG_INCLUDE_INDIRECT;
+    DWORD dwPrefMaxLen = -1;
+    DWORD dwEntriesRead = 0;
+    DWORD dwTotalEntries = 0;
+    NET_API_STATUS nStatus;
+    DWORD i;
+    DWORD dwTotalCount = 0;
+
+/* Convert ANSI User Name and Group to Unicode */
+
+    MultiByteToWideChar(CP_ACP, 0, UserName,
+	strlen(UserName) + 1, wszUserName,
+	sizeof(wszUserName) / sizeof(wszUserName[0]));
+    MultiByteToWideChar(CP_ACP, 0, Group,
+	strlen(Group) + 1, wszGroup, sizeof(wszGroup) / sizeof(wszGroup[0]));
+
+    /*
+     * Call the NetUserGetLocalGroups function 
+	 * specifying information level 0.
+	 * 
+	 * The LG_INCLUDE_INDIRECT flag specifies that the 
+	 * function should also return the names of the local 
+	 * groups in which the user is indirectly a member.
+	 */
+	nStatus = NetUserGetLocalGroups(NULL,
+	    wszUserName,
+	    dwLevel,
+	    dwFlags,
+	    (LPBYTE *) & pBuf, dwPrefMaxLen, &dwEntriesRead, &dwTotalEntries);
+	/*
+	 * If the call succeeds,
+	 */
+    if (nStatus == NERR_Success) {
+	if ((pTmpBuf = pBuf) != NULL) {
+	    for (i = 0; i < dwEntriesRead; i++) {
+		assert(pTmpBuf != NULL);
+		if (pTmpBuf == NULL) {
+		    result = FALSE;
+		    break;
+		}
+		if (wcscmp(pTmpBuf->lgrui0_name, wszGroup) == 0) {
+		    result = TRUE;
+		    break;
+		}
+		pTmpBuf++;
+		dwTotalCount++;
+	    }
+	}
+    } else
+	    result = FALSE;
+/*
+ * Free the allocated memory.
+ */
+    if (pBuf != NULL)
+	NetApiBufferFree(pBuf);
+    return result;
+}
+
+/* returns 0 on success, > 0 on failure */
+static int
+init_challenge(void)
+{
+    static unsigned hash;
+    int r;
+    int i;
+
+    r = (int) rand();
+    r = (hash ^ r) + r;
+    for (i = 0; i < NONCE_LEN; i++) {
+	challenge[i] = r;
+	r = (r >> 2) ^ r;
+    }
+    hash = r;
+    return 0;
+}
+
+char * AllocStrFromLSAStr(LSA_UNICODE_STRING LsaStr)
+{
+    size_t len;
+    static char * target;
+
+    len = LsaStr.Length/sizeof(WCHAR) + 1;
+
+    /* allocate buffer for str + null termination */
+    safe_free(target);
+    target = (char *)xmalloc(len);
+    if (target == NULL)
+	return NULL;
+
+    /* copy unicode buffer */
+    WideCharToMultiByte(CP_ACP, 0, LsaStr.Buffer, LsaStr.Length, target, len, NULL, NULL );
+
+    /* add null termination */
+    target[len-1] = '\0';
+    return target;
+}
+
+char * GetDomainName(void)
+
+{
+    LSA_HANDLE PolicyHandle;
+    LSA_OBJECT_ATTRIBUTES ObjectAttributes;
+    NTSTATUS status;
+    PPOLICY_PRIMARY_DOMAIN_INFO ppdiDomainInfo;
+    PWKSTA_INFO_100 pwkiWorkstationInfo;
+    DWORD netret;
+    char * DomainName = NULL;
+
+    /* 
+     * Always initialize the object attributes to all zeroes.
+     */ 
+    memset(&ObjectAttributes, '\0', sizeof(ObjectAttributes));
+
+    /* 
+     * You need the local workstation name. Use NetWkstaGetInfo at level
+     * 100 to retrieve a WKSTA_INFO_100 structure.
+     * 
+     * The wki100_computername field contains a pointer to a UNICODE
+     * string containing the local computer name.
+     */ 
+    netret = NetWkstaGetInfo(NULL, 100, (LPBYTE *)&pwkiWorkstationInfo);
+    if (netret == NERR_Success) {
+	/* 
+	 * We have the workstation name in:
+	 * pwkiWorkstationInfo->wki100_computername
+	 * 
+	 * Next, open the policy object for the local system using
+	 * the LsaOpenPolicy function.
+	 */ 
+	status = LsaOpenPolicy(
+                 NULL,
+                 &ObjectAttributes,
+                 GENERIC_READ | POLICY_VIEW_LOCAL_INFORMATION,
+                 &PolicyHandle
+                );
+	
+	/* 
+	 * Error checking.
+	 */ 
+	if (status) {
+	    debug("OpenPolicy Error: %d\n", status);
+	} else {
+
+	    /* 
+	     * You have a handle to the policy object. Now, get the
+	     * domain information using LsaQueryInformationPolicy.
+	     */ 
+	    status = LsaQueryInformationPolicy(PolicyHandle,
+		PolicyPrimaryDomainInformation,
+		&ppdiDomainInfo);
+	    if (status) {
+		debug("LsaQueryInformationPolicy Error: %d\n", status);
+	    } else  {
+
+		/* Get name in useable format */
+		DomainName = AllocStrFromLSAStr(ppdiDomainInfo->Name);
+
+		/* 
+		 * Check the Sid pointer, if it is null, the
+		 * workstation is either a stand-alone computer
+		 * or a member of a workgroup.
+		 */ 
+		if (ppdiDomainInfo->Sid) {
+
+		    /* 
+		     * Member of a domain. Display it in debug mode.
+		     */ 
+		    debug("Member of Domain %s\n",DomainName);
+		} else {
+		    DomainName = NULL;
+                }
+	    }
+	}
+
+	/* 
+	 * Clean up all the memory buffers created by the LSA and
+	 * Net* APIs.
+	 */ 
+	NetApiBufferFree(pwkiWorkstationInfo);
+	LsaFreeMemory((LPVOID)ppdiDomainInfo);
+    } else 
+	debug("NetWkstaGetInfo Error: %d\n", netret);
+    return DomainName;
+}
+
+const char *
+make_challenge(void)
+{
+    static char ComputerName[MAX_COMPUTERNAME_LENGTH + 1];
+    static char * DomainName;
+    DWORD CNsize = MAX_COMPUTERNAME_LENGTH + 1;
+
+    GetComputerName(ComputerName, &CNsize);
+    uc(ComputerName);
+    if ((DomainName=GetDomainName()) == NULL)
+	return NULL;
+    uc(DomainName);
+    debug("ComputerName:%s DomainName: %s",ComputerName,DomainName);
+    if (init_challenge() > 0)
+	return NULL;
+    return ntlm_make_challenge(DomainName, ComputerName, challenge, NONCE_LEN);
+}
+
+int ntlm_errno;
+static char credentials[1024];	/* we can afford to waste */
+
+
+/* Fetches the user's credentials from the challenge.
+ * Returns NULL if domain or user is not defined
+ * No identity control is performed.
+ * WARNING! The result is static storage, shared with ntlm_check_auth
+ */
+char *
+fetch_credentials(ntlm_authenticate * auth, int auth_length)
+{
+    char *p = credentials;
+    lstring tmp;
+    tmp = ntlm_fetch_string((char *) auth, auth_length, &auth->domain);
+    if (tmp.str == NULL)
+	return NULL;
+    memcpy(p, tmp.str, tmp.l);
+    p += tmp.l;
+    *p++ = '\\';
+    tmp = ntlm_fetch_string((char *) auth, auth_length, &auth->user);
+    if (tmp.str == NULL)
+	return NULL;
+    *(p + tmp.l) = '\0';
+    return credentials;
+}
+
+/* returns NULL on failure, or a pointer to
+ * the user's credentials (domain\\username)
+ * upon success. WARNING. It's pointing to static storage.
+ * In case of problem sets as side-effect ntlm_errno to one of the
+ * codes defined in ntlm.h
+ */
+char *
+ntlm_check_auth(ntlm_authenticate * auth, int auth_length)
+{
+    int rv;
+    char pass[25] /*, encrypted_pass[40] */;
+    char *domain = credentials;
+    char *user;
+    lstring tmp;
+
+/*      debug("fetching domain\n"); */
+    tmp = ntlm_fetch_string((char *) auth, auth_length, &auth->domain);
+    if (tmp.str == NULL || tmp.l == 0) {
+	debug("No domain supplied. Returning no-auth\n");
+	ntlm_errno = NTLM_LOGON_ERROR;
+	return NULL;
+    }
+    memcpy(domain, tmp.str, tmp.l);
+    user = domain + tmp.l;
+    *user++ = '\0';
+
+/*      debug("fetching user name\n"); */
+    tmp = ntlm_fetch_string((char *) auth, auth_length, &auth->user);
+    if (tmp.str == NULL || tmp.l == 0) {
+	debug("No username supplied. Returning no-auth\n");
+	ntlm_errno = NTLM_LOGON_ERROR;
+	return NULL;
+    }
+    memcpy(user, tmp.str, tmp.l);
+    *(user + tmp.l) = '\0';
+
+		
+		/* Authenticating against the NT response doesn't seem to work... */
+    tmp = ntlm_fetch_string((char *) auth, auth_length, &auth->lmresponse);
+    if (tmp.str == NULL || tmp.l == 0) {
+	fprintf(stderr, "No auth at all. Returning no-auth\n");
+	ntlm_errno = NTLM_LOGON_ERROR;
+	return NULL;
+    }
+		
+    memcpy(pass, tmp.str, tmp.l);
+    pass[25] = '\0';
+
+#if 1
+		debug ("Empty LM pass detection: user: '%s', ours:'%s', his: '%s'"
+					 "(length: %d)\n",
+					 user,lmencoded_empty_pass,tmp.str,tmp.l);
+		if (memcmp(tmp.str,lmencoded_empty_pass,ENCODED_PASS_LEN)==0) {
+			fprintf(stderr,"Empty LM password supplied for user %s\\%s. "
+							"No-auth\n",domain,user);
+			ntlm_errno=NTLM_LOGON_ERROR;
+			return NULL;
+		}
+		
+		tmp = ntlm_fetch_string ((char *) auth, auth_length, &auth->ntresponse);
+		if (tmp.str != NULL && tmp.l != 0) {
+			debug ("Empty NT pass detection: user: '%s', ours:'%s', his: '%s'"
+						 "(length: %d)\n",
+						 user,ntencoded_empty_pass,tmp.str,tmp.l);
+			if (memcmp(tmp.str,lmencoded_empty_pass,ENCODED_PASS_LEN)==0) {
+				fprintf(stderr,"Empty NT password supplied for user %s\\%s. "
+								"No-auth\n",domain,user);
+				ntlm_errno=NTLM_LOGON_ERROR;
+				return NULL;
+			}
+		}
+#endif
+
+		/* TODO: check against empty password!!!!! */
+		
+
+
+    debug("checking domain: '%s', user: '%s', pass='%s'\n", domain, user, pass);
+    rv = SSPLogonUser(user, pass, domain);
+
+    debug("Login attempt had result %d\n", rv);
+
+    if (!rv) {			/* failed */
+	ntlm_errno = ss;
+	return NULL;
+    }
+    *(user - 1) = '\\';		/* hack. Performing, but ugly. */
+    
+    if (UseAllowedGroup) {
+	if (!Valid_Group(credentials, NTAllowedGroup)) {
+	    ntlm_errno = NTLM_BAD_NTGROUP;
+	    debug("User %s not in allowed Group %s\n", credentials, NTAllowedGroup);
+	    return NULL;
+	}
+    }
+    if (UseDisallowedGroup) {
+	if (Valid_Group(credentials, NTDisAllowedGroup)) {
+	    ntlm_errno = NTLM_BAD_NTGROUP;
+	    debug("User %s is in denied Group %s\n", credentials, NTDisAllowedGroup);
+	    return NULL;
+	}
+    }
+
+    debug("credentials: %s\n", credentials);
+    return credentials;
+}
--- /dev/null	Wed Feb 14 01:00:00 2007
+++ squid/helpers/ntlm_auth/NTLMSSP-WIN32/ntlm.h	Wed Feb 14 01:00:03 2007
@@ -0,0 +1,116 @@
+/*
+ * (C) 2002 Guido Serassio <serassio@libero.it>
+ * Based on previous work of Francesco Chemolli, Robert Collins and Andrew 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_
+
+#include "sspwin32.h"
+#include <windows.h>
+#include <sspi.h>
+#include <security.h>
+#include "ntlmauth.h"
+#undef debug
+
+/************* CONFIGURATION ***************/
+/*
+ * define this if you want debugging
+ */
+#ifdef _SQUID_MSWIN_
+#ifdef _DEBUG
+#define DEBUG
+#endif
+#else
+#ifndef DEBUG
+#define DEBUG
+#endif
+#endif
+
+#define DEAD_DC_RETRY_INTERVAL 30
+
+/************* END CONFIGURATION ***************/
+
+#include <sys/types.h>
+
+/* Debugging stuff */
+
+#ifdef __GNUC__			/* this is really a gcc-ism */
+#ifdef DEBUG
+#include <stdio.h>
+#include <unistd.h>
+static char *__foo;
+extern char debug_enabled;
+#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__ */
+extern char debug_enabled;
+static void
+debug(char *format,...) 
+{
+#ifdef DEBUG
+#ifdef _SQUID_MSWIN_
+    if (debug_enabled) {
+	va_list args;
+
+	va_start(args,format);
+	fprintf(stderr, "ntlm-auth[%d]: ",getpid());
+	vfprintf(stderr, format, args);
+	fprintf(stderr, "\n");
+	va_end(args);
+    }
+#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
+
+extern char * NTAllowedGroup;
+extern char * NTDisAllowedGroup;
+extern int UseDisallowedGroup;
+extern int UseAllowedGroup;
+extern int ntlm_errno;
+
+#define NTLM_NO_ERROR SEC_E_OK
+#define NTLM_SERVER_ERROR SEC_E_NOT_SUPPORTED
+#define NTLM_PROTOCOL_ERROR SEC_E_INCOMPLETE_MESSAGE
+#define NTLM_LOGON_ERROR SEC_E_LOGON_DENIED
+#define NTLM_UNTRUSTED_DOMAIN SEC_E_NO_AUTHENTICATING_AUTHORITY
+#define NTLM_BAD_PROTOCOL SEC_E_BAD_PKGID
+#define NTLM_NOT_CONNECTED SEC_E_SECPKG_NOT_FOUND
+#define NTLM_BAD_NTGROUP -1
+
+extern void uc(char *);
+
+const char *make_challenge(void);
+extern char *ntlm_check_auth(ntlm_authenticate * auth, int auth_length);
+extern char *fetch_credentials(ntlm_authenticate * auth, int auth_length);
+
+#endif /* _NTLM_H_ */
--- /dev/null	Wed Feb 14 01:00:00 2007
+++ squid/helpers/ntlm_auth/NTLMSSP-WIN32/ntlm_auth.c	Wed Feb 14 01:00:03 2007
@@ -0,0 +1,294 @@
+/*
+ * $Id: ntlm_auth.c,v 1.1.8.1 2002/07/16 13:22:02 serassio Exp $
+ *
+ * This is a helper for NTLM Authentication for Squid Cache
+ * Copyright (C) 2002 Guido Serassio <squidnt@serassio.it>
+ * Based on previous work of Francesco Chemolli and Robert Collins
+ *
+ * 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 "squid.h"
+#include "ntlm.h"
+
+#define BUFFER_SIZE 10240
+
+
+#ifdef DEBUG
+char error_messages_buffer[BUFFER_SIZE];
+#endif
+
+char load_balance = 0, protocol_pedantic = 0;
+#ifdef NTLM_FAIL_OPEN
+char last_ditch_enabled = 0;
+#endif
+
+char debug_enabled=0;
+
+
+char * NTAllowedGroup;
+char * NTDisAllowedGroup;
+int UseDisallowedGroup = 0;
+int UseAllowedGroup = 0;
+
+/* makes a null-terminated string upper-case. Changes CONTENTS! */
+void
+uc(char *string)
+{
+    char *p = string, c;
+    while ((c = *p)) {
+	*p = toupper(c);
+	p++;
+    }
+}
+
+/* makes a null-terminated string lower-case. Changes CONTENTS! */
+static void
+lc(char *string)
+{
+    char *p = string, c;
+    while ((c = *p)) {
+	*p = tolower(c);
+	p++;
+    }
+}
+
+
+void
+send_bh_or_ld(char *bhmessage, ntlm_authenticate * failedauth, int authlen)
+{
+#ifdef NTLM_FAIL_OPEN
+    char *creds = NULL;
+    if (last_ditch_enabled) {
+	creds = fetch_credentials(failedauth, authlen);
+	if (creds) {
+	    lc(creds);
+	    SEND2("LD %s", creds);
+	} else {
+	    SEND("NA last-ditch on, but no credentials");
+	}
+    } else {
+#endif
+	SEND2("BH %s", bhmessage);
+#ifdef NTLM_FAIL_OPEN
+    }
+#endif
+}
+
+/*
+ * options:
+ * -v enables debugging statements if DEBUG was defined at build-time.
+ * -l if specified, changes behavior on failures to last-ditch.
+ * -a can specify a Windows Local Group name allowed to authenticate.
+ * -d can specify a Windows Local Group name not allowed to authenticate.
+ * domain\controller ...
+ */
+char *my_program_name = NULL;
+
+void
+usage()
+{
+    fprintf(stderr,
+#ifdef NTLM_FAIL_OPEN
+	"%s usage:\n%s [-v] [-a UserGroup] [-d UserGroup] [-l]\n"
+#else
+	"%s usage:\n%s [-v] [-a UserGroup] [-d UserGroup]\n"
+#endif
+	"-v enables verbose debugging statements if DEBUG was defined at build-time.\n"
+#ifdef NTLM_FAIL_OPEN
+	"-l if specified, changes behavior on failures to last-ditch.\n"
+#endif
+	"-a can specify a Windows Local Group name allowed to authenticate.\n"
+	"-d can specify a Windows Local Group name not allowed to authenticate.\n\n",
+	my_program_name, my_program_name);
+}
+
+
+void
+process_options(int argc, char *argv[])
+{
+    int opt, had_error = 0;
+#ifdef NTLM_FAIL_OPEN
+    while (-1 != (opt = getopt(argc, argv, "vla:d:"))) {
+#else
+    while (-1 != (opt = getopt(argc, argv, "va:d:"))) {
+#endif
+	switch (opt) {
+	case 'a':
+	    safe_free(NTAllowedGroup);
+	    NTAllowedGroup=xstrdup(optarg);
+	    UseAllowedGroup = 1;
+	    break;
+	case 'd':
+	    safe_free(NTDisAllowedGroup);
+	    NTDisAllowedGroup=xstrdup(optarg);
+	    UseDisallowedGroup = 1;
+	    break;
+#ifdef NTLM_FAIL_OPEN
+	case 'l':
+	    last_ditch_enabled = 1;
+	    break;
+#endif
+	case 'v':
+	    debug_enabled=1;
+	    break;
+	default:
+	    fprintf(stderr, "unknown option: -%c. Exiting\n", opt);
+	    usage();
+	    had_error = 1;
+	}
+    }
+    if (had_error)
+	exit(1);
+}
+
+/* tries connecting to the domain controllers in the "controllers" ring,
+ * with failover if the adequate option is specified.
+ */
+const char *
+obtain_challenge()
+{
+    int j = 0;
+    const char *ch = NULL;
+
+    debug("attempting challenge retrieval\n");
+    ch = make_challenge();
+    debug("make_challenge returned %p\n", ch);
+    if (ch) {
+	debug("Got it\n");
+	return ch;		/* All went OK, returning */
+    }
+    return NULL;
+}
+
+
+void
+manage_request()
+{
+    ntlmhdr *fast_header;
+    char buf[BUFFER_SIZE];
+    const char *ch;
+    char *ch2, *decoded, *cred;
+    int plen;
+
+    if (fgets(buf, BUFFER_SIZE, stdin) == NULL) {
+	debug("fgets() failed! dying..... errno=%d (%s)\n", errno,
+	    strerror(errno));
+	exit(1);		/* BIIG buffer */
+    }
+    debug("managing request\n");
+    ch2 = memchr(buf, '\n', BUFFER_SIZE);	/* safer against overrun than strchr */
+    if (ch2) {
+	*ch2 = '\0';		/* terminate the string at newline. */
+	ch = ch2;
+    }
+    debug("ntlm authenticator. Got '%s' from Squid\n", buf);
+
+    if (memcmp(buf, "KK ", 3) == 0) {	/* authenticate-request */
+	/* figure out what we got */
+	decoded = base64_decode(buf + 3);
+	/* Note: we don't need to manage memory at this point, since
+	 *  base64_decode returns a pointer to static storage.
+	 */
+
+	if (!decoded) {		/* decoding failure, return error */
+	    SEND("NA Packet format error, couldn't base64-decode");
+	    return;
+	}
+	/* fast-track-decode request type. */
+	fast_header = (struct _ntlmhdr *) decoded;
+
+	/* sanity-check: it IS a NTLMSSP packet, isn't it? */
+	if (memcmp(fast_header->signature, "NTLMSSP", 8) != 0) {
+	    SEND("NA Broken authentication packet");
+	    return;
+	}
+	switch (fast_header->type) {
+	case NTLM_NEGOTIATE:
+	    SEND("NA Invalid negotiation request received");
+	    return;
+	    /* notreached */
+	case NTLM_CHALLENGE:
+	    SEND
+		("NA Got a challenge. We refuse to have our authority disputed");
+	    return;
+	    /* notreached */
+	case NTLM_AUTHENTICATE:
+	    /* check against SSPI */
+	    plen = strlen(buf) * 3 / 4;		/* we only need it here. Optimization */
+	    cred = ntlm_check_auth((ntlm_authenticate *) decoded, plen);
+	    if (cred == NULL) {
+		switch (ntlm_errno) {
+		case NTLM_LOGON_ERROR:
+		    SEND("NA Logon Failure");
+		    return;
+		case NTLM_BAD_NTGROUP:
+		    SEND("NA Incorrect Group Membership");
+		    return;
+		case NTLM_UNTRUSTED_DOMAIN:
+		    SEND("NA No authority could be contacted for authentication");
+		    return;
+		default:
+		    SEND("NA SSPI Error");
+		    return;
+		}
+	    }
+	    lc(cred);		/* let's lowercase them for our convenience */
+	    SEND2("AF %s", cred);
+	    return;
+	default:
+	    SEND("BH unknown authentication packet type");
+	    return;
+	}
+	return;
+    }
+    if (memcmp(buf, "YR", 2) == 0) {	/* refresh-request */
+	ch = obtain_challenge();
+	SEND2("TT %s", ch);
+	return;
+    }
+    SEND("BH Helper detected protocol error");
+    return;
+/********* END ********/
+}
+
+int
+main(int argc, char *argv[])
+{
+    
+    debug("ntlm_auth build " __DATE__ ", " __TIME__ " starting up...\n");
+
+    my_program_name = argv[0];
+    process_options(argc, argv);
+    debug("options processed OK\n");
+    srand( (unsigned)time( NULL ) );
+
+    if (LoadSecurityDll(SSP_NTLM) == NULL) {
+	fprintf(stderr, "FATAL, can't initialize SSPI, exiting.\n");
+	exit(1);
+    }
+    debug("SSPI initialized OK\n");
+
+    atexit(UnloadSecurityDll);
+
+    /* initialize FDescs */
+    setbuf(stdout, NULL);
+    setbuf(stderr, NULL);
+
+    while (1) {
+	manage_request();
+    }
+    return 0;
+}
--- /dev/null	Wed Feb 14 01:00:00 2007
+++ squid/helpers/ntlm_auth/NTLMSSP-WIN32/readme.txt	Wed Feb 14 01:00:03 2007
@@ -0,0 +1,31 @@
+ntlm_auth.exe
+
+Native Windows NTLM authenticator for Squid 2.5
+
+Usage:
+
+ntlm_auth [-v] [-a UserGroup] [-d UserGroup] [-l]
+
+-v enables debugging statements if DEBUG was defined at build-time.
+-l if specified, changes behavior on failures to last-ditch.
+-a can specify a Windows Local Group name allowed to authenticate.
+-d can specify a Windows Local Group name not allowed to authenticate.
+
+This is released under the GNU General Public License
+
+
+Allowing Users
+
+Users that are allowed to access the web proxy must have the Windows NT
+User Rights "logon from the network" and must be included in the NT LOCAL User Groups 
+specified in the Authenticator's command line. 
+This can be accomplished creating a local user group on the NT machine, grant the privilege,
+and adding users to it.
+
+Refer to Squid documentation for the required changes to squid.conf.
+
+
+Contact details -
+
+To contact the maintainer of this package, e-mail on squidnt@serassio.it.
+The latest version may be found on http://www.serassio.it/SquidNT.htm.
--- /dev/null	Wed Feb 14 01:00:00 2007
+++ squid/include/sspwin32.h	Wed Feb 14 01:00:03 2007
@@ -0,0 +1,45 @@
+/*
+ * (C) 2002 Guido Serassio <serassio@libero.it>
+ * Based on previous work of Francesco Chemolli, Robert Collins and Andrew 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 _LIBSSPWIN32_H_
+#define _LIBSSPWIN32_H_
+#define SECURITY_WIN32
+#define NTLM_PACKAGE_NAME "NTLM"
+
+#ifdef _SQUID_CYGWIN_
+#include <wchar.h>
+#define _T(x) TEXT(x)
+#else
+#include <tchar.h>
+#endif
+#include <windows.h>
+#include <security.h>
+#include <sspi.h>
+
+#define WINNT_SECURITY_DLL "security.dll"
+#define WIN9X_SECURITY_DLL "secur32.dll"
+
+#define SSP_BASIC 1
+#define SSP_NTLM 2
+
+HMODULE LoadSecurityDll(int);
+void UnloadSecurityDll(void);
+BOOL WINAPI SSPLogonUser(PTSTR, PTSTR, PTSTR);
+
+extern SECURITY_STATUS ss;
+
+#endif /* LIBSSPWIN32_H_ */
Index: squid/lib/Makefile.am
===================================================================
RCS file: /cvsroot/squid-sf//squid/lib/Makefile.am,v
retrieving revision 1.2.6.4
retrieving revision 1.2.6.5
diff -u -r1.2.6.4 -r1.2.6.5
--- squid/lib/Makefile.am	13 Apr 2002 18:55:47 -0000	1.2.6.4
+++ squid/lib/Makefile.am	16 Jul 2002 13:26:14 -0000	1.2.6.5
@@ -21,6 +21,7 @@
 	@LIBDLMALLOC@ \
 	libmiscutil.a \
 	libntlmauth.a \
+	libsspwin32.a \
 	@LIBREGEX@
 EXTRA_libmiscutil_a_SOURCES = \
 	md5.c \
@@ -58,5 +59,7 @@
 	ntlmauth.c
 libntlmauth_a_LIBADD = \
 	@LIBOBJS@
+libsspwin32_a_SOURCES = \
+	sspwin32.c
 
-INCLUDES	= -I$(top_builddir)/include -I$(top_srcdir)/include
+INCLUDES	= -I$(top_builddir)/include -I$(top_srcdir)/include -I$(top_srcdir)/src -I$(top_builddir)/src
--- /dev/null	Wed Feb 14 01:00:00 2007
+++ squid/lib/sspwin32.c	Wed Feb 14 01:00:03 2007
@@ -0,0 +1,411 @@
+/*
+ * (C) 2002 Guido Serassio <serassio@libero.it>
+ * Based on previous work of Francesco Chemolli, Robert Collins
+ *
+ * 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 "squid.h"
+#include "sspwin32.h"
+
+
+
+typedef struct _AUTH_SEQ {
+    BOOL fInitialized;
+    BOOL fHaveCredHandle;
+    BOOL fHaveCtxtHandle;
+    CredHandle hcred;
+    struct _SecHandle hctxt;
+} AUTH_SEQ, *PAUTH_SEQ;
+
+static HMODULE hModule;
+static int NTLM_mode = SSP_BASIC;
+SECURITY_STATUS ss = SEC_E_OK;
+
+/* Function pointers */
+ACCEPT_SECURITY_CONTEXT_FN _AcceptSecurityContext = NULL;
+ACQUIRE_CREDENTIALS_HANDLE_FN _AcquireCredentialsHandle = NULL;
+COMPLETE_AUTH_TOKEN_FN _CompleteAuthToken = NULL;
+DELETE_SECURITY_CONTEXT_FN _DeleteSecurityContext = NULL;
+FREE_CONTEXT_BUFFER_FN _FreeContextBuffer = NULL;
+FREE_CREDENTIALS_HANDLE_FN _FreeCredentialsHandle = NULL;
+INITIALIZE_SECURITY_CONTEXT_FN _InitializeSecurityContext = NULL;
+QUERY_SECURITY_PACKAGE_INFO_FN _QuerySecurityPackageInfo = NULL;
+
+
+void UnloadSecurityDll(void)
+{
+    if (hModule)
+	FreeLibrary(hModule);
+    _AcceptSecurityContext      = NULL;
+    _AcquireCredentialsHandle   = NULL;
+    _CompleteAuthToken          = NULL;
+    _DeleteSecurityContext      = NULL;
+    _FreeContextBuffer          = NULL;
+    _FreeCredentialsHandle      = NULL;
+    _InitializeSecurityContext  = NULL;
+    _QuerySecurityPackageInfo   = NULL;
+    hModule = NULL;
+}
+
+
+HMODULE LoadSecurityDll(int mode) 
+{
+    TCHAR lpszDLL[MAX_PATH];
+    OSVERSIONINFO VerInfo;
+/* 
+ *  Find out which security DLL to use, depending on
+ *  whether we are on NT or Win95 or 2000 or XP or .NET Server
+ *  We have to use security.dll on Windows NT 4.0.
+ *  All other operating systems, we have to use Secur32.dll
+ */ 
+    hModule = NULL;
+    if ((mode != SSP_BASIC) && (mode != SSP_NTLM))
+	return hModule;
+    NTLM_mode = mode;
+    VerInfo.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
+    if (!GetVersionEx (&VerInfo)) {   /* If this fails, something has gone wrong */
+	return hModule;
+    }
+    if (VerInfo.dwPlatformId == VER_PLATFORM_WIN32_NT &&
+	VerInfo.dwMajorVersion == 4 &&
+	VerInfo.dwMinorVersion == 0)
+    {
+	lstrcpy (lpszDLL, _T(WINNT_SECURITY_DLL));
+    } else {
+	lstrcpy (lpszDLL, _T(WIN9X_SECURITY_DLL));
+    }
+    hModule = LoadLibrary(lpszDLL);
+    if (!hModule)
+	return hModule;
+    _AcceptSecurityContext = (ACCEPT_SECURITY_CONTEXT_FN) 
+	GetProcAddress(hModule, "AcceptSecurityContext");
+    if (!_AcceptSecurityContext) {
+	UnloadSecurityDll();
+	hModule = NULL;
+    	return hModule;
+    }
+#ifdef UNICODE
+    _AcquireCredentialsHandle = (ACQUIRE_CREDENTIALS_HANDLE_FN)
+	GetProcAddress(hModule, "AcquireCredentialsHandleW");
+#else
+    _AcquireCredentialsHandle = (ACQUIRE_CREDENTIALS_HANDLE_FN)
+	GetProcAddress(hModule, "AcquireCredentialsHandleA");
+#endif
+    if (!_AcquireCredentialsHandle) {
+	UnloadSecurityDll();
+	hModule = NULL;
+    	return hModule;
+    }
+/* CompleteAuthToken is not present on Windows 9x Secur32.dll
+ * Do not check for the availablity of the function if it is NULL
+ */
+    _CompleteAuthToken = (COMPLETE_AUTH_TOKEN_FN) 
+	GetProcAddress(hModule, "CompleteAuthToken");
+    _DeleteSecurityContext = (DELETE_SECURITY_CONTEXT_FN) 
+	GetProcAddress(hModule, "DeleteSecurityContext");
+    if (!_DeleteSecurityContext) {
+	UnloadSecurityDll();
+	hModule = NULL;
+    	return hModule;
+    }
+    _FreeContextBuffer = (FREE_CONTEXT_BUFFER_FN) 
+	GetProcAddress(hModule, "FreeContextBuffer");
+    if (!_FreeContextBuffer) {
+	UnloadSecurityDll();
+	hModule = NULL;
+    	return hModule;
+    }
+    _FreeCredentialsHandle = (FREE_CREDENTIALS_HANDLE_FN) 
+	GetProcAddress(hModule, "FreeCredentialsHandle");
+    if (!_FreeCredentialsHandle) {
+	UnloadSecurityDll();
+	hModule = NULL;
+    	return hModule;
+    }
+#ifdef UNICODE
+    _InitializeSecurityContext = (INITIALIZE_SECURITY_CONTEXT_FN)
+	GetProcAddress(hModule, "InitializeSecurityContextW");
+#else
+    _InitializeSecurityContext = (INITIALIZE_SECURITY_CONTEXT_FN) 
+	GetProcAddress(hModule, "InitializeSecurityContextA");
+#endif
+    if (!_InitializeSecurityContext) {
+	UnloadSecurityDll();
+	hModule = NULL;
+    	return hModule;
+    }
+#ifdef UNICODE
+    _QuerySecurityPackageInfo = (QUERY_SECURITY_PACKAGE_INFO_FN) 
+	GetProcAddress(hModule, "QuerySecurityPackageInfoW");
+#else
+    _QuerySecurityPackageInfo = (QUERY_SECURITY_PACKAGE_INFO_FN)
+	GetProcAddress(hModule, "QuerySecurityPackageInfoA");
+#endif
+    if (!_QuerySecurityPackageInfo) {
+	UnloadSecurityDll();
+	hModule = NULL;
+    }
+    return hModule;
+}
+
+
+BOOL GenClientContext(PAUTH_SEQ pAS, PSEC_WINNT_AUTH_IDENTITY pAuthIdentity,
+		      PVOID pIn, DWORD cbIn, PVOID pOut, PDWORD pcbOut, PBOOL pfDone)
+{
+/*
+ *  Routine Description:
+ *
+ *  Optionally takes an input buffer coming from the server and returns
+ *  a buffer of information to send back to the server. Also returns
+ *  an indication of whether or not the context is complete.
+ *
+ *  Return Value:
+ *  Returns TRUE if successful; otherwise FALSE.
+ */ 
+    TimeStamp       tsExpiry;
+    SecBufferDesc   sbdOut;
+    SecBuffer       sbOut;
+    SecBufferDesc   sbdIn;
+    SecBuffer       sbIn;
+    ULONG           fContextAttr;
+
+    if (!pAS->fInitialized) {
+	ss = _AcquireCredentialsHandle(NULL, _T(NTLM_PACKAGE_NAME), 
+	    SECPKG_CRED_OUTBOUND, NULL, (NTLM_mode == SSP_NTLM) ? NULL : pAuthIdentity, NULL, NULL,
+	    &pAS->hcred, &tsExpiry);
+	if (ss < 0) {
+	    fprintf(stderr, "AcquireCredentialsHandle failed with %08X\n", ss);
+	    return FALSE;
+	}
+	pAS->fHaveCredHandle = TRUE;
+    }
+    
+    /* Prepare output buffer */
+    sbdOut.ulVersion = 0;
+    sbdOut.cBuffers = 1;
+    sbdOut.pBuffers = &sbOut;
+    sbOut.cbBuffer = *pcbOut;
+    sbOut.BufferType = SECBUFFER_TOKEN;
+    sbOut.pvBuffer = pOut;
+    
+    /* Prepare input buffer */
+    if (pAS->fInitialized)  {
+	sbdIn.ulVersion = 0;
+	sbdIn.cBuffers = 1;
+	sbdIn.pBuffers = &sbIn;
+	sbIn.cbBuffer = cbIn;
+	sbIn.BufferType = SECBUFFER_TOKEN;
+	sbIn.pvBuffer = pIn;
+    }
+    ss = _InitializeSecurityContext(&pAS->hcred, 
+	pAS->fInitialized ? &pAS->hctxt : NULL, NULL, 0, 0, 
+	SECURITY_NATIVE_DREP, pAS->fInitialized ? &sbdIn : NULL,
+	0, &pAS->hctxt, &sbdOut, &fContextAttr, &tsExpiry);
+    if (ss < 0)  { 
+	// <winerror.h>
+	fprintf(stderr, "InitializeSecurityContext failed with %08X\n", ss);
+	return FALSE;
+    }
+    pAS->fHaveCtxtHandle = TRUE;
+    
+    /* If necessary, complete token */
+    if (ss == SEC_I_COMPLETE_NEEDED || ss == SEC_I_COMPLETE_AND_CONTINUE) {
+	if (_CompleteAuthToken) {
+	    ss = _CompleteAuthToken(&pAS->hctxt, &sbdOut);
+	    if (ss < 0) {
+		fprintf(stderr, "CompleteAuthToken failed with %08X\n", ss);
+		return FALSE;
+	    }
+	} else {
+	    fprintf (stderr, "CompleteAuthToken not supported.\n");
+	    return FALSE;
+	}
+    }
+    *pcbOut = sbOut.cbBuffer;
+    if (!pAS->fInitialized)
+	pAS->fInitialized = TRUE;
+    *pfDone = !(ss == SEC_I_CONTINUE_NEEDED 
+	|| ss == SEC_I_COMPLETE_AND_CONTINUE );
+    return TRUE;
+}
+
+
+BOOL GenServerContext(PAUTH_SEQ pAS, PVOID pIn, DWORD cbIn, PVOID pOut, 
+		      PDWORD pcbOut, PBOOL pfDone)
+{
+/*
+ *   Routine Description:
+ *
+ *   Takes an input buffer coming from the client and returns a buffer
+ *   to be sent to the client.  Also returns an indication of whether or
+ *   not the context is complete.
+ *
+ *   Return Value:
+ *
+ *   Returns TRUE if successful; otherwise FALSE.
+ */
+
+    TimeStamp       tsExpiry;
+    SecBufferDesc   sbdOut;
+    SecBuffer       sbOut;
+    SecBufferDesc   sbdIn;
+    SecBuffer       sbIn;
+    ULONG           fContextAttr;
+
+    if (!pAS->fInitialized)  {
+	ss = _AcquireCredentialsHandle(NULL, _T("NTLM"), 
+	    SECPKG_CRED_INBOUND, NULL, NULL, NULL, NULL, &pAS->hcred, 
+	    &tsExpiry);
+	if (ss < 0) {
+	    fprintf(stderr, "AcquireCredentialsHandle failed with %08X\n", ss);
+	    return FALSE;
+	}
+	pAS->fHaveCredHandle = TRUE;
+    }
+    
+    /* Prepare output buffer */
+    sbdOut.ulVersion = 0;
+    sbdOut.cBuffers = 1;
+    sbdOut.pBuffers = &sbOut;
+    sbOut.cbBuffer = *pcbOut;
+    sbOut.BufferType = SECBUFFER_TOKEN;
+    sbOut.pvBuffer = pOut;
+
+    /* Prepare input buffer */
+    sbdIn.ulVersion = 0;
+    sbdIn.cBuffers = 1;
+    sbdIn.pBuffers = &sbIn;
+    sbIn.cbBuffer = cbIn;
+    sbIn.BufferType = SECBUFFER_TOKEN;
+    sbIn.pvBuffer = pIn;
+    ss = _AcceptSecurityContext(&pAS->hcred, 
+	pAS->fInitialized ? &pAS->hctxt : NULL, &sbdIn, (NTLM_mode == SSP_NTLM) ? ASC_REQ_DELEGATE : 0, 
+	SECURITY_NATIVE_DREP, &pAS->hctxt, &sbdOut, &fContextAttr, 
+	&tsExpiry);
+    if (ss < 0) {
+	fprintf(stderr, "AcceptSecurityContext failed with %08X\n", ss);
+	return FALSE;
+    }
+    pAS->fHaveCtxtHandle = TRUE;
+    
+    /* If necessary, complete token */
+    if (ss == SEC_I_COMPLETE_NEEDED || ss == SEC_I_COMPLETE_AND_CONTINUE) {
+	if (_CompleteAuthToken) {
+	    ss = _CompleteAuthToken(&pAS->hctxt, &sbdOut);
+	    if (ss < 0) {
+		fprintf(stderr, "CompleteAuthToken failed with %08X\n", ss);
+		return FALSE;
+	    }
+	} else {
+	    fprintf (stderr, "CompleteAuthToken not supported.\n");
+	    return FALSE;
+	}
+    }
+    *pcbOut = sbOut.cbBuffer;
+    if (!pAS->fInitialized)
+	pAS->fInitialized = TRUE;
+    *pfDone = !(ss = SEC_I_CONTINUE_NEEDED 
+	|| ss == SEC_I_COMPLETE_AND_CONTINUE);
+    return TRUE;
+}
+
+
+BOOL WINAPI SSPLogonUser(PTSTR szUser, PTSTR szPassword, PTSTR szDomain) 
+{
+    AUTH_SEQ    asServer   = {0};
+    AUTH_SEQ    asClient   = {0};
+    BOOL        fDone      = FALSE;
+    BOOL        fResult    = FALSE;
+    DWORD       cbOut      = 0;
+    DWORD       cbIn       = 0;
+    DWORD       cbMaxToken = 0;
+    PVOID       pClientBuf = NULL;
+    PVOID       pServerBuf = NULL;
+    PSecPkgInfo pSPI       = NULL;
+    
+    SEC_WINNT_AUTH_IDENTITY ai;
+
+    do {
+	if (!hModule)
+	    break;
+	/* Get max token size */
+	_QuerySecurityPackageInfo(_T("NTLM"), &pSPI);
+	cbMaxToken = pSPI->cbMaxToken;
+	_FreeContextBuffer(pSPI);
+
+	/* Allocate buffers for client and server messages */
+	pClientBuf = xcalloc(cbMaxToken, sizeof(char));
+	pServerBuf = xcalloc(cbMaxToken, sizeof(char));
+
+	/* Initialize auth identity structure */
+	ZeroMemory(&ai, sizeof(ai));
+	ai.Domain = szDomain;
+	ai.DomainLength = lstrlen(szDomain);
+	ai.User = szUser;
+	ai.UserLength = lstrlen(szUser);
+	ai.Password = szPassword;
+	ai.PasswordLength = lstrlen(szPassword);
+#if defined(UNICODE) || defined(_UNICODE)
+	ai.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
+#else      
+	ai.Flags = SEC_WINNT_AUTH_IDENTITY_ANSI;
+#endif
+
+	/* Prepare client message (negotiate) */
+	cbOut = cbMaxToken;
+	if (!GenClientContext(&asClient, &ai, NULL, 0, pClientBuf, &cbOut, &fDone))
+	    break;
+
+	/* Prepare server message (challenge) */
+	cbIn = cbOut;
+	cbOut = cbMaxToken;
+	if (!GenServerContext(&asServer, pClientBuf, cbIn, pServerBuf, &cbOut, 
+	    &fDone))
+	    break;
+/* Most likely failure: AcceptServerContext fails with SEC_E_LOGON_DENIED
+ * in the case of bad szUser or szPassword.
+ * Unexpected Result: Logon will succeed if you pass in a bad szUser and 
+ * the guest account is enabled in the specified domain.
+ */
+
+	/* Prepare client message (authenticate) */
+	cbIn = cbOut;
+	cbOut = cbMaxToken;
+	if (!GenClientContext(&asClient, &ai, pServerBuf, cbIn, pClientBuf, &cbOut,
+	    &fDone))
+	    break;
+
+	/* Prepare server message (authentication) */
+	cbIn = cbOut;
+	cbOut = cbMaxToken;
+	if (!GenServerContext(&asServer, pClientBuf, cbIn, pServerBuf, &cbOut, 
+	    &fDone))
+	    break;
+	fResult = TRUE;
+    } while(0);
+
+    /* Clean up resources */
+    if (asClient.fHaveCtxtHandle)
+	_DeleteSecurityContext(&asClient.hctxt);
+    if (asClient.fHaveCredHandle)
+	_FreeCredentialsHandle(&asClient.hcred);
+    if (asServer.fHaveCtxtHandle)
+	_DeleteSecurityContext(&asServer.hctxt);
+    if (asServer.fHaveCredHandle)
+	_FreeCredentialsHandle(&asServer.hcred);
+    xfree(pClientBuf);
+    xfree(pServerBuf);
+
+    return fResult;
+}
