diff --git a/winsup/cygwin/ChangeLog b/winsup/cygwin/ChangeLog index 7c89307d5..de0ba04dd 100644 --- a/winsup/cygwin/ChangeLog +++ b/winsup/cygwin/ChangeLog @@ -1,3 +1,26 @@ +2015-02-23 Corinna Vinschen + + * autoload.cc (LsaLookupSids): Import. + * cygserver_pwdgrp.h: Include userinfo.h. Drop workaround defining + fetch_user_arg_type_t locally. + * grp.cc (internal_getgrsid_cachedonly): New function. + (internal_getgrfull): Ditto. + (internal_getgroups): Rearrange function. Center around fetching all + cached group info first, calling LsaLookupSids on all so far non-cached + groups second. Pass all available info to new internal_getgrfull call. + * pwdgrp.h: Include userinfo.h. Move definitions of + fetch_user_arg_type_t and fetch_user_arg_t there. + (pwdgrp::add_group_from_windows): Declare with getting full group info. + Called from internal_getgrfull. + * uinfo.cc (pwdgrp::add_group_from_windows): Define. + (pwdgrp::fetch_account_from_line): Add default case. + (pwdgrp::fetch_account_from_file): Ditto. + (pwdgrp::fetch_account_from_windows): Handle FULL_grp_arg. + (client_request_pwdgrp::client_request_pwdgrp): Add default case. + * userinfo.h: New header. + (enum fetch_user_arg_type_t): Add FULL_grp_arg. + (struct fetch_full_grp_t): New datatype. + 2015-02-23 Corinna Vinschen * grp.cc (internal_getgroups): Check for group attributes and diff --git a/winsup/cygwin/autoload.cc b/winsup/cygwin/autoload.cc index ce5d32834..a4812a678 100644 --- a/winsup/cygwin/autoload.cc +++ b/winsup/cygwin/autoload.cc @@ -545,6 +545,7 @@ LoadDLLfunc (LookupAccountSidW, 28, advapi32) LoadDLLfunc (LsaClose, 4, advapi32) LoadDLLfunc (LsaEnumerateAccountRights, 16, advapi32) LoadDLLfunc (LsaFreeMemory, 4, advapi32) +LoadDLLfunc (LsaLookupSids, 20, advapi32) LoadDLLfunc (LsaOpenPolicy, 16, advapi32) LoadDLLfunc (LsaQueryInformationPolicy, 12, advapi32) LoadDLLfunc (LsaRetrievePrivateData, 12, advapi32) diff --git a/winsup/cygwin/cygserver_pwdgrp.h b/winsup/cygwin/cygserver_pwdgrp.h index 52b9b42ba..03a6a65d0 100644 --- a/winsup/cygwin/cygserver_pwdgrp.h +++ b/winsup/cygwin/cygserver_pwdgrp.h @@ -1,6 +1,6 @@ /* cygserver_pwdgrp.h: Request account information - Copyright 2014 Red Hat, Inc. + Copyright 2014, 2015 Red Hat, Inc. This file is part of Cygwin. @@ -13,21 +13,11 @@ details. */ #include #include "cygserver.h" +#include "userinfo.h" class transport_layer_base; class process_cache; -#ifdef __INSIDE_CYGWIN__ -#include "pwdgrp.h" -#else -/* Don't include pwdgrp.h, but keep this in sync. */ -enum fetch_user_arg_type_t { - SID_arg, - NAME_arg, - ID_arg -}; -#endif - class client_request_pwdgrp : public client_request { friend class client_request; diff --git a/winsup/cygwin/grp.cc b/winsup/cygwin/grp.cc index ba6584c8e..676dd7672 100644 --- a/winsup/cygwin/grp.cc +++ b/winsup/cygwin/grp.cc @@ -14,6 +14,7 @@ details. */ #include "winsup.h" #include +#include #include #include #include @@ -117,6 +118,65 @@ internal_getgrsid (cygpsid &sid, cyg_ldap *pldap) return NULL; } +/* Like internal_getgrsid but return only already cached data, + NULL otherwise. */ +static struct group * +internal_getgrsid_cachedonly (cygpsid &sid) +{ + struct group *ret; + + /* Check caches only. */ + if (cygheap->pg.nss_cygserver_caching () + && (ret = cygheap->pg.grp_cache.cygserver.find_group (sid))) + return ret; + if (cygheap->pg.nss_grp_files () + && (ret = cygheap->pg.grp_cache.file.find_group (sid))) + return ret; + if (cygheap->pg.nss_grp_db () + && (ret = cygheap->pg.grp_cache.win.find_group (sid))) + return ret; + return NULL; +} + +/* Called from internal_getgroups. The full information required to create + a group account entry is already available from the LookupAccountSids + call. internal_getgrfull passes all available info into + pwdgrp::fetch_account_from_line, thus avoiding a LookupAccountSid call + for each group. This is quite a bit faster, especially in slower + environments. */ +static struct group * __attribute__((used)) +internal_getgrfull (fetch_full_grp_t &full_grp, cyg_ldap *pldap) +{ + struct group *ret; + + cygheap->pg.nss_init (); + /* Check caches first. */ + if (cygheap->pg.nss_cygserver_caching () + && (ret = cygheap->pg.grp_cache.cygserver.find_group (full_grp.sid))) + return ret; + if (cygheap->pg.nss_grp_files () + && (ret = cygheap->pg.grp_cache.file.find_group (full_grp.sid))) + return ret; + if (cygheap->pg.nss_grp_db () + && (ret = cygheap->pg.grp_cache.win.find_group (full_grp.sid))) + return ret; + /* Ask sources afterwards. */ + if (cygheap->pg.nss_cygserver_caching () + && (ret = cygheap->pg.grp_cache.cygserver.add_group_from_cygserver + (full_grp.sid))) + return ret; + if (cygheap->pg.nss_grp_files ()) + { + cygheap->pg.grp_cache.file.check_file (); + if ((ret = cygheap->pg.grp_cache.file.add_group_from_file + (full_grp.sid))) + return ret; + } + if (cygheap->pg.nss_grp_db ()) + return cygheap->pg.grp_cache.win.add_group_from_windows (full_grp, pldap); + return NULL; +} + /* This function gets only called from mkgroup via cygwin_internal. */ struct group * internal_getgrsid_from_db (cygpsid &sid) @@ -502,8 +562,15 @@ internal_getgroups (int gidsetsize, gid_t *grouplist, cyg_ldap *pldap, NTSTATUS status; HANDLE tok; ULONG size; - int cnt = 0; + PTOKEN_GROUPS groups; + PSID *sidp_buf; + ULONG scnt; + PLSA_REFERENCED_DOMAIN_LIST dlst = NULL; + PLSA_TRANSLATED_NAME nlst = NULL; + + tmp_pathbuf tp; struct group *grp; + int cnt = 0; if (cygheap->user.groups.issetgroups ()) { @@ -515,53 +582,101 @@ internal_getgroups (int gidsetsize, gid_t *grouplist, cyg_ldap *pldap, grouplist[cnt] = grp->gr_gid; ++cnt; if (gidsetsize && cnt > gidsetsize) - goto error; + { + cnt = -1; + break; + } } - return cnt; + goto out; } /* If impersonated, use impersonation token. */ - tok = cygheap->user.issetuid () ? cygheap->user.primary_token () : hProcToken; + tok = cygheap->user.issetuid () ? cygheap->user.primary_token () + : hProcToken; - status = NtQueryInformationToken (tok, TokenGroups, NULL, 0, &size); - if (NT_SUCCESS (status) || status == STATUS_BUFFER_TOO_SMALL) + /* Fetch groups from user token. */ + groups = (PTOKEN_GROUPS) tp.w_get (); + status = NtQueryInformationToken (tok, TokenGroups, groups, 2 * NT_MAX_PATH, + &size); + if (!NT_SUCCESS (status)) { - PTOKEN_GROUPS groups = (PTOKEN_GROUPS) alloca (size); - - status = NtQueryInformationToken (tok, TokenGroups, groups, size, &size); + system_printf ("token group list > 64K? status = %u", status); + goto out; + } + /* Iterate over the group list and check which of them are already cached. + Those are simply copied to grouplist. The non-cached ones are collected + in sidp_buf for a later call to LsaLookupSids. */ + sidp_buf = (PSID *) tp.w_get (); + scnt = 0; + for (DWORD pg = 0; pg < groups->GroupCount; ++pg) + { + cygpsid sid = groups->Groups[pg].Sid; + if ((groups->Groups[pg].Attributes + & (SE_GROUP_ENABLED | SE_GROUP_INTEGRITY_ENABLED)) == 0 + || sid == well_known_world_sid) + continue; + if ((grp = internal_getgrsid_cachedonly (sid))) + { + if (cnt < gidsetsize) + grouplist[cnt] = grp->gr_gid; + ++cnt; + if (gidsetsize && cnt > gidsetsize) + { + cnt = -1; + goto out; + } + } + else + sidp_buf[scnt++] = sid; + } + /* If there are non-cached groups left, call LsaLookupSids and call + internal_getgrfull on the returned groups. This performs a lot + better than calling internal_getgrsid on each group. */ + if (scnt > 0) + { + status = STATUS_ACCESS_DENIED; + HANDLE lsa = lsa_open_policy (NULL, POLICY_LOOKUP_NAMES); + if (!lsa) + { + system_printf ("POLICY_LOOKUP_NAMES not given?"); + goto out; + } + status = LsaLookupSids (lsa, scnt, sidp_buf, &dlst, &nlst); + lsa_close_policy (lsa); if (NT_SUCCESS (status)) { - ULONGLONG t0; - - if (timeout_ns) - t0 = GetTickCount_ns (); - for (DWORD pg = 0; pg < groups->GroupCount; ++pg) + for (ULONG ncnt = 0; ncnt < scnt; ++ncnt) { - cygpsid sid = groups->Groups[pg].Sid; - if ((groups->Groups[pg].Attributes - & (SE_GROUP_ENABLED | SE_GROUP_INTEGRITY_ENABLED)) == 0 - || sid == well_known_world_sid) - continue; - if ((grp = internal_getgrsid (sid, pldap))) + fetch_full_grp_t full_grp = + { + .sid = sidp_buf[ncnt], + .name = &nlst[ncnt].Name, + .dom = &dlst->Domains[nlst[ncnt].DomainIndex].Name, + .acc_type = nlst[ncnt].Use + }; + if ((grp = internal_getgrfull (full_grp, pldap))) { if (cnt < gidsetsize) grouplist[cnt] = grp->gr_gid; ++cnt; if (gidsetsize && cnt > gidsetsize) - goto error; + { + cnt = -1; + goto out; + } } - if (timeout_ns && GetTickCount_ns () - t0 >= timeout_ns) - break; } } } - else - debug_printf ("%u = NtQueryInformationToken(NULL) %y", size, status); - return cnt; -error: - set_errno (EINVAL); - return -1; +out: + if (dlst) + LsaFreeMemory (dlst); + if (nlst) + LsaFreeMemory (nlst); + if (cnt == -1) + set_errno (EINVAL); + return cnt; } extern "C" int diff --git a/winsup/cygwin/pwdgrp.h b/winsup/cygwin/pwdgrp.h index 24a397185..ad2108063 100644 --- a/winsup/cygwin/pwdgrp.h +++ b/winsup/cygwin/pwdgrp.h @@ -15,6 +15,7 @@ details. */ #include "sync.h" #include "ldap.h" #include "miscfuncs.h" +#include "userinfo.h" /* These functions are needed to allow searching and walking through the passwd and group lists */ @@ -37,24 +38,6 @@ void *setgrent_filtered (int enums, PCWSTR enum_tdoms); void *getgrent_filtered (void *gr); void endgrent_filtered (void *gr); -enum fetch_user_arg_type_t { - SID_arg, - NAME_arg, - ID_arg -}; - -struct fetch_user_arg_t -{ - fetch_user_arg_type_t type; - union { - cygpsid *sid; - const char *name; - uint32_t id; - }; - /* Only used in fetch_account_from_file/line. */ - size_t len; -}; - struct pg_pwd { struct passwd p; @@ -176,6 +159,8 @@ public: { return (struct group *) add_account_from_windows (name, pldap); } struct group *add_group_from_windows (uint32_t id, cyg_ldap *pldap = NULL) { return (struct group *) add_account_from_windows (id, pldap); } + struct group *add_group_from_windows (fetch_full_grp_t &full_grp, + cyg_ldap *pldap = NULL); struct group *find_group (cygpsid &sid); struct group *find_group (const char *name); struct group *find_group (gid_t gid); diff --git a/winsup/cygwin/uinfo.cc b/winsup/cygwin/uinfo.cc index 1e8e7b671..c24d528f3 100644 --- a/winsup/cygwin/uinfo.cc +++ b/winsup/cygwin/uinfo.cc @@ -1545,6 +1545,19 @@ pwdgrp::add_account_from_windows (uint32_t id, cyg_ldap *pldap) return add_account_post_fetch (line, true); } +/* Called from internal_getgrfull, in turn called from internal_getgroups. */ +struct group * +pwdgrp::add_group_from_windows (fetch_full_grp_t &full_grp, cyg_ldap *pldap) +{ + fetch_user_arg_t arg; + arg.type = FULL_grp_arg; + arg.full_grp = &full_grp; + char *line = fetch_account_from_windows (arg, pldap); + if (!line) + return NULL; + return (struct group *) add_account_post_fetch (line, true); +} + /* Check if file exists and if it has been written to since last checked. If file has been changed, invalidate the current cache. @@ -1627,6 +1640,8 @@ pwdgrp::fetch_account_from_line (fetch_user_arg_t &arg, const char *line) if (strtoul (p + 1, &e, 10) != arg.id || !e || *e != ':') return NULL; break; + default: + return NULL; } return cstrdup (line); } @@ -1653,6 +1668,8 @@ pwdgrp::fetch_account_from_file (fetch_user_arg_t &arg) break; case ID_arg: break; + default: + return NULL; } if (rl.init (&attr, buf, NT_MAX_PATH)) while ((buf = rl.gets ())) @@ -1742,6 +1759,17 @@ pwdgrp::fetch_account_from_windows (fetch_user_arg_t &arg, cyg_ldap *pldap) switch (arg.type) { + case FULL_grp_arg: + { + sid = arg.full_grp->sid; + *wcpncpy (name, arg.full_grp->name->Buffer, + arg.full_grp->name->Length / sizeof (WCHAR)) = L'\0'; + *wcpncpy (dom, arg.full_grp->dom->Buffer, + arg.full_grp->dom->Length / sizeof (WCHAR)) = L'\0'; + acc_type = arg.full_grp->acc_type; + ret = acc_type != SidTypeUnknown; + } + break; case SID_arg: sid = *arg.sid; ret = LookupAccountSidW (NULL, sid, name, &nlen, dom, &dlen, &acc_type); @@ -2489,6 +2517,9 @@ client_request_pwdgrp::client_request_pwdgrp (fetch_user_arg_t &arg, bool group) case ID_arg: _parameters.in.arg.id = arg.id; len = sizeof (uint32_t); + default: + api_fatal ("Fetching account info from cygserver with wrong arg.type " + "%d", arg.type); } msglen (__builtin_offsetof (struct _pwdgrp_param_t::_pwdgrp_in_t, arg) + len); } diff --git a/winsup/cygwin/userinfo.h b/winsup/cygwin/userinfo.h new file mode 100644 index 000000000..db557a77e --- /dev/null +++ b/winsup/cygwin/userinfo.h @@ -0,0 +1,38 @@ +/* userinfo.h + + Copyright 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. */ + +#pragma once + +enum fetch_user_arg_type_t { + SID_arg, + NAME_arg, + ID_arg, + FULL_grp_arg, +}; + +struct fetch_full_grp_t { + cygpsid sid; + PUNICODE_STRING name; + PUNICODE_STRING dom; + SID_NAME_USE acc_type; +}; + +struct fetch_user_arg_t +{ + fetch_user_arg_type_t type; + union { + cygpsid *sid; + const char *name; + uint32_t id; + fetch_full_grp_t *full_grp; + }; + /* Only used in fetch_account_from_file/line. */ + size_t len; +};