diff options
Diffstat (limited to 'winsup/cygwin/ldap.cc')
-rw-r--r-- | winsup/cygwin/ldap.cc | 629 |
1 files changed, 0 insertions, 629 deletions
diff --git a/winsup/cygwin/ldap.cc b/winsup/cygwin/ldap.cc deleted file mode 100644 index df7756809..000000000 --- a/winsup/cygwin/ldap.cc +++ /dev/null @@ -1,629 +0,0 @@ -/* ldap.cc: Helper functions for ldap access to Active Directory. - - Copyright 2014 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" - -#define CYG_LDAP_ENUM_PAGESIZE 100 /* entries per page */ - -static PWCHAR rootdse_attr[] = -{ - (PWCHAR) L"defaultNamingContext", - (PWCHAR) L"supportedCapabilities", - NULL -}; - -static PWCHAR user_attr[] = -{ - (PWCHAR) L"primaryGroupID", - (PWCHAR) L"gecos", - (PWCHAR) L"unixHomeDirectory", - (PWCHAR) L"loginShell", - (PWCHAR) L"uidNumber", - NULL -}; - -static PWCHAR group_attr[] = -{ - (PWCHAR) L"cn", - (PWCHAR) L"gidNumber", - 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 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, INFINITE, cw_sig | cw_sig_eintr) != WAIT_OBJECT_0) - { - thr->terminate_thread (); - _my_tls.call_signal_handler (); - return EINTR; - } - 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; - PWCHAR filter; - PWCHAR *attrs; - ULONG ret; -}; - -ULONG -cyg_ldap::search_s (PWCHAR base, PWCHAR filter, PWCHAR *attrs) -{ - ULONG ret; - - if ((ret = ldap_search_sW (lh, base, LDAP_SCOPE_SUBTREE, 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->filter, cl->attrs); - return 0; -} - -inline int -cyg_ldap::search (PWCHAR base, PWCHAR filter, PWCHAR *attrs) -{ - cyg_ldap_search cl = { this, base, 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, &srch_msg); - } - while (ret == LDAP_SUCCESS && ldap_count_entries (lh, srch_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 = 0; - - /* Already open? */ - if (lh) - return 0; - - 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 ROOTDSE value for %W", domain); - goto err; - } - if (!(rootdse = wcsdup (val[0]))) - { - debug_printf ("wcsdup(%W, ROOTDSE) %d", domain, 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 0; -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 (srch_msg) - ldap_msgfree (srch_msg); - if (msg) - ldap_msgfree (msg); - if (val) - ldap_value_freeW (val); - if (rootdse) - free (rootdse); - lh = NULL; - msg = entry = NULL; - val = NULL; - rootdse = NULL; - srch_id = NULL; - srch_msg = srch_entry = NULL; -} - -bool -cyg_ldap::fetch_ad_account (PSID sid, bool group, PCWSTR domain) -{ - WCHAR filter[140], *f, *rdse = rootdse; - LONG len = (LONG) RtlLengthSid (sid); - PBYTE s = (PBYTE) sid; - static WCHAR hex_wchars[] = L"0123456789abcdef"; - tmp_pathbuf tp; - - if (msg) - { - ldap_msgfree (msg); - msg = entry = NULL; - } - if (val) - { - ldap_value_freeW (val); - val = NULL; - } - f = wcpcpy (filter, L"(objectSid="); - while (len-- > 0) - { - *f++ = L'\\'; - *f++ = hex_wchars[*s >> 4]; - *f++ = hex_wchars[*s++ & 0xf]; - } - wcpcpy (f, L")"); - 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. */ - rdse = tp.w_get (); - PWCHAR r = rdse; - for (PWCHAR dotp = (PWCHAR) domain; dotp && *dotp; domain = dotp) - { - dotp = wcschr (domain, L'.'); - if (dotp) - *dotp++ = L'\0'; - if (r > rdse) - *r++ = L','; - r = wcpcpy (r, L"DC="); - r = wcpcpy (r, domain); - } - } - attr = group ? group_attr : user_attr; - if (search (rdse, filter, attr) != 0) - return false; - if (!(entry = ldap_first_entry (lh, msg))) - { - debug_printf ("No entry for %W in rootdse %W", filter, rdse); - return false; - } - 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"(&(objectClass=User)" - "(objectCategory=Person)" - /* 512 == ADS_UF_NORMAL_ACCOUNT */ - "(userAccountControl:" LDAP_MATCHING_RULE_BIT_AND ":=512)" - "(objectSid=*))"; - else if (!domain) - filter = L"(&(objectClass=Group)" - "(objectSid=*))"; - else - filter = L"(&(objectClass=Group)" - /* 1 == ACCOUNT_GROUP */ - "(!(groupType:" LDAP_MATCHING_RULE_BIT_AND ":=1))" - "(objectSid=*))"; - srch_id = ldap_search_init_pageW (lh, rootdse, LDAP_SCOPE_SUBTREE, - (PWCHAR) filter, sid_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", - rootdse, filter, LdapGetLastError ()); - return map_ldaperr_to_errno (LdapGetLastError ()); - } - return NO_ERROR; -} - -int -cyg_ldap::next_account (cygsid &sid) -{ - ULONG ret; - PLDAP_BERVAL *bval; - - if (srch_entry) - { - if ((srch_entry = ldap_next_entry (lh, srch_entry)) - && (bval = ldap_get_values_lenW (lh, srch_entry, sid_attr[0]))) - { - sid = (PSID) bval[0]->bv_val; - ldap_value_free_len (bval); - return NO_ERROR; - } - ldap_msgfree (srch_msg); - srch_msg = srch_entry = NULL; - } - ret = next_page (); - if (ret == NO_ERROR) - { - if ((srch_entry = ldap_first_entry (lh, srch_msg)) - && (bval = ldap_get_values_lenW (lh, srch_entry, sid_attr[0]))) - { - 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; -} - -/* 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 filter[300]; - - if (msg) - { - ldap_msgfree (msg); - msg = entry = NULL; - } - if (val) - { - ldap_value_freeW (val); - val = NULL; - } - /* If domain name has no dot, it's a Netbios name. In that case, filter - by flatName rather than by name. */ - __small_swprintf (filter, L"(&(objectClass=trustedDomain)(%W=%W))", - wcschr (domain, L'.') ? L"name" : L"flatName", domain); - if (search (rootdse, filter, attr = tdom_attr) != 0) - return UINT32_MAX; - if (!(entry = ldap_first_entry (lh, msg))) - { - debug_printf ("No entry for %W in rootdse %W", filter, rootdse); - return UINT32_MAX; - } - return get_num_attribute (0); -} - -PWCHAR -cyg_ldap::get_string_attribute (int idx) -{ - if (val) - ldap_value_freeW (val); - val = ldap_get_valuesW (lh, entry, attr[idx]); - if (val) - return val[0]; - return NULL; -} - -uint32_t -cyg_ldap::get_num_attribute (int idx) -{ - PWCHAR ret = get_string_attribute (idx); - if (ret) - return (uint32_t) wcstoul (ret, NULL, 10); - return (uint32_t) -1; -} - -bool -cyg_ldap::fetch_unix_sid_from_ad (uint32_t id, cygsid &sid, bool group) -{ - WCHAR filter[48]; - PLDAP_BERVAL *bval; - - if (msg) - { - ldap_msgfree (msg); - msg = entry = NULL; - } - if (group) - __small_swprintf (filter, L"(&(objectClass=Group)(gidNumber=%u))", id); - else - __small_swprintf (filter, L"(&(objectClass=User)(uidNumber=%u))", id); - if (search (rootdse, filter, sid_attr) != 0) - return false; - if ((entry = ldap_first_entry (lh, msg)) - && (bval = ldap_get_values_lenW (lh, entry, sid_attr[0]))) - { - sid = (PSID) bval[0]->bv_val; - ldap_value_free_len (bval); - return true; - } - return false; -} - -PWCHAR -cyg_ldap::fetch_unix_name_from_rfc2307 (uint32_t id, bool group) -{ - WCHAR filter[52]; - - if (msg) - { - ldap_msgfree (msg); - msg = entry = NULL; - } - if (val) - { - ldap_value_freeW (val); - val = NULL; - } - attr = group ? rfc2307_gid_attr : rfc2307_uid_attr; - if (group) - __small_swprintf (filter, L"(&(objectClass=posixGroup)(gidNumber=%u))", id); - else - __small_swprintf (filter, L"(&(objectClass=posixAccount)(uidNumber=%u))", - id); - if (search (rootdse, filter, attr) != 0) - return NULL; - if (!(entry = ldap_first_entry (lh, msg))) - { - debug_printf ("No entry for %W in rootdse %W", filter, rootdse); - return NULL; - } - return get_string_attribute (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; -} |