---------------------
PatchSet 4485 
Date: 2002/07/15 10:35:59
Author: serassio
Branch: cygwin-svc-2_5
Tag: (none) 
Log:
Added native WIN32 External ACL group helper

Members: 
	helpers/external_acl/Makefile.am:1.2.10.1->1.2.10.2 
	helpers/external_acl/win32_group/.cvsignore:1.1->1.1.4.1 
	helpers/external_acl/win32_group/Makefile.am:1.1->1.1.10.1 
	helpers/external_acl/win32_group/readme.txt:1.1->1.1.10.1 
	helpers/external_acl/win32_group/win32_check_group.c:1.1->1.1.10.1 
	helpers/external_acl/win32_group/win32_check_group.h:1.1->1.1.4.1 

Index: squid/helpers/external_acl/Makefile.am
===================================================================
RCS file: /cvsroot/squid-sf//squid/helpers/external_acl/Makefile.am,v
retrieving revision 1.2.10.1
retrieving revision 1.2.10.2
diff -u -r1.2.10.1 -r1.2.10.2
--- squid/helpers/external_acl/Makefile.am	13 Jul 2002 20:08:18 -0000	1.2.10.1
+++ squid/helpers/external_acl/Makefile.am	15 Jul 2002 10:35:59 -0000	1.2.10.2
@@ -1,7 +1,7 @@
 #  Makefile for storage modules in the Squid Object Cache server
 #
-#  $Id: Makefile.am,v 1.2.10.1 2002/07/13 20:08:18 serassio Exp $
+#  $Id: Makefile.am,v 1.2.10.2 2002/07/15 10:35:59 serassio Exp $
 #
 
-DIST_SUBDIRS	= ip_user ldap_group unix_group wbinfo_group winbind_group
+DIST_SUBDIRS	= ip_user ldap_group unix_group wbinfo_group winbind_group win32_group
 SUBDIRS		= @EXTERNAL_ACL_HELPERS@
--- /dev/null	Wed Feb 14 01:00:00 2007
+++ squid/helpers/external_acl/win32_group/.cvsignore	Wed Feb 14 01:00:01 2007
@@ -0,0 +1,2 @@
+.cvsignore
+Makefile.in
--- /dev/null	Wed Feb 14 01:00:00 2007
+++ squid/helpers/external_acl/win32_group/Makefile.am	Wed Feb 14 01:00:01 2007
@@ -0,0 +1,14 @@
+#
+#  Makefile for the Squid Object Cache server
+#
+#  $Id: Makefile.am,v 1.1.10.1 2002/07/15 10:35:59 serassio Exp $
+#
+#  Uncomment and customize the following to suit your needs:
+#
+
+
+libexec_PROGRAMS = win32_check_group
+
+win32_check_group_SOURCES = win32_check_group.c 
+
+LDADD	= -lnetapi32 -ladvapi32
--- /dev/null	Wed Feb 14 01:00:00 2007
+++ squid/helpers/external_acl/win32_group/readme.txt	Wed Feb 14 01:00:01 2007
@@ -0,0 +1,71 @@
+
+This is the readme.txt file for win32_check_group, an external
+helper fo the External ACL Scheme for Squid.
+
+
+This helper must be used in with an authentication scheme, tipcally basic
+or NTLM based on Windows NT/2000 domain users. 
+It reads two new line terminated argument from the standard input
+(the domain username and group) and tries to match it against
+the groups membership of the specified username.
+
+
+==============
+Program Syntax
+==============
+
+win32_check_group [-Gd]
+
+-G start helper in Global Group mode
+-d enable debug mode
+
+
+================
+squid.conf usage
+================
+
+external_acl_type NT_global_group %LOGIN /usr/local/squid/libexec/win32_check_group -G
+external_acl_type NT_local_group %LOGIN /usr/local/squid/libexec/win32_check_group
+
+acl GProxyUsers external NT_global_group GProxyUsers
+acl LProxyUsers external NT_local_group LProxyUsers
+acl password proxy_auth REQUIRED
+
+http_access allow password GProxyUsers
+http_access allow password LProxyUsers
+http_access deny all
+
+In the previous example all validated NT users member of GProxyUsers Global 
+domain group or member of LProxyUsers machine local group are allowed to 
+use the cache.
+
+Groups with spaces in name must be quoted, for example "Domain Users"
+
+NOTES: 
+- The group name comparation is case sensitive, so group name
+  must be specified with same case as in the NT/2000 Domain.
+- Native WIN32 NTLM and Basic Helpers must be used without the
+  -a & -d switches.
+
+Refer to Squid documentation for the more details on squid.conf.
+
+
+=======
+Testing
+=======
+
+I strongly urge that win32_check_group is tested prior to being used in a 
+production environment. It may behave differently on different platforms.
+To test it, run it from the command line. Enter username and group
+pairs separated by a space (username must entered with domain\\username
+syntax). Press ENTER to get an OK or ERR message.
+Make sure pressing <CTRL><D> behaves the same as a carriage return.
+Make sure pressing <CTRL><C> aborts the program.
+
+Test that entering no details does not result in an OK or ERR message.
+Test that entering an invalid username and group results in an ERR message.
+Test that entering an valid username and group results in an OK message.
+
+--
+Serassio Guido
+squidnt@serassio.it
--- /dev/null	Wed Feb 14 01:00:00 2007
+++ squid/helpers/external_acl/win32_group/win32_check_group.c	Wed Feb 14 01:00:01 2007
@@ -0,0 +1,477 @@
+/*
+ * $Id: win32_check_group.c,v 1.1.10.1 2002/07/15 10:35:59 serassio Exp $
+ *
+ * This is a helper for the external ACL interface for Squid Cache
+ * Copyright (C) 2002 Guido Serassio <squidnt@serassio.it>
+ * Based on previous work of Rodrigo Albani de Campos
+ * 
+ * It reads STDIN looking for a username that matches a NT/2000 global
+ * Domain group.
+ * Returns `OK' if the user belongs to the group or `ERR' otherwise, as 
+ * described on http://devel.squid-cache.org/external_acl/config.html
+ * To compile this program, use:
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  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"
+#if defined(_SQUID_CYGWIN_)
+#include <wchar.h>
+#endif
+#undef assert
+#include <assert.h>
+#include <windows.h>
+#include <lm.h>
+#include <ntsecapi.h>
+
+#define BUFSIZE 8192		/* the stdin buffer size */
+int use_global = 0;
+char debug_enabled = 0;
+char *myname;
+pid_t mypid;
+char * machinedomain;
+
+#include "win32_check_group.h"
+
+static char *
+strwordtok(char *buf, char **t)
+{
+    unsigned char *word = NULL;
+    unsigned char *p = (unsigned char *) buf;
+    unsigned char *d;
+    unsigned char ch;
+    int quoted = 0;
+    if (!p)
+	p = (unsigned char *) *t;
+    if (!p)
+	goto error;
+    while (*p && isspace(*p))
+	p++;
+    if (!*p)
+	goto error;
+    word = d = p;
+    while ((ch = *p)) {
+	switch (ch) {
+	case '\\':
+	    p++;
+	    *d++ = ch = *p;
+	    if (ch)
+		p++;
+	    break;
+	case '"':
+	    quoted = !quoted;
+	    p++;
+	default:
+	    if (!quoted && isspace(*p)) {
+		p++;
+		goto done;
+	    }
+	    *d++ = *p++;
+	    break;
+	}
+    }
+  done:
+    *d++ = '\0';
+  error:
+    *t = (char *) p;
+    return (char *) word;
+}
+
+
+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;
+}
+
+
+/* returns 1 on success, 0 on failure */
+int
+Valid_Local_Group(char *UserName, char *Group)
+{
+    int result = 0;
+    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 = 0;
+		    break;
+		}
+		if (wcscmp(pTmpBuf->lgrui0_name, wszGroup) == 0) {
+		    result = 1;
+		    break;
+		}
+		pTmpBuf++;
+		dwTotalCount++;
+	    }
+	}
+    } else
+        result = 0;
+/*
+ * Free the allocated memory.
+ */
+    if (pBuf != NULL)
+	NetApiBufferFree(pBuf);
+    return result;
+}
+
+
+/* returns 1 on success, 0 on failure */
+int
+Valid_Global_Group(char *UserName, char *Group)
+{
+    int result = 0;
+    WCHAR wszUserName[256];	// Unicode user name
+    WCHAR wszGroup[256];	// Unicode Group
+    WCHAR wszLocalDomain[256];	// Unicode Local Domain
+    WCHAR wszUserDomain[256];	// Unicode User Domain
+
+    char NTDomain[256];
+    char *domain_qualify;
+    char User[256];
+
+    LPCWSTR LclDCptr = NULL;
+    LPCWSTR UsrDCptr = NULL;
+    LPGROUP_USERS_INFO_0 pBuf = NULL;
+    LPGROUP_USERS_INFO_0 pTmpBuf;
+    DWORD dwLevel = 0;
+    DWORD dwPrefMaxLen = -1;
+    DWORD dwEntriesRead = 0;
+    DWORD dwTotalEntries = 0;
+    NET_API_STATUS nStatus;
+    DWORD i;
+    DWORD dwTotalCount = 0;
+
+    strcpy(NTDomain, UserName);
+    if ((domain_qualify = strchr(NTDomain, '\\')) == NULL) {
+	return result;
+    } else {
+	strcpy(User, domain_qualify + 1);
+	domain_qualify[0] = '\0';
+	_strlwr(NTDomain);
+    }
+
+/* Convert ANSI User Name and Group to Unicode */
+
+    MultiByteToWideChar(CP_ACP, 0, User,
+	strlen(User) + 1, wszUserName,
+	sizeof(wszUserName) / sizeof(wszUserName[0]));
+    MultiByteToWideChar(CP_ACP, 0, Group,
+	strlen(Group) + 1, wszGroup, sizeof(wszGroup) / sizeof(wszGroup[0]));
+    MultiByteToWideChar(CP_ACP, 0, machinedomain,
+	strlen(machinedomain) + 1, wszLocalDomain, sizeof(wszLocalDomain) / sizeof(wszLocalDomain[0]));
+
+    nStatus = NetGetAnyDCName(
+	NULL,
+	wszLocalDomain,
+	(LPBYTE *) & LclDCptr);
+
+    if (nStatus == NERR_Success) {
+	if (strcmp(NTDomain, machinedomain) != 0) {
+	    MultiByteToWideChar(CP_ACP, 0, NTDomain,
+		strlen(NTDomain) + 1, wszUserDomain, sizeof(wszUserDomain) / sizeof(wszUserDomain[0]));
+	    nStatus = NetGetAnyDCName(
+		LclDCptr,
+		wszUserDomain,
+		(LPBYTE *) & UsrDCptr);
+	    if (nStatus != NERR_Success) {
+		fprintf(stderr, "%s Can't find DC for domain %s\n", myname, NTDomain);
+		if (LclDCptr != NULL)
+		    NetApiBufferFree((LPVOID) LclDCptr);
+		if (UsrDCptr != NULL)
+		    NetApiBufferFree((LPVOID) UsrDCptr);
+		return result;
+	    }
+	} else
+	    UsrDCptr = LclDCptr;
+
+	/*
+	 * 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 = NetUserGetGroups(UsrDCptr,
+		wszUserName,
+		dwLevel,
+		(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 = 0;
+			break;
+		    }
+		    if (wcscmp(pTmpBuf->grui0_name, wszGroup) == 0) {
+			result = 1;
+			break;
+		    }
+		    pTmpBuf++;
+		    dwTotalCount++;
+		}
+	    }
+	} else
+		result = 0;
+    } else {
+	fprintf(stderr, "%s Can't find DC for domain %s\n", myname, machinedomain);
+    }
+    /*
+     * Free the allocated memory.
+     */
+    if (pBuf != NULL)
+        NetApiBufferFree(pBuf);
+    if (LclDCptr != NULL)
+        NetApiBufferFree((LPVOID) LclDCptr);
+    if (UsrDCptr != NULL)
+        NetApiBufferFree((LPVOID) UsrDCptr);
+    return result;
+}
+
+
+void
+process_options(int argc, char *argv[])
+{
+    int opt;
+
+    while (-1 != (opt = getopt(argc, argv, "Gd"))) {
+	switch (opt) {
+	case 'G':
+	    use_global = 1;
+	    break;
+	case 'd':
+	    debug_enabled = 1;
+	    break;
+	default:
+	    fprintf(stderr, "%s Unknown option: -%c. Exiting\n", myname, opt);
+	    exit(1);
+	    break;		/* not reached */
+	}
+    }
+    return;
+}
+
+
+int
+main (int argc, char *argv[])
+{
+    char *p, *t;
+    char buf[BUFSIZE];
+    char *username;
+    char *group;
+
+    if (argc > 0) {	/* should always be true */
+	myname=strrchr(argv[0],'/');
+	if (myname==NULL)
+	    myname=argv[0];
+    } else {
+        myname="(unknown)";
+    }
+    mypid=getpid();
+
+    setbuf(stdout, NULL);
+    setbuf(stderr, NULL);
+
+    /* Check Command Line */
+    process_options(argc, argv);
+
+    if (use_global) {
+        if ((machinedomain = GetDomainName()) == NULL) {
+	    fprintf(stderr, "%s Can't read machine domain\n", myname);
+	    exit(1);
+	}
+	_strlwr(machinedomain);
+    }
+
+    debug("External ACL win32 group helper build " __DATE__ ", " __TIME__
+    " starting up...\n");
+
+    /* Main Loop */
+    while (fgets (buf, BUFSIZE, stdin))
+    {
+	
+	if ((p = strchr(buf, '\n')) != NULL)
+	    *p = '\0';		/* strip \n */
+	if ((p = strchr(buf, '\r')) != NULL)
+	    *p = '\0';		/* strip \r */
+
+	debug("Got '%s' from Squid (length: %d).\n",buf,sizeof(buf));
+
+	username = strwordtok(buf, &t);
+	group = strwordtok(NULL, &t);
+
+	if ((use_global ? Valid_Global_Group(username, group) : Valid_Local_Group(username, group))) {
+	    printf ("OK\n");
+	} else {
+	    printf ("ERR\n");
+	}
+    }
+    return 0;
+}
--- /dev/null	Wed Feb 14 01:00:00 2007
+++ squid/helpers/external_acl/win32_group/win32_check_group.h	Wed Feb 14 01:00:01 2007
@@ -0,0 +1,78 @@
+/*
+ * (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.
+ */
+
+#undef debug
+
+/************* CONFIGURATION ***************/
+/*
+ * define this if you want debugging
+ */
+#ifndef DEBUG
+#define DEBUG
+#endif
+
+/************* 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,"%s[%d](%s:%d): ", myname, mypid, \
+                    ((__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, "%s[%d]: ", myname, mypid);
+	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
+
