--------------------- 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 + * 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 +#include +#include + +#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 + * 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 +#include +#include +#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 + +/* Debugging stuff */ + +#ifdef __GNUC__ /* this is really a gcc-ism */ +#ifdef DEBUG +#include +#include +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 + * 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 + * 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 +#define _T(x) TEXT(x) +#else +#include +#endif +#include +#include +#include + +#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 + * 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) { + // + 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; +}