Squid Web Cache master
Loading...
Searching...
No Matches
ext_ad_group_acl.cc
Go to the documentation of this file.
1/*
2 * Copyright (C) 1996-2025 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#include "rfc1738.h"
72
73#if _SQUID_CYGWIN_
74#include <cwchar>
75int _wcsicmp(const wchar_t *, const wchar_t *);
76#endif
77
78#undef assert
79#include <cassert>
80#include <cctype>
81#include <cstring>
82
83#if HAVE_GETOPT_H
84#include <getopt.h>
85#endif
86#if HAVE_OBJBASE_H
87#include <objbase.h>
88#endif
89#if HAVE_INITGUID_H
90#include <initguid.h>
91#endif
92#if HAVE_ADSIID_H
93#include <adsiid.h>
94#endif
95#if HAVE_IADS_H
96#include <iads.h>
97#endif
98#if HAVE_ADSHLP_H
99#include <adshlp.h>
100#endif
101#if HAVE_ADSERR_H
102#include <adserr.h>
103#endif
104#if HAVE_LM_H
105#include <lm.h>
106#endif
107#if HAVE_DSROLE_H
108#include <dsrole.h>
109#endif
110#if HAVE_SDDL_H
111#include <sddl.h>
112#endif
113
118
121pid_t mypid;
124char *DefaultDomain = nullptr;
125const char NTV_VALID_DOMAIN_SEPARATOR[] = "\\/";
128char *WIN32_ErrorMessage = nullptr;
129wchar_t **User_Groups;
131
132static wchar_t *My_NameTranslate(wchar_t *, int, int);
133static char *Get_WIN32_ErrorMessage(HRESULT);
134
135static void
137{
138 if (WIN32_COM_initialized == 1)
139 CoUninitialize();
140}
141
142static HRESULT
143GetLPBYTEtoOctetString(VARIANT * pVar, LPBYTE * ppByte)
144{
145 HRESULT hr = E_FAIL;
146 void HUGEP *pArray;
147 long lLBound, lUBound, cElements;
148
149 if ((!pVar) || (!ppByte))
150 return E_INVALIDARG;
151 if ((pVar->vt) != (VT_UI1 | VT_ARRAY))
152 return E_INVALIDARG;
153
154 hr = SafeArrayGetLBound(V_ARRAY(pVar), 1, &lLBound);
155 hr = SafeArrayGetUBound(V_ARRAY(pVar), 1, &lUBound);
156
157 cElements = lUBound - lLBound + 1;
158 hr = SafeArrayAccessData(V_ARRAY(pVar), &pArray);
159 if (SUCCEEDED(hr)) {
160 LPBYTE pTemp = (LPBYTE) pArray;
161 *ppByte = (LPBYTE) CoTaskMemAlloc(cElements);
162 if (*ppByte)
163 memcpy(*ppByte, pTemp, cElements);
164 else
165 hr = E_OUTOFMEMORY;
166 }
167 SafeArrayUnaccessData(V_ARRAY(pVar));
168
169 return hr;
170}
171
172static wchar_t *
173Get_primaryGroup(IADs * pUser)
174{
175 HRESULT hr;
176 VARIANT var;
177 unsigned User_primaryGroupID;
178 char tmpSID[SECURITY_MAX_SID_SIZE * 2];
179 wchar_t *wc = nullptr, *result = nullptr;
180 int wcsize;
181
182 VariantInit(&var);
183
184 /* Get the primaryGroupID property */
185 static const auto primaryGroupIdStr = SysAllocString(L"primaryGroupID");
186 hr = pUser->Get(primaryGroupIdStr, &var);
187 if (SUCCEEDED(hr)) {
188 User_primaryGroupID = var.uintVal;
189 } else {
190 debug("Get_primaryGroup: cannot get primaryGroupID, ERROR: %s\n", Get_WIN32_ErrorMessage(hr));
191 VariantClear(&var);
192 return result;
193 }
194 VariantClear(&var);
195
196 /*Get the objectSid property */
197 static const auto objectSidStr = SysAllocString(L"objectSid");
198 hr = pUser->Get(objectSidStr, &var);
199 if (SUCCEEDED(hr)) {
200 PSID pObjectSID;
201 LPBYTE pByte = nullptr;
202 char *szSID = nullptr;
203 hr = GetLPBYTEtoOctetString(&var, &pByte);
204
205 pObjectSID = (PSID) pByte;
206
207 /* Convert SID to string. */
208 ConvertSidToStringSid(pObjectSID, &szSID);
209 CoTaskMemFree(pByte);
210
211 *(strrchr(szSID, '-') + 1) = '\0';
212 snprintf(tmpSID, sizeof(tmpSID)-1, "%s%u", szSID, User_primaryGroupID);
213
214 wcsize = MultiByteToWideChar(CP_ACP, 0, tmpSID, -1, wc, 0);
215 wc = (wchar_t *) xmalloc(wcsize * sizeof(wchar_t));
216 MultiByteToWideChar(CP_ACP, 0, tmpSID, -1, wc, wcsize);
217 LocalFree(szSID);
218
219 result = My_NameTranslate(wc, ADS_NAME_TYPE_SID_OR_SID_HISTORY_NAME, ADS_NAME_TYPE_1779);
220 safe_free(wc);
221
222 if (!result)
223 debug("Get_primaryGroup: cannot get DN for %s.\n", tmpSID);
224 else
225 debug("Get_primaryGroup: Primary group DN: %S.\n", result);
226 } else {
227 debug("Get_primaryGroup: cannot get objectSid, ERROR: %s\n", Get_WIN32_ErrorMessage(hr));
228 }
229 VariantClear(&var);
230 return result;
231}
232
233static char *
235{
236 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
237 FORMAT_MESSAGE_IGNORE_INSERTS,
238 nullptr,
239 hr,
240 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
241 (LPTSTR) & WIN32_ErrorMessage,
242 0,
243 nullptr);
244 return WIN32_ErrorMessage;
245}
246
247static wchar_t *
248My_NameTranslate(wchar_t * name, int in_format, int out_format)
249{
250 IADsNameTranslate *pNto;
251 HRESULT hr;
252 wchar_t *wc;
253
254 if (WIN32_COM_initialized == 0) {
255 hr = CoInitialize(nullptr);
256 if (FAILED(hr)) {
257 debug("My_NameTranslate: cannot initialize COM interface, ERROR: %s\n", Get_WIN32_ErrorMessage(hr));
258 /* This is a fatal error */
259 exit(EXIT_FAILURE);
260 }
262 }
263 hr = CoCreateInstance(CLSID_NameTranslate,
264 nullptr,
265 CLSCTX_INPROC_SERVER,
266 IID_IADsNameTranslate,
267 (void **) &pNto);
268 if (FAILED(hr)) {
269 debug("My_NameTranslate: cannot create COM instance, ERROR: %s\n", Get_WIN32_ErrorMessage(hr));
270 /* This is a fatal error */
271 exit(EXIT_FAILURE);
272 }
273 static const auto emptyStr = SysAllocString(L"");
274 hr = pNto->Init(ADS_NAME_INITTYPE_GC, emptyStr);
275 if (FAILED(hr)) {
276 debug("My_NameTranslate: cannot initialise NameTranslate API, ERROR: %s\n", Get_WIN32_ErrorMessage(hr));
277 pNto->Release();
278 /* This is a fatal error */
279 exit(EXIT_FAILURE);
280 }
281 hr = pNto->Set(in_format, name);
282 if (FAILED(hr)) {
283 debug("My_NameTranslate: cannot set translate of %S, ERROR: %s\n", name, Get_WIN32_ErrorMessage(hr));
284 pNto->Release();
285 return nullptr;
286 }
287 BSTR bstr;
288 hr = pNto->Get(out_format, &bstr);
289 if (FAILED(hr)) {
290 debug("My_NameTranslate: cannot get translate of %S, ERROR: %s\n", name, Get_WIN32_ErrorMessage(hr));
291 pNto->Release();
292 return nullptr;
293 }
294 debug("My_NameTranslate: %S translated to %S\n", name, bstr);
295
296 wc = (wchar_t *) xmalloc((wcslen(bstr) + 1) * sizeof(wchar_t));
297 wcscpy(wc, bstr);
298 SysFreeString(bstr);
299 pNto->Release();
300 return wc;
301}
302
303static wchar_t *
304GetLDAPPath(wchar_t * Base_DN, int query_mode)
305{
306 wchar_t *wc;
307
308 wc = (wchar_t *) xmalloc((wcslen(Base_DN) + 8) * sizeof(wchar_t));
309
310 if (query_mode == LDAP_MODE)
311 wcscpy(wc, L"LDAP://");
312 else
313 wcscpy(wc, L"GC://");
314 wcscat(wc, Base_DN);
315
316 return wc;
317}
318
319static char *
321{
322 static char *DomainName = nullptr;
323 PDSROLE_PRIMARY_DOMAIN_INFO_BASIC pDSRoleInfo = nullptr;
324 DWORD netret;
325
326 if ((netret = DsRoleGetPrimaryDomainInformation(nullptr, DsRolePrimaryDomainInfoBasic, (PBYTE *) & pDSRoleInfo)) == ERROR_SUCCESS) {
327 /*
328 * Check the machine role.
329 */
330
331 if ((pDSRoleInfo->MachineRole == DsRole_RoleMemberWorkstation) ||
332 (pDSRoleInfo->MachineRole == DsRole_RoleMemberServer) ||
333 (pDSRoleInfo->MachineRole == DsRole_RoleBackupDomainController) ||
334 (pDSRoleInfo->MachineRole == DsRole_RolePrimaryDomainController)) {
335
336 size_t len = wcslen(pDSRoleInfo->DomainNameFlat);
337
338 /* allocate buffer for str + null termination */
339 safe_free(DomainName);
340 DomainName = (char *) xmalloc(len + 1);
341
342 /* copy unicode buffer */
343 WideCharToMultiByte(CP_ACP, 0, pDSRoleInfo->DomainNameFlat, -1, DomainName, len, nullptr, nullptr);
344
345 /* add null termination */
346 DomainName[len] = '\0';
347
348 /*
349 * Member of a domain. Display it in debug mode.
350 */
351 debug("Member of Domain %s\n", DomainName);
352 debug("Into forest %S\n", pDSRoleInfo->DomainForestName);
353
354 } else {
355 debug("Not a Domain member\n");
356 }
357 } else {
358 debug("GetDomainName: ERROR DsRoleGetPrimaryDomainInformation returned: %s\n", Get_WIN32_ErrorMessage(netret));
359 }
360
361 /*
362 * Free the allocated memory.
363 */
364 if (pDSRoleInfo)
365 DsRoleFreeMemory(pDSRoleInfo);
366
367 return DomainName;
368}
369
370static int
371add_User_Group(wchar_t * Group)
372{
373 wchar_t **array;
374
375 if (User_Groups_Count == 0) {
376 User_Groups = (wchar_t **) xmalloc(sizeof(wchar_t *));
377 *User_Groups = nullptr;
379 }
380 array = User_Groups;
381 while (*array) {
382 if (wcscmp(Group, *array) == 0)
383 return 0;
384 ++array;
385 }
386 User_Groups = (wchar_t **) xrealloc(User_Groups, sizeof(wchar_t *) * (User_Groups_Count + 1));
388 User_Groups[User_Groups_Count - 1] = (wchar_t *) xmalloc((wcslen(Group) + 1) * sizeof(wchar_t));
389 wcscpy(User_Groups[User_Groups_Count - 1], Group);
391
392 return 1;
393}
394
395/* returns true on match, false if no match */
396/* TODO: convert to std::containers */
397static bool
398wStrIsInArray(const wchar_t * str, wchar_t ** array)
399{
400 if (!array)
401 return false;
402 while (*array) {
403 debug("Windows group: %S, Squid group: %S\n", str, *array);
404 if (wcscmp(str, *array) == 0)
405 return true;
406 ++array;
407 }
408 return false;
409}
410
411/* returns 0 on match, -1 if no match */
412static int
413wcstrcmparray(const wchar_t * str, const char **array)
414{
415 WCHAR wszGroup[GNLEN + 1]; // Unicode Group
416
417 while (*array) {
418 MultiByteToWideChar(CP_ACP, 0, *array,
419 strlen(*array) + 1, wszGroup, sizeof(wszGroup) / sizeof(wszGroup[0]));
420 debug("Windows group: %S, Squid group: %S\n", str, wszGroup);
421 if ((use_case_insensitive_compare ? _wcsicmp(str, wszGroup) : wcscmp(str, wszGroup)) == 0)
422 return 0;
423 ++array;
424 }
425 return -1;
426}
427
428static HRESULT
430{
431 VARIANT var;
432 long lBound, uBound;
433 HRESULT hr;
434
435 VariantInit(&var);
436 static const auto memberOfStr = SysAllocString(L"memberOf");
437 hr = pObj->Get(memberOfStr, &var);
438 if (SUCCEEDED(hr)) {
439 if (VT_BSTR == var.vt) {
440 if (add_User_Group(var.bstrVal)) {
441 wchar_t *Group_Path;
442 IADs *pGrp;
443
444 Group_Path = GetLDAPPath(var.bstrVal, GC_MODE);
445 hr = ADsGetObject(Group_Path, IID_IADs, (void **) &pGrp);
446 if (SUCCEEDED(hr)) {
447 hr = Recursive_Memberof(pGrp);
448 pGrp->Release();
449 safe_free(Group_Path);
450 Group_Path = GetLDAPPath(var.bstrVal, LDAP_MODE);
451 hr = ADsGetObject(Group_Path, IID_IADs, (void **) &pGrp);
452 if (SUCCEEDED(hr)) {
453 hr = Recursive_Memberof(pGrp);
454 pGrp->Release();
455 } else {
456 debug("Recursive_Memberof: ERROR ADsGetObject for %S failed: %s\n", Group_Path, Get_WIN32_ErrorMessage(hr));
457 }
458 } else {
459 debug("Recursive_Memberof: ERROR ADsGetObject for %S failed: %s\n", Group_Path, Get_WIN32_ErrorMessage(hr));
460 }
461 safe_free(Group_Path);
462 }
463 } else {
464 if (SUCCEEDED(SafeArrayGetLBound(V_ARRAY(&var), 1, &lBound)) &&
465 SUCCEEDED(SafeArrayGetUBound(V_ARRAY(&var), 1, &uBound))) {
466 VARIANT elem;
467 while (lBound <= uBound) {
468 hr = SafeArrayGetElement(V_ARRAY(&var), &lBound, &elem);
469 if (SUCCEEDED(hr)) {
470 if (add_User_Group(elem.bstrVal)) {
471 wchar_t *Group_Path;
472 IADs *pGrp;
473
474 Group_Path = GetLDAPPath(elem.bstrVal, GC_MODE);
475 hr = ADsGetObject(Group_Path, IID_IADs, (void **) &pGrp);
476 if (SUCCEEDED(hr)) {
477 hr = Recursive_Memberof(pGrp);
478 pGrp->Release();
479 safe_free(Group_Path);
480 Group_Path = GetLDAPPath(elem.bstrVal, LDAP_MODE);
481 hr = ADsGetObject(Group_Path, IID_IADs, (void **) &pGrp);
482 if (SUCCEEDED(hr)) {
483 hr = Recursive_Memberof(pGrp);
484 pGrp->Release();
485 safe_free(Group_Path);
486 } else {
487 debug("Recursive_Memberof: ERROR ADsGetObject for %S failed: %s\n", Group_Path, Get_WIN32_ErrorMessage(hr));
488 }
489 } else {
490 debug("Recursive_Memberof: ERROR ADsGetObject for %S failed: %s\n", Group_Path, Get_WIN32_ErrorMessage(hr));
491 }
492 safe_free(Group_Path);
493 }
494 VariantClear(&elem);
495 } else {
496 debug("Recursive_Memberof: ERROR SafeArrayGetElement failed: %s\n", Get_WIN32_ErrorMessage(hr));
497 VariantClear(&elem);
498 }
499 ++lBound;
500 }
501 } else {
502 debug("Recursive_Memberof: ERROR SafeArrayGetxBound failed: %s\n", Get_WIN32_ErrorMessage(hr));
503 }
504 }
505 VariantClear(&var);
506 } else {
507 if (hr != E_ADS_PROPERTY_NOT_FOUND)
508 debug("Recursive_Memberof: ERROR getting memberof attribute: %s\n", Get_WIN32_ErrorMessage(hr));
509 }
510 return hr;
511}
512
513static wchar_t **
514build_groups_DN_array(const char **array, char *userdomain)
515{
516 wchar_t *wc = nullptr;
517 int wcsize;
518 int source_group_format;
519 char Group[GNLEN + 1];
520
521 wchar_t **wc_array, **entry;
522
523 entry = wc_array = (wchar_t **) xmalloc((numberofgroups + 1) * sizeof(wchar_t *));
524
525 while (*array) {
526 if (strchr(*array, '/')) {
527 xstrncpy(Group, *array, GNLEN);
528 source_group_format = ADS_NAME_TYPE_CANONICAL;
529 } else {
530 source_group_format = ADS_NAME_TYPE_NT4;
531 if (!strchr(*array, '\\')) {
532 strcpy(Group, userdomain);
533 strcat(Group, "\\");
534 strncat(Group, *array, GNLEN - sizeof(userdomain) - 1);
535 } else
536 xstrncpy(Group, *array, GNLEN);
537 }
538
539 wcsize = MultiByteToWideChar(CP_ACP, 0, Group, -1, wc, 0);
540 wc = (wchar_t *) xmalloc(wcsize * sizeof(wchar_t));
541 MultiByteToWideChar(CP_ACP, 0, Group, -1, wc, wcsize);
542 *entry = My_NameTranslate(wc, source_group_format, ADS_NAME_TYPE_1779);
543 safe_free(wc);
544 ++array;
545 if (!*entry) {
546 debug("build_groups_DN_array: cannot get DN for '%s'.\n", Group);
547 continue;
548 }
549 ++entry;
550 }
551 *entry = nullptr;
552 return wc_array;
553}
554
555/* returns 1 on success, 0 on failure */
556static int
557Valid_Local_Groups(char *UserName, const char **Groups)
558{
559 int result = 0;
560 char *Domain_Separator;
561 WCHAR wszUserName[UNLEN + 1]; /* Unicode user name */
562
563 LPLOCALGROUP_USERS_INFO_0 pBuf;
564 LPLOCALGROUP_USERS_INFO_0 pTmpBuf;
565 DWORD dwLevel = 0;
566 DWORD dwFlags = LG_INCLUDE_INDIRECT;
567 DWORD dwPrefMaxLen = -1;
568 DWORD dwEntriesRead = 0;
569 DWORD dwTotalEntries = 0;
570 NET_API_STATUS nStatus;
571 DWORD i;
572 DWORD dwTotalCount = 0;
573 LPBYTE pBufTmp = nullptr;
574
575 if ((Domain_Separator = strchr(UserName, '/')))
576 *Domain_Separator = '\\';
577
578 debug("Valid_Local_Groups: checking group membership of '%s'.\n", UserName);
579
580 /* Convert ANSI User Name and Group to Unicode */
581
582 MultiByteToWideChar(CP_ACP, 0, UserName,
583 strlen(UserName) + 1, wszUserName, sizeof(wszUserName) / sizeof(wszUserName[0]));
584
585 /*
586 * Call the NetUserGetLocalGroups function
587 * specifying information level 0.
588 *
589 * The LG_INCLUDE_INDIRECT flag specifies that the
590 * function should also return the names of the local
591 * groups in which the user is indirectly a member.
592 */
593 nStatus = NetUserGetLocalGroups(nullptr,
594 wszUserName,
595 dwLevel,
596 dwFlags,
597 &pBufTmp,
598 dwPrefMaxLen,
599 &dwEntriesRead,
600 &dwTotalEntries);
601 pBuf = (LPLOCALGROUP_USERS_INFO_0) pBufTmp;
602 /*
603 * If the call succeeds,
604 */
605 if (nStatus == NERR_Success) {
606 if ((pTmpBuf = pBuf)) {
607 for (i = 0; i < dwEntriesRead; ++i) {
608 if (!pTmpBuf) {
609 result = 0;
610 break;
611 }
612 if (wcstrcmparray(pTmpBuf->lgrui0_name, Groups) == 0) {
613 result = 1;
614 break;
615 }
616 ++pTmpBuf;
617 ++dwTotalCount;
618 }
619 }
620 } else {
621 debug("Valid_Local_Groups: ERROR NetUserGetLocalGroups returned: %s\n", Get_WIN32_ErrorMessage(nStatus));
622 result = 0;
623 }
624 /*
625 * Free the allocated memory.
626 */
627 if (pBuf)
628 NetApiBufferFree(pBuf);
629 return result;
630}
631
632/* returns 1 on success, 0 on failure */
633static int
634Valid_Global_Groups(char *UserName, const char **Groups)
635{
636 int result = 0;
637 WCHAR wszUser[DNLEN + UNLEN + 2]; /* Unicode user name */
638 char NTDomain[DNLEN + UNLEN + 2];
639
640 char *domain_qualify = nullptr;
641 char User[DNLEN + UNLEN + 2];
642 size_t j;
643
644 wchar_t *User_DN = nullptr, *User_LDAP_path = nullptr;
645 wchar_t *User_PrimaryGroup = nullptr;
646 IADs *pUser;
647 HRESULT hr;
648
649 xstrncpy(NTDomain, UserName, sizeof(NTDomain));
650
651 for (j = 0; j < strlen(NTV_VALID_DOMAIN_SEPARATOR); ++j) {
652 if ((domain_qualify = strchr(NTDomain, NTV_VALID_DOMAIN_SEPARATOR[j])))
653 break;
654 }
655 if (!domain_qualify) {
656 xstrncpy(User, DefaultDomain, DNLEN);
657 strcat(User, "\\");
658 strncat(User, UserName, UNLEN);
659 xstrncpy(NTDomain, DefaultDomain, DNLEN);
660 } else {
661 domain_qualify[0] = '\\';
662 xstrncpy(User, NTDomain, DNLEN + UNLEN + 2);
663 domain_qualify[0] = '\0';
664 }
665
666 debug("Valid_Global_Groups: checking group membership of '%s'.\n", User);
667
668 /* Convert ANSI User Name to Unicode */
669
670 MultiByteToWideChar(CP_ACP, 0, User,
671 strlen(User) + 1, wszUser,
672 sizeof(wszUser) / sizeof(wszUser[0]));
673
674 /* Get CN of User */
675 if (!(User_DN = My_NameTranslate(wszUser, ADS_NAME_TYPE_NT4, ADS_NAME_TYPE_1779))) {
676 debug("Valid_Global_Groups: cannot get DN for '%s'.\n", User);
677 return result;
678 }
679 auto wszGroups = build_groups_DN_array(Groups, NTDomain);
680
681 User_LDAP_path = GetLDAPPath(User_DN, GC_MODE);
682
683 hr = ADsGetObject(User_LDAP_path, IID_IADs, (void **) &pUser);
684 if (SUCCEEDED(hr)) {
685 wchar_t *User_PrimaryGroup_Path;
686 IADs *pGrp;
687
688 User_PrimaryGroup = Get_primaryGroup(pUser);
689 if (!User_PrimaryGroup) {
690 debug("Valid_Global_Groups: cannot get Primary Group for '%s'.\n", User);
691 } else {
692 add_User_Group(User_PrimaryGroup);
693 User_PrimaryGroup_Path = GetLDAPPath(User_PrimaryGroup, GC_MODE);
694 hr = ADsGetObject(User_PrimaryGroup_Path, IID_IADs, (void **) &pGrp);
695 if (SUCCEEDED(hr)) {
696 hr = Recursive_Memberof(pGrp);
697 pGrp->Release();
698 safe_free(User_PrimaryGroup_Path);
699 User_PrimaryGroup_Path = GetLDAPPath(User_PrimaryGroup, LDAP_MODE);
700 hr = ADsGetObject(User_PrimaryGroup_Path, IID_IADs, (void **) &pGrp);
701 if (SUCCEEDED(hr)) {
702 hr = Recursive_Memberof(pGrp);
703 pGrp->Release();
704 } else {
705 debug("Valid_Global_Groups: ADsGetObject for %S failed, ERROR: %s\n", User_PrimaryGroup_Path, Get_WIN32_ErrorMessage(hr));
706 }
707 } else {
708 debug("Valid_Global_Groups: ADsGetObject for %S failed, ERROR: %s\n", User_PrimaryGroup_Path, Get_WIN32_ErrorMessage(hr));
709 }
710 safe_free(User_PrimaryGroup_Path);
711 }
712 hr = Recursive_Memberof(pUser);
713 pUser->Release();
714 safe_free(User_LDAP_path);
715 User_LDAP_path = GetLDAPPath(User_DN, LDAP_MODE);
716 hr = ADsGetObject(User_LDAP_path, IID_IADs, (void **) &pUser);
717 if (SUCCEEDED(hr)) {
718 hr = Recursive_Memberof(pUser);
719 pUser->Release();
720 } else {
721 debug("Valid_Global_Groups: ADsGetObject for %S failed, ERROR: %s\n", User_LDAP_path, Get_WIN32_ErrorMessage(hr));
722 }
723
724 auto tmp = User_Groups;
725 while (*tmp) {
726 if (wStrIsInArray(*tmp, wszGroups)) {
727 result = 1;
728 break;
729 }
730 ++tmp;
731 }
732 } else {
733 debug("Valid_Global_Groups: ADsGetObject for %S failed, ERROR: %s\n", User_LDAP_path, Get_WIN32_ErrorMessage(hr));
734 }
735
736 safe_free(User_DN);
737 safe_free(User_LDAP_path);
738 safe_free(User_PrimaryGroup);
739 auto tmp = wszGroups;
740 while (*tmp) {
741 safe_free(*tmp);
742 ++tmp;
743 }
744 safe_free(wszGroups);
745
746 tmp = User_Groups;
747 while (*tmp) {
748 safe_free(*tmp);
749 ++tmp;
750 }
753
754 return result;
755}
756
757static void
758usage(const char *program)
759{
760 fprintf(stderr, "Usage: %s [-D domain][-G][-c][-d][-h]\n"
761 " -D default user Domain\n"
762 " -G enable Active Directory Global group mode\n"
763 " -c use case insensitive compare (local mode only)\n"
764 " -d enable debugging\n"
765 " -h this message\n",
766 program);
767}
768
769static void
770process_options(int argc, char *argv[])
771{
772 int opt;
773
774 opterr = 0;
775 while (-1 != (opt = getopt(argc, argv, "D:Gcdh"))) {
776 switch (opt) {
777 case 'D':
778 DefaultDomain = xstrndup(optarg, DNLEN + 1);
779 strlwr(DefaultDomain);
780 break;
781 case 'G':
782 use_global = 1;
783 break;
784 case 'c':
786 break;
787 case 'd':
788 debug_enabled = 1;
789 break;
790 case 'h':
791 usage(argv[0]);
792 exit(EXIT_SUCCESS);
793 case '?':
794 opt = optopt;
795 [[fallthrough]];
796 default:
797 fprintf(stderr, "%s: FATAL: Unknown option: -%c. Exiting\n", program_name, opt);
798 usage(argv[0]);
799 exit(EXIT_FAILURE);
800 break; /* not reached */
801 }
802 }
803}
804
805int
806main(int argc, char *argv[])
807{
808 char *p;
809 char buf[HELPER_INPUT_BUFFER];
810 char *username;
811 char *group;
812 const char *groups[512];
813 int n;
814
815 assert(argc > 0);
816 program_name = strrchr(argv[0], '/');
817 if (!program_name)
818 program_name = argv[0];
819 mypid = getpid();
820
821 setbuf(stdout, nullptr);
822 setbuf(stderr, nullptr);
823
824 /* Check Command Line */
825 process_options(argc, argv);
826
827 if (use_global) {
828 if (!(machinedomain = GetDomainName())) {
829 fprintf(stderr, "%s: FATAL: Can't read machine domain\n", program_name);
830 exit(EXIT_FAILURE);
831 }
832 strlwr(machinedomain);
833 if (!DefaultDomain)
835 }
836 debug("%s " VERSION " " SQUID_BUILD_INFO " starting up...\n", argv[0]);
837 if (use_global)
838 debug("Domain Global group mode enabled using '%s' as default domain.\n", DefaultDomain);
840 debug("Warning: running in case insensitive mode !!!\n");
841
842 atexit(CloseCOM);
843
844 /* Main Loop */
845 while (fgets(buf, HELPER_INPUT_BUFFER, stdin)) {
846 if (!strchr(buf, '\n')) {
847 /* too large message received.. skip and deny */
848 fprintf(stderr, "%s: ERROR: Too large: %s\n", argv[0], buf);
849 while (fgets(buf, HELPER_INPUT_BUFFER, stdin)) {
850 fprintf(stderr, "%s: ERROR: Too large..: %s\n", argv[0], buf);
851 if (strchr(buf, '\n'))
852 break;
853 }
854 SEND_BH(HLP_MSG("Invalid Request. Too Long."));
855 continue;
856 }
857 if ((p = strchr(buf, '\n')))
858 *p = '\0'; /* strip \n */
859 if ((p = strchr(buf, '\r')))
860 *p = '\0'; /* strip \r */
861
862 debug("Got '%s' from Squid (length: %zu).\n", buf, strlen(buf));
863
864 if (buf[0] == '\0') {
865 SEND_BH(HLP_MSG("Invalid Request. No Input."));
866 continue;
867 }
868 username = strtok(buf, " ");
869 for (n = 0; (group = strtok(nullptr, " ")); ++n) {
870 rfc1738_unescape(group);
871 groups[n] = group;
872 }
873 groups[n] = nullptr;
874 numberofgroups = n;
875
876 if (!username) {
877 SEND_BH(HLP_MSG("Invalid Request. No Username."));
878 continue;
879 }
880 rfc1738_unescape(username);
881
882 if ((use_global ? Valid_Global_Groups(username, groups) : Valid_Local_Groups(username, groups))) {
883 SEND_OK("");
884 } else {
885 SEND_ERR("");
886 }
887 }
888 return EXIT_SUCCESS;
889}
890
#define assert(EX)
Definition assert.h:17
#define VERSION
Definition autoconf.h:1683
#define SQUID_BUILD_INFO
Definition autoconf.h:1426
#define HELPER_INPUT_BUFFER
int debug_enabled
Definition debug.cc:13
void debug(const char *format,...)
Definition debug.cc:19
static int Valid_Local_Groups(char *UserName, const char **Groups)
static int Valid_Global_Groups(char *UserName, const char **Groups)
int WIN32_COM_initialized
static bool wStrIsInArray(const wchar_t *str, wchar_t **array)
enum ADSI_PATH ADSI_Path
char * WIN32_ErrorMessage
char * program_name
pid_t mypid
static char * GetDomainName(void)
static int wcstrcmparray(const wchar_t *str, const char **array)
char * machinedomain
char * DefaultDomain
static wchar_t * My_NameTranslate(wchar_t *, int, int)
static HRESULT GetLPBYTEtoOctetString(VARIANT *pVar, LPBYTE *ppByte)
static void CloseCOM(void)
static void process_options(int argc, char *argv[])
@ LDAP_MODE
@ GC_MODE
wchar_t ** User_Groups
int use_global
const char NTV_VALID_DOMAIN_SEPARATOR[]
int numberofgroups
int use_case_insensitive_compare
static wchar_t ** build_groups_DN_array(const char **array, char *userdomain)
int User_Groups_Count
static char * Get_WIN32_ErrorMessage(HRESULT)
static HRESULT Recursive_Memberof(IADs *pObj)
static int add_User_Group(wchar_t *Group)
static wchar_t * Get_primaryGroup(IADs *pUser)
static wchar_t * GetLDAPPath(wchar_t *Base_DN, int query_mode)
static void usage(void)
int optopt
Definition getopt.c:49
int getopt(int nargc, char *const *nargv, const char *ostr)
Definition getopt.c:62
char * optarg
Definition getopt.c:51
int opterr
Definition getopt.c:47
int main()
#define xstrdup
#define xmalloc
#define SEND_ERR(x)
#define SEND_OK(x)
#define HLP_MSG(text)
#define SEND_BH(x)
void rfc1738_unescape(char *url)
Definition rfc1738.c:146
void * xrealloc(void *s, size_t sz)
Definition xalloc.cc:126
#define safe_free(x)
Definition xalloc.h:73
char * xstrncpy(char *dst, const char *src, size_t n)
Definition xstring.cc:37
char * xstrndup(const char *s, size_t n)
Definition xstring.cc:56