diff options
Diffstat (limited to 'winsup/cygwin/uinfo.cc')
-rw-r--r-- | winsup/cygwin/uinfo.cc | 1582 |
1 files changed, 103 insertions, 1479 deletions
diff --git a/winsup/cygwin/uinfo.cc b/winsup/cygwin/uinfo.cc index ca764269a..4ca901f35 100644 --- a/winsup/cygwin/uinfo.cc +++ b/winsup/cygwin/uinfo.cc @@ -1,7 +1,7 @@ /* uinfo.cc: user info (uid, gid, etc...) Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, - 2007, 2008, 2009, 2010, 2011, 2012, 2014 Red Hat, Inc. + 2007, 2008, 2009, 2010, 2011, 2012 Red Hat, Inc. This file is part of Cygwin. @@ -10,14 +10,12 @@ Cygwin license. Please consult the file "CYGWIN_LICENSE" for details. */ #include "winsup.h" -#include <iptypes.h> -#include <lm.h> -#include <ntsecapi.h> -#include <wininet.h> #include <unistd.h> +#include <wininet.h> #include <stdlib.h> -#include <stdio.h> #include <wchar.h> +#include <lm.h> +#include <iptypes.h> #include <sys/cygwin.h> #include "cygerrno.h" #include "pinfo.h" @@ -29,11 +27,9 @@ details. */ #include "registry.h" #include "child_info.h" #include "environ.h" +#include "pwdgrp.h" #include "tls_pbuf.h" -#include "miscfuncs.h" #include "ntdll.h" -#include "ldap.h" -#include "cygserver_pwdgrp.h" /* Initialize the part of cygheap_user that does not depend on files. The information is used in shared.cc for the user shared. @@ -85,7 +81,7 @@ cygheap_user::init () status = NtSetInformationToken (hProcToken, TokenOwner, &effec_cygsid, sizeof (cygsid)); if (!NT_SUCCESS (status)) - debug_printf ("NtSetInformationToken (TokenOwner), %y", status); + debug_printf ("NtSetInformationToken(TokenOwner), %y", status); /* Standard way to build a security descriptor with the usual DACL */ PSECURITY_ATTRIBUTES sa_buf = (PSECURITY_ATTRIBUTES) alloca (1024); @@ -116,63 +112,41 @@ cygheap_user::init () void internal_getlogin (cygheap_user &user) { - struct passwd *pwd; - struct group *pgrp, *grp, *grp2; - cyg_ldap cldap; - - /* 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_cygserver_caching ()) - internal_getgroups (0, NULL, &cldap); - if (!pwd) - debug_printf ("user not found in passwd DB"); + struct passwd *pw = NULL; + + cygpsid psid = user.sid (); + pw = internal_getpwsid (psid); + + if (!pw && !(pw = internal_getpwnam (user.name ())) + && !(pw = internal_getpwuid (DEFAULT_UID))) + debug_printf ("user not found in augmented /etc/passwd"); else { cygsid gsid; - user.set_name (pwd->pw_name); - myself->uid = pwd->pw_uid; - myself->gid = pwd->pw_gid; - /* If 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) + myself->uid = pw->pw_uid; + myself->gid = pw->pw_gid; + user.set_name (pw->pw_name); + if (gsid.getfromgr (internal_getgrgid (pw->pw_gid))) { - if (gsid.getfromgr (grp = internal_getgrgid (pwd->pw_gid, &cldap))) + if (gsid != user.groups.pgsid) { - /* 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 (); - } + 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 (); } - else - debug_printf ("group not found in group DB"); } + else + debug_printf ("gsid not found in augmented /etc/group"); } - cygheap->user.ontherange (CH_HOME, pwd); + cygheap->user.ontherange (CH_HOME, pw); } void @@ -441,7 +415,7 @@ cygheap_user::env_logsrv (const char *name, size_t namelen) WCHAR wlogsrv[INTERNET_MAX_HOST_NAME_LENGTH + 3]; sys_mbstowcs (wdomain, MAX_DOMAIN_NAME_LEN + 1, mydomain); cfree_and_set (plogsrv, almost_null); - if (get_logon_server (wdomain, wlogsrv, DS_IS_FLAT_NAME)) + if (get_logon_server (wdomain, wlogsrv, false)) sys_wcstombs_alloc (&plogsrv, HEAP_STR, wlogsrv); return plogsrv; } @@ -551,13 +525,21 @@ pwdgrp::add_line (char *eptr) { if (eptr) { + lptr = eptr; + eptr = strchr (lptr, '\n'); + if (eptr) + { + if (eptr > lptr && eptr[-1] == '\r') + eptr[-1] = '\0'; + else + *eptr = '\0'; + eptr++; + } if (curr_lines >= max_lines) { max_lines += 10; - pwdgrp_buf = crealloc_abort (pwdgrp_buf, - max_lines * pwdgrp_buf_elem_size); + *pwdgrp_buf = realloc (*pwdgrp_buf, max_lines * pwdgrp_buf_elem_size); } - lptr = eptr; if ((this->*parse) ()) curr_lines++; } @@ -565,1440 +547,82 @@ pwdgrp::add_line (char *eptr) } void -cygheap_pwdgrp::init () +pwdgrp::load (const wchar_t *rel_path) { - pwd_cache.cygserver.init_pwd (); - pwd_cache.file.init_pwd (); - pwd_cache.win.init_pwd (); - grp_cache.cygserver.init_grp (); - grp_cache.file.init_grp (); - grp_cache.win.init_grp (); - /* Default settings: - - passwd: files db - group: files db - db_prefix: auto - db_cache: yes - db_separator: + - db_enum: cache builtin - */ - pwd_src = (NSS_FILES | NSS_DB); - grp_src = (NSS_FILES | NSS_DB); - prefix = NSS_AUTO; - separator[0] = L'+'; - caching = true; - enums = (ENUM_CACHE | ENUM_BUILTIN); - enum_tdoms = NULL; -} + static const char failed[] = "failed"; + static const char succeeded[] = "succeeded"; + const char *res = failed; + HANDLE fh = NULL; -/* The /etc/nsswitch.conf file is read exactly once by the root process of a - process tree. We can't afford methodical changes during the lifetime of a - process tree. */ -void -cygheap_pwdgrp::nss_init_line (const char *line) -{ - const char *c = line + strspn (line, " \t"); - switch (*c) - { - case 'p': - case 'g': - { - int *src = NULL; - if (!strncmp (c, "passwd:", 7)) - { - src = &pwd_src; - c += 7; - } - else if (!strncmp (c, "group:", 6)) - { - src = &grp_src; - c += 6; - } - if (src) - { - *src = 0; - while (*c) - { - c += strspn (c, " \t"); - if (!*c || *c == '#') - break; - if (!strncmp (c, "files", 5) && strchr (" \t", c[5])) - { - *src |= NSS_FILES; - c += 5; - } - else if (!strncmp (c, "db", 2) && strchr (" \t", c[2])) - { - *src |= NSS_DB; - c += 2; - } - else - { - c += strcspn (c, " \t"); - debug_printf ("Invalid nsswitch.conf content: %s", line); - } - } - if (*src == 0) - *src = (NSS_FILES | NSS_DB); - } - } - break; - case 'd': - if (strncmp (c, "db_", 3)) - { - debug_printf ("Invalid nsswitch.conf content: %s", line); - break; - } - c += 3; - if (!strncmp (c, "prefix:", 7)) - { - c += 7; - c += strspn (c, " \t"); - if (!strncmp (c, "auto", 4) && strchr (" \t", c[4])) - prefix = NSS_AUTO; - else if (!strncmp (c, "primary", 7) && strchr (" \t", c[7])) - prefix = NSS_PRIMARY; - else if (!strncmp (c, "always", 6) && strchr (" \t", c[6])) - prefix = NSS_ALWAYS; - else - debug_printf ("Invalid nsswitch.conf content: %s", line); - } - else if (!strncmp (c, "separator:", 10)) - { - c += 10; - c += strspn (c, " \t"); - if ((unsigned char) *c <= 0x7f && *c != ':' && strchr (" \t", c[1])) - separator[0] = (unsigned char) *c; - else - debug_printf ("Invalid nsswitch.conf content: %s", line); - } - else if (!strncmp (c, "enum:", 5)) - { - tmp_pathbuf tp; - char *tdoms = tp.c_get (); - char *td = tdoms; - int new_enums = ENUM_NONE; - - td[0] = '\0'; - c += 5; - c += strspn (c, " \t"); - while (!strchr (" \t", *c)) - { - const char *e = c + strcspn (c, " \t"); - if (!strncmp (c, "none", 4) && strchr (" \t", c[4])) - new_enums = ENUM_NONE; - else if (!strncmp (c, "builtin", 7) && strchr (" \t", c[7])) - new_enums |= ENUM_BUILTIN; - else if (!strncmp (c, "cache", 5) && strchr (" \t", c[5])) - new_enums |= ENUM_CACHE; - else if (!strncmp (c, "files", 5) && strchr (" \t", c[5])) - new_enums |= ENUM_FILES; - else if (!strncmp (c, "local", 5) && strchr (" \t", c[5])) - new_enums |= ENUM_LOCAL; - else if (!strncmp (c, "primary", 7) && strchr (" \t", c[7])) - new_enums |= ENUM_PRIMARY; - else if (!strncmp (c, "alltrusted", 10) && strchr (" \t", c[10])) - new_enums |= ENUM_TDOMS | ENUM_TDOMS_ALL; - else if (!strncmp (c, "all", 3) && strchr (" \t", c[3])) - new_enums |= ENUM_ALL; - else - { - td = stpcpy (stpncpy (td, c, e - c), " "); - new_enums |= ENUM_TDOMS; - } - c = e; - c += strspn (c, " \t"); - } - if ((new_enums & (ENUM_TDOMS | ENUM_TDOMS_ALL)) == ENUM_TDOMS) - { - if (td > tdoms) - { - PWCHAR spc; - sys_mbstowcs_alloc (&enum_tdoms, HEAP_BUF, tdoms); - /* Convert string to REG_MULTI_SZ-style. */ - while ((spc = wcsrchr (enum_tdoms, L' '))) - *spc = L'\0'; - } - else - new_enums &= ~(ENUM_TDOMS | ENUM_TDOMS_ALL); - } - enums = new_enums; - } - break; - case '\0': - case '#': - break; - default: - debug_printf ("Invalid nsswitch.conf content: %s", line); - break; - } -} - -void -cygheap_pwdgrp::_nss_init () -{ - UNICODE_STRING path; - OBJECT_ATTRIBUTES attr; - NT_readline rl; - tmp_pathbuf tp; - char *buf = tp.c_get (); - - PCWSTR rel_path = L"\\etc\\nsswitch.conf"; - path.Buffer = (PWCHAR) alloca ((wcslen (cygheap->installation_root) - + wcslen (rel_path) + 1) * sizeof (WCHAR)); - wcpcpy (wcpcpy (path.Buffer, cygheap->installation_root), rel_path); - RtlInitUnicodeString (&path, path.Buffer); - InitializeObjectAttributes (&attr, &path, OBJ_CASE_INSENSITIVE, - NULL, NULL); - if (rl.init (&attr, buf, NT_MAX_PATH)) - while ((buf = rl.gets ())) - nss_init_line (buf); - nss_inited = true; -} - -/* Override the ParentIndex value of the PDS_DOMAIN_TRUSTSW entry with the - PosixOffset. */ -#define PosixOffset ParentIndex - -bool -cygheap_domain_info::init () -{ - HANDLE lsa; NTSTATUS status; - ULONG ret; - /* We *have* to copy the information. Apart from our wish to have the - stuff in the cygheap, even when not calling LsaFreeMemory on the result, - the data will be overwritten later. From what I gather, the information - is, in fact, stored on the stack. */ - PPOLICY_DNS_DOMAIN_INFO pdom; - PPOLICY_ACCOUNT_DOMAIN_INFO adom; - PDS_DOMAIN_TRUSTSW td; - ULONG tdom_cnt; - - if (adom_name) - return true; - lsa = lsa_open_policy (NULL, POLICY_VIEW_LOCAL_INFORMATION); - if (!lsa) - { - system_printf ("lsa_open_policy(NULL) failed"); - return false; - } - /* Fetch primary domain information from local LSA. */ - status = LsaQueryInformationPolicy (lsa, PolicyDnsDomainInformation, - (PVOID *) &pdom); - if (status != STATUS_SUCCESS) - { - system_printf ("LsaQueryInformationPolicy(Primary) %y", status); - return false; - } - /* Copy primary domain info to cygheap. */ - pdom_name = cwcsdup (pdom->Name.Buffer); - pdom_dns_name = pdom->DnsDomainName.Length - ? cwcsdup (pdom->DnsDomainName.Buffer) : NULL; - pdom_sid = pdom->Sid; - LsaFreeMemory (pdom); - /* Fetch account domain information from local LSA. */ - status = LsaQueryInformationPolicy (lsa, PolicyAccountDomainInformation, - (PVOID *) &adom); - if (status != STATUS_SUCCESS) - { - system_printf ("LsaQueryInformationPolicy(Account) %y", status); - return false; - } - /* Copy account domain info to cygheap. If we're running on a DC the account - domain is identical to the primary domain. This leads to confusion when - trying to compute the uid/gid values. Therefore we invalidate the account - domain name if we're running on a DC. */ - adom_sid = adom->DomainSid; - adom_name = cwcsdup (pdom_sid == adom_sid ? L"@" : adom->DomainName.Buffer); - LsaFreeMemory (adom); - lsa_close_policy (lsa); - if (cygheap->dom.member_machine ()) - { - ret = DsEnumerateDomainTrustsW (NULL, DS_DOMAIN_DIRECT_INBOUND - | DS_DOMAIN_DIRECT_OUTBOUND - | DS_DOMAIN_IN_FOREST, - &td, &tdom_cnt); - if (ret != ERROR_SUCCESS) - { - SetLastError (ret); - debug_printf ("DsEnumerateDomainTrusts: %E"); - return true; - } - if (tdom_cnt == 0) - { - return true; - } - /* Copy trusted domain info to cygheap, setting PosixOffset on the fly. */ - tdom = (PDS_DOMAIN_TRUSTSW) - cmalloc_abort (HEAP_BUF, tdom_cnt * sizeof (DS_DOMAIN_TRUSTSW)); - memcpy (tdom, td, tdom_cnt * sizeof (DS_DOMAIN_TRUSTSW)); - for (ULONG idx = 0; idx < tdom_cnt; ++idx) - { - /* Copy... */ - tdom[idx].NetbiosDomainName = cwcsdup (td[idx].NetbiosDomainName); - /* DnsDomainName as well as DomainSid can be NULL. The reason is - usually a domain of type TRUST_TYPE_DOWNLEVEL. This can be an - old pre-AD domain, or a Netware domain, etc. If DnsDomainName - is NULL, just set it to NetbiosDomainName. This simplifies - subsequent code which doesn't have to check for a NULL pointer. */ - tdom[idx].DnsDomainName = td[idx].DnsDomainName - ? cwcsdup (td[idx].DnsDomainName) - : tdom[idx].NetbiosDomainName; - if (td[idx].DomainSid) - { - ULONG len = RtlLengthSid (td[idx].DomainSid); - tdom[idx].DomainSid = cmalloc_abort(HEAP_BUF, len); - RtlCopySid (len, tdom[idx].DomainSid, td[idx].DomainSid); - } - /* ...and set PosixOffset to 0. This */ - tdom[idx].PosixOffset = 0; - } - NetApiBufferFree (td); - tdom_count = tdom_cnt; - } - /* If we have Microsoft Client for NFS installed, we make use of a name - mapping server. This can be either Active Directory to map uids/gids - directly to Windows SIDs, or an AD LDS or other RFC 2307 compatible - identity store. The name of the mapping domain can be fetched from the - registry key created by the NFS client installation and entered by the - user via nfsadmin or the "Services For NFS" MMC snap-in. - - Reference: - http://blogs.technet.com/b/filecab/archive/2012/10/09/nfs-identity-mapping-in-windows-server-2012.aspx - Note that we neither support UNMP nor local passwd/group file mapping, - nor UUUA. - - This function returns the mapping server from the aforementioned registry - key, or, if none is configured, NULL, which will be resolved to the - primary domain of the machine by the ldap_init function. - - The latter is useful to get an RFC 2307 mapping for Samba UNIX accounts, - even if no NFS name mapping is configured on the machine. Fortunately, - the posixAccount and posixGroup schemas are already available in the - Active Directory default setup since Windows Server 2003 R2. */ - reg_key reg (HKEY_LOCAL_MACHINE, KEY_READ | KEY_WOW64_64KEY, - L"SOFTWARE", L"Microsoft", L"ServicesForNFS", NULL); - if (!reg.error ()) - { - DWORD rfc2307 = reg.get_dword (L"Rfc2307", 0); - if (rfc2307) - { - rfc2307_domain_buf = (PWCHAR) ccalloc_abort (HEAP_STR, 257, - sizeof (WCHAR)); - reg.get_string (L"Rfc2307Domain", rfc2307_domain_buf, 257, L""); - if (!rfc2307_domain_buf[0]) - { - cfree (rfc2307_domain_buf); - rfc2307_domain_buf = NULL; - } - } - } - return true; -} + OBJECT_ATTRIBUTES attr; + IO_STATUS_BLOCK io; + FILE_STANDARD_INFORMATION fsi; -/* Per session, so it changes potentially when switching the user context. */ -static cygsid logon_sid (""); + if (buf) + free (buf); + buf = NULL; + curr_lines = 0; -static void -get_logon_sid () -{ - if (PSID (logon_sid) == NO_SID) + if (!path && + !(path = (PWCHAR) malloc ((wcslen (cygheap->installation_root) + + wcslen (rel_path) + 1) * sizeof (WCHAR)))) { - NTSTATUS status; - ULONG size; - tmp_pathbuf tp; - PTOKEN_GROUPS groups = (PTOKEN_GROUPS) tp.c_get (); - - status = NtQueryInformationToken (hProcToken, TokenGroups, groups, - NT_MAX_PATH, &size); - if (!NT_SUCCESS (status)) - debug_printf ("NtQueryInformationToken() %y", status); - else - { - for (DWORD pg = 0; pg < groups->GroupCount; ++pg) - if (groups->Groups[pg].Attributes & SE_GROUP_LOGON_ID) - { - logon_sid = groups->Groups[pg].Sid; - break; - } - } - } -} - -void * -pwdgrp::add_account_post_fetch (char *line, bool lock) -{ - if (line) - { - void *ret; - if (lock) - pglock.init ("pglock")->acquire (); - add_line (line); - ret = ((char *) pwdgrp_buf) + (curr_lines - 1) * pwdgrp_buf_elem_size; - if (lock) - pglock.release (); - return ret; + paranoid_printf ("malloc (%W) failed", rel_path); + goto out; } - return NULL; -} - -void * -pwdgrp::add_account_from_file (cygpsid &sid) -{ - if (!path.MaximumLength) - return NULL; - fetch_user_arg_t arg; - arg.type = SID_arg; - arg.sid = &sid; - char *line = fetch_account_from_file (arg); - return (struct passwd *) add_account_post_fetch (line, true); -} - -void * -pwdgrp::add_account_from_file (const char *name) -{ - if (!path.MaximumLength) - return NULL; - fetch_user_arg_t arg; - arg.type = NAME_arg; - arg.name = name; - char *line = fetch_account_from_file (arg); - return (struct passwd *) add_account_post_fetch (line, true); -} + wcpcpy (wcpcpy (path, cygheap->installation_root), rel_path); + RtlInitUnicodeString (&upath, path); -void * -pwdgrp::add_account_from_file (uint32_t id) -{ - if (!path.MaximumLength) - return NULL; - fetch_user_arg_t arg; - arg.type = ID_arg; - arg.id = id; - char *line = fetch_account_from_file (arg); - return (struct passwd *) add_account_post_fetch (line, true); -} + InitializeObjectAttributes (&attr, &upath, OBJ_CASE_INSENSITIVE, NULL, NULL); + etc_ix = etc::init (etc_ix, &attr); -void * -pwdgrp::add_account_from_windows (cygpsid &sid, cyg_ldap *pldap) -{ - fetch_user_arg_t arg; - arg.type = SID_arg; - arg.sid = &sid; - char *line = fetch_account_from_windows (arg, pldap); - if (!line) - return NULL; - return add_account_post_fetch (line, true); -} + paranoid_printf ("%S", &upath); -void * -pwdgrp::add_account_from_windows (const char *name, cyg_ldap *pldap) -{ - fetch_user_arg_t arg; - arg.type = NAME_arg; - arg.name = name; - char *line = fetch_account_from_windows (arg, pldap); - if (!line) - return NULL; - return add_account_post_fetch (line, true); -} - -void * -pwdgrp::add_account_from_windows (uint32_t id, cyg_ldap *pldap) -{ - fetch_user_arg_t arg; - arg.type = ID_arg; - arg.id = id; - char *line = fetch_account_from_windows (arg, pldap); - if (!line) - return NULL; - return 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. - - If the file doesn't exist when this function is called the first time, - by the first Cygwin process in a process tree, the file will never be - visited again by any process in this process tree. This is important, - because we cannot allow a change of UID/GID values for the lifetime - of a process tree. - - If the file gets deleted or unreadable, the file cache will stay in - place, but we won't try to read new accounts from the file. - - The return code indicates to the calling function if the file exists. */ -bool -pwdgrp::check_file () -{ - FILE_BASIC_INFORMATION fbi; - NTSTATUS status; - - if (!path.Buffer) - { - PCWSTR rel_path = is_group () ? L"\\etc\\group" : L"\\etc\\passwd"; - path.Buffer = (PWCHAR) cmalloc_abort (HEAP_BUF, - (wcslen (cygheap->installation_root) - + wcslen (rel_path) + 1) - * sizeof (WCHAR)); - wcpcpy (wcpcpy (path.Buffer, cygheap->installation_root), rel_path); - RtlInitUnicodeString (&path, path.Buffer); - InitializeObjectAttributes (&attr, &path, OBJ_CASE_INSENSITIVE, - NULL, NULL); - } - else if (path.MaximumLength == 0) /* Indicates that the file doesn't exist. */ - return false; - status = NtQueryAttributesFile (&attr, &fbi); + status = NtOpenFile (&fh, SYNCHRONIZE | FILE_READ_DATA, &attr, &io, + FILE_SHARE_VALID_FLAGS, + FILE_SYNCHRONOUS_IO_NONALERT + | FILE_OPEN_FOR_BACKUP_INTENT); if (!NT_SUCCESS (status)) { - if (last_modified.QuadPart) - last_modified.QuadPart = 0LL; - else - path.MaximumLength = 0; - return false; + paranoid_printf ("NtOpenFile(%S) failed, status %y", &upath, status); + goto out; } - if (fbi.LastWriteTime.QuadPart > last_modified.QuadPart) - { - last_modified.QuadPart = fbi.LastWriteTime.QuadPart; - if (curr_lines > 0) - { - pglock.init ("pglock")->acquire (); - int curr = curr_lines; - curr_lines = 0; - for (int i = 0; i < curr; ++i) - cfree (is_group () ? this->group ()[i].g.gr_name - : this->passwd ()[i].p.pw_name); - pglock.release (); - } - } - return true; -} - -char * -pwdgrp::fetch_account_from_line (fetch_user_arg_t &arg, const char *line) -{ - char *p, *e; - - switch (arg.type) - { - case SID_arg: - /* Ignore fields, just scan for SID string. */ - if (!(p = strstr (line, arg.name)) || p[arg.len] != ':') - return NULL; - break; - case NAME_arg: - /* First field is always name. */ - if (!strncasematch (line, arg.name, arg.len) || line[arg.len] != ':') - return NULL; - break; - case ID_arg: - /* Skip to third field. */ - if (!(p = strchr (line, ':')) || !(p = strchr (p + 1, ':'))) - return NULL; - if (strtoul (p + 1, &e, 10) != arg.id || !e || *e != ':') - return NULL; - break; - } - return cstrdup (line); -} - -char * -pwdgrp::fetch_account_from_file (fetch_user_arg_t &arg) -{ - NT_readline rl; - tmp_pathbuf tp; - char *buf = tp.c_get (); - char str[128]; - char *ret = NULL; - - /* Create search string. */ - switch (arg.type) - { - case SID_arg: - /* Override SID with SID string. */ - arg.sid->string (str); - arg.name = str; - /*FALLTHRU*/ - case NAME_arg: - arg.len = strlen (arg.name); - break; - case ID_arg: - break; - } - if (rl.init (&attr, buf, NT_MAX_PATH)) - while ((buf = rl.gets ())) - if ((ret = fetch_account_from_line (arg, buf))) - return ret; - return NULL; -} - -static ULONG -fetch_posix_offset (PDS_DOMAIN_TRUSTSW td, cyg_ldap *cldap) -{ - uint32_t id_val = UINT32_MAX; - - if (!td->PosixOffset && !(td->Flags & DS_DOMAIN_PRIMARY) && td->DomainSid) - { - if (cldap->open (NULL) == NO_ERROR) - id_val = cldap->fetch_posix_offset_for_domain (td->DnsDomainName); - if (id_val < PRIMARY_POSIX_OFFSET) - { - /* If the offset is less than the primay domain offset, we're bound - to suffer collisions with system and local accounts. Move offset - to a fixed replacement fake offset. This may result in collisions - between other domains all of which were moved to this replacement - offset, but we can't fix all problems caused by careless admins. */ - id_val = UNUSABLE_POSIX_OFFSET; - } - else if (id_val == UINT32_MAX) - { - /* We're probably running under a local account, so we're not allowed - to fetch any information from AD beyond the most obvious. Fake a - reasonable posix offset as above and hope for the best. */ - id_val = NOACCESS_POSIX_OFFSET; - } - td->PosixOffset = id_val; - } - return td->PosixOffset; -} - -/* Helper function to replace colons with semicolons in pw_gecos field. */ -static PWCHAR -colon_to_semicolon (PWCHAR str) -{ - PWCHAR cp = str; - while ((cp = wcschr (cp, L':')) != NULL) - *cp++ = L';'; - return str; -} - -/* CV 2014-05-08: USER_INFO_24 is not yet defined in Mingw64, but will be in - the next release. For the time being, define the structure here with - another name which won't collide with the upcoming correct definition - in lmaccess.h. */ -struct cyg_USER_INFO_24 -{ - BOOL usri24_internet_identity; - DWORD usri24_flags; - LPWSTR usri24_internet_provider_name; - LPWSTR usri24_internet_principal_name; - PSID usri24_user_sid; -}; - -char * -pwdgrp::fetch_account_from_windows (fetch_user_arg_t &arg, cyg_ldap *pldap) -{ - /* Used in LookupAccount calls. */ - WCHAR namebuf[UNLEN + 1], *name = namebuf; - WCHAR dom[DNLEN + 1] = L""; - cygsid csid; - DWORD nlen = UNLEN + 1; - DWORD dlen = DNLEN + 1; - DWORD slen = SECURITY_MAX_SID_SIZE; - cygpsid sid (NO_SID); - SID_NAME_USE acc_type; - BOOL ret = false; - /* Cygwin user name style. */ - bool fully_qualified_name = false; - /* Computed stuff. */ - uid_t uid = ILLEGAL_UID; - gid_t gid = ILLEGAL_GID; - bool is_domain_account = true; - PCWSTR domain = NULL; - PWCHAR shell = NULL; - PWCHAR home = NULL; - PWCHAR gecos = NULL; - /* Temporary stuff. */ - PWCHAR p; - WCHAR sidstr[128]; - ULONG posix_offset = 0; - uint32_t id_val; - cyg_ldap loc_ldap; - cyg_ldap *cldap = pldap ?: &loc_ldap; - - /* Initialize */ - if (!cygheap->dom.init ()) - return NULL; - - switch (arg.type) - { - case SID_arg: - sid = *arg.sid; - ret = LookupAccountSidW (NULL, sid, name, &nlen, dom, &dlen, &acc_type); - if (!ret - && cygheap->dom.member_machine () - && sid_id_auth (sid) == 5 /* SECURITY_NT_AUTHORITY */ - && sid_sub_auth (sid, 0) == SECURITY_BUILTIN_DOMAIN_RID) - { - /* LookupAccountSid called on a non-DC cannot resolve aliases which - are not defined in the local SAM. If we encounter an alias which - can't be resolved, and if we're a domain member machine, ask a DC. - Do *not* use LookupAccountSidW. It can take ages when called on a - DC for some weird reason. Use LDAP instead. */ - PWCHAR val; - - if (cldap->open (NULL) == NO_ERROR - && cldap->fetch_ad_account (sid, is_group ()) - && (val = cldap->get_group_name ())) - { - wcpcpy (name, val); - wcpcpy (dom, L"BUILTIN"); - acc_type = SidTypeAlias; - ret = true; - } - } - if (!ret) - debug_printf ("LookupAccountSid(%W), %E", sid.string (sidstr)); - break; - case NAME_arg: - /* Skip leading domain separator. This denotes an alias or well-known - group, which will be found first by LookupAccountNameW anyway. - Otherwise, if the name has no leading domain name, it's either a - standalone machine, or the username must be from the primary domain. - In the latter case, prepend the primary domain name so as not to - collide with an account from the account domain with the same name. */ - bool fq_name; - - fq_name = false; - /* Copy over to wchar for search. */ - sys_mbstowcs (name, UNLEN + 1, arg.name); - /* Replace domain separator char with backslash and make sure p is NULL - or points to the backslash, so... */ - if ((p = wcschr (name, cygheap->pg.nss_separator ()[0]))) - { - fq_name = true; - *p = L'\\'; - } - sid = csid; - ret = LookupAccountNameW (NULL, name, sid, &slen, dom, &dlen, &acc_type); - /* If this is a name-only S-1-5-21 account *and* it's a machine account - on a domain member machine, then we found the wrong one. Another - weird, but perfectly valid case is, if the group name is identical - to the domain name. Try again with domain name prepended. */ - if (ret - && !fq_name - && sid_id_auth (sid) == 5 /* SECURITY_NT_AUTHORITY */ - && sid_sub_auth (sid, 0) == SECURITY_NT_NON_UNIQUE - && cygheap->dom.member_machine () - && (wcscasecmp (dom, cygheap->dom.account_flat_name ()) == 0 - || acc_type == SidTypeDomain)) - { - p = wcpcpy (name, cygheap->dom.primary_flat_name ()); - *p = L'\\'; - sys_mbstowcs (p + 1, UNLEN + 1, arg.name); - slen = SECURITY_MAX_SID_SIZE; - dlen = DNLEN + 1; - sid = csid; - ret = LookupAccountNameW (NULL, name, sid, &slen, dom, &dlen, - &acc_type); - } - if (!ret) - { - debug_printf ("LookupAccountNameW (%W), %E", name); - return NULL; - } - /* We can skip the backslash in the rest of this function. */ - if (p) - name = p + 1; - /* Last but not least, some validity checks on the name style. */ - if (!fq_name) - { - /* name_only only if db_prefix is auto. */ - if (!cygheap->pg.nss_prefix_auto ()) - { - debug_printf ("Invalid account name <%s> (name only/" - "db_prefix not auto)", arg.name); - return NULL; - } - /* name_only account is either builtin or primary domain, or - account domain on non-domain machines. */ - if (sid_id_auth (sid) == 5 /* SECURITY_NT_AUTHORITY */ - && sid_sub_auth (sid, 0) == SECURITY_NT_NON_UNIQUE) - { - if (cygheap->dom.member_machine ()) - { - if (wcscasecmp (dom, cygheap->dom.primary_flat_name ()) != 0) - { - debug_printf ("Invalid account name <%s> (name only/" - "non primary on domain machine)", arg.name); - return NULL; - } - } - else if (wcscasecmp (dom, cygheap->dom.account_flat_name ()) != 0) - { - debug_printf ("Invalid account name <%s> (name only/" - "non machine on non-domain machine)", arg.name); - return NULL; - } - } - } - else - { - /* All is well if db_prefix is always. */ - if (cygheap->pg.nss_prefix_always ()) - break; - /* Otherwise, no fully_qualified for builtin accounts. */ - if (sid_id_auth (sid) != 5 /* SECURITY_NT_AUTHORITY */ - || sid_sub_auth (sid, 0) != SECURITY_NT_NON_UNIQUE) - { - debug_printf ("Invalid account name <%s> (fully qualified/" - "not NON_UNIQUE)", arg.name); - return NULL; - } - /* All is well if db_prefix is primary. */ - if (cygheap->pg.nss_prefix_primary ()) - break; - /* Domain member and domain == primary domain? */ - if (cygheap->dom.member_machine ()) - { - if (!wcscasecmp (dom, cygheap->dom.primary_flat_name ())) - { - debug_printf ("Invalid account name <%s> (fully qualified/" - "primary domain account)", arg.name); - return NULL; - } - } - /* Not domain member and domain == account domain? */ - else if (!wcscasecmp (dom, cygheap->dom.account_flat_name ())) - { - debug_printf ("Invalid account name <%s> (fully qualified/" - "local account)", arg.name); - return NULL; - } - } - break; - case ID_arg: - /* Construct SID from ID using the SFU rules, just like the code below - goes the opposite route. */ -#ifndef INTERIX_COMPATIBLE - /* Except for Builtin and Alias groups in the SECURITY_NT_AUTHORITY. - We create uid/gid values compatible with the old values generated - by mkpasswd/mkgroup. */ - if (arg.id < 0x200) - __small_swprintf (sidstr, L"S-1-5-%u", arg.id & 0x1ff); - else if (arg.id == 0x3e8) /* Special case "Other Organization" */ - wcpcpy (sidstr, L"S-1-5-1000"); - else if (arg.id <= 0x7ff) - __small_swprintf (sidstr, L"S-1-5-32-%u", arg.id & 0x7ff); - else -#endif - if (arg.id == 0xffe) - { - /* OtherSession != Logon SID. */ - get_logon_sid (); - /* LookupAccountSidW will fail. */ - sid = csid = logon_sid; - sid_sub_auth_rid (sid) = 0; - break; - } - else if (arg.id == 0xfff) - { - /* CurrentSession == Logon SID. */ - get_logon_sid (); - /* LookupAccountSidW will fail. */ - sid = logon_sid; - break; - } - else if (arg.id < 0x10000) - { - /* Nothing. */ - debug_printf ("Invalid POSIX id %u", arg.id); - return NULL; - } - else if (arg.id < 0x20000) - { - /* Well-Known Group */ - arg.id -= 0x10000; - __small_swprintf (sidstr, L"S-1-%u-%u", arg.id >> 8, arg.id & 0xff); - } - else if (arg.id >= 0x30000 && arg.id < 0x40000) - { - /* Account domain user or group. */ - PWCHAR s = cygheap->dom.account_sid ().pstring (sidstr); - __small_swprintf (s, L"-%u", arg.id & 0xffff); - } - else if (arg.id < 0x60000) - { - /* Builtin Alias */ - __small_swprintf (sidstr, L"S-1-5-%u-%u", - arg.id >> 12, arg.id & 0xffff); - } - else if (arg.id < 0x70000) - { - /* Mandatory Label. */ - __small_swprintf (sidstr, L"S-1-16-%u", arg.id & 0xffff); - } - else if (arg.id < 0x80000) - { - /* Identity assertion SIDs. */ - __small_swprintf (sidstr, L"S-1-18-%u", arg.id & 0xffff); - } - else if (arg.id < PRIMARY_POSIX_OFFSET) - { - /* Nothing. */ - debug_printf ("Invalid POSIX id %u", arg.id); - return NULL; - } - else if (arg.id == ILLEGAL_UID) - { - /* Just some fake. */ - sid = csid = "S-1-99-0"; - break; - } - else if (arg.id >= UNIX_POSIX_OFFSET) - { - /* UNIX (unknown NFS or Samba) user account. */ - __small_swprintf (sidstr, L"S-1-22-%u-%u", - is_group () ? 2 : 1, arg.id & UNIX_POSIX_MASK); - /* LookupAccountSidW will fail. */ - sid = csid = sidstr; - break; - } - else - { - /* Some trusted domain? */ - PDS_DOMAIN_TRUSTSW td = NULL, this_td = NULL; - - for (ULONG idx = 0; (td = cygheap->dom.trusted_domain (idx)); ++idx) - { - fetch_posix_offset (td, &loc_ldap); - if (td->PosixOffset > posix_offset && td->PosixOffset <= arg.id) - posix_offset = (this_td = td)->PosixOffset; - } - if (this_td) - { - cygpsid tsid (this_td->DomainSid); - PWCHAR s = tsid.pstring (sidstr); - __small_swprintf (s, L"-%u", arg.id - posix_offset); - } - else - { - /* Primary domain */ - PWCHAR s = cygheap->dom.primary_sid ().pstring (sidstr); - __small_swprintf (s, L"-%u", arg.id - PRIMARY_POSIX_OFFSET); - } - posix_offset = 0; - } - sid = csid = sidstr; - ret = LookupAccountSidW (NULL, sid, name, &nlen, dom, &dlen, &acc_type); - if (!ret) - { - debug_printf ("LookupAccountSidW (%W), %E", sidstr); - return NULL; - } - break; - } - if (ret) - { - /* Builtin account? SYSTEM, for instance, is returned as SidTypeUser, - if a process is running as LocalSystem service. - Microsoft Account? These show up in the user's group list, using the - undocumented security authority 11. Even though this is officially a - user account, it only matters as part of the group list, so we convert - it to a well-known group here. */ - if (acc_type == SidTypeUser - && (sid_sub_auth_count (sid) <= 3 || sid_id_auth (sid) == 11)) - acc_type = SidTypeWellKnownGroup; - switch (acc_type) - { - case SidTypeUser: - case SidTypeGroup: - case SidTypeAlias: - /* Predefined alias? */ - if (acc_type == SidTypeAlias - && sid_sub_auth (sid, 0) != SECURITY_NT_NON_UNIQUE) - { -#ifdef INTERIX_COMPATIBLE - posix_offset = 0x30000; - uid = 0x1000 * sid_sub_auth (sid, 0) - + (sid_sub_auth_rid (sid) & 0xffff); -#else - posix_offset = 0; -#endif - fully_qualified_name = cygheap->pg.nss_prefix_always (); - is_domain_account = false; - } - /* Account domain account? */ - else if (!wcscasecmp (dom, cygheap->dom.account_flat_name ())) - { - posix_offset = 0x30000; - if (cygheap->dom.member_machine () - || !cygheap->pg.nss_prefix_auto ()) - fully_qualified_name = true; - is_domain_account = false; - } - /* Domain member machine? */ - else if (cygheap->dom.member_machine ()) - { - /* Primary domain account? */ - if (!wcscasecmp (dom, cygheap->dom.primary_flat_name ())) - { - posix_offset = PRIMARY_POSIX_OFFSET; - /* In theory domain should have been set to - cygheap->dom.primary_dns_name (), but it turns out that - not setting the domain here has advantages. We open the - ldap connection to NULL (== some DC of our primary domain) - anyway. So the domain is only used later on. So, don't - set domain here to non-NULL, unless you're sure you have - also changed subsequent assumptions that domain is NULL - if it's a primary domain account. */ - if (!cygheap->pg.nss_prefix_auto ()) - fully_qualified_name = true; - } - else - { - /* No, fetch POSIX offset. */ - PDS_DOMAIN_TRUSTSW td = NULL; - - fully_qualified_name = true; - for (ULONG idx = 0; - (td = cygheap->dom.trusted_domain (idx)); - ++idx) - if (!wcscasecmp (dom, td->NetbiosDomainName)) - { - domain = td->DnsDomainName; - posix_offset = - fetch_posix_offset (td, &loc_ldap); - break; - } - - if (!domain) - { - debug_printf ("Unknown domain %W", dom); - return NULL; - } - } - } - /* If the domain returned by LookupAccountSid is not our machine - name, and if our machine is no domain member, we lose. We have - nobody to ask for the POSIX offset. */ - else - { - debug_printf ("Unknown domain %W", dom); - return NULL; - } - /* Generate values. */ - if (uid == ILLEGAL_UID) - uid = posix_offset + sid_sub_auth_rid (sid); - - /* We only care for extended user information if we're creating a - passwd entry and the account is a user or alias. */ - if (is_group () || acc_type == SidTypeGroup) - break; - - if (acc_type == SidTypeUser) - { - /* 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; - } - - if (is_domain_account) - { - /* Use LDAP to fetch domain account infos. */ - if (cldap->open (NULL) != NO_ERROR) - break; - if (cldap->fetch_ad_account (sid, is_group (), domain)) - { - PWCHAR val; - - if ((id_val = cldap->get_primary_gid ()) != ILLEGAL_GID) - gid = posix_offset + id_val; - if ((val = cldap->get_gecos ())) - gecos = colon_to_semicolon ( - wcscpy ((PWCHAR) alloca ((wcslen (val) + 1) - * sizeof (WCHAR)), val)); - if ((val = cldap->get_home ())) - home = wcscpy ((PWCHAR) alloca ((wcslen (val) + 1) - * sizeof (WCHAR)), val); - if ((val = cldap->get_shell ())) - shell = wcscpy ((PWCHAR) alloca ((wcslen (val) + 1) - * sizeof (WCHAR)), val); - /* Check and, if necessary, add unix<->windows id mapping on - the fly, unless we're called from getpwent. */ - if (!pldap) - { - id_val = cldap->get_unix_uid (); - if (id_val != ILLEGAL_UID - && cygheap->ugid_cache.get_uid (id_val) - == ILLEGAL_UID) - cygheap->ugid_cache.add_uid (id_val, uid); - } - } - } - /* Otherwise check account domain (local SAM).*/ - else - { - NET_API_STATUS nas; - PUSER_INFO_4 ui; - PLOCALGROUP_INFO_1 gi; - PCWSTR comment; - PWCHAR pgrp = NULL; - PWCHAR uxid = NULL; - struct { - PCWSTR str; - size_t len; - PWCHAR *tgt; - bool group; - } search[] = { - { L"unix=\"", 6, &uxid, true }, - { L"home=\"", 6, &home, false }, - { L"shell=\"", 7, &shell, false }, - { L"group=\"", 7, &pgrp, false }, - { NULL, 0, NULL } - }; - PWCHAR s, e; - - if (acc_type == SidTypeUser) - { - nas = NetUserGetInfo (NULL, name, 4, (PBYTE *) &ui); - if (nas != NERR_Success) - { - debug_printf ("NetUserGetInfo(%W) %u", name, nas); - break; - } - /* Set comment variable for below attribute loop. */ - comment = ui->usri4_comment; - /* Logging in with a Microsoft Account, the user's primary - group SID is the user's SID. Security sensitive tools - expecting tight file permissions choke on that. We need - an explicit primary group which is not identical to the - user account. Unfortunately, while the default primary - group of the account in SAM is still "None", "None" is not - in the user token group list. So, what we do here is to - use "Users" as a sane default primary group instead. */ - if (wincap.has_microsoft_accounts ()) - { - struct cyg_USER_INFO_24 *ui24; - nas = NetUserGetInfo (NULL, name, 24, (PBYTE *) &ui24); - if (nas == NERR_Success) - { - if (ui24->usri24_internet_identity) - gid = DOMAIN_ALIAS_RID_USERS; - NetApiBufferFree (ui24); - } - } - } - else /* acc_type == SidTypeAlias */ - { - nas = NetLocalGroupGetInfo (NULL, name, 1, (PBYTE *) &gi); - if (nas != NERR_Success) - { - debug_printf ("NetLocalGroupGetInfo(%W) %u", name, nas); - break; - } - /* Set comment variable for below attribute loop. */ - comment = gi->lgrpi1_comment; - } - /* Local SAM accounts have only a handful attributes - available to home users. Therefore, fetch additional - passwd/group attributes from the "Description" field - in XML short style. */ - if ((s = wcsstr (comment, L"<cygwin ")) - && (e = wcsstr (s + 8, L"/>"))) - { - s += 8; - *e = L'\0'; - while (*s) - { - bool found = false; - - while (*s == L' ') - ++s; - for (size_t i = 0; search[i].str; ++i) - if ((acc_type == SidTypeUser || search[i].group) - && !wcsncmp (s, search[i].str, search[i].len)) - { - s += search[i].len; - if ((e = wcschr (s, L'"')) - && (i > 0 || wcsncmp (name, s, e - s))) - { - *search[i].tgt = - (PWCHAR) alloca ((e - s + 1) - * sizeof (WCHAR)); - *wcpncpy (*search[i].tgt, s, e - s) = L'\0'; - s = e + 1; - found = true; - } - else - break; - } - if (!found) - break; - } - } - if (acc_type == SidTypeUser) - NetApiBufferFree (ui); - else - NetApiBufferFree (gi); - if (pgrp) - { - /* For setting the primary group, we have to test - with and without prepended separator. */ - char gname[2 * UNLEN + 2]; - struct group *gr; - - *gname = cygheap->pg.nss_separator ()[0]; - sys_wcstombs (gname + 1, 2 * UNLEN + 1, pgrp); - if ((gr = internal_getgrnam (gname, cldap)) - || (gr = internal_getgrnam (gname + 1, cldap))) - gid = gr->gr_gid; - } - if (!pldap && uxid && ((id_val = wcstoul (uxid, &e, 10)), !*e)) - { - if (acc_type == SidTypeUser) - { - if (cygheap->ugid_cache.get_uid (id_val) == ILLEGAL_UID) - cygheap->ugid_cache.add_uid (id_val, uid); - } - else if (cygheap->ugid_cache.get_gid (id_val) == ILLEGAL_GID) - cygheap->ugid_cache.add_gid (id_val, uid); - } - } - break; - case SidTypeWellKnownGroup: - fully_qualified_name = (cygheap->pg.nss_prefix_always () - /* Microsoft Account */ - || sid_id_auth (sid) == 11); -#ifdef INTERIX_COMPATIBLE - if (sid_id_auth (sid) == 5 /* SECURITY_NT_AUTHORITY */ - && sid_sub_auth_count (sid) > 1) - { - uid = 0x1000 * sid_sub_auth (sid, 0) - + (sid_sub_auth_rid (sid) & 0xffff); - fully_qualified_name = true; - } - else - uid = 0x10000 + 0x100 * sid_id_auth (sid) - + (sid_sub_auth_rid (sid) & 0xff); -#else - if (sid_id_auth (sid) != 5 /* SECURITY_NT_AUTHORITY */) - uid = 0x10000 + 0x100 * sid_id_auth (sid) - + (sid_sub_auth_rid (sid) & 0xff); - else if (sid_sub_auth (sid, 0) < SECURITY_PACKAGE_BASE_RID - || sid_sub_auth (sid, 0) > SECURITY_MAX_BASE_RID) - uid = sid_sub_auth_rid (sid) & 0x7ff; - else - { - uid = 0x1000 * sid_sub_auth (sid, 0) - + (sid_sub_auth_rid (sid) & 0xffff); - } -#endif - /* Special case for "Everyone". We don't want to return Everyone - as user or group. Ever. */ - if (uid == 0x10100) /* Computed from S-1-1-0. */ - return NULL; - break; - case SidTypeLabel: - uid = 0x60000 + sid_sub_auth_rid (sid); - fully_qualified_name = cygheap->pg.nss_prefix_always (); - break; - default: - return NULL; - } - } - else if (sid_id_auth (sid) == 5 /* SECURITY_NT_AUTHORITY */ - && sid_sub_auth (sid, 0) == SECURITY_LOGON_IDS_RID) - { - /* Logon ID. Mine or other? */ - get_logon_sid (); - if (PSID (logon_sid) == NO_SID) - return NULL; - if (RtlEqualSid (sid, logon_sid)) - { - uid = 0xfff; - wcpcpy (name = namebuf, L"CurrentSession"); - } - else - { - uid = 0xffe; - wcpcpy (name = namebuf, L"OtherSession"); - } - acc_type = SidTypeUnknown; - } - else if (sid_id_auth (sid) == 18) - { - /* Authentication assertion SIDs. - - Available when using a 2012R2 DC, but not supported by - LookupAccountXXX on pre Windows 8/2012 machines */ - uid = 0x11200 + sid_sub_auth_rid (sid); - wcpcpy (name = namebuf, sid_sub_auth_rid (sid) == 1 - ? (PWCHAR) L"Authentication authority asserted identity" - : (PWCHAR) L"Service asserted identity"); - fully_qualified_name = false; - acc_type = SidTypeUnknown; - } - else if (sid_id_auth (sid) == 22) + status = NtQueryInformationFile (fh, &io, &fsi, sizeof fsi, + FileStandardInformation); + if (!NT_SUCCESS (status)) { - /* Samba UNIX Users/Groups - - This *might* collide with a posix_offset of some trusted domain. - It's just very unlikely. */ - uid = MAP_UNIX_TO_CYGWIN_ID (sid_sub_auth_rid (sid)); - /* Unfortunately we have no access to the file server from here, - so we can't generate correct user names. */ - p = wcpcpy (dom, L"Unix_"); - wcpcpy (p, sid_sub_auth (sid, 0) == 1 ? L"User" : L"Group"); - __small_swprintf (name = namebuf, L"%d", uid & UNIX_POSIX_MASK); - fully_qualified_name = true; - acc_type = SidTypeUnknown; + paranoid_printf ("NtQueryInformationFile(%S) failed, status %y", + &upath, status); + goto out; } - else - { - if (cygheap->dom.member_machine () - && sid_id_auth (sid) == 5 /* SECURITY_NT_AUTHORITY */ - && sid_sub_auth (sid, 0) == SECURITY_NT_NON_UNIQUE) - { - /* Check if we know the domain. If so, create a passwd/group - entry with domain prefix and RID as username. */ - PDS_DOMAIN_TRUSTSW td = NULL; - - sid_sub_auth_count (sid) = sid_sub_auth_count (sid) - 1; - if (RtlEqualSid (sid, cygheap->dom.primary_sid ())) - { - domain = cygheap->dom.primary_flat_name (); - posix_offset = PRIMARY_POSIX_OFFSET; - } - 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, &loc_ldap); - break; - } - } - if (domain) - { - sid_sub_auth_count (sid) = sid_sub_auth_count (sid) + 1; - wcscpy (dom, domain); - __small_swprintf (name = namebuf, L"%W(%u)", - is_group () ? L"Group" : L"User", - sid_sub_auth_rid (sid)); - uid = posix_offset + sid_sub_auth_rid (sid); - } - else - { - wcpcpy (dom, L"Unknown"); - wcpcpy (name = namebuf, is_group () ? L"Group" : L"User"); - } - fully_qualified_name = true; - acc_type = SidTypeUnknown; - } - - tmp_pathbuf tp; - PWCHAR linebuf = tp.w_get (); - char *line = NULL; - - WCHAR posix_name[UNLEN + 1 + DNLEN + 1]; - p = posix_name; - if (gid == ILLEGAL_GID) - gid = uid; - if (fully_qualified_name) - p = wcpcpy (wcpcpy (p, dom), cygheap->pg.nss_separator ()); - wcpcpy (p, name); - - if (is_group ()) - __small_swprintf (linebuf, L"%W:%W:%u:", - posix_name, sid.string (sidstr), uid); - /* For non-users, create a passwd entry which doesn't allow interactive - logon. Unless it's the SYSTEM account. This conveniently allows to - logon interactively as SYSTEM for debugging purposes. */ - else if (acc_type != SidTypeUser && sid != well_known_system_sid) - __small_swprintf (linebuf, L"%W:*:%u:%u:,%W:/:/sbin/nologin", - posix_name, uid, gid, sid.string (sidstr)); - else - __small_swprintf (linebuf, L"%W:*:%u:%u:%W%WU-%W\\%W,%W:%W%W:%W", - posix_name, uid, gid, - gecos ?: L"", gecos ? L"," : L"", - dom, name, - sid.string (sidstr), - home ? L"" : L"/home/", home ?: name, - shell ?: L"/bin/bash"); - sys_wcstombs_alloc (&line, HEAP_BUF, linebuf); - debug_printf ("line: <%s>", line); - return line; -} - -client_request_pwdgrp::client_request_pwdgrp (fetch_user_arg_t &arg, bool group) - : client_request (CYGSERVER_REQUEST_PWDGRP, &_parameters, sizeof (_parameters)) -{ - size_t len = 0; - char *p; - - _parameters.in.group = group; - _parameters.in.type = arg.type; - switch (arg.type) + /* FIXME: Should we test for HighPart set? If so, the + passwd or group file is way beyond what we can handle. */ + /* FIXME 2: It's still ugly that we keep the file in memory. + Big organizations have naturally large passwd files. */ + buf = (char *) malloc (fsi.EndOfFile.LowPart + 1); + if (!buf) { - case SID_arg: - RtlCopySid (sizeof (DBGSID), (PSID) &_parameters.in.arg.sid, *arg.sid); - len = RtlLengthSid (*arg.sid); - break; - case NAME_arg: - p = stpcpy (_parameters.in.arg.name, arg.name); - len = p - _parameters.in.arg.name; - break; - case ID_arg: - _parameters.in.arg.id = arg.id; - len = sizeof (uint32_t); + paranoid_printf ("malloc (%u) failed", fsi.EndOfFile.LowPart); + goto out; } - msglen (__builtin_offsetof (struct _pwdgrp_param_t::_pwdgrp_in_t, arg) + len); -} - -char * -pwdgrp::fetch_account_from_cygserver (fetch_user_arg_t &arg) -{ - client_request_pwdgrp request (arg, is_group ()); - if (request.make_request () == -1 || request.error_code ()) + status = NtReadFile (fh, NULL, NULL, NULL, &io, buf, fsi.EndOfFile.LowPart, + NULL, NULL); + if (!NT_SUCCESS (status)) { - /* Cygserver not running? Don't try again. This will automatically - avoid an endless loop in cygserver itself. */ - if (request.error_code () == ENOSYS) - cygheap->pg.nss_disable_cygserver_caching (); - return NULL; - } - if (!request.line ()) - return NULL; - return cstrdup (request.line ()); -} - -void * -pwdgrp::add_account_from_cygserver (cygpsid &sid) -{ - /* No, Everyone is no group in terms of POSIX. */ - if (sid_id_auth (sid) == 1 /* SECURITY_WORLD_SID_AUTHORITY */ - && sid_sub_auth (sid, 0) == SECURITY_WORLD_RID) - return NULL; - fetch_user_arg_t arg; - arg.type = SID_arg; - arg.sid = &sid; - char *line = fetch_account_from_cygserver (arg); - return add_account_post_fetch (line, true); -} - -void * -pwdgrp::add_account_from_cygserver (const char *name) -{ - fetch_user_arg_t arg; - arg.type = NAME_arg; - arg.name = name; - char *line = fetch_account_from_cygserver (arg); - return add_account_post_fetch (line, true); -} - -void * -pwdgrp::add_account_from_cygserver (uint32_t id) -{ - fetch_user_arg_t arg; - arg.type = ID_arg; - arg.id = id; - char *line = fetch_account_from_cygserver (arg); - return add_account_post_fetch (line, true); + paranoid_printf ("NtReadFile(%S) failed, status %y", &upath, status); + free (buf); + goto out; + } + buf[fsi.EndOfFile.LowPart] = '\0'; + for (char *eptr = buf; (eptr = add_line (eptr)); ) + continue; + debug_printf ("%W curr_lines %d", rel_path, curr_lines); + res = succeeded; + +out: + if (fh) + NtClose (fh); + debug_printf ("%W load %s", rel_path, res); + initialized = true; } |