--------------------- PatchSet 4446 Date: 2002/07/07 19:51:18 Author: serassio Branch: nt-2_5 Tag: (none) Log: Updated External ACL win32 group helper Members: helpers/external_acl/win32_group/Makefile.am:1.1.8.1->1.1.8.2 helpers/external_acl/win32_group/readme.txt:1.1.8.1->1.1.8.2 helpers/external_acl/win32_group/win32_check_group.c:1.1.8.1->1.1.8.2 port/win32/squid.dsw:1.1.2.15->1.1.2.16 port/win32/squid/buildver.h:1.1.2.3->1.1.2.4 port/win32/win32_check_group/win32_check_group.dsp:1.1.4.1->1.1.4.2 Index: squid/helpers/external_acl/win32_group/Makefile.am =================================================================== RCS file: /cvsroot/squid-sf//squid/helpers/external_acl/win32_group/Attic/Makefile.am,v retrieving revision 1.1.8.1 retrieving revision 1.1.8.2 diff -u -r1.1.8.1 -r1.1.8.2 --- squid/helpers/external_acl/win32_group/Makefile.am 29 Jun 2002 13:21:45 -0000 1.1.8.1 +++ squid/helpers/external_acl/win32_group/Makefile.am 7 Jul 2002 19:51:18 -0000 1.1.8.2 @@ -1,7 +1,7 @@ # # Makefile for the Squid Object Cache server # -# $Id: Makefile.am,v 1.1.8.1 2002/06/29 13:21:45 serassio Exp $ +# $Id: Makefile.am,v 1.1.8.2 2002/07/07 19:51:18 serassio Exp $ # # Uncomment and customize the following to suit your needs: # @@ -11,4 +11,4 @@ win32_check_group_SOURCES = win32_check_group.c -LDADD = -lnetapi32 +LDADD = -lnetapi32 -ladvapi32 Index: squid/helpers/external_acl/win32_group/readme.txt =================================================================== RCS file: /cvsroot/squid-sf//squid/helpers/external_acl/win32_group/Attic/readme.txt,v retrieving revision 1.1.8.1 retrieving revision 1.1.8.2 diff -u -r1.1.8.1 -r1.1.8.2 --- squid/helpers/external_acl/win32_group/readme.txt 29 Jun 2002 13:21:45 -0000 1.1.8.1 +++ squid/helpers/external_acl/win32_group/readme.txt 7 Jul 2002 19:51:18 -0000 1.1.8.2 @@ -1,15 +1,68 @@ -$Id: readme.txt,v 1.1.8.1 2002/06/29 13:21:45 serassio Exp $ This is the readme.txt file for win32_check_group, an external helper fo the External ACL Scheme for Squid. -More information about the External ACL scheme may -be found at http://devel.squid-cache.org/external_acl/ -This program reads one new line terminated argument in the -standard input (the username and group) and tries to match. +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" + +NOTE: the group name comparation is case sensitive, so group name +must be specified with same case as in the NT/2000 Domain. + +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 - Index: squid/helpers/external_acl/win32_group/win32_check_group.c =================================================================== RCS file: /cvsroot/squid-sf//squid/helpers/external_acl/win32_group/Attic/win32_check_group.c,v retrieving revision 1.1.8.1 retrieving revision 1.1.8.2 diff -u -r1.1.8.1 -r1.1.8.2 --- squid/helpers/external_acl/win32_group/win32_check_group.c 29 Jun 2002 13:21:45 -0000 1.1.8.1 +++ squid/helpers/external_acl/win32_group/win32_check_group.c 7 Jul 2002 19:51:18 -0000 1.1.8.2 @@ -1,11 +1,12 @@ /* - * $Id: win32_check_group.c,v 1.1.8.1 2002/06/29 13:21:45 serassio Exp $ + * $Id: win32_check_group.c,v 1.1.8.2 2002/07/07 19:51:18 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 specified group + * 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: @@ -24,28 +25,186 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. * - * Change Log: - * $Log: win32_check_group.c,v $ - * Revision 1.1.8.1 2002/06/29 13:21:45 serassio - * Added NT group external_acl helper - * */ -#include +#include "squid.h" #if defined(_SQUID_CYGWIN_) #include #endif +#undef assert +#include #include -#include -#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_Group(char *UserName, char *Group) +Valid_Local_Group(char *UserName, char *Group) { int result = 0; WCHAR wszUserName[256]; // Unicode user name @@ -65,8 +224,7 @@ /* Convert ANSI User Name and Group to Unicode */ MultiByteToWideChar(CP_ACP, 0, UserName, - strlen(UserName) + 1, wszUserName, - sizeof(wszUserName) / sizeof(wszUserName[0])); + strlen(UserName) + 1, wszUserName, sizeof(wszUserName) / sizeof(wszUserName[0])); MultiByteToWideChar(CP_ACP, 0, Group, strlen(Group) + 1, wszGroup, sizeof(wszGroup) / sizeof(wszGroup[0])); @@ -78,11 +236,15 @@ * 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); + nStatus = NetUserGetLocalGroups( + NULL, + wszUserName, + dwLevel, + dwFlags, + (LPBYTE *) & pBuf, + dwPrefMaxLen, + &dwEntriesRead, + &dwTotalEntries); /* * If the call succeeds, */ @@ -103,7 +265,7 @@ } } } else - result = 0; + result = 0; /* * Free the allocated memory. */ @@ -112,30 +274,185 @@ return result; } -static void -usage (char *program) + +/* returns 1 on success, 0 on failure */ +int +Valid_Global_Group(char *UserName, char *Group) { - fprintf (stderr, "Usage: %s\n\n", program); + 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; + char *p, *t; char buf[BUFSIZE]; - char username[BUFSIZE]; - char group[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(); - /* make standard output line buffered */ - setvbuf (stdout, NULL, _IOLBF, 0); + setbuf(stdout, NULL); + setbuf(stderr, NULL); /* Check Command Line */ - if (argc != 1) { - usage (argv[0]); - exit (1); + 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)) { @@ -145,12 +462,12 @@ if ((p = strchr(buf, '\r')) != NULL) *p = '\0'; /* strip \r */ - /* Clear any current settings */ - username[0] = '\0'; - group[0] = '\0'; - sscanf(buf, "%s %s", username, group); /* Extract parameters */ + debug("Got '%s' from Squid (length: %d).\n",buf,sizeof(buf)); + + username = strwordtok(buf, &t); + group = strwordtok(NULL, &t); - if (Valid_Group(username, group)) { + if ((use_global ? Valid_Global_Group(username, group) : Valid_Local_Group(username, group))) { printf ("OK\n"); } else { printf ("ERR\n"); Index: squid/port/win32/squid.dsw =================================================================== RCS file: /cvsroot/squid-sf//squid/port/win32/Attic/squid.dsw,v retrieving revision 1.1.2.15 retrieving revision 1.1.2.16 diff -u -r1.1.2.15 -r1.1.2.16 --- squid/port/win32/squid.dsw 29 Jun 2002 10:42:04 -0000 1.1.2.15 +++ squid/port/win32/squid.dsw 7 Jul 2002 19:51:18 -0000 1.1.2.16 @@ -515,6 +515,9 @@ Package=<4> {{{ + Begin Project Dependency + Project_Dep_Name libmiscutil + End Project Dependency }}} ############################################################################### Index: squid/port/win32/squid/buildver.h =================================================================== RCS file: /cvsroot/squid-sf//squid/port/win32/squid/Attic/buildver.h,v retrieving revision 1.1.2.3 retrieving revision 1.1.2.4 diff -u -r1.1.2.3 -r1.1.2.4 --- squid/port/win32/squid/buildver.h 29 Jun 2002 13:14:52 -0000 1.1.2.3 +++ squid/port/win32/squid/buildver.h 7 Jul 2002 19:51:18 -0000 1.1.2.4 @@ -1,4 +1,4 @@ -#define FILEVER 2,5,8,6 -#define PRODUCTVER 2,5,8,6 -#define STRFILEVER "2, 5, 8, 6\0" -#define STRPRODUCTVER "2, 5, 8, 6\0" +#define FILEVER 2,5,8,40 +#define PRODUCTVER 2,5,8,40 +#define STRFILEVER "2, 5, 8, 40\0" +#define STRPRODUCTVER "2, 5, 8, 40\0" Index: squid/port/win32/win32_check_group/win32_check_group.dsp =================================================================== RCS file: /cvsroot/squid-sf//squid/port/win32/win32_check_group/Attic/win32_check_group.dsp,v retrieving revision 1.1.4.1 retrieving revision 1.1.4.2 diff -u -r1.1.4.1 -r1.1.4.2 --- squid/port/win32/win32_check_group/win32_check_group.dsp 29 Jun 2002 13:21:45 -0000 1.1.4.1 +++ squid/port/win32/win32_check_group/win32_check_group.dsp 7 Jul 2002 19:51:18 -0000 1.1.4.2 @@ -42,7 +42,7 @@ # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c -# ADD CPP /nologo /G6 /W3 /GX /O2 /I "../../../include" /I "../../../src" /I "../include" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /G6 /MT /W3 /GX /O2 /I "../../../include" /I "../../../src" /I "../include" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c # ADD BASE RSC /l 0x410 /d "NDEBUG" # ADD RSC /l 0x410 /d "NDEBUG" BSC32=bscmake.exe @@ -50,7 +50,7 @@ # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 -# ADD LINK32 netapi32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 netapi32.lib Advapi32.lib /nologo /subsystem:console /machine:I386 !ELSEIF "$(CFG)" == "win32_check_group - Win32 Debug" @@ -66,7 +66,7 @@ # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c -# ADD CPP /nologo /G6 /W3 /Gm /GX /ZI /Od /I "../../../include" /I "../../../src" /I "../include" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /G6 /MTd /W3 /Gm /GX /ZI /Od /I "../../../include" /I "../../../src" /I "../include" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c # ADD BASE RSC /l 0x410 /d "_DEBUG" # ADD RSC /l 0x410 /d "_DEBUG" BSC32=bscmake.exe @@ -74,7 +74,7 @@ # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept -# ADD LINK32 netapi32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 netapi32.lib Advapi32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept !ENDIF @@ -93,6 +93,10 @@ # Begin Group "Header Files" # PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=..\..\..\helpers\external_acl\win32_group\win32_check_group.h +# End Source File # End Group # Begin Group "Resource Files"