717 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			717 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			C++
		
	
	
	
| /* ldap.cc: Helper functions for ldap access to Active Directory.
 | |
| 
 | |
|    Copyright 2014, 2015 Red Hat, Inc.
 | |
| 
 | |
| This file is part of Cygwin.
 | |
| 
 | |
| This software is a copyrighted work licensed under the terms of the
 | |
| Cygwin license.  Please consult the file "CYGWIN_LICENSE" for
 | |
| details. */
 | |
| 
 | |
| #include "winsup.h"
 | |
| #include "ldap.h"
 | |
| #include "cygerrno.h"
 | |
| #include "security.h"
 | |
| #include "path.h"
 | |
| #include "fhandler.h"
 | |
| #include "dtable.h"
 | |
| #include "cygheap.h"
 | |
| #include "registry.h"
 | |
| #include "pinfo.h"
 | |
| #include "lm.h"
 | |
| #include "dsgetdc.h"
 | |
| #include "tls_pbuf.h"
 | |
| #include <sys/param.h>
 | |
| 
 | |
| #define CYG_LDAP_ENUM_PAGESIZE	100	/* entries per page */
 | |
| 
 | |
| static PWCHAR rootdse_attr[] =
 | |
| {
 | |
|   (PWCHAR) L"defaultNamingContext",
 | |
|   (PWCHAR) L"supportedCapabilities",
 | |
|   NULL
 | |
| };
 | |
| 
 | |
| static const PCWSTR std_user_attr[] =
 | |
| {
 | |
|   L"sAMAccountName",
 | |
|   L"objectSid",
 | |
|   L"primaryGroupID",
 | |
|   L"uidNumber",
 | |
|   L"cygwinUnixUid",		/* TODO */
 | |
|   /* windows scheme */
 | |
|   L"displayName",
 | |
|   L"homeDrive",
 | |
|   L"homeDirectory",
 | |
|   /* cygwin scheme */
 | |
|   L"cygwinGecos",
 | |
|   L"cygwinHome",
 | |
|   L"cygwinShell",
 | |
|   /* unix scheme */
 | |
|   L"gecos",
 | |
|   L"unixHomeDirectory",
 | |
|   L"loginShell",
 | |
|   /* desc scheme */
 | |
|   L"description"
 | |
| };
 | |
| 
 | |
| static PWCHAR group_attr[] =
 | |
| {
 | |
|   (PWCHAR) L"sAMAccountName",
 | |
|   (PWCHAR) L"objectSid",
 | |
|   (PWCHAR) L"gidNumber",
 | |
|   (PWCHAR) L"cygwinUnixGid",	/* TODO */
 | |
|   NULL
 | |
| };
 | |
| 
 | |
| PWCHAR tdom_attr[] =
 | |
| {
 | |
|   (PWCHAR) L"trustPosixOffset",
 | |
|   NULL
 | |
| };
 | |
| 
 | |
| PWCHAR sid_attr[] =
 | |
| {
 | |
|   (PWCHAR) L"objectSid",
 | |
|   NULL
 | |
| };
 | |
| 
 | |
| PWCHAR rfc2307_uid_attr[] =
 | |
| {
 | |
|   (PWCHAR) L"uid",
 | |
|   NULL
 | |
| };
 | |
| 
 | |
| PWCHAR rfc2307_gid_attr[] =
 | |
| {
 | |
|   (PWCHAR) L"cn",
 | |
|   NULL
 | |
| };
 | |
| 
 | |
| /* ================================================================= */
 | |
| /* Helper method of cygheap_pwdgrp class.  It sets the user attribs  */
 | |
| /* from the settings in nsswitch.conf.				     */
 | |
| /* ================================================================= */
 | |
| 
 | |
| #define user_attr	(cygheap->pg.ldap_user_attr)
 | |
| 
 | |
| void
 | |
| cygheap_pwdgrp::init_ldap_user_attr ()
 | |
| {
 | |
|   ldap_user_attr = (PWCHAR *)
 | |
|     ccalloc_abort (HEAP_BUF, sizeof (std_user_attr) / sizeof (*std_user_attr)
 | |
| 			     + 3 * NSS_SCHEME_MAX + 1, sizeof (PWCHAR));
 | |
|   memcpy (ldap_user_attr, std_user_attr, sizeof (std_user_attr));
 | |
|   uint16_t freeattr_idx = sizeof (std_user_attr) / sizeof (*std_user_attr);
 | |
|   for (uint16_t idx = 0; idx < NSS_SCHEME_MAX; ++idx)
 | |
|     {
 | |
|       if (home_scheme[idx].method == NSS_SCHEME_FREEATTR)
 | |
| 	ldap_user_attr[freeattr_idx++] = home_scheme[idx].attrib;
 | |
|       if (shell_scheme[idx].method == NSS_SCHEME_FREEATTR)
 | |
| 	ldap_user_attr[freeattr_idx++] = shell_scheme[idx].attrib;
 | |
|       if (gecos_scheme[idx].method == NSS_SCHEME_FREEATTR)
 | |
| 	ldap_user_attr[freeattr_idx++] = gecos_scheme[idx].attrib;
 | |
|     }
 | |
| }
 | |
| 
 | |
| /* ================================================================= */
 | |
| /* Helper methods.						     */
 | |
| /* ================================================================= */
 | |
| 
 | |
| inline int
 | |
| cyg_ldap::map_ldaperr_to_errno (ULONG lerr)
 | |
| {
 | |
|   switch (lerr)
 | |
|     {
 | |
|     case LDAP_SUCCESS:
 | |
|       return NO_ERROR;
 | |
|     case LDAP_NO_RESULTS_RETURNED:
 | |
|       /* LdapMapErrorToWin32 maps LDAP_NO_RESULTS_RETURNED to ERROR_MORE_DATA,
 | |
| 	 which in turn is mapped to EMSGSIZE by geterrno_from_win_error.  This
 | |
| 	 is SO wrong, especially considering that LDAP_MORE_RESULTS_TO_RETURN
 | |
| 	 is mapped to ERROR_MORE_DATA as well :-P */
 | |
|       return ENMFILE;
 | |
|     default:
 | |
|       break;
 | |
|     }
 | |
|   return geterrno_from_win_error (LdapMapErrorToWin32 (lerr));
 | |
| }
 | |
| 
 | |
| inline int
 | |
| cyg_ldap::wait (cygthread *thr)
 | |
| {
 | |
|   if (!thr)
 | |
|     return EIO;
 | |
|   if (cygwait (*thr, cw_infinite, cw_sig | cw_sig_restart) != WAIT_OBJECT_0)
 | |
|     {
 | |
|       thr->terminate_thread ();
 | |
|       return EIO;
 | |
|     }
 | |
|   thr->detach ();
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| /* ================================================================= */
 | |
| /* Helper struct and functions for interruptible LDAP initalization. */
 | |
| /* ================================================================= */
 | |
| 
 | |
| struct cyg_ldap_init {
 | |
|   cyg_ldap *that;
 | |
|   PCWSTR domain;
 | |
|   bool ssl;
 | |
|   ULONG ret;
 | |
| };
 | |
| 
 | |
| ULONG
 | |
| cyg_ldap::connect_ssl (PCWSTR domain)
 | |
| {
 | |
|   ULONG ret;
 | |
| 
 | |
|   if (!(lh = ldap_sslinitW ((PWCHAR) domain, LDAP_SSL_PORT, 1)))
 | |
|     {
 | |
|       debug_printf ("ldap_init(%W) error 0x%02x", domain, LdapGetLastError ());
 | |
|       return LdapGetLastError ();
 | |
|     }
 | |
|   if ((ret = ldap_bind_s (lh, NULL, NULL, LDAP_AUTH_NEGOTIATE)) != LDAP_SUCCESS)
 | |
|     debug_printf ("ldap_bind(%W) 0x%02x", domain, ret);
 | |
|   else if ((ret = ldap_search_sW (lh, NULL, LDAP_SCOPE_BASE,
 | |
| 				  (PWCHAR) L"(objectclass=*)", rootdse_attr,
 | |
| 				  0, &msg))
 | |
|       != LDAP_SUCCESS)
 | |
|     debug_printf ("ldap_search(%W, ROOTDSE) error 0x%02x", domain, ret);
 | |
|   return ret;
 | |
| }
 | |
| 
 | |
| ULONG
 | |
| cyg_ldap::connect_non_ssl (PCWSTR domain)
 | |
| {
 | |
|   ULONG ret;
 | |
| 
 | |
|   if (!(lh = ldap_initW ((PWCHAR) domain, LDAP_PORT)))
 | |
|     {
 | |
|       debug_printf ("ldap_init(%W) error 0x%02x", domain, LdapGetLastError ());
 | |
|       return LdapGetLastError ();
 | |
|     }
 | |
|   if ((ret = ldap_set_option (lh, LDAP_OPT_SIGN, LDAP_OPT_ON))
 | |
|       != LDAP_SUCCESS)
 | |
|     debug_printf ("ldap_set_option(LDAP_OPT_SIGN) error 0x%02x", ret);
 | |
|   if ((ret = ldap_set_option (lh, LDAP_OPT_ENCRYPT, LDAP_OPT_ON))
 | |
|       != LDAP_SUCCESS)
 | |
|     debug_printf ("ldap_set_option(LDAP_OPT_ENCRYPT) error 0x%02x", ret);
 | |
|   if ((ret = ldap_bind_s (lh, NULL, NULL, LDAP_AUTH_NEGOTIATE)) != LDAP_SUCCESS)
 | |
|     debug_printf ("ldap_bind(%W) 0x%02x", domain, ret);
 | |
|   else if ((ret = ldap_search_sW (lh, NULL, LDAP_SCOPE_BASE,
 | |
| 				  (PWCHAR) L"(objectclass=*)", rootdse_attr,
 | |
| 				  0, &msg))
 | |
|       != LDAP_SUCCESS)
 | |
|     debug_printf ("ldap_search(%W, ROOTDSE) error 0x%02x", domain, ret);
 | |
|   return ret;
 | |
| }
 | |
| 
 | |
| static DWORD WINAPI
 | |
| ldap_init_thr (LPVOID param)
 | |
| {
 | |
|   cyg_ldap_init *cl = (cyg_ldap_init *) param;
 | |
|   cl->ret = cl->ssl ? cl->that->connect_ssl (cl->domain)
 | |
| 		    : cl->that->connect_non_ssl (cl->domain);
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| inline int
 | |
| cyg_ldap::connect (PCWSTR domain)
 | |
| {
 | |
|   /* FIXME?  connect_ssl can take ages even when failing, so we're trying to
 | |
|      do everything the non-SSL (but still encrypted) way. */
 | |
|   cyg_ldap_init cl = { this, domain, false, NO_ERROR };
 | |
|   cygthread *thr = new cygthread (ldap_init_thr, &cl, "ldap_init");
 | |
|   return wait (thr) ?: map_ldaperr_to_errno (cl.ret);
 | |
| }
 | |
| 
 | |
| /* ================================================================= */
 | |
| /* Helper struct and functions for interruptible LDAP search.        */
 | |
| /* ================================================================= */
 | |
| 
 | |
| struct cyg_ldap_search {
 | |
|   cyg_ldap *that;
 | |
|   PWCHAR base;
 | |
|   ULONG scope;
 | |
|   PWCHAR filter;
 | |
|   PWCHAR *attrs;
 | |
|   ULONG ret;
 | |
| };
 | |
| 
 | |
| ULONG
 | |
| cyg_ldap::search_s (PWCHAR base, ULONG scope, PWCHAR filter, PWCHAR *attrs)
 | |
| {
 | |
|   ULONG ret;
 | |
|   
 | |
|   if ((ret = ldap_search_sW (lh, base, scope, filter, attrs, 0, &msg))
 | |
|       != LDAP_SUCCESS)
 | |
|     debug_printf ("ldap_search_sW(%W,%W) error 0x%02x", base, filter, ret);
 | |
|   return ret;
 | |
| }
 | |
| 
 | |
| static DWORD WINAPI
 | |
| ldap_search_thr (LPVOID param)
 | |
| {
 | |
|   cyg_ldap_search *cl = (cyg_ldap_search *) param;
 | |
|   cl->ret = cl->that->search_s (cl->base, cl->scope, cl->filter, cl->attrs);
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| inline int
 | |
| cyg_ldap::search (PWCHAR base, ULONG scope, PWCHAR filter, PWCHAR *attrs)
 | |
| {
 | |
|   cyg_ldap_search cl = { this, base, scope, filter, attrs, NO_ERROR };
 | |
|   cygthread *thr = new cygthread (ldap_search_thr, &cl, "ldap_search");
 | |
|   return wait (thr) ?: map_ldaperr_to_errno (cl.ret);
 | |
| }
 | |
| 
 | |
| /* ================================================================= */
 | |
| /* Helper struct and functions for interruptible LDAP page search.        */
 | |
| /* ================================================================= */
 | |
| 
 | |
| struct cyg_ldap_next_page {
 | |
|   cyg_ldap *that;
 | |
|   ULONG ret;
 | |
| };
 | |
| 
 | |
| ULONG
 | |
| cyg_ldap::next_page_s ()
 | |
| {
 | |
|   ULONG total;
 | |
|   ULONG ret;
 | |
|   
 | |
|   do
 | |
|     {
 | |
|       ret = ldap_get_next_page_s (lh, srch_id, NULL, CYG_LDAP_ENUM_PAGESIZE,
 | |
| 				  &total, &msg);
 | |
|     }
 | |
|   while (ret == LDAP_SUCCESS && ldap_count_entries (lh, msg) == 0);
 | |
|   if (ret && ret != LDAP_NO_RESULTS_RETURNED)
 | |
|     debug_printf ("ldap_result() error 0x%02x", ret);
 | |
|   return ret;
 | |
| }
 | |
| 
 | |
| static DWORD WINAPI
 | |
| ldap_next_page_thr (LPVOID param)
 | |
| {
 | |
|   cyg_ldap_next_page *cl = (cyg_ldap_next_page *) param;
 | |
|   cl->ret = cl->that->next_page_s ();
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| inline int
 | |
| cyg_ldap::next_page ()
 | |
| {
 | |
|   cyg_ldap_next_page cl = { this, NO_ERROR };
 | |
|   cygthread *thr = new cygthread (ldap_next_page_thr, &cl, "ldap_next_page");
 | |
|   return wait (thr) ?: map_ldaperr_to_errno (cl.ret);
 | |
| }
 | |
| 
 | |
| /* ================================================================= */
 | |
| /* Public methods.						     */
 | |
| /* ================================================================= */
 | |
| 
 | |
| int
 | |
| cyg_ldap::open (PCWSTR domain)
 | |
| {
 | |
|   int ret = NO_ERROR;
 | |
| 
 | |
|   /* Already open? */
 | |
|   if (lh)
 | |
|     return NO_ERROR;
 | |
| 
 | |
|   if ((ret = connect (domain)) != NO_ERROR)
 | |
|     goto err;
 | |
|   /* Prime `ret' and fetch ROOTDSE search result. */
 | |
|   ret = EIO;
 | |
|   if (!(entry = ldap_first_entry (lh, msg)))
 | |
|     {
 | |
|       debug_printf ("No ROOTDSE entry for %W", domain);
 | |
|       goto err;
 | |
|     }
 | |
|   if (!(val = ldap_get_valuesW (lh, entry, rootdse_attr[0])))
 | |
|     {
 | |
|       debug_printf ("No %W value for %W", rootdse_attr[0], domain);
 | |
|       goto err;
 | |
|     }
 | |
|   if (!(def_context = wcsdup (val[0])))
 | |
|     {
 | |
|       debug_printf ("wcsdup(%W, %W) %d", domain, rootdse_attr[0],
 | |
|       					 get_errno ());
 | |
|       goto err;
 | |
|     }
 | |
|   ldap_value_freeW (val);
 | |
|   if ((val = ldap_get_valuesW (lh, entry, rootdse_attr[1])))
 | |
|     {
 | |
|       for (ULONG idx = 0; idx < ldap_count_valuesW (val); ++idx)
 | |
| 	if (!wcscmp (val[idx], LDAP_CAP_ACTIVE_DIRECTORY_OID_W))
 | |
| 	  {
 | |
| 	    isAD = true;
 | |
| 	    break;
 | |
| 	  }
 | |
|     }
 | |
|   ldap_value_freeW (val);
 | |
|   val = NULL;
 | |
|   ldap_msgfree (msg);
 | |
|   msg = entry = NULL;
 | |
|   return NO_ERROR;
 | |
| err:
 | |
|   close ();
 | |
|   return ret;
 | |
| }
 | |
| 
 | |
| void
 | |
| cyg_ldap::close ()
 | |
| {
 | |
|   if (srch_id != NULL)
 | |
|     ldap_search_abandon_page (lh, srch_id);
 | |
|   if (lh)
 | |
|     ldap_unbind (lh);
 | |
|   if (msg)
 | |
|     ldap_msgfree (msg);
 | |
|   if (val)
 | |
|     ldap_value_freeW (val);
 | |
|   if (def_context)
 | |
|     free (def_context);
 | |
|   lh = NULL;
 | |
|   msg = entry = NULL;
 | |
|   val = NULL;
 | |
|   def_context = NULL;
 | |
|   srch_id = NULL;
 | |
|   last_fetched_sid = NO_SID;
 | |
| }
 | |
| 
 | |
| PWCHAR
 | |
| cyg_ldap::get_string_attribute (PCWSTR name)
 | |
| {
 | |
|   if (val)
 | |
|     ldap_value_freeW (val);
 | |
|   val = ldap_get_valuesW (lh, entry, (PWCHAR) name);
 | |
|   if (val)
 | |
|     return val[0];
 | |
|   return NULL;
 | |
| }
 | |
| 
 | |
| uint32_t
 | |
| cyg_ldap::get_num_attribute (PCWSTR name)
 | |
| {
 | |
|   PWCHAR ret = get_string_attribute (name);
 | |
|   if (ret)
 | |
|     return (uint32_t) wcstoul (ret, NULL, 10);
 | |
|   return (uint32_t) -1;
 | |
| }
 | |
| 
 | |
| #define ACCOUNT_FILTER_START	L"(&(|(&(objectCategory=Person)" \
 | |
| 				       "(objectClass=User))" \
 | |
| 				     "(objectClass=Group))" \
 | |
| 				   "(objectSid="
 | |
| 
 | |
| #define ACCOUNT_FILTER_END	L"))"
 | |
| 
 | |
| bool
 | |
| cyg_ldap::fetch_ad_account (PSID sid, bool group, PCWSTR domain)
 | |
| {
 | |
|   WCHAR filter[sizeof (ACCOUNT_FILTER_START) + sizeof (ACCOUNT_FILTER_END)
 | |
| 	       + 3 * SECURITY_MAX_SID_SIZE + 1];
 | |
|   PWCHAR f, base = NULL;
 | |
|   LONG len = (LONG) RtlLengthSid (sid);
 | |
|   PBYTE s = (PBYTE) sid;
 | |
|   static WCHAR hex_wchars[] = L"0123456789abcdef";
 | |
|   tmp_pathbuf tp;
 | |
| 
 | |
|   if (last_fetched_sid == sid)
 | |
|     return true;
 | |
| 
 | |
|   if (open (NULL) != NO_ERROR)
 | |
|     return false;
 | |
| 
 | |
|   if (msg)
 | |
|     {
 | |
|       ldap_msgfree (msg);
 | |
|       msg = entry = NULL;
 | |
|     }
 | |
|   if (val)
 | |
|     {
 | |
|       ldap_value_freeW (val);
 | |
|       val = NULL;
 | |
|     }
 | |
|   f = wcpcpy (filter, ACCOUNT_FILTER_START);
 | |
|   while (len-- > 0)
 | |
|     {
 | |
|       *f++ = L'\\';
 | |
|       *f++ = hex_wchars[*s >> 4];
 | |
|       *f++ = hex_wchars[*s++ & 0xf];
 | |
|     }
 | |
|   wcpcpy (f, ACCOUNT_FILTER_END);
 | |
|   if (domain)
 | |
|     {
 | |
|       /* FIXME:  This is a hack.  The most correct solution is probably to
 | |
|          open a connection to the DC of the trusted domain.  But this always
 | |
| 	 takes extra time, so we're trying to avoid it.  If this results in
 | |
| 	 problems, we know what to do. */
 | |
|       base = tp.w_get ();
 | |
|       PWCHAR b = base;
 | |
|       for (PWCHAR dotp = (PWCHAR) domain; dotp && *dotp; domain = dotp)
 | |
| 	{
 | |
| 	  dotp = wcschr (domain, L'.');
 | |
| 	  if (dotp)
 | |
| 	    *dotp++ = L'\0';
 | |
| 	  if (b > base)
 | |
| 	    *b++ = L',';
 | |
| 	  b = wcpcpy (b, L"DC=");
 | |
| 	  b = wcpcpy (b, domain);
 | |
| 	}
 | |
|     }
 | |
|   else
 | |
|     {
 | |
|       /* def_context is only valid after open. */
 | |
|       base = def_context;
 | |
|     }
 | |
|   if (!user_attr)
 | |
|     cygheap->pg.init_ldap_user_attr ();
 | |
|   attr = group ? group_attr : user_attr;
 | |
|   if (search (base, LDAP_SCOPE_SUBTREE, filter, attr) != 0)
 | |
|       return false;
 | |
|   if (!(entry = ldap_first_entry (lh, msg)))
 | |
|     {
 | |
|       debug_printf ("No entry for %W in base %W", filter, base);
 | |
|       return false;
 | |
|     }
 | |
|   last_fetched_sid = sid;
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| int
 | |
| cyg_ldap::enumerate_ad_accounts (PCWSTR domain, bool group)
 | |
| {
 | |
|   int ret;
 | |
|   tmp_pathbuf tp;
 | |
|   PCWSTR filter;
 | |
| 
 | |
|   close ();
 | |
|   if ((ret = open (domain)) != NO_ERROR)
 | |
|     return ret;
 | |
| 
 | |
|   if (!group)
 | |
|     filter = L"(&(objectCategory=Person)"
 | |
| 		"(objectClass=User)"
 | |
| 		/* 512 == ADS_UF_NORMAL_ACCOUNT
 | |
| 		   Without checking this flag we'd enumerate undesired accounts
 | |
| 		   like, e.g., interdomain trusts. */
 | |
| 	        "(userAccountControl:" LDAP_MATCHING_RULE_BIT_AND ":=512)"
 | |
| 	        "(objectSid=*))";
 | |
|   else if (!domain)
 | |
|     /* From the local domain, we fetch well-known groups. */
 | |
|     filter = L"(&(objectClass=Group)"
 | |
| 		"(objectSid=*))";
 | |
|   else
 | |
|     /* From foreign domains, we don't. */
 | |
|     filter = L"(&(objectClass=Group)"
 | |
| 		/* 1 == BUILTIN_LOCAL_GROUP */
 | |
| 		"(!(groupType:" LDAP_MATCHING_RULE_BIT_AND ":=1))"
 | |
| 		"(objectSid=*))";
 | |
|   if (!user_attr)
 | |
|     cygheap->pg.init_ldap_user_attr ();
 | |
|   attr = group ? group_attr : user_attr;
 | |
|   srch_id = ldap_search_init_pageW (lh, def_context, LDAP_SCOPE_SUBTREE,
 | |
| 				    (PWCHAR) filter, attr, 0, NULL, NULL,
 | |
| 				    INFINITE, CYG_LDAP_ENUM_PAGESIZE, NULL);
 | |
|   if (srch_id == NULL)
 | |
|     {
 | |
|       debug_printf ("ldap_search_init_pageW(%W,%W) error 0x%02x",
 | |
| 		    def_context, filter, LdapGetLastError ());
 | |
|       return map_ldaperr_to_errno (LdapGetLastError ());
 | |
|     }
 | |
|   return NO_ERROR;
 | |
| }
 | |
| 
 | |
| int
 | |
| cyg_ldap::next_account (cygsid &sid)
 | |
| {
 | |
|   ULONG ret;
 | |
|   PLDAP_BERVAL *bval;
 | |
| 
 | |
|   if (entry)
 | |
|     {
 | |
|       if ((entry = ldap_next_entry (lh, entry))
 | |
| 	  && (bval = ldap_get_values_lenW (lh, entry, (PWCHAR) L"objectSid")))
 | |
| 	{
 | |
| 	  last_fetched_sid = sid = (PSID) bval[0]->bv_val;
 | |
| 	  ldap_value_free_len (bval);
 | |
| 	  return NO_ERROR;
 | |
| 	}
 | |
|       ldap_msgfree (msg);
 | |
|       msg = entry = NULL;
 | |
|     }
 | |
|   ret = next_page ();
 | |
|   if (ret == NO_ERROR)
 | |
|     {
 | |
|       if ((entry = ldap_first_entry (lh, msg))
 | |
| 	  && (bval = ldap_get_values_lenW (lh, entry, (PWCHAR) L"objectSid")))
 | |
| 	{
 | |
| 	  last_fetched_sid = sid = (PSID) bval[0]->bv_val;
 | |
| 	  ldap_value_free_len (bval);
 | |
| 	  return NO_ERROR;
 | |
| 	}
 | |
|       ret = EIO;
 | |
|     }
 | |
|   ldap_search_abandon_page (lh, srch_id);
 | |
|   srch_id = NULL;
 | |
|   return ret;
 | |
| }
 | |
| 
 | |
| #define SYSTEM_CONTAINER	L"CN=System,"
 | |
| 
 | |
| #define PSX_OFFSET_FILTER	L"(&(objectClass=trustedDomain)(name=%W))"
 | |
| #define PSX_OFFSET_FILTER_FLAT	L"(&(objectClass=trustedDomain)(flatName=%W))"
 | |
| 
 | |
| /* Return UINT32_MAX on error to allow differing between not being able
 | |
|    to fetch a value and a real 0 offset. */
 | |
| uint32_t
 | |
| cyg_ldap::fetch_posix_offset_for_domain (PCWSTR domain)
 | |
| {
 | |
|   WCHAR base[wcslen (def_context) + sizeof (SYSTEM_CONTAINER) / sizeof (WCHAR)];
 | |
|   WCHAR filter[sizeof (PSX_OFFSET_FILTER_FLAT) + wcslen (domain) + 1];
 | |
| 
 | |
|   if (msg)
 | |
|     {
 | |
|       ldap_msgfree (msg);
 | |
|       msg = entry = NULL;
 | |
|     }
 | |
|   if (val)
 | |
|     {
 | |
|       ldap_value_freeW (val);
 | |
|       val = NULL;
 | |
|     }
 | |
|   /* As base, use system container within default naming context to restrict
 | |
|      the search to this container only. */
 | |
|   wcpcpy (wcpcpy (base, SYSTEM_CONTAINER), def_context);
 | |
|   /* If domain name has no dot, it's a Netbios name.  In that case, filter
 | |
|      by flatName rather than by name. */
 | |
|   __small_swprintf (filter, wcschr (domain, L'.') ? PSX_OFFSET_FILTER
 | |
| 						  : PSX_OFFSET_FILTER_FLAT,
 | |
| 		    domain);
 | |
|   if (search (base, LDAP_SCOPE_ONELEVEL, filter, attr = tdom_attr) != 0)
 | |
|     return UINT32_MAX;
 | |
|   if (!(entry = ldap_first_entry (lh, msg)))
 | |
|     {
 | |
|       debug_printf ("No entry for %W in def_context %W", filter, def_context);
 | |
|       return UINT32_MAX;
 | |
|     }
 | |
|   return get_num_attribute (tdom_attr[0]);
 | |
| }
 | |
| 
 | |
| #define UXID_FILTER_GRP L"(&(objectClass=Group)" \
 | |
| 			   "(gidNumber=%u))"
 | |
| 
 | |
| #define UXID_FILTER_USR L"(&(objectCategory=Person)" \
 | |
| 			   "(objectClass=User)" \
 | |
| 			   "(uidNumber=%u))"
 | |
| 
 | |
| bool
 | |
| cyg_ldap::fetch_unix_sid_from_ad (uint32_t id, cygsid &sid, bool group)
 | |
| {
 | |
|   WCHAR filter[MAX (sizeof (UXID_FILTER_GRP), sizeof (UXID_FILTER_USR)) + 16];
 | |
|   PLDAP_BERVAL *bval;
 | |
| 
 | |
|   if (msg)
 | |
|     {
 | |
|       ldap_msgfree (msg);
 | |
|       msg = entry = NULL;
 | |
|     }
 | |
|   __small_swprintf (filter, group ? UXID_FILTER_GRP : UXID_FILTER_USR, id);
 | |
|   if (search (def_context, LDAP_SCOPE_SUBTREE, filter, sid_attr) != 0)
 | |
|     return false;
 | |
|   if ((entry = ldap_first_entry (lh, msg))
 | |
|       && (bval = ldap_get_values_lenW (lh, entry, (PWCHAR) L"objectSid")))
 | |
|     {
 | |
|       sid = (PSID) bval[0]->bv_val;
 | |
|       ldap_value_free_len (bval);
 | |
|       return true;
 | |
|     }
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| #define PSXID_FILTER_GRP L"(&(objectClass=posixGroup)" \
 | |
| 			    "(gidNumber=%u))"
 | |
| 
 | |
| #define PSXID_FILTER_USR L"(&(objectClass=posixAccount)" \
 | |
| 			    "(uidNumber=%u))"
 | |
| 
 | |
| PWCHAR
 | |
| cyg_ldap::fetch_unix_name_from_rfc2307 (uint32_t id, bool group)
 | |
| {
 | |
|   WCHAR filter[MAX (sizeof (PSXID_FILTER_GRP), sizeof (PSXID_FILTER_USR)) + 16];
 | |
| 
 | |
|   if (msg)
 | |
|     {
 | |
|       ldap_msgfree (msg);
 | |
|       msg = entry = NULL;
 | |
|     }
 | |
|   if (val)
 | |
|     {
 | |
|       ldap_value_freeW (val);
 | |
|       val = NULL;
 | |
|     }
 | |
|   attr = group ? rfc2307_gid_attr : rfc2307_uid_attr;
 | |
|   __small_swprintf (filter, group ? PSXID_FILTER_GRP : PSXID_FILTER_USR, id);
 | |
|   if (search (def_context, LDAP_SCOPE_SUBTREE, filter, attr) != 0)
 | |
|     return NULL;
 | |
|   if (!(entry = ldap_first_entry (lh, msg)))
 | |
|     {
 | |
|       debug_printf ("No entry for %W in def_context %W", filter, def_context);
 | |
|       return NULL;
 | |
|     }
 | |
|   return get_string_attribute (attr[0]);
 | |
| }
 | |
| 
 | |
| uid_t
 | |
| cyg_ldap::remap_uid (uid_t uid)
 | |
| {
 | |
|   cygsid user (NO_SID);
 | |
|   PWCHAR name;
 | |
|   struct passwd *pw;
 | |
| 
 | |
|   if (isAD)
 | |
|     {
 | |
|       if (fetch_unix_sid_from_ad (uid, user, false)
 | |
| 	  && user != NO_SID
 | |
| 	  && (pw = internal_getpwsid (user, this)))
 | |
| 	return pw->pw_uid;
 | |
|     }
 | |
|   else if ((name = fetch_unix_name_from_rfc2307 (uid, false)))
 | |
|     {
 | |
|       char *mbname = NULL;
 | |
|       sys_wcstombs_alloc (&mbname, HEAP_NOTHEAP, name);
 | |
|       if ((pw = internal_getpwnam (mbname)))
 | |
| 	return pw->pw_uid;
 | |
|     }
 | |
|   return ILLEGAL_UID;
 | |
| }
 | |
| 
 | |
| gid_t
 | |
| cyg_ldap::remap_gid (gid_t gid)
 | |
| {
 | |
|   cygsid group (NO_SID);
 | |
|   PWCHAR name;
 | |
|   struct group *gr;
 | |
| 
 | |
|   if (isAD)
 | |
|     {
 | |
|       if (fetch_unix_sid_from_ad (gid, group, true)
 | |
| 	  && group != NO_SID
 | |
| 	  && (gr = internal_getgrsid (group, this)))
 | |
| 	return gr->gr_gid;
 | |
|     }
 | |
|   else if ((name = fetch_unix_name_from_rfc2307 (gid, true)))
 | |
|     {
 | |
|       char *mbname = NULL;
 | |
|       sys_wcstombs_alloc (&mbname, HEAP_NOTHEAP, name);
 | |
|       if ((gr = internal_getgrnam (mbname)))
 | |
| 	return gr->gr_gid;
 | |
|     }
 | |
|   return ILLEGAL_GID;
 | |
| }
 |