diff --git a/winsup/cygwin/ChangeLog b/winsup/cygwin/ChangeLog index 5ba1e3e9d..70547fcc2 100644 --- a/winsup/cygwin/ChangeLog +++ b/winsup/cygwin/ChangeLog @@ -1,3 +1,28 @@ +2014-02-28 Corinna Vinschen + + * cygheap.h (cygheap_user::sid): Return reference to cygpsid rather + than PSID. + (cygheap_user::saved_sid): Ditto. + (cygheap_pwdgrp::cache_t): New type. + (cygheap_pwdgrp::caching): Convert to cache_t. + (cygheap_pwdgrp::nss_db_caching): Change accordingly. + (cygheap_pwdgrp::nss_db_full_caching): New inline method. + * grp.cc (internal_getgroups): Reinvent. Take cyg_ldap pointer as + third parameter and use throughout. + (getgroups32): Call internal_getgroups. + * pwdgrp.h (internal_getgroups): Declare. + * uinfo.cc (internal_getlogin): Partial rewrite to accommodate having + no connection to the DC. Give primary group from user token more + weight. Generate group entries for all groups in the user token if + caching is set to NSS_FULL_CACHING. + (cygheap_pwdgrp::init): Initialize caching to NSS_FULL_CACHING. + (cygheap_pwdgrp::nss_init_line): Handle "db_cache: full". + (pwdgrp::add_account_from_windows): Fix group handling in non-caching + mode. + (pwdgrp::fetch_account_from_windows): Default primary group for the + current user to primary group from user token. Check for primary + domain first after LookupAccountSid failed. + 2014-02-27 Corinna Vinschen * autoload.cc (CheckTokenMembership): Import. diff --git a/winsup/cygwin/cygheap.h b/winsup/cygwin/cygheap.h index 7d4198a49..66603c0c7 100644 --- a/winsup/cygwin/cygheap.h +++ b/winsup/cygwin/cygheap.h @@ -156,8 +156,8 @@ public: } void set_sid (PSID new_sid) { effec_cygsid = new_sid;} void set_saved_sid () { saved_cygsid = effec_cygsid; } - PSID sid () { return effec_cygsid; } - PSID saved_sid () { return saved_cygsid; } + cygpsid &sid () { return effec_cygsid; } + cygpsid &saved_sid () { return saved_cygsid; } const char *ontherange (homebodies what, struct passwd * = NULL); #define NO_IMPERSONATION NULL bool issetuid () const { return curr_imp_token != NO_IMPERSONATION; } @@ -400,14 +400,19 @@ class cygheap_pwdgrp NSS_PRIMARY, NSS_ALWAYS }; - bool nss_inited; - int pwd_src; - int grp_src; - pfx_t prefix; - WCHAR separator[2]; - bool caching; - int enums; - PWCHAR enum_tdoms; + enum cache_t { + NSS_NO_CACHING = 0, + NSS_CACHING, + NSS_FULL_CACHING + }; + bool nss_inited; + int pwd_src; + int grp_src; + pfx_t prefix; + WCHAR separator[2]; + cache_t caching; + int enums; + PWCHAR enum_tdoms; void nss_init_line (const char *line); void _nss_init (); @@ -433,7 +438,9 @@ public: inline bool nss_prefix_primary () const { return prefix == NSS_PRIMARY; } inline bool nss_prefix_always () const { return prefix == NSS_ALWAYS; } inline PCWSTR nss_separator () const { return separator; } - inline bool nss_db_caching () const { return caching; } + inline bool nss_db_caching () const { return caching != NSS_NO_CACHING; } + inline bool nss_db_full_caching () const + { return caching == NSS_FULL_CACHING; } inline int nss_db_enums () const { return enums; } inline PCWSTR nss_db_enum_tdoms () const { return enum_tdoms; } }; diff --git a/winsup/cygwin/grp.cc b/winsup/cygwin/grp.cc index 31bfeda2b..cd4afd4a1 100644 --- a/winsup/cygwin/grp.cc +++ b/winsup/cygwin/grp.cc @@ -459,21 +459,20 @@ endgrent_filtered (void *gr) ((gr_ent *) gr)->endgrent (); } -extern "C" int -getgroups32 (int gidsetsize, gid_t *grouplist) +int +internal_getgroups (int gidsetsize, gid_t *grouplist, cyg_ldap *pldap) { NTSTATUS status; HANDLE tok; ULONG size; int cnt = 0; struct group *grp; - cyg_ldap cldap; if (cygheap->user.groups.issetgroups ()) { for (int pg = 0; pg < cygheap->user.groups.sgsids.count (); ++pg) if ((grp = internal_getgrsid (cygheap->user.groups.sgsids.sids[pg], - &cldap))) + pldap))) { if (cnt < gidsetsize) grouplist[cnt] = grp->gr_gid; @@ -500,7 +499,7 @@ getgroups32 (int gidsetsize, gid_t *grouplist) for (DWORD pg = 0; pg < groups->GroupCount; ++pg) { cygpsid sid = groups->Groups[pg].Sid; - if ((grp = internal_getgrsid (sid, &cldap))) + if ((grp = internal_getgrsid (sid, pldap))) { if ((groups->Groups[pg].Attributes & (SE_GROUP_ENABLED | SE_GROUP_INTEGRITY_ENABLED)) @@ -525,6 +524,14 @@ error: return -1; } +extern "C" int +getgroups32 (int gidsetsize, gid_t *grouplist) +{ + cyg_ldap cldap; + + return internal_getgroups (gidsetsize, grouplist, &cldap); +} + #ifdef __x86_64__ EXPORT_ALIAS (getgroups32, getgroups) #else diff --git a/winsup/cygwin/pwdgrp.h b/winsup/cygwin/pwdgrp.h index 5a8267cea..41098de6e 100644 --- a/winsup/cygwin/pwdgrp.h +++ b/winsup/cygwin/pwdgrp.h @@ -27,6 +27,8 @@ extern struct group *internal_getgrsid_from_db (cygpsid &sid); extern struct group *internal_getgrgid (gid_t, cyg_ldap * = NULL); extern struct group *internal_getgrnam (const char *, cyg_ldap * = NULL); +extern int internal_getgroups (int, gid_t *, cyg_ldap *); + /* These functions are called from mkpasswd/mkgroup via cygwin_internal. */ void *setpwent_filtered (int enums, PCWSTR enum_tdoms); void *getpwent_filtered (void *gr); diff --git a/winsup/cygwin/uinfo.cc b/winsup/cygwin/uinfo.cc index e39e08601..dc22082c1 100644 --- a/winsup/cygwin/uinfo.cc +++ b/winsup/cygwin/uinfo.cc @@ -116,48 +116,63 @@ cygheap_user::init () void internal_getlogin (cygheap_user &user) { - struct passwd *pw = NULL; - struct group *gr, *gr2; + struct passwd *pwd; + struct group *pgrp, *grp, *grp2; cyg_ldap cldap; - cygpsid psid = user.sid (); - pw = internal_getpwsid (psid, &cldap); - - if (!pw && !(pw = internal_getpwnam (user.name (), &cldap))) - debug_printf ("user not found in /etc/passwd"); + /* Fetch (and potentially generate) passwd and group entries for the user + and the primary group in the token. */ + pwd = internal_getpwsid (user.sid (), &cldap); + pgrp = internal_getgrsid (user.groups.pgsid, &cldap); + if (cygheap->pg.nss_db_full_caching ()) + internal_getgroups (0, NULL, &cldap); + if (!pwd) + debug_printf ("user not found in passwd DB"); else { cygsid gsid; - myself->uid = pw->pw_uid; - myself->gid = pw->pw_gid; - user.set_name (pw->pw_name); - if (gsid.getfromgr (gr = internal_getgrgid (pw->pw_gid, &cldap))) + user.set_name (pwd->pw_name); + myself->uid = pwd->pw_uid; + myself->gid = pwd->pw_gid; + /* Is the primary group in the passwd DB is different from the primary + group in the user token, we have to find the SID of that group and + try to override the token primary group. */ + if (!pgrp || myself->gid != pgrp->gr_gid) { - /* We might have a group file with a group entry for the current - user's primary group, but the current user has no entry in passwd. - If so, pw_gid is taken from windows and might disagree with the - gr_gid from the group file. Overwrite it brutally. */ - if ((gr2 = internal_getgrsid (gsid, &cldap)) && gr2 != gr) - myself->gid = pw->pw_gid = gr2->gr_gid; - /* Set primary group to the group in /etc/passwd. */ - if (gsid != user.groups.pgsid) + if (gsid.getfromgr (grp = internal_getgrgid (pwd->pw_gid, &cldap))) { - NTSTATUS status = NtSetInformationToken (hProcToken, - TokenPrimaryGroup, - &gsid, sizeof gsid); - if (!NT_SUCCESS (status)) - debug_printf ("NtSetInformationToken (TokenPrimaryGroup), %y", - status); - else - user.groups.pgsid = gsid; - clear_procimptoken (); + /* We might have a group file with a group entry for the current + user's primary group, but the current user has no entry in + passwd. If so, pw_gid is taken from windows and might + disagree with gr_gid from the group file. Overwrite it. */ + if ((grp2 = internal_getgrsid (gsid, &cldap)) && grp2 != grp) + myself->gid = pwd->pw_gid = grp2->gr_gid; + /* Set primary group to the group in /etc/passwd. */ + if (gsid != user.groups.pgsid) + { + NTSTATUS status = NtSetInformationToken (hProcToken, + TokenPrimaryGroup, + &gsid, sizeof gsid); + if (!NT_SUCCESS (status)) + { + debug_printf ("NtSetInformationToken (TokenPrimaryGroup)," + " %y", status); + /* Revert the primary group setting and override the + setting in the passwd entry. */ + if (pgrp) + myself->gid = pwd->pw_gid = pgrp->gr_gid; + } + else + user.groups.pgsid = gsid; + clear_procimptoken (); + } } + else + debug_printf ("group not found in group DB"); } - else - debug_printf ("gsid not found in augmented /etc/group"); } - cygheap->user.ontherange (CH_HOME, pw); + cygheap->user.ontherange (CH_HOME, pwd); } void @@ -569,7 +584,7 @@ cygheap_pwdgrp::init () grp_src = (NSS_FILES | NSS_DB); prefix = NSS_AUTO; separator[0] = L'+'; - caching = true; + caching = NSS_FULL_CACHING; enums = (ENUM_CACHE | ENUM_BUILTIN); enum_tdoms = NULL; } @@ -659,10 +674,12 @@ cygheap_pwdgrp::nss_init_line (const char *line) { c += 6; c += strspn (c, " \t"); - if (!strncmp (c, "yes", 3) && strchr (" \t", c[3])) - caching = true; + if (!strncmp (c, "full", 3) && strchr (" \t", c[3])) + caching = NSS_FULL_CACHING; + else if (!strncmp (c, "yes", 3) && strchr (" \t", c[3])) + caching = NSS_CACHING; else if (!strncmp (c, "no", 2) && strchr (" \t", c[2])) - caching = false; + caching = NSS_NO_CACHING; else debug_printf ("Invalid nsswitch.conf content: %s", line); } @@ -986,6 +1003,8 @@ pwdgrp::add_account_from_windows (cygpsid &sid, bool group, cyg_ldap *pldap) return NULL; if (cygheap->pg.nss_db_caching ()) return add_account_post_fetch (line, true); + if (group) + return (prep_tls_grbuf ())->add_account_post_fetch (line, false); return (prep_tls_pwbuf ())->add_account_post_fetch (line, false); } @@ -1000,6 +1019,8 @@ pwdgrp::add_account_from_windows (const char *name, bool group, cyg_ldap *pldap) return NULL; if (cygheap->pg.nss_db_caching ()) return add_account_post_fetch (line, true); + if (group) + return (prep_tls_grbuf ())->add_account_post_fetch (line, false); return (prep_tls_pwbuf ())->add_account_post_fetch (line, false); } @@ -1014,6 +1035,8 @@ pwdgrp::add_account_from_windows (uint32_t id, bool group, cyg_ldap *pldap) return NULL; if (cygheap->pg.nss_db_caching ()) return add_account_post_fetch (line, true); + if (group) + return (prep_tls_grbuf ())->add_account_post_fetch (line, false); return (prep_tls_pwbuf ())->add_account_post_fetch (line, false); } @@ -1481,7 +1504,16 @@ pwdgrp::fetch_account_from_windows (fetch_user_arg_t &arg, bool group, if (acc_type != SidTypeUser) break; - gid = posix_offset + DOMAIN_GROUP_RID_USERS; /* Default. */ + /* Default primary group. If the sid is the current user, fetch + the default group from the current user token, otherwise make + the educated guess that the user is in group "Domain Users" + or "None". */ + if (sid == cygheap->user.sid ()) + gid = posix_offset + + sid_sub_auth_rid (cygheap->user.groups.pgsid); + else + gid = posix_offset + DOMAIN_GROUP_RID_USERS; + /* Use LDAP to fetch domain account infos. */ if (!cldap->open (NULL)) break; @@ -1724,13 +1756,19 @@ pwdgrp::fetch_account_from_windows (fetch_user_arg_t &arg, bool group, PDS_DOMAIN_TRUSTSW td = NULL; sid_sub_auth_count (sid) = sid_sub_auth_count (sid) - 1; - for (ULONG idx = 0; (td = cygheap->dom.trusted_domain (idx)); ++idx) - if (td->DomainSid && RtlEqualSid (sid, td->DomainSid)) - { - domain = td->NetbiosDomainName; - posix_offset = fetch_posix_offset (td, cldap); - break; - } + if (RtlEqualSid (sid, cygheap->dom.primary_sid ())) + { + domain = cygheap->dom.primary_flat_name (); + posix_offset = 0x100000; + } + else + for (ULONG idx = 0; (td = cygheap->dom.trusted_domain (idx)); ++idx) + if (td->DomainSid && RtlEqualSid (sid, td->DomainSid)) + { + domain = td->NetbiosDomainName; + posix_offset = fetch_posix_offset (td, cldap); + break; + } } if (domain) {