2014-02-10 03:44:56 +08:00
|
|
|
/* ldap.cc: Helper functions for ldap access to Active Directory.
|
|
|
|
|
|
|
|
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"
|
2019-03-02 19:43:34 +08:00
|
|
|
#include <lm.h>
|
|
|
|
#include <dsgetdc.h>
|
|
|
|
#include <iptypes.h>
|
|
|
|
#include <sys/param.h>
|
2014-02-10 03:44:56 +08:00
|
|
|
#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"
|
2014-02-17 23:36:33 +08:00
|
|
|
#include "tls_pbuf.h"
|
2014-02-10 03:44:56 +08:00
|
|
|
|
2014-06-23 17:09:54 +08:00
|
|
|
#define CYG_LDAP_ENUM_PAGESIZE 100 /* entries per page */
|
|
|
|
|
2014-02-10 03:44:56 +08:00
|
|
|
static PWCHAR rootdse_attr[] =
|
|
|
|
{
|
|
|
|
(PWCHAR) L"defaultNamingContext",
|
|
|
|
(PWCHAR) L"supportedCapabilities",
|
|
|
|
NULL
|
|
|
|
};
|
|
|
|
|
2014-11-27 03:46:59 +08:00
|
|
|
static const PCWSTR std_user_attr[] =
|
2014-02-10 03:44:56 +08:00
|
|
|
{
|
2015-02-25 04:52:57 +08:00
|
|
|
L"sAMAccountName",
|
|
|
|
L"objectSid",
|
2014-11-27 03:46:59 +08:00
|
|
|
L"primaryGroupID",
|
|
|
|
L"uidNumber",
|
2019-02-24 00:22:44 +08:00
|
|
|
L"profilePath",
|
2014-11-27 03:46:59 +08:00
|
|
|
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"
|
2014-02-10 03:44:56 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
static PWCHAR group_attr[] =
|
|
|
|
{
|
2015-02-25 04:52:57 +08:00
|
|
|
(PWCHAR) L"sAMAccountName",
|
|
|
|
(PWCHAR) L"objectSid",
|
2014-02-10 03:44:56 +08:00
|
|
|
(PWCHAR) L"gidNumber",
|
2014-11-27 03:46:59 +08:00
|
|
|
(PWCHAR) L"cygwinUnixGid", /* TODO */
|
2014-02-10 03:44:56 +08:00
|
|
|
NULL
|
|
|
|
};
|
|
|
|
|
|
|
|
PWCHAR tdom_attr[] =
|
|
|
|
{
|
|
|
|
(PWCHAR) L"trustPosixOffset",
|
|
|
|
NULL
|
|
|
|
};
|
|
|
|
|
2014-02-17 23:36:33 +08:00
|
|
|
PWCHAR sid_attr[] =
|
2014-02-10 03:44:56 +08:00
|
|
|
{
|
|
|
|
(PWCHAR) L"objectSid",
|
|
|
|
NULL
|
|
|
|
};
|
|
|
|
|
|
|
|
PWCHAR rfc2307_uid_attr[] =
|
|
|
|
{
|
|
|
|
(PWCHAR) L"uid",
|
|
|
|
NULL
|
|
|
|
};
|
|
|
|
|
|
|
|
PWCHAR rfc2307_gid_attr[] =
|
|
|
|
{
|
|
|
|
(PWCHAR) L"cn",
|
|
|
|
NULL
|
|
|
|
};
|
|
|
|
|
2014-11-27 03:46:59 +08:00
|
|
|
/* ================================================================= */
|
|
|
|
/* 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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-06-25 17:10:50 +08:00
|
|
|
/* ================================================================= */
|
|
|
|
/* 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;
|
2015-02-27 01:27:59 +08:00
|
|
|
if (cygwait (*thr, cw_infinite, cw_sig | cw_sig_restart) != WAIT_OBJECT_0)
|
2014-06-25 17:10:50 +08:00
|
|
|
{
|
|
|
|
thr->terminate_thread ();
|
2015-02-27 01:27:59 +08:00
|
|
|
return EIO;
|
2014-06-25 17:10:50 +08:00
|
|
|
}
|
|
|
|
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
|
2014-02-10 03:44:56 +08:00
|
|
|
cyg_ldap::connect_ssl (PCWSTR domain)
|
|
|
|
{
|
2014-06-25 17:10:50 +08:00
|
|
|
ULONG ret;
|
2014-02-10 03:44:56 +08:00
|
|
|
|
|
|
|
if (!(lh = ldap_sslinitW ((PWCHAR) domain, LDAP_SSL_PORT, 1)))
|
|
|
|
{
|
|
|
|
debug_printf ("ldap_init(%W) error 0x%02x", domain, LdapGetLastError ());
|
2014-06-25 17:10:50 +08:00
|
|
|
return LdapGetLastError ();
|
2014-02-10 03:44:56 +08:00
|
|
|
}
|
|
|
|
if ((ret = ldap_bind_s (lh, NULL, NULL, LDAP_AUTH_NEGOTIATE)) != LDAP_SUCCESS)
|
2014-06-25 17:10:50 +08:00
|
|
|
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;
|
2014-02-10 03:44:56 +08:00
|
|
|
}
|
|
|
|
|
2014-06-25 17:10:50 +08:00
|
|
|
ULONG
|
2014-02-10 03:44:56 +08:00
|
|
|
cyg_ldap::connect_non_ssl (PCWSTR domain)
|
|
|
|
{
|
2014-06-25 17:10:50 +08:00
|
|
|
ULONG ret;
|
2014-02-10 03:44:56 +08:00
|
|
|
|
|
|
|
if (!(lh = ldap_initW ((PWCHAR) domain, LDAP_PORT)))
|
|
|
|
{
|
|
|
|
debug_printf ("ldap_init(%W) error 0x%02x", domain, LdapGetLastError ());
|
2014-06-25 17:10:50 +08:00
|
|
|
return LdapGetLastError ();
|
2014-02-10 03:44:56 +08:00
|
|
|
}
|
|
|
|
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)
|
2014-06-25 17:10:50 +08:00
|
|
|
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;
|
2015-02-18 18:25:14 +08:00
|
|
|
ULONG scope;
|
2014-06-25 17:10:50 +08:00
|
|
|
PWCHAR filter;
|
|
|
|
PWCHAR *attrs;
|
|
|
|
ULONG ret;
|
|
|
|
};
|
|
|
|
|
|
|
|
ULONG
|
2015-02-18 18:25:14 +08:00
|
|
|
cyg_ldap::search_s (PWCHAR base, ULONG scope, PWCHAR filter, PWCHAR *attrs)
|
2014-06-25 17:10:50 +08:00
|
|
|
{
|
|
|
|
ULONG ret;
|
2019-02-13 19:43:25 +08:00
|
|
|
|
2015-02-18 18:25:14 +08:00
|
|
|
if ((ret = ldap_search_sW (lh, base, scope, filter, attrs, 0, &msg))
|
|
|
|
!= LDAP_SUCCESS)
|
2014-06-25 17:10:50 +08:00
|
|
|
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;
|
2015-02-18 18:25:14 +08:00
|
|
|
cl->ret = cl->that->search_s (cl->base, cl->scope, cl->filter, cl->attrs);
|
2014-06-25 17:10:50 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline int
|
2015-02-18 18:25:14 +08:00
|
|
|
cyg_ldap::search (PWCHAR base, ULONG scope, PWCHAR filter, PWCHAR *attrs)
|
2014-06-25 17:10:50 +08:00
|
|
|
{
|
2015-02-18 18:25:14 +08:00
|
|
|
cyg_ldap_search cl = { this, base, scope, filter, attrs, NO_ERROR };
|
2014-06-25 17:10:50 +08:00
|
|
|
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;
|
2019-02-13 19:43:25 +08:00
|
|
|
|
2014-06-25 17:10:50 +08:00
|
|
|
do
|
2014-02-10 03:44:56 +08:00
|
|
|
{
|
2014-06-25 17:10:50 +08:00
|
|
|
ret = ldap_get_next_page_s (lh, srch_id, NULL, CYG_LDAP_ENUM_PAGESIZE,
|
2015-02-25 04:52:57 +08:00
|
|
|
&total, &msg);
|
2014-02-10 03:44:56 +08:00
|
|
|
}
|
2015-02-25 04:52:57 +08:00
|
|
|
while (ret == LDAP_SUCCESS && ldap_count_entries (lh, msg) == 0);
|
2014-06-25 17:10:50 +08:00
|
|
|
if (ret && ret != LDAP_NO_RESULTS_RETURNED)
|
|
|
|
debug_printf ("ldap_result() error 0x%02x", ret);
|
|
|
|
return ret;
|
2014-02-10 03:44:56 +08:00
|
|
|
}
|
|
|
|
|
2014-06-25 17:10:50 +08:00
|
|
|
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
|
2014-02-10 03:44:56 +08:00
|
|
|
cyg_ldap::open (PCWSTR domain)
|
|
|
|
{
|
2015-02-13 00:55:38 +08:00
|
|
|
int ret = NO_ERROR;
|
2014-02-10 03:44:56 +08:00
|
|
|
|
2014-02-27 20:57:27 +08:00
|
|
|
/* Already open? */
|
|
|
|
if (lh)
|
2015-02-13 00:55:38 +08:00
|
|
|
return NO_ERROR;
|
2014-02-27 20:57:27 +08:00
|
|
|
|
2014-06-25 17:10:50 +08:00
|
|
|
if ((ret = connect (domain)) != NO_ERROR)
|
|
|
|
goto err;
|
|
|
|
/* Prime `ret' and fetch ROOTDSE search result. */
|
|
|
|
ret = EIO;
|
2014-02-10 03:44:56 +08:00
|
|
|
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])))
|
|
|
|
{
|
2015-02-18 05:08:01 +08:00
|
|
|
debug_printf ("No %W value for %W", rootdse_attr[0], domain);
|
2014-02-10 03:44:56 +08:00
|
|
|
goto err;
|
|
|
|
}
|
2015-02-18 05:08:01 +08:00
|
|
|
if (!(def_context = wcsdup (val[0])))
|
2014-02-10 03:44:56 +08:00
|
|
|
{
|
2015-02-18 05:08:01 +08:00
|
|
|
debug_printf ("wcsdup(%W, %W) %d", domain, rootdse_attr[0],
|
2019-02-13 19:43:25 +08:00
|
|
|
get_errno ());
|
2014-02-10 03:44:56 +08:00
|
|
|
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;
|
2014-05-23 18:29:11 +08:00
|
|
|
ldap_msgfree (msg);
|
2014-06-25 17:10:50 +08:00
|
|
|
msg = entry = NULL;
|
2015-02-13 00:55:38 +08:00
|
|
|
return NO_ERROR;
|
2014-02-10 03:44:56 +08:00
|
|
|
err:
|
|
|
|
close ();
|
2014-06-25 17:10:50 +08:00
|
|
|
return ret;
|
2014-02-10 03:44:56 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
cyg_ldap::close ()
|
|
|
|
{
|
2014-05-23 04:07:25 +08:00
|
|
|
if (srch_id != NULL)
|
|
|
|
ldap_search_abandon_page (lh, srch_id);
|
2014-02-10 03:44:56 +08:00
|
|
|
if (lh)
|
|
|
|
ldap_unbind (lh);
|
|
|
|
if (msg)
|
2014-05-23 18:29:11 +08:00
|
|
|
ldap_msgfree (msg);
|
2014-02-10 03:44:56 +08:00
|
|
|
if (val)
|
|
|
|
ldap_value_freeW (val);
|
2015-02-18 05:08:01 +08:00
|
|
|
if (def_context)
|
|
|
|
free (def_context);
|
2014-02-10 03:44:56 +08:00
|
|
|
lh = NULL;
|
|
|
|
msg = entry = NULL;
|
|
|
|
val = NULL;
|
2015-02-18 05:08:01 +08:00
|
|
|
def_context = NULL;
|
2014-05-23 04:07:25 +08:00
|
|
|
srch_id = NULL;
|
2015-02-13 00:55:38 +08:00
|
|
|
last_fetched_sid = NO_SID;
|
2014-02-10 03:44:56 +08:00
|
|
|
}
|
|
|
|
|
2015-02-25 04:52:57 +08:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2015-02-18 18:04:14 +08:00
|
|
|
#define ACCOUNT_FILTER_START L"(&(|(&(objectCategory=Person)" \
|
|
|
|
"(objectClass=User))" \
|
|
|
|
"(objectClass=Group))" \
|
|
|
|
"(objectSid="
|
|
|
|
|
|
|
|
#define ACCOUNT_FILTER_END L"))"
|
|
|
|
|
2014-02-10 03:44:56 +08:00
|
|
|
bool
|
2014-05-22 22:50:24 +08:00
|
|
|
cyg_ldap::fetch_ad_account (PSID sid, bool group, PCWSTR domain)
|
2014-02-10 03:44:56 +08:00
|
|
|
{
|
2015-02-18 18:04:14 +08:00
|
|
|
WCHAR filter[sizeof (ACCOUNT_FILTER_START) + sizeof (ACCOUNT_FILTER_END)
|
|
|
|
+ 3 * SECURITY_MAX_SID_SIZE + 1];
|
|
|
|
PWCHAR f, base = NULL;
|
2014-02-10 03:44:56 +08:00
|
|
|
LONG len = (LONG) RtlLengthSid (sid);
|
|
|
|
PBYTE s = (PBYTE) sid;
|
|
|
|
static WCHAR hex_wchars[] = L"0123456789abcdef";
|
2014-05-22 22:50:24 +08:00
|
|
|
tmp_pathbuf tp;
|
2014-02-10 03:44:56 +08:00
|
|
|
|
2015-02-13 00:55:38 +08:00
|
|
|
if (last_fetched_sid == sid)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
if (open (NULL) != NO_ERROR)
|
|
|
|
return false;
|
|
|
|
|
2014-02-10 03:44:56 +08:00
|
|
|
if (msg)
|
|
|
|
{
|
2014-05-23 18:29:11 +08:00
|
|
|
ldap_msgfree (msg);
|
2014-02-10 03:44:56 +08:00
|
|
|
msg = entry = NULL;
|
|
|
|
}
|
|
|
|
if (val)
|
|
|
|
{
|
|
|
|
ldap_value_freeW (val);
|
|
|
|
val = NULL;
|
|
|
|
}
|
2015-02-18 18:04:14 +08:00
|
|
|
f = wcpcpy (filter, ACCOUNT_FILTER_START);
|
2014-02-10 03:44:56 +08:00
|
|
|
while (len-- > 0)
|
|
|
|
{
|
|
|
|
*f++ = L'\\';
|
|
|
|
*f++ = hex_wchars[*s >> 4];
|
|
|
|
*f++ = hex_wchars[*s++ & 0xf];
|
|
|
|
}
|
2015-02-18 18:04:14 +08:00
|
|
|
wcpcpy (f, ACCOUNT_FILTER_END);
|
2014-05-22 22:50:24 +08:00
|
|
|
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. */
|
2015-02-18 05:08:01 +08:00
|
|
|
base = tp.w_get ();
|
|
|
|
PWCHAR b = base;
|
2019-03-02 19:43:34 +08:00
|
|
|
for (PCWSTR dotp = domain; dotp && *dotp; domain = dotp)
|
2014-05-22 22:50:24 +08:00
|
|
|
{
|
|
|
|
dotp = wcschr (domain, L'.');
|
2015-02-18 05:08:01 +08:00
|
|
|
if (b > base)
|
|
|
|
*b++ = L',';
|
|
|
|
b = wcpcpy (b, L"DC=");
|
2019-03-02 19:43:34 +08:00
|
|
|
b = dotp ? wcpncpy (b, domain, dotp++ - domain) : wcpcpy (b, domain);
|
2014-05-22 22:50:24 +08:00
|
|
|
}
|
2019-03-02 19:43:34 +08:00
|
|
|
debug_printf ("naming context <%W>", base);
|
2014-05-22 22:50:24 +08:00
|
|
|
}
|
2015-02-13 00:55:38 +08:00
|
|
|
else
|
|
|
|
{
|
2015-02-18 05:08:01 +08:00
|
|
|
/* def_context is only valid after open. */
|
|
|
|
base = def_context;
|
2015-02-13 00:55:38 +08:00
|
|
|
}
|
2014-11-27 03:46:59 +08:00
|
|
|
if (!user_attr)
|
|
|
|
cygheap->pg.init_ldap_user_attr ();
|
2014-02-10 03:44:56 +08:00
|
|
|
attr = group ? group_attr : user_attr;
|
2015-02-18 18:25:14 +08:00
|
|
|
if (search (base, LDAP_SCOPE_SUBTREE, filter, attr) != 0)
|
2014-02-10 03:44:56 +08:00
|
|
|
return false;
|
|
|
|
if (!(entry = ldap_first_entry (lh, msg)))
|
|
|
|
{
|
2015-02-18 05:08:01 +08:00
|
|
|
debug_printf ("No entry for %W in base %W", filter, base);
|
2014-02-10 03:44:56 +08:00
|
|
|
return false;
|
|
|
|
}
|
2015-02-13 00:55:38 +08:00
|
|
|
last_fetched_sid = sid;
|
2014-02-10 03:44:56 +08:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2014-06-25 17:10:50 +08:00
|
|
|
int
|
2014-02-17 23:36:33 +08:00
|
|
|
cyg_ldap::enumerate_ad_accounts (PCWSTR domain, bool group)
|
|
|
|
{
|
2014-06-25 17:10:50 +08:00
|
|
|
int ret;
|
2014-02-17 23:36:33 +08:00
|
|
|
tmp_pathbuf tp;
|
|
|
|
PCWSTR filter;
|
|
|
|
|
2014-03-06 04:58:33 +08:00
|
|
|
close ();
|
2014-06-25 17:10:50 +08:00
|
|
|
if ((ret = open (domain)) != NO_ERROR)
|
|
|
|
return ret;
|
2014-03-06 04:58:33 +08:00
|
|
|
|
2014-02-17 23:36:33 +08:00
|
|
|
if (!group)
|
2015-02-18 05:08:01 +08:00
|
|
|
filter = L"(&(objectCategory=Person)"
|
|
|
|
"(objectClass=User)"
|
2015-02-25 04:52:57 +08:00
|
|
|
/* 512 == ADS_UF_NORMAL_ACCOUNT
|
|
|
|
Without checking this flag we'd enumerate undesired accounts
|
|
|
|
like, e.g., interdomain trusts. */
|
2014-02-17 23:36:33 +08:00
|
|
|
"(userAccountControl:" LDAP_MATCHING_RULE_BIT_AND ":=512)"
|
|
|
|
"(objectSid=*))";
|
|
|
|
else if (!domain)
|
2015-02-25 04:52:57 +08:00
|
|
|
/* From the local domain, we fetch well-known groups. */
|
2014-02-17 23:36:33 +08:00
|
|
|
filter = L"(&(objectClass=Group)"
|
|
|
|
"(objectSid=*))";
|
|
|
|
else
|
2015-02-25 04:52:57 +08:00
|
|
|
/* From foreign domains, we don't. */
|
2014-02-17 23:36:33 +08:00
|
|
|
filter = L"(&(objectClass=Group)"
|
2014-11-27 03:46:59 +08:00
|
|
|
/* 1 == BUILTIN_LOCAL_GROUP */
|
2014-02-17 23:36:33 +08:00
|
|
|
"(!(groupType:" LDAP_MATCHING_RULE_BIT_AND ":=1))"
|
|
|
|
"(objectSid=*))";
|
2015-02-25 04:52:57 +08:00
|
|
|
if (!user_attr)
|
|
|
|
cygheap->pg.init_ldap_user_attr ();
|
|
|
|
attr = group ? group_attr : user_attr;
|
2015-02-18 05:08:01 +08:00
|
|
|
srch_id = ldap_search_init_pageW (lh, def_context, LDAP_SCOPE_SUBTREE,
|
2015-02-25 04:52:57 +08:00
|
|
|
(PWCHAR) filter, attr, 0, NULL, NULL,
|
2014-06-25 17:10:50 +08:00
|
|
|
INFINITE, CYG_LDAP_ENUM_PAGESIZE, NULL);
|
2014-05-23 04:07:25 +08:00
|
|
|
if (srch_id == NULL)
|
2014-02-17 23:36:33 +08:00
|
|
|
{
|
2014-05-23 04:07:25 +08:00
|
|
|
debug_printf ("ldap_search_init_pageW(%W,%W) error 0x%02x",
|
2015-02-18 05:08:01 +08:00
|
|
|
def_context, filter, LdapGetLastError ());
|
2014-06-25 17:10:50 +08:00
|
|
|
return map_ldaperr_to_errno (LdapGetLastError ());
|
2014-02-17 23:36:33 +08:00
|
|
|
}
|
2014-06-25 17:10:50 +08:00
|
|
|
return NO_ERROR;
|
2014-02-17 23:36:33 +08:00
|
|
|
}
|
|
|
|
|
2014-06-25 17:10:50 +08:00
|
|
|
int
|
2014-02-17 23:36:33 +08:00
|
|
|
cyg_ldap::next_account (cygsid &sid)
|
|
|
|
{
|
|
|
|
ULONG ret;
|
|
|
|
PLDAP_BERVAL *bval;
|
|
|
|
|
2015-02-25 04:52:57 +08:00
|
|
|
if (entry)
|
2014-02-17 23:36:33 +08:00
|
|
|
{
|
2015-02-25 04:52:57 +08:00
|
|
|
if ((entry = ldap_next_entry (lh, entry))
|
|
|
|
&& (bval = ldap_get_values_lenW (lh, entry, (PWCHAR) L"objectSid")))
|
2014-05-23 04:07:25 +08:00
|
|
|
{
|
2015-02-25 04:52:57 +08:00
|
|
|
last_fetched_sid = sid = (PSID) bval[0]->bv_val;
|
2014-05-23 04:07:25 +08:00
|
|
|
ldap_value_free_len (bval);
|
2014-06-25 17:10:50 +08:00
|
|
|
return NO_ERROR;
|
2014-05-23 04:07:25 +08:00
|
|
|
}
|
2015-02-25 04:52:57 +08:00
|
|
|
ldap_msgfree (msg);
|
|
|
|
msg = entry = NULL;
|
2014-02-17 23:36:33 +08:00
|
|
|
}
|
2014-06-25 17:10:50 +08:00
|
|
|
ret = next_page ();
|
|
|
|
if (ret == NO_ERROR)
|
2014-02-17 23:36:33 +08:00
|
|
|
{
|
2015-02-25 04:52:57 +08:00
|
|
|
if ((entry = ldap_first_entry (lh, msg))
|
|
|
|
&& (bval = ldap_get_values_lenW (lh, entry, (PWCHAR) L"objectSid")))
|
2014-06-25 17:10:50 +08:00
|
|
|
{
|
2015-02-25 04:52:57 +08:00
|
|
|
last_fetched_sid = sid = (PSID) bval[0]->bv_val;
|
2014-06-25 17:10:50 +08:00
|
|
|
ldap_value_free_len (bval);
|
|
|
|
return NO_ERROR;
|
|
|
|
}
|
|
|
|
ret = EIO;
|
2014-02-17 23:36:33 +08:00
|
|
|
}
|
2014-05-23 18:29:11 +08:00
|
|
|
ldap_search_abandon_page (lh, srch_id);
|
|
|
|
srch_id = NULL;
|
2014-06-25 17:10:50 +08:00
|
|
|
return ret;
|
2014-02-17 23:36:33 +08:00
|
|
|
}
|
|
|
|
|
2015-02-18 05:08:01 +08:00
|
|
|
#define SYSTEM_CONTAINER L"CN=System,"
|
|
|
|
|
2015-02-18 18:04:14 +08:00
|
|
|
#define PSX_OFFSET_FILTER L"(&(objectClass=trustedDomain)(name=%W))"
|
|
|
|
#define PSX_OFFSET_FILTER_FLAT L"(&(objectClass=trustedDomain)(flatName=%W))"
|
|
|
|
|
2014-07-29 16:53:13 +08:00
|
|
|
/* Return UINT32_MAX on error to allow differing between not being able
|
|
|
|
to fetch a value and a real 0 offset. */
|
2014-02-10 03:44:56 +08:00
|
|
|
uint32_t
|
|
|
|
cyg_ldap::fetch_posix_offset_for_domain (PCWSTR domain)
|
|
|
|
{
|
2015-02-18 05:08:01 +08:00
|
|
|
WCHAR base[wcslen (def_context) + sizeof (SYSTEM_CONTAINER) / sizeof (WCHAR)];
|
2015-02-18 18:04:14 +08:00
|
|
|
WCHAR filter[sizeof (PSX_OFFSET_FILTER_FLAT) + wcslen (domain) + 1];
|
2014-02-10 03:44:56 +08:00
|
|
|
|
|
|
|
if (msg)
|
|
|
|
{
|
2014-05-23 18:29:11 +08:00
|
|
|
ldap_msgfree (msg);
|
2014-02-10 03:44:56 +08:00
|
|
|
msg = entry = NULL;
|
|
|
|
}
|
|
|
|
if (val)
|
|
|
|
{
|
|
|
|
ldap_value_freeW (val);
|
|
|
|
val = NULL;
|
|
|
|
}
|
2015-02-18 18:04:14 +08:00
|
|
|
/* As base, use system container within default naming context to restrict
|
|
|
|
the search to this container only. */
|
|
|
|
wcpcpy (wcpcpy (base, SYSTEM_CONTAINER), def_context);
|
2014-02-13 22:04:03 +08:00
|
|
|
/* If domain name has no dot, it's a Netbios name. In that case, filter
|
|
|
|
by flatName rather than by name. */
|
2015-02-18 18:04:14 +08:00
|
|
|
__small_swprintf (filter, wcschr (domain, L'.') ? PSX_OFFSET_FILTER
|
|
|
|
: PSX_OFFSET_FILTER_FLAT,
|
|
|
|
domain);
|
2015-02-18 18:25:14 +08:00
|
|
|
if (search (base, LDAP_SCOPE_ONELEVEL, filter, attr = tdom_attr) != 0)
|
2014-07-29 16:53:13 +08:00
|
|
|
return UINT32_MAX;
|
2014-02-10 03:44:56 +08:00
|
|
|
if (!(entry = ldap_first_entry (lh, msg)))
|
|
|
|
{
|
2015-02-18 05:08:01 +08:00
|
|
|
debug_printf ("No entry for %W in def_context %W", filter, def_context);
|
2014-07-29 16:53:13 +08:00
|
|
|
return UINT32_MAX;
|
2014-02-10 03:44:56 +08:00
|
|
|
}
|
2015-02-25 04:52:57 +08:00
|
|
|
return get_num_attribute (tdom_attr[0]);
|
2014-02-10 03:44:56 +08:00
|
|
|
}
|
|
|
|
|
2015-02-18 18:04:14 +08:00
|
|
|
#define UXID_FILTER_GRP L"(&(objectClass=Group)" \
|
|
|
|
"(gidNumber=%u))"
|
|
|
|
|
|
|
|
#define UXID_FILTER_USR L"(&(objectCategory=Person)" \
|
|
|
|
"(objectClass=User)" \
|
|
|
|
"(uidNumber=%u))"
|
|
|
|
|
2014-02-10 03:44:56 +08:00
|
|
|
bool
|
|
|
|
cyg_ldap::fetch_unix_sid_from_ad (uint32_t id, cygsid &sid, bool group)
|
|
|
|
{
|
2015-02-18 18:04:14 +08:00
|
|
|
WCHAR filter[MAX (sizeof (UXID_FILTER_GRP), sizeof (UXID_FILTER_USR)) + 16];
|
2014-02-10 03:44:56 +08:00
|
|
|
PLDAP_BERVAL *bval;
|
|
|
|
|
|
|
|
if (msg)
|
|
|
|
{
|
2014-05-23 18:29:11 +08:00
|
|
|
ldap_msgfree (msg);
|
2014-02-10 03:44:56 +08:00
|
|
|
msg = entry = NULL;
|
|
|
|
}
|
2015-02-18 18:04:14 +08:00
|
|
|
__small_swprintf (filter, group ? UXID_FILTER_GRP : UXID_FILTER_USR, id);
|
2015-02-18 18:25:14 +08:00
|
|
|
if (search (def_context, LDAP_SCOPE_SUBTREE, filter, sid_attr) != 0)
|
2014-06-25 17:10:50 +08:00
|
|
|
return false;
|
2014-02-10 03:44:56 +08:00
|
|
|
if ((entry = ldap_first_entry (lh, msg))
|
2015-02-25 04:52:57 +08:00
|
|
|
&& (bval = ldap_get_values_lenW (lh, entry, (PWCHAR) L"objectSid")))
|
2014-02-10 03:44:56 +08:00
|
|
|
{
|
|
|
|
sid = (PSID) bval[0]->bv_val;
|
|
|
|
ldap_value_free_len (bval);
|
2014-02-17 23:36:33 +08:00
|
|
|
return true;
|
2014-02-10 03:44:56 +08:00
|
|
|
}
|
2014-02-17 23:36:33 +08:00
|
|
|
return false;
|
2014-02-10 03:44:56 +08:00
|
|
|
}
|
|
|
|
|
2015-02-18 18:04:14 +08:00
|
|
|
#define PSXID_FILTER_GRP L"(&(objectClass=posixGroup)" \
|
|
|
|
"(gidNumber=%u))"
|
|
|
|
|
|
|
|
#define PSXID_FILTER_USR L"(&(objectClass=posixAccount)" \
|
|
|
|
"(uidNumber=%u))"
|
|
|
|
|
2014-02-10 03:44:56 +08:00
|
|
|
PWCHAR
|
|
|
|
cyg_ldap::fetch_unix_name_from_rfc2307 (uint32_t id, bool group)
|
|
|
|
{
|
2015-02-18 18:04:14 +08:00
|
|
|
WCHAR filter[MAX (sizeof (PSXID_FILTER_GRP), sizeof (PSXID_FILTER_USR)) + 16];
|
2014-02-10 03:44:56 +08:00
|
|
|
|
|
|
|
if (msg)
|
|
|
|
{
|
2014-05-23 18:29:11 +08:00
|
|
|
ldap_msgfree (msg);
|
2014-02-10 03:44:56 +08:00
|
|
|
msg = entry = NULL;
|
|
|
|
}
|
|
|
|
if (val)
|
|
|
|
{
|
|
|
|
ldap_value_freeW (val);
|
|
|
|
val = NULL;
|
|
|
|
}
|
|
|
|
attr = group ? rfc2307_gid_attr : rfc2307_uid_attr;
|
2015-02-18 18:04:14 +08:00
|
|
|
__small_swprintf (filter, group ? PSXID_FILTER_GRP : PSXID_FILTER_USR, id);
|
2015-02-18 18:25:14 +08:00
|
|
|
if (search (def_context, LDAP_SCOPE_SUBTREE, filter, attr) != 0)
|
2014-06-25 17:10:50 +08:00
|
|
|
return NULL;
|
2014-02-10 03:44:56 +08:00
|
|
|
if (!(entry = ldap_first_entry (lh, msg)))
|
|
|
|
{
|
2015-02-18 05:08:01 +08:00
|
|
|
debug_printf ("No entry for %W in def_context %W", filter, def_context);
|
2014-02-10 03:44:56 +08:00
|
|
|
return NULL;
|
|
|
|
}
|
2015-02-25 04:52:57 +08:00
|
|
|
return get_string_attribute (attr[0]);
|
2014-02-10 03:44:56 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
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
|
2014-02-27 20:57:27 +08:00
|
|
|
&& (pw = internal_getpwsid (user, this)))
|
2014-02-10 03:44:56 +08:00
|
|
|
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
|
2014-02-27 20:57:27 +08:00
|
|
|
&& (gr = internal_getgrsid (group, this)))
|
2014-02-10 03:44:56 +08:00
|
|
|
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;
|
|
|
|
}
|