--------------------- 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 behaves the same as a carriage return. +Make sure pressing 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 + * 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 +#endif +#undef assert +#include +#include +#include +#include + +#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 + * 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 + +/* 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,"%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 +