ext_ad_group_acl.cc
Go to the documentation of this file.
1 /*
2  * Copyright (C) 1996-2017 The Squid Software Foundation and contributors
3  *
4  * Squid software is distributed under GPLv2+ license and includes
5  * contributions from numerous individuals and organizations.
6  * Please see the COPYING and CONTRIBUTORS files for details.
7  */
8 
9 /*
10  * ext_ad_group_acl: lookup group membership in a Windows
11  * Active Directory domain
12  *
13  * (C)2008-2009 Guido Serassio - Acme Consulting S.r.l.
14  *
15  * Authors:
16  * Guido Serassio <guido.serassio@acmeconsulting.it>
17  * Acme Consulting S.r.l., Italy <http://www.acmeconsulting.it>
18  *
19  * With contributions from others mentioned in the change history section
20  * below.
21  *
22  * Based on mswin_check_lm_group by Guido Serassio.
23  *
24  * Dependencies: Windows 2000 SP4 and later.
25  *
26  * This program is free software; you can redistribute it and/or modify
27  * it under the terms of the GNU General Public License as published by
28  * the Free Software Foundation; either version 2 of the License, or
29  * (at your option) any later version.
30  *
31  * This program is distributed in the hope that it will be useful,
32  * but WITHOUT ANY WARRANTY; without even the implied warranty of
33  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
34  * GNU General Public License for more details.
35  *
36  * You should have received a copy of the GNU General Public License
37  * along with this program; if not, write to the Free Software
38  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
39  *
40  * History:
41  *
42  * Version 2.1
43  * 20-09-2009 Guido Serassio
44  * Added explicit Global Catalog query
45  *
46  * Version 2.0
47  * 20-07-2009 Guido Serassio
48  * Global groups support rewritten, now is based on ADSI.
49  * New Features:
50  * - support for Domain Local, Domain Global ad Universal
51  * groups
52  * - full group nesting support
53  * Version 1.0
54  * 02-05-2008 Guido Serassio
55  * First release, based on mswin_check_lm_group.
56  *
57  * This is a helper for the external ACL interface for Squid Cache
58  *
59  * It reads from the standard input the domain username and a list of
60  * groups and tries to match it against the groups membership of the
61  * specified username.
62  *
63  * Returns `OK' if the user belongs to a group or `ERR' otherwise, as
64  * described on http://devel.squid-cache.org/external_acl/config.html
65  *
66  */
67 
68 #include "squid.h"
70 #include "include/util.h"
71 
72 #if _SQUID_CYGWIN_
73 #include <wchar.h>
74 int _wcsicmp(const wchar_t *, const wchar_t *);
75 #endif
76 
77 #undef assert
78 #include <cassert>
79 #include <cctype>
80 #include <cstring>
81 #if HAVE_GETOPT_H
82 #include <getopt.h>
83 #endif
84 #include <windows.h>
85 #include <objbase.h>
86 #include <initguid.h>
87 #include <adsiid.h>
88 #include <iads.h>
89 #include <adshlp.h>
90 #include <adserr.h>
91 #include <lm.h>
92 #include <dsrole.h>
93 #include <sddl.h>
94 
95 enum ADSI_PATH {
98 } ADSI_Path;
99 
100 int use_global = 0;
102 pid_t mypid;
106 const char NTV_VALID_DOMAIN_SEPARATOR[] = "\\/";
110 wchar_t **User_Groups;
112 
113 wchar_t *My_NameTranslate(wchar_t *, int, int);
114 char *Get_WIN32_ErrorMessage(HRESULT);
115 
116 void
117 CloseCOM(void)
118 {
119  if (WIN32_COM_initialized == 1)
120  CoUninitialize();
121 }
122 
123 HRESULT
124 GetLPBYTEtoOctetString(VARIANT * pVar, LPBYTE * ppByte)
125 {
126  HRESULT hr = E_FAIL;
127  void HUGEP *pArray;
128  long lLBound, lUBound, cElements;
129 
130  if ((!pVar) || (!ppByte))
131  return E_INVALIDARG;
132  if ((pVar->n1.n2.vt) != (VT_UI1 | VT_ARRAY))
133  return E_INVALIDARG;
134 
135  hr = SafeArrayGetLBound(V_ARRAY(pVar), 1, &lLBound);
136  hr = SafeArrayGetUBound(V_ARRAY(pVar), 1, &lUBound);
137 
138  cElements = lUBound - lLBound + 1;
139  hr = SafeArrayAccessData(V_ARRAY(pVar), &pArray);
140  if (SUCCEEDED(hr)) {
141  LPBYTE pTemp = (LPBYTE) pArray;
142  *ppByte = (LPBYTE) CoTaskMemAlloc(cElements);
143  if (*ppByte)
144  memcpy(*ppByte, pTemp, cElements);
145  else
146  hr = E_OUTOFMEMORY;
147  }
148  SafeArrayUnaccessData(V_ARRAY(pVar));
149 
150  return hr;
151 }
152 
153 wchar_t *
154 Get_primaryGroup(IADs * pUser)
155 {
156  HRESULT hr;
157  VARIANT var;
158  unsigned User_primaryGroupID;
159  char tmpSID[SECURITY_MAX_SID_SIZE * 2];
160  wchar_t *wc = NULL, *result = NULL;
161  int wcsize;
162 
163  VariantInit(&var);
164 
165  /* Get the primaryGroupID property */
166  hr = pUser->lpVtbl->Get(pUser, L"primaryGroupID", &var);
167  if (SUCCEEDED(hr)) {
168  User_primaryGroupID = var.n1.n2.n3.uintVal;
169  } else {
170  debug("Get_primaryGroup: cannot get primaryGroupID, ERROR: %s\n", Get_WIN32_ErrorMessage(hr));
171  VariantClear(&var);
172  return result;
173  }
174  VariantClear(&var);
175 
176  /*Get the objectSid property */
177  hr = pUser->lpVtbl->Get(pUser, L"objectSid", &var);
178  if (SUCCEEDED(hr)) {
179  PSID pObjectSID;
180  LPBYTE pByte = NULL;
181  char *szSID = NULL;
182  hr = GetLPBYTEtoOctetString(&var, &pByte);
183 
184  pObjectSID = (PSID) pByte;
185 
186  /* Convert SID to string. */
187  ConvertSidToStringSid(pObjectSID, &szSID);
188  CoTaskMemFree(pByte);
189 
190  *(strrchr(szSID, '-') + 1) = '\0';
191  snprintf(tmpSID, sizeof(tmpSID)-1, "%s%u", szSID, User_primaryGroupID);
192 
193  wcsize = MultiByteToWideChar(CP_ACP, 0, tmpSID, -1, wc, 0);
194  wc = (wchar_t *) xmalloc(wcsize * sizeof(wchar_t));
195  MultiByteToWideChar(CP_ACP, 0, tmpSID, -1, wc, wcsize);
196  LocalFree(szSID);
197 
198  result = My_NameTranslate(wc, ADS_NAME_TYPE_SID_OR_SID_HISTORY_NAME, ADS_NAME_TYPE_1779);
199  safe_free(wc);
200 
201  if (result == NULL)
202  debug("Get_primaryGroup: cannot get DN for %s.\n", tmpSID);
203  else
204  debug("Get_primaryGroup: Primary group DN: %S.\n", result);
205  } else
206  debug("Get_primaryGroup: cannot get objectSid, ERROR: %s\n", Get_WIN32_ErrorMessage(hr));
207  VariantClear(&var);
208  return result;
209 }
210 
211 char *
213 {
214  FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
215  FORMAT_MESSAGE_IGNORE_INSERTS,
216  NULL,
217  hr,
218  MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
219  (LPTSTR) & WIN32_ErrorMessage,
220  0,
221  NULL);
222  return WIN32_ErrorMessage;
223 }
224 
225 wchar_t *
226 My_NameTranslate(wchar_t * name, int in_format, int out_format)
227 {
228  IADsNameTranslate *pNto;
229  HRESULT hr;
230  BSTR bstr;
231  wchar_t *wc;
232 
233  if (WIN32_COM_initialized == 0) {
234  hr = CoInitialize(NULL);
235  if (FAILED(hr)) {
236  debug("My_NameTranslate: cannot initialize COM interface, ERROR: %s\n", Get_WIN32_ErrorMessage(hr));
237  /* This is a fatal error */
238  exit(EXIT_FAILURE);
239  }
241  }
242  hr = CoCreateInstance(&CLSID_NameTranslate,
243  NULL,
244  CLSCTX_INPROC_SERVER,
245  &IID_IADsNameTranslate,
246  (void **) &pNto);
247  if (FAILED(hr)) {
248  debug("My_NameTranslate: cannot create COM instance, ERROR: %s\n", Get_WIN32_ErrorMessage(hr));
249  /* This is a fatal error */
250  exit(EXIT_FAILURE);
251  }
252  hr = pNto->lpVtbl->Init(pNto, ADS_NAME_INITTYPE_GC, L"");
253  if (FAILED(hr)) {
254  debug("My_NameTranslate: cannot initialise NameTranslate API, ERROR: %s\n", Get_WIN32_ErrorMessage(hr));
255  pNto->lpVtbl->Release(pNto);
256  /* This is a fatal error */
257  exit(EXIT_FAILURE);
258  }
259  hr = pNto->lpVtbl->Set(pNto, in_format, name);
260  if (FAILED(hr)) {
261  debug("My_NameTranslate: cannot set translate of %S, ERROR: %s\n", name, Get_WIN32_ErrorMessage(hr));
262  pNto->lpVtbl->Release(pNto);
263  return NULL;
264  }
265  hr = pNto->lpVtbl->Get(pNto, out_format, &bstr);
266  if (FAILED(hr)) {
267  debug("My_NameTranslate: cannot get translate of %S, ERROR: %s\n", name, Get_WIN32_ErrorMessage(hr));
268  pNto->lpVtbl->Release(pNto);
269  return NULL;
270  }
271  debug("My_NameTranslate: %S translated to %S\n", name, bstr);
272 
273  wc = (wchar_t *) xmalloc((wcslen(bstr) + 1) * sizeof(wchar_t));
274  wcscpy(wc, bstr);
275  SysFreeString(bstr);
276  pNto->lpVtbl->Release(pNto);
277  return wc;
278 }
279 
280 wchar_t *
281 GetLDAPPath(wchar_t * Base_DN, int query_mode)
282 {
283  wchar_t *wc;
284 
285  wc = (wchar_t *) xmalloc((wcslen(Base_DN) + 8) * sizeof(wchar_t));
286 
287  if (query_mode == LDAP_MODE)
288  wcscpy(wc, L"LDAP://");
289  else
290  wcscpy(wc, L"GC://");
291  wcscat(wc, Base_DN);
292 
293  return wc;
294 }
295 
296 char *
298 {
299  static char *DomainName = NULL;
300  PDSROLE_PRIMARY_DOMAIN_INFO_BASIC pDSRoleInfo;
301  DWORD netret;
302 
303  if ((netret = DsRoleGetPrimaryDomainInformation(NULL, DsRolePrimaryDomainInfoBasic, (PBYTE *) & pDSRoleInfo) == ERROR_SUCCESS)) {
304  /*
305  * Check the machine role.
306  */
307 
308  if ((pDSRoleInfo->MachineRole == DsRole_RoleMemberWorkstation) ||
309  (pDSRoleInfo->MachineRole == DsRole_RoleMemberServer) ||
310  (pDSRoleInfo->MachineRole == DsRole_RoleBackupDomainController) ||
311  (pDSRoleInfo->MachineRole == DsRole_RolePrimaryDomainController)) {
312 
313  size_t len = wcslen(pDSRoleInfo->DomainNameFlat);
314 
315  /* allocate buffer for str + null termination */
316  safe_free(DomainName);
317  DomainName = (char *) xmalloc(len + 1);
318 
319  /* copy unicode buffer */
320  WideCharToMultiByte(CP_ACP, 0, pDSRoleInfo->DomainNameFlat, -1, DomainName, len, NULL, NULL);
321 
322  /* add null termination */
323  DomainName[len] = '\0';
324 
325  /*
326  * Member of a domain. Display it in debug mode.
327  */
328  debug("Member of Domain %s\n", DomainName);
329  debug("Into forest %S\n", pDSRoleInfo->DomainForestName);
330 
331  } else {
332  debug("Not a Domain member\n");
333  }
334  } else
335  debug("GetDomainName: ERROR DsRoleGetPrimaryDomainInformation returned: %s\n", Get_WIN32_ErrorMessage(netret));
336 
337  /*
338  * Free the allocated memory.
339  */
340  if (pDSRoleInfo != NULL)
341  DsRoleFreeMemory(pDSRoleInfo);
342 
343  return DomainName;
344 }
345 
346 int
347 add_User_Group(wchar_t * Group)
348 {
349  wchar_t **array;
350 
351  if (User_Groups_Count == 0) {
352  User_Groups = (wchar_t **) xmalloc(sizeof(wchar_t *));
353  *User_Groups = NULL;
355  }
356  array = User_Groups;
357  while (*array) {
358  if (wcscmp(Group, *array) == 0)
359  return 0;
360  ++array;
361  }
362  User_Groups = (wchar_t **) xrealloc(User_Groups, sizeof(wchar_t *) * (User_Groups_Count + 1));
364  User_Groups[User_Groups_Count - 1] = (wchar_t *) xmalloc((wcslen(Group) + 1) * sizeof(wchar_t));
365  wcscpy(User_Groups[User_Groups_Count - 1], Group);
367 
368  return 1;
369 }
370 
371 /* returns 0 on match, -1 if no match */
372 static int
373 wccmparray(const wchar_t * str, const wchar_t ** array)
374 {
375  while (*array) {
376  debug("Windows group: %S, Squid group: %S\n", str, *array);
377  if (wcscmp(str, *array) == 0)
378  return 0;
379  ++array;
380  }
381  return -1;
382 }
383 
384 /* returns 0 on match, -1 if no match */
385 static int
386 wcstrcmparray(const wchar_t * str, const char **array)
387 {
388  WCHAR wszGroup[GNLEN + 1]; // Unicode Group
389 
390  while (*array) {
391  MultiByteToWideChar(CP_ACP, 0, *array,
392  strlen(*array) + 1, wszGroup, sizeof(wszGroup) / sizeof(wszGroup[0]));
393  debug("Windows group: %S, Squid group: %S\n", str, wszGroup);
394  if ((use_case_insensitive_compare ? _wcsicmp(str, wszGroup) : wcscmp(str, wszGroup)) == 0)
395  return 0;
396  ++array;
397  }
398  return -1;
399 }
400 
401 HRESULT
402 Recursive_Memberof(IADs * pObj)
403 {
404  VARIANT var;
405  long lBound, uBound;
406  HRESULT hr;
407 
408  VariantInit(&var);
409  hr = pObj->lpVtbl->Get(pObj, L"memberOf", &var);
410  if (SUCCEEDED(hr)) {
411  if (VT_BSTR == var.n1.n2.vt) {
412  if (add_User_Group(var.n1.n2.n3.bstrVal)) {
413  wchar_t *Group_Path;
414  IADs *pGrp;
415 
416  Group_Path = GetLDAPPath(var.n1.n2.n3.bstrVal, GC_MODE);
417  hr = ADsGetObject(Group_Path, &IID_IADs, (void **) &pGrp);
418  if (SUCCEEDED(hr)) {
419  hr = Recursive_Memberof(pGrp);
420  pGrp->lpVtbl->Release(pGrp);
421  safe_free(Group_Path);
422  Group_Path = GetLDAPPath(var.n1.n2.n3.bstrVal, LDAP_MODE);
423  hr = ADsGetObject(Group_Path, &IID_IADs, (void **) &pGrp);
424  if (SUCCEEDED(hr)) {
425  hr = Recursive_Memberof(pGrp);
426  pGrp->lpVtbl->Release(pGrp);
427  } else
428  debug("Recursive_Memberof: ERROR ADsGetObject for %S failed: %s\n", Group_Path, Get_WIN32_ErrorMessage(hr));
429  } else
430  debug("Recursive_Memberof: ERROR ADsGetObject for %S failed: %s\n", Group_Path, Get_WIN32_ErrorMessage(hr));
431  safe_free(Group_Path);
432  }
433  } else {
434  if (SUCCEEDED(SafeArrayGetLBound(V_ARRAY(&var), 1, &lBound)) &&
435  SUCCEEDED(SafeArrayGetUBound(V_ARRAY(&var), 1, &uBound))) {
436  VARIANT elem;
437  while (lBound <= uBound) {
438  hr = SafeArrayGetElement(V_ARRAY(&var), &lBound, &elem);
439  if (SUCCEEDED(hr)) {
440  if (add_User_Group(elem.n1.n2.n3.bstrVal)) {
441  wchar_t *Group_Path;
442  IADs *pGrp;
443 
444  Group_Path = GetLDAPPath(elem.n1.n2.n3.bstrVal, GC_MODE);
445  hr = ADsGetObject(Group_Path, &IID_IADs, (void **) &pGrp);
446  if (SUCCEEDED(hr)) {
447  hr = Recursive_Memberof(pGrp);
448  pGrp->lpVtbl->Release(pGrp);
449  safe_free(Group_Path);
450  Group_Path = GetLDAPPath(elem.n1.n2.n3.bstrVal, LDAP_MODE);
451  hr = ADsGetObject(Group_Path, &IID_IADs, (void **) &pGrp);
452  if (SUCCEEDED(hr)) {
453  hr = Recursive_Memberof(pGrp);
454  pGrp->lpVtbl->Release(pGrp);
455  safe_free(Group_Path);
456  } else
457  debug("Recursive_Memberof: ERROR ADsGetObject for %S failed: %s\n", Group_Path, Get_WIN32_ErrorMessage(hr));
458  } else
459  debug("Recursive_Memberof: ERROR ADsGetObject for %S failed: %s\n", Group_Path, Get_WIN32_ErrorMessage(hr));
460  safe_free(Group_Path);
461  }
462  VariantClear(&elem);
463  } else {
464  debug("Recursive_Memberof: ERROR SafeArrayGetElement failed: %s\n", Get_WIN32_ErrorMessage(hr));
465  VariantClear(&elem);
466  }
467  ++lBound;
468  }
469  } else
470  debug("Recursive_Memberof: ERROR SafeArrayGetxBound failed: %s\n", Get_WIN32_ErrorMessage(hr));
471  }
472  VariantClear(&var);
473  } else {
474  if (hr != E_ADS_PROPERTY_NOT_FOUND)
475  debug("Recursive_Memberof: ERROR getting memberof attribute: %s\n", Get_WIN32_ErrorMessage(hr));
476  }
477  return hr;
478 }
479 
480 static wchar_t **
481 build_groups_DN_array(const char **array, char *userdomain)
482 {
483  wchar_t *wc = NULL;
484  int wcsize;
485  int source_group_format;
486  char Group[GNLEN + 1];
487 
488  wchar_t **wc_array, **entry;
489 
490  entry = wc_array = (wchar_t **) xmalloc((numberofgroups + 1) * sizeof(wchar_t *));
491 
492  while (*array) {
493  if (strchr(*array, '/') != NULL) {
494  strncpy(Group, *array, GNLEN);
495  source_group_format = ADS_NAME_TYPE_CANONICAL;
496  } else {
497  source_group_format = ADS_NAME_TYPE_NT4;
498  if (strchr(*array, '\\') == NULL) {
499  strcpy(Group, userdomain);
500  strcat(Group, "\\");
501  strncat(Group, *array, GNLEN - sizeof(userdomain) - 1);
502  } else
503  strncpy(Group, *array, GNLEN);
504  }
505 
506  wcsize = MultiByteToWideChar(CP_ACP, 0, Group, -1, wc, 0);
507  wc = (wchar_t *) xmalloc(wcsize * sizeof(wchar_t));
508  MultiByteToWideChar(CP_ACP, 0, Group, -1, wc, wcsize);
509  *entry = My_NameTranslate(wc, source_group_format, ADS_NAME_TYPE_1779);
510  safe_free(wc);
511  ++array;
512  if (*entry == NULL) {
513  debug("build_groups_DN_array: cannot get DN for '%s'.\n", Group);
514  continue;
515  }
516  ++entry;
517  }
518  *entry = NULL;
519  return wc_array;
520 }
521 
522 /* returns 1 on success, 0 on failure */
523 int
524 Valid_Local_Groups(char *UserName, const char **Groups)
525 {
526  int result = 0;
527  char *Domain_Separator;
528  WCHAR wszUserName[UNLEN + 1]; /* Unicode user name */
529 
530  LPLOCALGROUP_USERS_INFO_0 pBuf;
531  LPLOCALGROUP_USERS_INFO_0 pTmpBuf;
532  DWORD dwLevel = 0;
533  DWORD dwFlags = LG_INCLUDE_INDIRECT;
534  DWORD dwPrefMaxLen = -1;
535  DWORD dwEntriesRead = 0;
536  DWORD dwTotalEntries = 0;
537  NET_API_STATUS nStatus;
538  DWORD i;
539  DWORD dwTotalCount = 0;
540  LPBYTE pBufTmp = NULL;
541 
542  if ((Domain_Separator = strchr(UserName, '/')) != NULL)
543  *Domain_Separator = '\\';
544 
545  debug("Valid_Local_Groups: checking group membership of '%s'.\n", UserName);
546 
547  /* Convert ANSI User Name and Group to Unicode */
548 
549  MultiByteToWideChar(CP_ACP, 0, UserName,
550  strlen(UserName) + 1, wszUserName, sizeof(wszUserName) / sizeof(wszUserName[0]));
551 
552  /*
553  * Call the NetUserGetLocalGroups function
554  * specifying information level 0.
555  *
556  * The LG_INCLUDE_INDIRECT flag specifies that the
557  * function should also return the names of the local
558  * groups in which the user is indirectly a member.
559  */
560  nStatus = NetUserGetLocalGroups(NULL,
561  wszUserName,
562  dwLevel,
563  dwFlags,
564  &pBufTmp,
565  dwPrefMaxLen,
566  &dwEntriesRead,
567  &dwTotalEntries);
568  pBuf = (LPLOCALGROUP_USERS_INFO_0) pBufTmp;
569  /*
570  * If the call succeeds,
571  */
572  if (nStatus == NERR_Success) {
573  if ((pTmpBuf = pBuf) != NULL) {
574  for (i = 0; i < dwEntriesRead; ++i) {
575  assert(pTmpBuf != NULL);
576  if (pTmpBuf == NULL) {
577  result = 0;
578  break;
579  }
580  if (wcstrcmparray(pTmpBuf->lgrui0_name, Groups) == 0) {
581  result = 1;
582  break;
583  }
584  ++pTmpBuf;
585  ++dwTotalCount;
586  }
587  }
588  } else {
589  debug("Valid_Local_Groups: ERROR NetUserGetLocalGroups returned: %s\n", Get_WIN32_ErrorMessage(nStatus));
590  result = 0;
591  }
592  /*
593  * Free the allocated memory.
594  */
595  if (pBuf != NULL)
596  NetApiBufferFree(pBuf);
597  return result;
598 }
599 
600 /* returns 1 on success, 0 on failure */
601 int
602 Valid_Global_Groups(char *UserName, const char **Groups)
603 {
604  int result = 0;
605  WCHAR wszUser[DNLEN + UNLEN + 2]; /* Unicode user name */
606  char NTDomain[DNLEN + UNLEN + 2];
607 
608  char *domain_qualify = NULL;
609  char User[DNLEN + UNLEN + 2];
610  size_t j;
611 
612  wchar_t *User_DN, *User_LDAP_path, *User_PrimaryGroup;
613  wchar_t **wszGroups, **tmp;
614  IADs *pUser;
615  HRESULT hr;
616 
617  strncpy(NTDomain, UserName, sizeof(NTDomain));
618 
619  for (j = 0; j < strlen(NTV_VALID_DOMAIN_SEPARATOR); ++j) {
620  if ((domain_qualify = strchr(NTDomain, NTV_VALID_DOMAIN_SEPARATOR[j])) != NULL)
621  break;
622  }
623  if (domain_qualify == NULL) {
624  strncpy(User, DefaultDomain, DNLEN);
625  strcat(User, "\\");
626  strncat(User, UserName, UNLEN);
627  strncpy(NTDomain, DefaultDomain, DNLEN);
628  } else {
629  domain_qualify[0] = '\\';
630  strncpy(User, NTDomain, DNLEN + UNLEN + 2);
631  domain_qualify[0] = '\0';
632  }
633 
634  debug("Valid_Global_Groups: checking group membership of '%s'.\n", User);
635 
636  /* Convert ANSI User Name to Unicode */
637 
638  MultiByteToWideChar(CP_ACP, 0, User,
639  strlen(User) + 1, wszUser,
640  sizeof(wszUser) / sizeof(wszUser[0]));
641 
642  /* Get CN of User */
643  if ((User_DN = My_NameTranslate(wszUser, ADS_NAME_TYPE_NT4, ADS_NAME_TYPE_1779)) == NULL) {
644  debug("Valid_Global_Groups: cannot get DN for '%s'.\n", User);
645  return result;
646  }
647  wszGroups = build_groups_DN_array(Groups, NTDomain);
648 
649  User_LDAP_path = GetLDAPPath(User_DN, GC_MODE);
650 
651  hr = ADsGetObject(User_LDAP_path, &IID_IADs, (void **) &pUser);
652  if (SUCCEEDED(hr)) {
653  wchar_t *User_PrimaryGroup_Path;
654  IADs *pGrp;
655 
656  User_PrimaryGroup = Get_primaryGroup(pUser);
657  if (User_PrimaryGroup == NULL)
658  debug("Valid_Global_Groups: cannot get Primary Group for '%s'.\n", User);
659  else {
660  add_User_Group(User_PrimaryGroup);
661  User_PrimaryGroup_Path = GetLDAPPath(User_PrimaryGroup, GC_MODE);
662  hr = ADsGetObject(User_PrimaryGroup_Path, &IID_IADs, (void **) &pGrp);
663  if (SUCCEEDED(hr)) {
664  hr = Recursive_Memberof(pGrp);
665  pGrp->lpVtbl->Release(pGrp);
666  safe_free(User_PrimaryGroup_Path);
667  User_PrimaryGroup_Path = GetLDAPPath(User_PrimaryGroup, LDAP_MODE);
668  hr = ADsGetObject(User_PrimaryGroup_Path, &IID_IADs, (void **) &pGrp);
669  if (SUCCEEDED(hr)) {
670  hr = Recursive_Memberof(pGrp);
671  pGrp->lpVtbl->Release(pGrp);
672  } else
673  debug("Valid_Global_Groups: ADsGetObject for %S failed, ERROR: %s\n", User_PrimaryGroup_Path, Get_WIN32_ErrorMessage(hr));
674  } else
675  debug("Valid_Global_Groups: ADsGetObject for %S failed, ERROR: %s\n", User_PrimaryGroup_Path, Get_WIN32_ErrorMessage(hr));
676  safe_free(User_PrimaryGroup_Path);
677  }
678  hr = Recursive_Memberof(pUser);
679  pUser->lpVtbl->Release(pUser);
680  safe_free(User_LDAP_path);
681  User_LDAP_path = GetLDAPPath(User_DN, LDAP_MODE);
682  hr = ADsGetObject(User_LDAP_path, &IID_IADs, (void **) &pUser);
683  if (SUCCEEDED(hr)) {
684  hr = Recursive_Memberof(pUser);
685  pUser->lpVtbl->Release(pUser);
686  } else
687  debug("Valid_Global_Groups: ADsGetObject for %S failed, ERROR: %s\n", User_LDAP_path, Get_WIN32_ErrorMessage(hr));
688 
689  tmp = User_Groups;
690  while (*tmp) {
691  if (wccmparray(*tmp, wszGroups) == 0) {
692  result = 1;
693  break;
694  }
695  ++tmp;
696  }
697  } else
698  debug("Valid_Global_Groups: ADsGetObject for %S failed, ERROR: %s\n", User_LDAP_path, Get_WIN32_ErrorMessage(hr));
699 
700  safe_free(User_DN);
701  safe_free(User_LDAP_path);
702  safe_free(User_PrimaryGroup);
703  tmp = wszGroups;
704  while (*tmp) {
705  safe_free(*tmp);
706  ++tmp;
707  }
708  safe_free(wszGroups);
709 
710  tmp = User_Groups;
711  while (*tmp) {
712  safe_free(*tmp);
713  ++tmp;
714  }
716  User_Groups_Count = 0;
717 
718  return result;
719 }
720 
721 static void
722 usage(const char *program)
723 {
724  fprintf(stderr, "Usage: %s [-D domain][-G][-c][-d][-h]\n"
725  " -D default user Domain\n"
726  " -G enable Active Directory Global group mode\n"
727  " -c use case insensitive compare (local mode only)\n"
728  " -d enable debugging\n"
729  " -h this message\n",
730  program);
731 }
732 
733 void
734 process_options(int argc, char *argv[])
735 {
736  int opt;
737 
738  opterr = 0;
739  while (-1 != (opt = getopt(argc, argv, "D:Gcdh"))) {
740  switch (opt) {
741  case 'D':
742  DefaultDomain = xstrndup(optarg, DNLEN + 1);
743  strlwr(DefaultDomain);
744  break;
745  case 'G':
746  use_global = 1;
747  break;
748  case 'c':
750  break;
751  case 'd':
752  debug_enabled = 1;
753  break;
754  case 'h':
755  usage(argv[0]);
756  exit(EXIT_SUCCESS);
757  case '?':
758  opt = optopt;
759  /* fall thru to default */
760  default:
761  fprintf(stderr, "%s: FATAL: Unknown option: -%c. Exiting\n", program_name, opt);
762  usage(argv[0]);
763  exit(EXIT_FAILURE);
764  break; /* not reached */
765  }
766  }
767 }
768 
769 int
770 main(int argc, char *argv[])
771 {
772  char *p;
773  char buf[HELPER_INPUT_BUFFER];
774  char *username;
775  char *group;
776  const char *groups[512];
777  int n;
778 
779  if (argc > 0) { /* should always be true */
780  program_name = strrchr(argv[0], '/');
781  if (program_name == NULL)
782  program_name = argv[0];
783  } else {
784  program_name = "(unknown)";
785  }
786  mypid = getpid();
787 
788  setbuf(stdout, NULL);
789  setbuf(stderr, NULL);
790 
791  /* Check Command Line */
792  process_options(argc, argv);
793 
794  if (use_global) {
795  if ((machinedomain = GetDomainName()) == NULL) {
796  fprintf(stderr, "%s: FATAL: Can't read machine domain\n", program_name);
797  exit(EXIT_FAILURE);
798  }
799  strlwr(machinedomain);
800  if (!DefaultDomain)
802  }
803  debug("%s " VERSION " " SQUID_BUILD_INFO " starting up...\n", argv[0]);
804  if (use_global)
805  debug("Domain Global group mode enabled using '%s' as default domain.\n", DefaultDomain);
807  debug("Warning: running in case insensitive mode !!!\n");
808 
809  atexit(CloseCOM);
810 
811  /* Main Loop */
812  while (fgets(buf, HELPER_INPUT_BUFFER, stdin)) {
813  if (NULL == strchr(buf, '\n')) {
814  /* too large message received.. skip and deny */
815  fprintf(stderr, "%s: ERROR: Too large: %s\n", argv[0], buf);
816  while (fgets(buf, HELPER_INPUT_BUFFER, stdin)) {
817  fprintf(stderr, "%s: ERROR: Too large..: %s\n", argv[0], buf);
818  if (strchr(buf, '\n') != NULL)
819  break;
820  }
821  SEND_BH(HLP_MSG("Invalid Request. Too Long."));
822  continue;
823  }
824  if ((p = strchr(buf, '\n')) != NULL)
825  *p = '\0'; /* strip \n */
826  if ((p = strchr(buf, '\r')) != NULL)
827  *p = '\0'; /* strip \r */
828 
829  debug("Got '%s' from Squid (length: %d).\n", buf, strlen(buf));
830 
831  if (buf[0] == '\0') {
832  SEND_BH(HLP_MSG("Invalid Request. No Input."));
833  continue;
834  }
835  username = strtok(buf, " ");
836  for (n = 0; (group = strtok(NULL, " ")) != NULL; ++n) {
837  rfc1738_unescape(group);
838  groups[n] = group;
839  }
840  groups[n] = NULL;
841  numberofgroups = n;
842 
843  if (NULL == username) {
844  SEND_BH(HLP_MSG("Invalid Request. No Username."));
845  continue;
846  }
847  rfc1738_unescape(username);
848 
849  if ((use_global ? Valid_Global_Groups(username, groups) : Valid_Local_Groups(username, groups))) {
850  SEND_OK("");
851  } else {
852  SEND_ERR("");
853  }
854  err = 0;
855  }
856  return EXIT_SUCCESS;
857 }
858 
char * WIN32_ErrorMessage
int Valid_Global_Groups(char *UserName, const char **Groups)
char * Get_WIN32_ErrorMessage(HRESULT)
#define assert(EX)
Definition: assert.h:17
ADSI_PATH
HRESULT Recursive_Memberof(IADs *pObj)
int main(int argc, char *argv[])
int Valid_Local_Groups(char *UserName, const char **Groups)
int debug_enabled
Definition: debug.cc:13
int i
Definition: membanger.c:49
#define xstrdup
pid_t mypid
char * xstrndup(const char *s, size_t n)
Definition: xstring.cc:56
#define safe_free(x)
Definition: xalloc.h:73
static int wcstrcmparray(const wchar_t *str, const char **array)
char * p
Definition: membanger.c:43
const char NTV_VALID_DOMAIN_SEPARATOR[]
char * GetDomainName(void)
wchar_t * My_NameTranslate(wchar_t *, int, int)
#define SEND_OK(x)
int User_Groups_Count
#define HELPER_INPUT_BUFFER
Definition: UserRequest.cc:26
#define HLP_MSG(text)
int opterr
Definition: getopt.c:47
char * DefaultDomain
wchar_t * Get_primaryGroup(IADs *pUser)
int getopt(int nargc, char *const *nargv, const char *ostr)
Definition: getopt.c:62
void process_options(int argc, char *argv[])
HRESULT GetLPBYTEtoOctetString(VARIANT *pVar, LPBYTE *ppByte)
void * xrealloc(void *s, size_t sz)
Definition: xalloc.cc:137
static void usage(const char *program)
int use_case_insensitive_compare
static int debug
Definition: tcp-banger3.c:105
std::vector< ServiceGroupPointer > Groups
enum ADSI_PATH ADSI_Path
wchar_t * GetLDAPPath(wchar_t *Base_DN, int query_mode)
int use_global
void CloseCOM(void)
int unsigned int const char *desc STUB void int len
Definition: stub_fd.cc:20
void const char * buf
Definition: stub_helper.cc:16
int numberofgroups
typedef DWORD
Definition: WinSvc.cc:73
char * machinedomain
#define xmalloc
#define SEND_BH(x)
int WIN32_COM_initialized
static int wccmparray(const wchar_t *str, const wchar_t **array)
#define VERSION
void rfc1738_unescape(char *url)
Definition: rfc1738.c:146
char * program_name
static wchar_t ** build_groups_DN_array(const char **array, char *userdomain)
int optopt
Definition: getopt.c:48
#define SEND_ERR(x)
char * optarg
Definition: getopt.c:51
#define NULL
Definition: types.h:166
wchar_t ** User_Groups
int add_User_Group(wchar_t *Group)

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors