diff options
author | Corinna Vinschen <corinna@vinschen.de> | 2014-06-25 13:10:50 +0400 |
---|---|---|
committer | Corinna Vinschen <corinna@vinschen.de> | 2014-06-25 13:10:50 +0400 |
commit | e35b92c202d85eed0bf2ac526370beb8d2f053de (patch) | |
tree | 7343a7d5c1a4a38d3add0d707588104c55245cc4 /winsup/cygwin/ldap.cc | |
parent | 4491d189ae389fcfa8ba909c5f53645a01dc6db8 (diff) |
* autoload.cc (ldap_search_sW): Replace ldap_search_stW.
(LdapMapErrorToWin32): Import.
* fhandler_disk_file.cc (fhandler_base::fstat_by_nfs_ea): Accommodate
change to cyg_ldap::open.
* ldap.cc (CYG_LDAP_TIMEOUT): Remove.
(CYG_LDAP_ENUM_TIMEOUT): Remove.
(def_tv): Remove.
(enum_tv): Remove.
(cyg_ldap::map_ldaperr_to_errno): New method to map LDAP error codes to
POSIX errno codes. Explicitly map LDAP_NO_RESULTS_RETURNED to ENMFILE.
(cyg_ldap::wait): Ditto.
(struct cyg_ldap_init): New struct.
(cyg_ldap::connect_ssl): Return ULONG. Drop setting LDAP_OPT_TIMELIMIT.
Add call to ldap_search_sW to fetch root DSE.
(cyg_ldap::connect_non_ssl): Ditto.
(ldap_init_thr): New static thread function.
(cyg_ldap::connect): New method to call connect_ssl/connect_non_ssl in
an interruptible cygthread.
(struct cyg_ldap_search): New struct.
(cyg_ldap::search_s): New method to perform generic synchronous search.
(ldap_search_thr): New static thread function.
(cyg_ldap::search): New method to call search_s in an interruptible
cygthread.
(struct cyg_ldap_next_page): New struct.
(cyg_ldap::next_page_s): New method to perform generic synchronous
paged search.
(ldap_next_page_thr): New static thread function.
(cyg_ldap::next_page): New method to call next_page_s in an
interruptible cygthread.
(cyg_ldap::open): Return POSIX errno. Call connect method.
(cyg_ldap::fetch_ad_account): Call search method rather than
ldap_search_stW.
(cyg_ldap::enumerate_ad_accounts): Return POSIX errno. Use infinite
timeout in call to ldap_search_init_pageW.
(cyg_ldap::next_account): Return POSIX errno. Call next_page method
rather than ldap_get_next_page_s.
(cyg_ldap::fetch_posix_offset_for_domain): Call search method rather
than ldap_search_stW.
(cyg_ldap::fetch_unix_sid_from_ad): Ditto.
(cyg_ldap::fetch_unix_name_from_rfc2307): Ditto.
* ldap.h (class cyg_ldap): Accommodate aforementioned changes.
* passwd.cc (pg_ent::enumerate_ad): Ditto. Break search if one of
cyg_ldap::enumerate_ad_accounts or cldap.next_account returns with
an error code other than ENMFILE.
* sec_helper.cc (cygpsid::get_id): Accommodate change to cyg_ldap::open.
* uinfo.cc (fetch_posix_offset): Ditto.
Diffstat (limited to 'winsup/cygwin/ldap.cc')
-rw-r--r-- | winsup/cygwin/ldap.cc | 316 |
1 files changed, 209 insertions, 107 deletions
diff --git a/winsup/cygwin/ldap.cc b/winsup/cygwin/ldap.cc index 0010c1df9..fa19c307e 100644 --- a/winsup/cygwin/ldap.cc +++ b/winsup/cygwin/ldap.cc @@ -22,14 +22,8 @@ details. */ #include "dsgetdc.h" #include "tls_pbuf.h" -#define CYG_LDAP_TIMEOUT 5 /* seconds */ -#define CYG_LDAP_ENUM_TIMEOUT 60 /* seconds */ - #define CYG_LDAP_ENUM_PAGESIZE 100 /* entries per page */ -static LDAP_TIMEVAL def_tv = { CYG_LDAP_TIMEOUT, 0 }; -static LDAP_TIMEVAL enum_tv = { CYG_LDAP_ENUM_TIMEOUT, 0 }; - static PWCHAR rootdse_attr[] = { (PWCHAR) L"defaultNamingContext", @@ -78,38 +72,84 @@ PWCHAR rfc2307_gid_attr[] = NULL }; -bool +/* ================================================================= */ +/* 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, timelimit = CYG_LDAP_TIMEOUT; + ULONG ret; if (!(lh = ldap_sslinitW ((PWCHAR) domain, LDAP_SSL_PORT, 1))) { debug_printf ("ldap_init(%W) error 0x%02x", domain, LdapGetLastError ()); - return false; + return LdapGetLastError (); } - if ((ret = ldap_set_option (lh, LDAP_OPT_TIMELIMIT, &timelimit)) - != LDAP_SUCCESS) - debug_printf ("ldap_set_option(LDAP_OPT_TIMELIMIT) 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); - ldap_unbind (lh); - lh = NULL; - return false; - } - return true; + 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; } -bool +ULONG cyg_ldap::connect_non_ssl (PCWSTR domain) { - ULONG ret, timelimit = CYG_LDAP_TIMEOUT; + ULONG ret; if (!(lh = ldap_initW ((PWCHAR) domain, LDAP_PORT))) { debug_printf ("ldap_init(%W) error 0x%02x", domain, LdapGetLastError ()); - return false; + return LdapGetLastError (); } if ((ret = ldap_set_option (lh, LDAP_OPT_SIGN, LDAP_OPT_ON)) != LDAP_SUCCESS) @@ -117,40 +157,133 @@ cyg_ldap::connect_non_ssl (PCWSTR domain) 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_set_option (lh, LDAP_OPT_TIMELIMIT, &timelimit)) - != LDAP_SUCCESS) - debug_printf ("ldap_set_option(LDAP_OPT_TIMELIMIT) 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 { - debug_printf ("ldap_bind(%W) 0x%02x", domain, ret); - ldap_unbind (lh); - lh = NULL; - return false; + ret = ldap_get_next_page_s (lh, srch_id, NULL, CYG_LDAP_ENUM_PAGESIZE, + &total, &srch_msg); } - return true; + 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; } -bool +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) { - ULONG ret; + int ret = 0; /* Already open? */ if (lh) - return true; + return 0; - /* FIXME? connect_ssl can take ages even when failing, so we're trying to - do everything the non-SSL (but still encrypted) way. */ - if (/*!connect_ssl (NULL) && */ !connect_non_ssl (domain)) - return false; - if ((ret = ldap_search_stW (lh, NULL, LDAP_SCOPE_BASE, - (PWCHAR) L"(objectclass=*)", rootdse_attr, - 0, &def_tv, &msg)) - != LDAP_SUCCESS) - { - debug_printf ("ldap_search(%W, ROOTDSE) error 0x%02x", domain, ret); - goto err; - } + 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); @@ -179,10 +312,11 @@ cyg_ldap::open (PCWSTR domain) ldap_value_freeW (val); val = NULL; ldap_msgfree (msg); - msg = entry = NULL; return true; + msg = entry = NULL; + return 0; err: close (); - return false; + return ret; } void @@ -215,7 +349,6 @@ cyg_ldap::fetch_ad_account (PSID sid, bool group, PCWSTR domain) LONG len = (LONG) RtlLengthSid (sid); PBYTE s = (PBYTE) sid; static WCHAR hex_wchars[] = L"0123456789abcdef"; - ULONG ret; tmp_pathbuf tp; if (msg) @@ -256,13 +389,8 @@ cyg_ldap::fetch_ad_account (PSID sid, bool group, PCWSTR domain) } } attr = group ? group_attr : user_attr; - if ((ret = ldap_search_stW (lh, rdse, LDAP_SCOPE_SUBTREE, filter, - attr, 0, &def_tv, &msg)) != LDAP_SUCCESS) - { - debug_printf ("ldap_search_stW(%W,%W) error 0x%02x", - rdse, filter, ret); + 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); @@ -271,15 +399,16 @@ cyg_ldap::fetch_ad_account (PSID sid, bool group, PCWSTR domain) return true; } -bool +int cyg_ldap::enumerate_ad_accounts (PCWSTR domain, bool group) { + int ret; tmp_pathbuf tp; PCWSTR filter; close (); - if (!open (domain)) - return false; + if ((ret = open (domain)) != NO_ERROR) + return ret; if (!group) filter = L"(&(objectClass=User)" @@ -296,26 +425,23 @@ cyg_ldap::enumerate_ad_accounts (PCWSTR domain, bool 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, CYG_LDAP_ENUM_TIMEOUT, - CYG_LDAP_ENUM_PAGESIZE, NULL); + (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 false; + return map_ldaperr_to_errno (LdapGetLastError ()); } - return true; + return NO_ERROR; } -bool +int cyg_ldap::next_account (cygsid &sid) { ULONG ret; PLDAP_BERVAL *bval; - ULONG total; - if (srch_entry) { if ((srch_entry = ldap_next_entry (lh, srch_entry)) @@ -323,38 +449,32 @@ cyg_ldap::next_account (cygsid &sid) { sid = (PSID) bval[0]->bv_val; ldap_value_free_len (bval); - return true; + return NO_ERROR; } ldap_msgfree (srch_msg); srch_msg = srch_entry = NULL; } - do - { - ret = ldap_get_next_page_s (lh, srch_id, &enum_tv, CYG_LDAP_ENUM_PAGESIZE, - &total, &srch_msg); - } - while (ret == LDAP_SUCCESS && ldap_count_entries (lh, srch_msg) == 0); - if (ret == LDAP_NO_RESULTS_RETURNED) - ; - else if (ret != LDAP_SUCCESS) - debug_printf ("ldap_result() error 0x%02x", ret); - else if ((srch_entry = ldap_first_entry (lh, srch_msg)) - && (bval = ldap_get_values_lenW (lh, srch_entry, sid_attr[0]))) + ret = next_page (); + if (ret == NO_ERROR) { - sid = (PSID) bval[0]->bv_val; - ldap_value_free_len (bval); - return true; + 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 false; + return ret; } uint32_t cyg_ldap::fetch_posix_offset_for_domain (PCWSTR domain) { WCHAR filter[300]; - ULONG ret; if (msg) { @@ -370,14 +490,8 @@ cyg_ldap::fetch_posix_offset_for_domain (PCWSTR domain) by flatName rather than by name. */ __small_swprintf (filter, L"(&(objectClass=trustedDomain)(%W=%W))", wcschr (domain, L'.') ? L"name" : L"flatName", domain); - if ((ret = ldap_search_stW (lh, rootdse, LDAP_SCOPE_SUBTREE, filter, - attr = tdom_attr, 0, &def_tv, &msg)) - != LDAP_SUCCESS) - { - debug_printf ("ldap_search_stW(%W,%W) error 0x%02x", - rootdse, filter, ret); - return 0; - } + if (search (rootdse, filter, attr = tdom_attr) != 0) + return 0; if (!(entry = ldap_first_entry (lh, msg))) { debug_printf ("No entry for %W in rootdse %W", filter, rootdse); @@ -410,7 +524,6 @@ bool cyg_ldap::fetch_unix_sid_from_ad (uint32_t id, cygsid &sid, bool group) { WCHAR filter[48]; - ULONG ret; PLDAP_BERVAL *bval; if (msg) @@ -422,13 +535,8 @@ cyg_ldap::fetch_unix_sid_from_ad (uint32_t id, cygsid &sid, bool group) __small_swprintf (filter, L"(&(objectClass=Group)(gidNumber=%u))", id); else __small_swprintf (filter, L"(&(objectClass=User)(uidNumber=%u))", id); - if ((ret = ldap_search_stW (lh, rootdse, LDAP_SCOPE_SUBTREE, filter, - sid_attr, 0, &def_tv, &msg)) != LDAP_SUCCESS) - { - debug_printf ("ldap_search_stW(%W,%W) error 0x%02x", - rootdse, filter, ret); - return false; - } + 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]))) { @@ -443,7 +551,6 @@ PWCHAR cyg_ldap::fetch_unix_name_from_rfc2307 (uint32_t id, bool group) { WCHAR filter[52]; - ULONG ret; if (msg) { @@ -461,13 +568,8 @@ cyg_ldap::fetch_unix_name_from_rfc2307 (uint32_t id, bool group) else __small_swprintf (filter, L"(&(objectClass=posixAccount)(uidNumber=%u))", id); - if ((ret = ldap_search_stW (lh, rootdse, LDAP_SCOPE_SUBTREE, filter, attr, - 0, &def_tv, &msg)) != LDAP_SUCCESS) - { - debug_printf ("ldap_search_stW(%W,%W) error 0x%02x", - rootdse, filter, ret); - return NULL; - } + 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); |