]> git.ipfire.org Git - thirdparty/squid.git/commitdiff
Author: Serassio Guido <serassio@squid-cache.org>
authorAmos Jeffries <squid3@treenet.co.nz>
Sun, 14 Feb 2010 05:36:46 +0000 (18:36 +1300)
committerAmos Jeffries <squid3@treenet.co.nz>
Sun, 14 Feb 2010 05:36:46 +0000 (18:36 +1300)
Windows port: Update mswin_check_ad_group to version 2.0

The global groups support was rewritten, now is based on ADSI.
New Features:
 - support for Domain Local, Domain Global ad Universal groups
 - full group nesting support

This helper, like the previous version, can be compiled only using
Microsoft Visual Studio because some needed library are not available
on MSYS+MinGW or Cygwin.

helpers/external_acl/mswin_ad_group/Makefile.am
helpers/external_acl/mswin_ad_group/mswin_check_ad_group.c
helpers/external_acl/mswin_ad_group/mswin_check_ad_group.h
helpers/external_acl/mswin_ad_group/readme.txt

index 049a00809084df1909bfac299c5e7a952643b598..5d8e9ba27c8ec9cc5a16c46171f9b1aebf3ec60f 100644 (file)
@@ -6,21 +6,30 @@
 #  Uncomment and customize the following to suit your needs:
 #
 
-
 libexec_PROGRAMS = mswin_check_ad_group
 
 mswin_check_ad_group_SOURCES = mswin_check_ad_group.c mswin_check_ad_group.h
 
 INCLUDES = \
        -I$(top_srcdir) \
+       -I$(top_srcdir)/src \
        -I$(top_srcdir)/include
 
+#
+# Currently activeds and adsiid libraries are not available on MinGW or Cygwin,
+# so the following library list is just a placeholder for future MinGW/Cygwin releases.
+# This helper can be compiled only using Microsoft Visual Studio.
+#
 LDADD = \
        $(top_builddir)/compat/libcompat.la \
        -L$(top_builddir)/lib -lmiscutil \
        -lnetapi32 \
        -ladvapi32 \
+       -lole32 \
+       -loleaut32 \
        -lntdll \
+       -lactiveds \
+       -ladsiid \
        $(XTRA_LIBS)
 
 EXTRA_DIST = readme.txt
index f0edab38b53a2f864ddbabc50468d20e8a164bc3..e1603d262a4387f4c2a324d08411c036f463f3be 100644 (file)
@@ -2,7 +2,7 @@
  * mswin_check_ad_group: lookup group membership in a Windows
  * Active Directory domain
  *
- * (C)2008 Guido Serassio - Acme Consulting S.r.l.
+ * (C)2008-2009 Guido Serassio - Acme Consulting S.r.l.
  *
  * Authors:
  *  Guido Serassio <guido.serassio@acmeconsulting.it>
  *
  * History:
  *
+ * Version 2.1
+ * 20-09-2009 Guido Serassio
+ *              Added explicit Global Catalog query
+ *
+ * Version 2.0
+ * 20-07-2009 Guido Serassio
+ *              Global groups support rewritten, now is based on ADSI.
+ *              New Features:
+ *              - support for Domain Local, Domain Global ad Universal 
+ *                groups
+ *              - full group nesting support
  * Version 1.0
  * 02-05-2008 Guido Serassio
  *              First release, based on mswin_check_lm_group.
  *
  * This is a helper for the external ACL interface for Squid Cache
- *
+ * 
  * It reads from the standard input the domain username and a list of
  * groups and tries to match it against the groups membership of the
  * specified username.
@@ -66,12 +77,23 @@ int _wcsicmp(const wchar_t *, const wchar_t *);
 #undef assert
 #include <assert.h>
 #include <windows.h>
+#include <objbase.h>
+#include <initguid.h>
+#include <adsiid.h>
+#include <iads.h>
+#include <adshlp.h>
+#include <adserr.h>
 #include <lm.h>
-#include <dsgetdc.h>
 #include <dsrole.h>
+#include <sddl.h>
 
 #include "util.h"
 
+enum ADSI_PATH {
+    LDAP_MODE,
+    GC_MODE
+} ADSI_Path;
+
 #define BUFSIZE 8192           /* the stdin buffer size */
 int use_global = 0;
 char debug_enabled = 0;
@@ -81,9 +103,203 @@ char *machinedomain;
 int use_case_insensitive_compare = 0;
 char *DefaultDomain = NULL;
 const char NTV_VALID_DOMAIN_SEPARATOR[] = "\\/";
+int numberofgroups = 0;
+int WIN32_COM_initialized = 0;
+char *WIN32_ErrorMessage = NULL;
+wchar_t **User_Groups;
+int User_Groups_Count = 0;
 
 #include "mswin_check_ad_group.h"
 
+wchar_t *My_NameTranslate(wchar_t *, int, int);
+char *Get_WIN32_ErrorMessage(HRESULT);
+
+
+void
+CloseCOM(void)
+{
+    if (WIN32_COM_initialized == 1)
+       CoUninitialize();
+}
+
+
+HRESULT
+GetLPBYTEtoOctetString(VARIANT * pVar, LPBYTE * ppByte)
+{
+    HRESULT hr = E_FAIL;
+    void HUGEP *pArray;
+    long lLBound, lUBound, cElements;
+
+    if ((!pVar) || (!ppByte))
+       return E_INVALIDARG;
+    if ((pVar->n1.n2.vt) != (VT_UI1 | VT_ARRAY))
+       return E_INVALIDARG;
+
+    hr = SafeArrayGetLBound(V_ARRAY(pVar), 1, &lLBound);
+    hr = SafeArrayGetUBound(V_ARRAY(pVar), 1, &lUBound);
+
+    cElements = lUBound - lLBound + 1;
+    hr = SafeArrayAccessData(V_ARRAY(pVar), &pArray);
+    if (SUCCEEDED(hr)) {
+       LPBYTE pTemp = (LPBYTE) pArray;
+       *ppByte = (LPBYTE) CoTaskMemAlloc(cElements);
+       if (*ppByte)
+           memcpy(*ppByte, pTemp, cElements);
+       else
+           hr = E_OUTOFMEMORY;
+    }
+    SafeArrayUnaccessData(V_ARRAY(pVar));
+
+    return hr;
+}
+
+
+wchar_t *
+Get_primaryGroup(IADs * pUser)
+{
+    HRESULT hr;
+    VARIANT var;
+    unsigned User_primaryGroupID;
+    char tmpSID[SECURITY_MAX_SID_SIZE * 2];
+    wchar_t *wc = NULL, *result = NULL;
+    int wcsize;
+
+    VariantInit(&var);
+
+    /* Get the primaryGroupID property */
+    hr = pUser->lpVtbl->Get(pUser, L"primaryGroupID", &var);
+    if (SUCCEEDED(hr)) {
+       User_primaryGroupID = var.n1.n2.n3.uintVal;
+    } else {
+       debug("Get_primaryGroup: cannot get primaryGroupID, ERROR: %s\n", Get_WIN32_ErrorMessage(hr));
+       VariantClear(&var);
+       return result;
+    }
+    VariantClear(&var);
+
+    /*Get the objectSid property */
+    hr = pUser->lpVtbl->Get(pUser, L"objectSid", &var);
+    if (SUCCEEDED(hr)) {
+       PSID pObjectSID;
+       LPBYTE pByte = NULL;
+       char *szSID = NULL;
+       hr = GetLPBYTEtoOctetString(&var, &pByte);
+
+       pObjectSID = (PSID) pByte;
+
+       /* Convert SID to string. */
+       ConvertSidToStringSid(pObjectSID, &szSID);
+       CoTaskMemFree(pByte);
+
+       *(strrchr(szSID, '-') + 1) = '\0';
+       sprintf(tmpSID, "%s%u", szSID, User_primaryGroupID);
+
+       wcsize = MultiByteToWideChar(CP_ACP, 0, tmpSID, -1, wc, 0);
+       wc = (wchar_t *) xmalloc(wcsize * sizeof(wchar_t));
+       MultiByteToWideChar(CP_ACP, 0, tmpSID, -1, wc, wcsize);
+       LocalFree(szSID);
+
+       result = My_NameTranslate(wc, ADS_NAME_TYPE_SID_OR_SID_HISTORY_NAME, ADS_NAME_TYPE_1779);
+       safe_free(wc);
+
+       if (result == NULL)
+           debug("Get_primaryGroup: cannot get DN for %s.\n", tmpSID);
+       else
+           debug("Get_primaryGroup: Primary group DN: %S.\n", result);
+    } else
+       debug("Get_primaryGroup: cannot get objectSid, ERROR: %s\n", Get_WIN32_ErrorMessage(hr));
+    VariantClear(&var);
+    return result;
+}
+
+
+char *
+Get_WIN32_ErrorMessage(HRESULT hr)
+{
+    FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
+       FORMAT_MESSAGE_IGNORE_INSERTS,
+       NULL,
+       hr,
+       MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+       (LPTSTR) & WIN32_ErrorMessage,
+       0,
+       NULL);
+    return WIN32_ErrorMessage;
+}
+
+
+wchar_t *
+My_NameTranslate(wchar_t * name, int in_format, int out_format)
+{
+    IADsNameTranslate *pNto;
+    HRESULT hr;
+    BSTR bstr;
+    wchar_t *wc;
+
+    if (WIN32_COM_initialized == 0) {
+       hr = CoInitialize(NULL);
+       if (FAILED(hr)) {
+           debug("My_NameTranslate: cannot initialize COM interface, ERROR: %s\n", Get_WIN32_ErrorMessage(hr));
+           /* This is a fatal error */
+           exit(1);
+       }
+       WIN32_COM_initialized = 1;
+    }
+    hr = CoCreateInstance(&CLSID_NameTranslate,
+       NULL,
+       CLSCTX_INPROC_SERVER,
+       &IID_IADsNameTranslate,
+       (void **) &pNto);
+    if (FAILED(hr)) {
+       debug("My_NameTranslate: cannot create COM instance, ERROR: %s\n", Get_WIN32_ErrorMessage(hr));
+       /* This is a fatal error */
+       exit(1);
+    }
+    hr = pNto->lpVtbl->Init(pNto, ADS_NAME_INITTYPE_GC, L"");
+    if (FAILED(hr)) {
+       debug("My_NameTranslate: cannot initialise NameTranslate API, ERROR: %s\n", Get_WIN32_ErrorMessage(hr));
+       pNto->lpVtbl->Release(pNto);
+       /* This is a fatal error */
+       exit(1);
+    }
+    hr = pNto->lpVtbl->Set(pNto, in_format, name);
+    if (FAILED(hr)) {
+       debug("My_NameTranslate: cannot set translate of %S, ERROR: %s\n", name, Get_WIN32_ErrorMessage(hr));
+       pNto->lpVtbl->Release(pNto);
+       return NULL;
+    }
+    hr = pNto->lpVtbl->Get(pNto, out_format, &bstr);
+    if (FAILED(hr)) {
+       debug("My_NameTranslate: cannot get translate of %S, ERROR: %s\n", name, Get_WIN32_ErrorMessage(hr));
+       pNto->lpVtbl->Release(pNto);
+       return NULL;
+    }
+    debug("My_NameTranslate: %S translated to %S\n", name, bstr);
+
+    wc = (wchar_t *) xmalloc((wcslen(bstr) + 1) * sizeof(wchar_t));
+    wcscpy(wc, bstr);
+    SysFreeString(bstr);
+    pNto->lpVtbl->Release(pNto);
+    return wc;
+}
+
+
+wchar_t *
+GetLDAPPath(wchar_t * Base_DN, int query_mode)
+{
+    wchar_t *wc;
+
+    wc = (wchar_t *) xmalloc((wcslen(Base_DN) + 8) * sizeof(wchar_t));
+
+    if (query_mode == LDAP_MODE)
+    wcscpy(wc, L"LDAP://");
+    else
+       wcscpy(wc, L"GC://");
+    wcscat(wc, Base_DN);
+
+    return wc;
+}
+
 
 char *
 GetDomainName(void)
@@ -93,50 +309,89 @@ GetDomainName(void)
     DWORD netret;
 
     if ((netret = DsRoleGetPrimaryDomainInformation(NULL, DsRolePrimaryDomainInfoBasic, (PBYTE *) & pDSRoleInfo) == ERROR_SUCCESS)) {
-        /*
-         * Check the machine role.
-         */
-
-        if ((pDSRoleInfo->MachineRole == DsRole_RoleMemberWorkstation) ||
-                (pDSRoleInfo->MachineRole == DsRole_RoleMemberServer) ||
-                (pDSRoleInfo->MachineRole == DsRole_RoleBackupDomainController) ||
-                (pDSRoleInfo->MachineRole == DsRole_RolePrimaryDomainController)) {
-
-            size_t len = wcslen(pDSRoleInfo->DomainNameFlat);
-
-            /* allocate buffer for str + null termination */
-            safe_free(DomainName);
-            DomainName = (char *) xmalloc(len + 1);
-            if (DomainName == NULL)
-                return NULL;
-
-            /* copy unicode buffer */
-            WideCharToMultiByte(CP_ACP, 0, pDSRoleInfo->DomainNameFlat, -1, DomainName, len, NULL, NULL);
-
-            /* add null termination */
-            DomainName[len] = '\0';
-
-            /*
-             * Member of a domain. Display it in debug mode.
-             */
-            debug("Member of Domain %s\n", DomainName);
-            debug("Into forest %S\n", pDSRoleInfo->DomainForestName);
-
-        } else {
-            debug("Not a Domain member\n");
-        }
+       /* 
+        * Check the machine role.
+        */
+
+       if ((pDSRoleInfo->MachineRole == DsRole_RoleMemberWorkstation) ||
+           (pDSRoleInfo->MachineRole == DsRole_RoleMemberServer) ||
+           (pDSRoleInfo->MachineRole == DsRole_RoleBackupDomainController) ||
+           (pDSRoleInfo->MachineRole == DsRole_RolePrimaryDomainController)) {
+
+           size_t len = wcslen(pDSRoleInfo->DomainNameFlat);
+
+           /* allocate buffer for str + null termination */
+           safe_free(DomainName);
+           DomainName = (char *) xmalloc(len + 1);
+
+           /* copy unicode buffer */
+           WideCharToMultiByte(CP_ACP, 0, pDSRoleInfo->DomainNameFlat, -1, DomainName, len, NULL, NULL);
+
+           /* add null termination */
+           DomainName[len] = '\0';
+
+           /* 
+            * Member of a domain. Display it in debug mode.
+            */
+           debug("Member of Domain %s\n", DomainName);
+           debug("Into forest %S\n", pDSRoleInfo->DomainForestName);
+
+       } else {
+           debug("Not a Domain member\n");
+       }
     } else
-        debug("DsRoleGetPrimaryDomainInformation Error: %ld\n", netret);
+       debug("GetDomainName: ERROR DsRoleGetPrimaryDomainInformation returned: %s\n", Get_WIN32_ErrorMessage(netret));
 
     /*
      * Free the allocated memory.
      */
     if (pDSRoleInfo != NULL)
-        DsRoleFreeMemory(pDSRoleInfo);
+       DsRoleFreeMemory(pDSRoleInfo);
 
     return DomainName;
 }
 
+
+int
+add_User_Group(wchar_t * Group)
+{
+    wchar_t **array;
+
+    if (User_Groups_Count == 0) {
+       User_Groups = (wchar_t **) xmalloc(sizeof(wchar_t *));
+       *User_Groups = NULL;
+       User_Groups_Count++;
+    }
+    array = User_Groups;
+    while (*array) {
+       if (wcscmp(Group, *array) == 0)
+           return 0;
+       array++;
+    }
+    User_Groups = (wchar_t **) xrealloc(User_Groups, sizeof(wchar_t *) * (User_Groups_Count + 1));
+    User_Groups[User_Groups_Count] = NULL;
+    User_Groups[User_Groups_Count - 1] = (wchar_t *) xmalloc((wcslen(Group) + 1) * sizeof(wchar_t));
+    wcscpy(User_Groups[User_Groups_Count - 1], Group);
+    User_Groups_Count++;
+
+    return 1;
+}
+
+
+/* returns 0 on match, -1 if no match */
+static int
+wccmparray(const wchar_t * str, const wchar_t ** array)
+{
+    while (*array) {
+       debug("Windows group: %S, Squid group: %S\n", str, *array);
+       if (wcscmp(str, *array) == 0)
+           return 0;
+       array++;
+    }
+    return -1;
+}
+
+
 /* returns 0 on match, -1 if no match */
 static int
 wcstrcmparray(const wchar_t * str, const char **array)
@@ -144,23 +399,147 @@ wcstrcmparray(const wchar_t * str, const char **array)
     WCHAR wszGroup[GNLEN + 1]; // Unicode Group
 
     while (*array) {
-        MultiByteToWideChar(CP_ACP, 0, *array,
-                            strlen(*array) + 1, wszGroup, sizeof(wszGroup) / sizeof(wszGroup[0]));
-        debug("Windows group: %S, Squid group: %S\n", str, wszGroup);
-        if ((use_case_insensitive_compare ? _wcsicmp(str, wszGroup) : wcscmp(str, wszGroup)) == 0)
-            return 0;
-        array++;
+       MultiByteToWideChar(CP_ACP, 0, *array,
+           strlen(*array) + 1, wszGroup, sizeof(wszGroup) / sizeof(wszGroup[0]));
+       debug("Windows group: %S, Squid group: %S\n", str, wszGroup);
+       if ((use_case_insensitive_compare ? _wcsicmp(str, wszGroup) : wcscmp(str, wszGroup)) == 0)
+           return 0;
+       array++;
     }
     return -1;
 }
 
+
+HRESULT
+Recursive_Memberof(IADs * pObj)
+{
+    VARIANT var;
+    long lBound, uBound;
+    HRESULT hr;
+
+    VariantInit(&var);
+    hr = pObj->lpVtbl->Get(pObj, L"memberOf", &var);
+    if (SUCCEEDED(hr)) {
+       if (VT_BSTR == var.n1.n2.vt) {
+           if (add_User_Group(var.n1.n2.n3.bstrVal)) {
+               wchar_t *Group_Path;
+               IADs *pGrp;
+
+               Group_Path = GetLDAPPath(var.n1.n2.n3.bstrVal, GC_MODE);
+               hr = ADsGetObject(Group_Path, &IID_IADs, (void **) &pGrp);
+               if (SUCCEEDED(hr)) {
+                   hr = Recursive_Memberof(pGrp);
+                   pGrp->lpVtbl->Release(pGrp);
+                   safe_free(Group_Path);
+                   Group_Path = GetLDAPPath(var.n1.n2.n3.bstrVal, LDAP_MODE);
+               hr = ADsGetObject(Group_Path, &IID_IADs, (void **) &pGrp);
+               if (SUCCEEDED(hr)) {
+                   hr = Recursive_Memberof(pGrp);
+                   pGrp->lpVtbl->Release(pGrp);
+               } else
+                   debug("Recursive_Memberof: ERROR ADsGetObject for %S failed: %s\n", Group_Path, Get_WIN32_ErrorMessage(hr));
+               } else
+                   debug("Recursive_Memberof: ERROR ADsGetObject for %S failed: %s\n", Group_Path, Get_WIN32_ErrorMessage(hr));
+               safe_free(Group_Path);
+           }
+       } else {
+           if (SUCCEEDED(SafeArrayGetLBound(V_ARRAY(&var), 1, &lBound)) &&
+               SUCCEEDED(SafeArrayGetUBound(V_ARRAY(&var), 1, &uBound))) {
+               VARIANT elem;
+               while (lBound <= uBound) {
+                   hr = SafeArrayGetElement(V_ARRAY(&var), &lBound, &elem);
+                   if (SUCCEEDED(hr)) {
+                       if (add_User_Group(elem.n1.n2.n3.bstrVal)) {
+                           wchar_t *Group_Path;
+                           IADs *pGrp;
+
+                           Group_Path = GetLDAPPath(elem.n1.n2.n3.bstrVal, GC_MODE);
+                           hr = ADsGetObject(Group_Path, &IID_IADs, (void **) &pGrp);
+                           if (SUCCEEDED(hr)) {
+                               hr = Recursive_Memberof(pGrp);
+                               pGrp->lpVtbl->Release(pGrp);
+                               safe_free(Group_Path);
+                               Group_Path = GetLDAPPath(elem.n1.n2.n3.bstrVal, LDAP_MODE);
+                               hr = ADsGetObject(Group_Path, &IID_IADs, (void **) &pGrp);
+                               if (SUCCEEDED(hr)) {
+                                   hr = Recursive_Memberof(pGrp);
+                                   pGrp->lpVtbl->Release(pGrp);
+                                   safe_free(Group_Path);
+                               } else
+                                   debug("Recursive_Memberof: ERROR ADsGetObject for %S failed: %s\n", Group_Path, Get_WIN32_ErrorMessage(hr));
+                           } else
+                               debug("Recursive_Memberof: ERROR ADsGetObject for %S failed: %s\n", Group_Path, Get_WIN32_ErrorMessage(hr));
+                           safe_free(Group_Path);
+                       }
+                       VariantClear(&elem);
+                   } else {
+                       debug("Recursive_Memberof: ERROR SafeArrayGetElement failed: %s\n", Get_WIN32_ErrorMessage(hr));
+                       VariantClear(&elem);
+                   }
+                   ++lBound;
+               }
+           } else
+               debug("Recursive_Memberof: ERROR SafeArrayGetxBound failed: %s\n", Get_WIN32_ErrorMessage(hr));
+       }
+       VariantClear(&var);
+    } else {
+       if (hr != E_ADS_PROPERTY_NOT_FOUND)
+           debug("Recursive_Memberof: ERROR getting memberof attribute: %s\n", Get_WIN32_ErrorMessage(hr));
+    }
+    return hr;
+}
+
+
+static wchar_t **
+build_groups_DN_array(const char **array, char *userdomain)
+{
+    wchar_t *wc = NULL;
+    int wcsize;
+    int source_group_format;
+    char Group[GNLEN + 1];
+
+    wchar_t **wc_array, **entry;
+
+    entry = wc_array = (wchar_t **) xmalloc((numberofgroups + 1) * sizeof(wchar_t *));
+
+    while (*array) {
+       if (strchr(*array, '/') != NULL) {
+           strncpy(Group, *array, GNLEN);
+           source_group_format = ADS_NAME_TYPE_CANONICAL;
+       } else {
+           source_group_format = ADS_NAME_TYPE_NT4;
+           if (strchr(*array, '\\') == NULL) {
+               strcpy(Group, userdomain);
+               strcat(Group, "\\");
+               strncat(Group, *array, GNLEN - sizeof(userdomain) - 1);
+           } else
+               strncpy(Group, *array, GNLEN);
+       }
+
+       wcsize = MultiByteToWideChar(CP_ACP, 0, Group, -1, wc, 0);
+       wc = (wchar_t *) xmalloc(wcsize * sizeof(wchar_t));
+       MultiByteToWideChar(CP_ACP, 0, Group, -1, wc, wcsize);
+       *entry = My_NameTranslate(wc, source_group_format, ADS_NAME_TYPE_1779);
+       safe_free(wc);
+       array++;
+       if (*entry == NULL) {
+           debug("build_groups_DN_array: cannot get DN for '%s'.\n", Group);
+           continue;
+       }
+       entry++;
+    }
+    *entry = NULL;
+    return wc_array;
+}
+
+
 /* returns 1 on success, 0 on failure */
 int
 Valid_Local_Groups(char *UserName, const char **Groups)
 {
     int result = 0;
     char *Domain_Separator;
-    WCHAR wszUserName[UNLEN + 1];      // Unicode user name
+    WCHAR wszUserName[UNLEN + 1];      /* Unicode user name */
 
     LPLOCALGROUP_USERS_INFO_0 pBuf;
     LPLOCALGROUP_USERS_INFO_0 pTmpBuf;
@@ -175,58 +554,60 @@ Valid_Local_Groups(char *UserName, const char **Groups)
     LPBYTE pBufTmp = NULL;
 
     if ((Domain_Separator = strchr(UserName, '/')) != NULL)
-        *Domain_Separator = '\\';
+       *Domain_Separator = '\\';
 
     debug("Valid_Local_Groups: checking group membership of '%s'.\n", UserName);
 
-    /* Convert ANSI User Name and Group to Unicode */
+/* 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]));
 
     /*
-     * Call the NetUserGetLocalGroups function
+     * 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
+     * 
+     * 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,
-                                    &pBufTmp,
-                                    dwPrefMaxLen,
-                                    &dwEntriesRead,
-                                    &dwTotalEntries);
+       wszUserName,
+       dwLevel,
+       dwFlags,
+       &pBufTmp,
+       dwPrefMaxLen,
+       &dwEntriesRead,
+       &dwTotalEntries);
     pBuf = (LPLOCALGROUP_USERS_INFO_0) pBufTmp;
     /*
      * 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 (wcstrcmparray(pTmpBuf->lgrui0_name, Groups) == 0) {
-                    result = 1;
-                    break;
-                }
-                pTmpBuf++;
-                dwTotalCount++;
-            }
-        }
-    } else
-        result = 0;
-    /*
-     * Free the allocated memory.
-     */
+       if ((pTmpBuf = pBuf) != NULL) {
+           for (i = 0; i < dwEntriesRead; i++) {
+               assert(pTmpBuf != NULL);
+               if (pTmpBuf == NULL) {
+                   result = 0;
+                   break;
+               }
+               if (wcstrcmparray(pTmpBuf->lgrui0_name, Groups) == 0) {
+                   result = 1;
+                   break;
+               }
+               pTmpBuf++;
+               dwTotalCount++;
+           }
+       }
+    } else {
+       debug("Valid_Local_Groups: ERROR NetUserGetLocalGroups returned: %s\n", Get_WIN32_ErrorMessage(nStatus));
+       result = 0;
+    }
+/*
+ * Free the allocated memory.
+ */
     if (pBuf != NULL)
-        NetApiBufferFree(pBuf);
+       NetApiBufferFree(pBuf);
     return result;
 }
 
@@ -236,125 +617,132 @@ int
 Valid_Global_Groups(char *UserName, const char **Groups)
 {
     int result = 0;
-    WCHAR wszUserName[UNLEN + 1];      // Unicode user name
-
-    WCHAR wszDomainControllerName[UNCLEN + 1];
-
+    WCHAR wszUser[DNLEN + UNLEN + 2];  /* Unicode user name */
     char NTDomain[DNLEN + UNLEN + 2];
+
     char *domain_qualify = NULL;
-    char User[UNLEN + 1];
+    char User[DNLEN + UNLEN + 2];
     size_t j;
 
-    LPGROUP_USERS_INFO_0 pUsrBuf = NULL;
-    LPGROUP_USERS_INFO_0 pTmpBuf;
-    PDOMAIN_CONTROLLER_INFO pDCInfo = NULL;
-    DWORD dwLevel = 0;
-    DWORD dwPrefMaxLen = -1;
-    DWORD dwEntriesRead = 0;
-    DWORD dwTotalEntries = 0;
-    NET_API_STATUS nStatus;
-    DWORD i;
-    DWORD dwTotalCount = 0;
-    LPBYTE pBufTmp = NULL;
+    wchar_t *User_DN, *User_LDAP_path, *User_PrimaryGroup;
+    wchar_t **wszGroups, **tmp;
+    IADs *pUser;
+    HRESULT hr;
 
     strncpy(NTDomain, UserName, sizeof(NTDomain));
 
     for (j = 0; j < strlen(NTV_VALID_DOMAIN_SEPARATOR); j++) {
-        if ((domain_qualify = strchr(NTDomain, NTV_VALID_DOMAIN_SEPARATOR[j])) != NULL)
-            break;
+       if ((domain_qualify = strchr(NTDomain, NTV_VALID_DOMAIN_SEPARATOR[j])) != NULL)
+           break;
     }
     if (domain_qualify == NULL) {
-        strcpy(User, NTDomain);
-        strcpy(NTDomain, DefaultDomain);
+       strncpy(User, DefaultDomain, DNLEN);
+       strcat(User, "\\");
+       strncat(User, UserName, UNLEN);
+       strncpy(NTDomain, DefaultDomain, DNLEN);
     } else {
-        strcpy(User, domain_qualify + 1);
-        domain_qualify[0] = '\0';
-        strlwr(NTDomain);
+       domain_qualify[0] = '\\';
+       strncpy(User, NTDomain, DNLEN + UNLEN + 2);
+       domain_qualify[0] = '\0';
     }
 
-    debug("Valid_Global_Groups: checking group membership of '%s\\%s'.\n", NTDomain, User);
+    debug("Valid_Global_Groups: checking group membership of '%s'.\n", User);
 
     /* Convert ANSI User Name to Unicode */
 
     MultiByteToWideChar(CP_ACP, 0, User,
-                        strlen(User) + 1, wszUserName,
-                        sizeof(wszUserName) / sizeof(wszUserName[0]));
-
-    /* Query AD for a DC */
+       strlen(User) + 1, wszUser,
+       sizeof(wszUser) / sizeof(wszUser[0]));
 
-    if (DsGetDcName(NULL, NTDomain, NULL, NULL, DS_IS_FLAT_NAME | DS_RETURN_FLAT_NAME, &pDCInfo) != NO_ERROR) {
-        fprintf(stderr, "%s DsGetDcName() failed.'\n", myname);
-        if (pDCInfo != NULL)
-            NetApiBufferFree(pDCInfo);
-        return result;
+    /* Get CN of User */
+    if ((User_DN = My_NameTranslate(wszUser, ADS_NAME_TYPE_NT4, ADS_NAME_TYPE_1779)) == NULL) {
+       debug("Valid_Global_Groups: cannot get DN for '%s'.\n", User);
+       return result;
     }
-    /* Convert ANSI Domain Controller Name to Unicode */
-
-    MultiByteToWideChar(CP_ACP, 0, pDCInfo->DomainControllerName,
-                        strlen(pDCInfo->DomainControllerName) + 1, wszDomainControllerName,
-                        sizeof(wszDomainControllerName) / sizeof(wszDomainControllerName[0]));
-
-    debug("Using '%S' as DC for '%s' user's domain.\n", wszDomainControllerName, NTDomain);
-    debug("DC Active Directory Site is %s\n", pDCInfo->DcSiteName);
-    debug("Machine Active Directory Site is %s\n", pDCInfo->ClientSiteName);
+    wszGroups = build_groups_DN_array(Groups, NTDomain);
+
+    User_LDAP_path = GetLDAPPath(User_DN, GC_MODE);
+
+    hr = ADsGetObject(User_LDAP_path, &IID_IADs, (void **) &pUser);
+    if (SUCCEEDED(hr)) {
+       wchar_t *User_PrimaryGroup_Path;
+       IADs *pGrp;
+
+       User_PrimaryGroup = Get_primaryGroup(pUser);
+       if (User_PrimaryGroup == NULL)
+           debug("Valid_Global_Groups: cannot get Primary Group for '%s'.\n", User);
+       else {
+           add_User_Group(User_PrimaryGroup);
+           User_PrimaryGroup_Path = GetLDAPPath(User_PrimaryGroup, GC_MODE);
+           hr = ADsGetObject(User_PrimaryGroup_Path, &IID_IADs, (void **) &pGrp);
+           if (SUCCEEDED(hr)) {
+               hr = Recursive_Memberof(pGrp);
+               pGrp->lpVtbl->Release(pGrp);
+               safe_free(User_PrimaryGroup_Path);
+               User_PrimaryGroup_Path = GetLDAPPath(User_PrimaryGroup, LDAP_MODE);
+           hr = ADsGetObject(User_PrimaryGroup_Path, &IID_IADs, (void **) &pGrp);
+           if (SUCCEEDED(hr)) {
+               hr = Recursive_Memberof(pGrp);
+               pGrp->lpVtbl->Release(pGrp);
+           } else
+               debug("Valid_Global_Groups: ADsGetObject for %S failed, ERROR: %s\n", User_PrimaryGroup_Path, Get_WIN32_ErrorMessage(hr));
+           } else
+               debug("Valid_Global_Groups: ADsGetObject for %S failed, ERROR: %s\n", User_PrimaryGroup_Path, Get_WIN32_ErrorMessage(hr));
+           safe_free(User_PrimaryGroup_Path);
+       }
+       hr = Recursive_Memberof(pUser);
+       pUser->lpVtbl->Release(pUser);
+       safe_free(User_LDAP_path);
+       User_LDAP_path = GetLDAPPath(User_DN, LDAP_MODE);
+       hr = ADsGetObject(User_LDAP_path, &IID_IADs, (void **) &pUser);
+       if (SUCCEEDED(hr)) {
+           hr = Recursive_Memberof(pUser);
+           pUser->lpVtbl->Release(pUser);
+       } else
+           debug("Valid_Global_Groups: ADsGetObject for %S failed, ERROR: %s\n", User_LDAP_path, Get_WIN32_ErrorMessage(hr));
+
+       tmp = User_Groups;
+       while (*tmp) {
+           if (wccmparray(*tmp, wszGroups) == 0) {
+               result = 1;
+               break;
+           }
+           tmp++;
+       }
+    } else
+       debug("Valid_Global_Groups: ADsGetObject for %S failed, ERROR: %s\n", User_LDAP_path, Get_WIN32_ErrorMessage(hr));
+
+    safe_free(User_DN);
+    safe_free(User_LDAP_path);
+    safe_free(User_PrimaryGroup);
+    tmp = wszGroups;
+    while (*tmp) {
+       safe_free(*tmp);
+       tmp++;
+    }
+    safe_free(wszGroups);
 
-    /*
-     * Call the NetUserGetGroups function
-     * specifying information level 0.
-     */
-    dwLevel = 0;
-    pBufTmp = NULL;
-    nStatus = NetUserGetGroups(wszDomainControllerName,
-                               wszUserName,
-                               dwLevel,
-                               &pBufTmp,
-                               dwPrefMaxLen,
-                               &dwEntriesRead,
-                               &dwTotalEntries);
-    pUsrBuf = (LPGROUP_USERS_INFO_0) pBufTmp;
-    /*
-     * If the call succeeds,
-     */
-    if (nStatus == NERR_Success) {
-        if ((pTmpBuf = pUsrBuf) != NULL) {
-            for (i = 0; i < dwEntriesRead; i++) {
-                assert(pTmpBuf != NULL);
-                if (pTmpBuf == NULL) {
-                    result = 0;
-                    break;
-                }
-                if (wcstrcmparray(pTmpBuf->grui0_name, Groups) == 0) {
-                    result = 1;
-                    break;
-                }
-                pTmpBuf++;
-                dwTotalCount++;
-            }
-        }
-    } else {
-        result = 0;
-        fprintf(stderr, "%s NetUserGetGroups() failed.'\n", myname);
+    tmp = User_Groups;
+    while (*tmp) {
+       safe_free(*tmp);
+       tmp++;
     }
-    /*
-     * Free the allocated memory.
-     */
-    if (pUsrBuf != NULL)
-        NetApiBufferFree(pUsrBuf);
-    if (pDCInfo != NULL)
-        NetApiBufferFree((LPVOID) pDCInfo);
+    safe_free(User_Groups);
+    User_Groups_Count = 0;
+
     return result;
 }
 
 static void
 usage(char *program)
 {
-    fprintf(stderr, "Usage: %s [-D domain][-G][-P][-c][-d][-h]\n"
-            " -D    default user Domain\n"
-            " -G    enable Domain Global group mode\n"
-            " -c    use case insensitive compare\n"
-            " -d    enable debugging\n"
-            " -h    this message\n",
-            program);
+    fprintf(stderr, "Usage: %s [-D domain][-G][-c][-d][-h]\n"
+       " -D    default user Domain\n"
+       " -G    enable Active Directory Global group mode\n"
+       " -c    use case insensitive compare (local mode only)\n"
+       " -d    enable debugging\n"
+       " -h    this message\n",
+       program);
 }
 
 void
@@ -364,32 +752,32 @@ process_options(int argc, char *argv[])
 
     opterr = 0;
     while (-1 != (opt = getopt(argc, argv, "D:Gcdh"))) {
-        switch (opt) {
-        case 'D':
-            DefaultDomain = xstrndup(optarg, DNLEN + 1);
-            strlwr(DefaultDomain);
-            break;
-        case 'G':
-            use_global = 1;
-            break;
-        case 'c':
-            use_case_insensitive_compare = 1;
-            break;
-        case 'd':
-            debug_enabled = 1;
-            break;
-        case 'h':
-            usage(argv[0]);
-            exit(0);
-        case '?':
-            opt = optopt;
-            /* fall thru to default */
-        default:
-            fprintf(stderr, "%s Unknown option: -%c. Exiting\n", myname, opt);
-            usage(argv[0]);
-            exit(1);
-            break;             /* not reached */
-        }
+       switch (opt) {
+       case 'D':
+           DefaultDomain = xstrndup(optarg, DNLEN + 1);
+           strlwr(DefaultDomain);
+           break;
+       case 'G':
+           use_global = 1;
+           break;
+       case 'c':
+           use_case_insensitive_compare = 1;
+           break;
+       case 'd':
+           debug_enabled = 1;
+           break;
+       case 'h':
+           usage(argv[0]);
+           exit(0);
+       case '?':
+           opt = optopt;
+           /* fall thru to default */
+       default:
+           fprintf(stderr, "%s Unknown option: -%c. Exiting\n", myname, opt);
+           usage(argv[0]);
+           exit(1);
+           break;              /* not reached */
+       }
     }
     return;
 }
@@ -407,11 +795,11 @@ main(int argc, char *argv[])
     int n;
 
     if (argc > 0) {            /* should always be true */
-        myname = strrchr(argv[0], '/');
-        if (myname == NULL)
-            myname = argv[0];
+       myname = strrchr(argv[0], '/');
+       if (myname == NULL)
+           myname = argv[0];
     } else {
-        myname = "(unknown)";
+       myname = "(unknown)";
     }
     mypid = getpid();
 
@@ -422,64 +810,68 @@ main(int argc, char *argv[])
     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);
-        if (!DefaultDomain)
-            DefaultDomain = xstrdup(machinedomain);
+       if ((machinedomain = GetDomainName()) == NULL) {
+           fprintf(stderr, "%s Can't read machine domain\n", myname);
+           exit(1);
+       }
+       strlwr(machinedomain);
+       if (!DefaultDomain)
+           DefaultDomain = xstrdup(machinedomain);
     }
     debug("External ACL win32 group helper build " __DATE__ ", " __TIME__
-          " starting up...\n");
+       " starting up...\n");
     if (use_global)
-        debug("Domain Global group mode enabled using '%s' as default domain.\n", DefaultDomain);
+       debug("Domain Global group mode enabled using '%s' as default domain.\n", DefaultDomain);
     if (use_case_insensitive_compare)
-        debug("Warning: running in case insensitive mode !!!\n");
+       debug("Warning: running in case insensitive mode !!!\n");
+
+    atexit(CloseCOM);
+
 
     /* Main Loop */
     while (fgets(buf, sizeof(buf), stdin)) {
-        if (NULL == strchr(buf, '\n')) {
-            /* too large message received.. skip and deny */
-            fprintf(stderr, "%s: ERROR: Too large: %s\n", argv[0], buf);
-            while (fgets(buf, sizeof(buf), stdin)) {
-                fprintf(stderr, "%s: ERROR: Too large..: %s\n", argv[0], buf);
-                if (strchr(buf, '\n') != NULL)
-                    break;
-            }
-            goto error;
-        }
-        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, strlen(buf));
-
-        if (buf[0] == '\0') {
-            fprintf(stderr, "Invalid Request\n");
-            goto error;
-        }
-        username = strtok(buf, " ");
-        for (n = 0; (group = strtok(NULL, " ")) != NULL; n++) {
-            rfc1738_unescape(group);
-            groups[n] = group;
-        }
-        groups[n] = NULL;
-
-        if (NULL == username) {
-            fprintf(stderr, "Invalid Request\n");
-            goto error;
-        }
-        rfc1738_unescape(username);
-
-        if ((use_global ? Valid_Global_Groups(username, groups) : Valid_Local_Groups(username, groups))) {
-            printf("OK\n");
-        } else {
-error:
-            printf("ERR\n");
-        }
-        err = 0;
+       if (NULL == strchr(buf, '\n')) {
+           /* too large message received.. skip and deny */
+           fprintf(stderr, "%s: ERROR: Too large: %s\n", argv[0], buf);
+           while (fgets(buf, sizeof(buf), stdin)) {
+               fprintf(stderr, "%s: ERROR: Too large..: %s\n", argv[0], buf);
+               if (strchr(buf, '\n') != NULL)
+                   break;
+           }
+           goto error;
+       }
+       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, strlen(buf));
+
+       if (buf[0] == '\0') {
+           fprintf(stderr, "Invalid Request\n");
+           goto error;
+       }
+       username = strtok(buf, " ");
+       for (n = 0; (group = strtok(NULL, " ")) != NULL; n++) {
+           rfc1738_unescape(group);
+           groups[n] = group;
+       }
+       groups[n] = NULL;
+       numberofgroups = n;
+
+       if (NULL == username) {
+           fprintf(stderr, "Invalid Request\n");
+           goto error;
+       }
+       rfc1738_unescape(username);
+
+       if ((use_global ? Valid_Global_Groups(username, groups) : Valid_Local_Groups(username, groups))) {
+           SEND("OK");
+       } else {
+         error:
+           SEND("ERR");
+       }
+       err = 0;
     }
     return 0;
 }
index 041879ed2164257c94c4c19b1bc0df137f1392c4..5b77b2e4da231557a7b48237990eb9bd6e3b3f38 100644 (file)
 #include <unistd.h>
 static char *__foo;
 extern char debug_enabled;
+extern char *WIN32_ErrorMessage;
 #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); }
+                    fprintf(stderr,X); \
+                    if (WIN32_ErrorMessage != NULL){ \
+                    LocalFree(WIN32_ErrorMessage); \
+                    WIN32_ErrorMessage = NULL; \
+                    } }
 #else /* DEBUG */
 #define debug(X...)            /* */
 #endif /* DEBUG */
 #else /* __GNUC__ */
 extern char debug_enabled;
+extern char *WIN32_ErrorMessage;
 static void
 debug(char *format,...)
 {
 #ifdef DEBUG
 #ifdef _SQUID_MSWIN_
     if (debug_enabled) {
-        va_list args;
+       va_list args;
 
-        va_start(args, format);
-        fprintf(stderr, "%s[%d]: ", myname, mypid);
-        vfprintf(stderr, format, args);
-        fprintf(stderr, "\n");
-        va_end(args);
+       va_start(args, format);
+       fprintf(stderr, "%s[%d]: ", myname, mypid);
+       vfprintf(stderr, format, args);
+       fprintf(stderr, "\n");
+       va_end(args);
+       if (WIN32_ErrorMessage != NULL) {
+           LocalFree(WIN32_ErrorMessage);
+           WIN32_ErrorMessage = NULL;
+       }
     }
 #endif /* _SQUID_MSWIN_ */
 #endif /* DEBUG */
index af8cc8598fbd5640710a8576385c407c370efc7d..0b2ecb66cebdd526290108df92072af5d0d8961d 100644 (file)
@@ -1,5 +1,5 @@
 
-This is the readme.txt file for mswin_check_ad_group, an external
+This is the readme.txt file for mswin_check_ad_group 2.0, an external
 helper for the External ACL Scheme for Squid.
 
 
@@ -9,18 +9,36 @@ It reads from the standard input the domain username and a list of groups
 and tries to match it against the groups membership of the specified
 username.
 
+Two running mode are available:
+
+- Local mode:
+membership is checked against machine's local groups, cannot be used when
+running on a Domain Controller.
+
+- Active Directory Global mode:
+membership is checked against the whole Active Directory Forest of the
+machine where Squid is running.
+
 The minimal Windows version needed to run mswin_check_ad_group is
 a Windows 2000 SP4 member of an Active Directory Domain.
 
+When running in Active Directory Global mode, all types of Active Directory 
+security groups are supported:
+- Domain Global
+- Domain Local from user's domain
+- Universal
+and Active Directory group nesting is fully supported.
+
+
 ==============
 Program Syntax
 ==============
 
-mswin_check_lm_group [-D domain][-G][-c][-d][-h]
+mswin_check_ad_group [-D domain][-G][-c][-d][-h]
 
 -D domain specify the default user's domain
--G        start helper in Domain Global Group mode
--c        use case insensitive compare
+-G        start helper in Active Directory Global mode
+-c        use case insensitive compare (local mode only)
 -d        enable debugging
 -h        this message
 
@@ -29,10 +47,24 @@ mswin_check_lm_group [-D domain][-G][-c][-d][-h]
 squid.conf usage
 ================
 
+When running in Active Directory Global mode, the AD Group can be specified using the
+following syntax:
+
+1. Plain NT4 Group Name
+2. Full NT4 Group Name
+3. Active Directory Canonical name
+
+As Example:
+1. Proxy-Users
+2. MYDOMAIN\Proxy-Users
+3. mydomain.local/Groups/Proxy-Users
+
+When using Plain NT4 Group Name, the Group is searched in the user's domain.  
+
 external_acl_type AD_global_group %LOGIN c:/squid/libexec/mswin_check_ad_group.exe -G
 external_acl_type NT_local_group %LOGIN c:/squid/libexec/mswin_check_ad_group.exe
 
-acl GProxyUsers external AD_global_group GProxyUsers
+acl GProxyUsers external AD_global_group MYDOMAIN\GProxyUsers
 acl LProxyUsers external NT_local_group LProxyUsers
 acl password proxy_auth REQUIRED
 
@@ -40,7 +72,7 @@ http_access allow password GProxyUsers
 http_access allow password LProxyUsers
 http_access deny all
 
-In the previous example all validated AD users member of GProxyUsers Global 
+In the previous example all validated AD users member of MYDOMAIN\GProxyUsers 
 domain group or member of LProxyUsers machine local group are allowed to 
 use the cache.
 
@@ -55,8 +87,12 @@ and the DomainUsers files will contain only the following line:
 "Domain Users"
 
 NOTES: 
-- The standard group name comparison is case sensitive, so group name
-  must be specified with same case as in the Active Directory Domain.
+- When running in Active Directory Global mode, for better performance,
+  all Domain Controllers of the Active Directory forest should be configured
+  as Global Catalog.
+- When running in local mode, the standard group name comparison is case
+  sensitive, so group name must be specified with same case as in the
+  local SAM database.
   It's possible to enable case insensitive group name comparison (-c),
   but on some not-english locales, the results can be unexpected.
 - Native WIN32 NTLM and Basic Helpers must be used without the