Welcome to mirror list, hosted at ThFree Co, Russian Federation.

cygwin.com/git/newlib-cygwin.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'winsup/cygwin/uinfo.cc')
-rw-r--r--winsup/cygwin/uinfo.cc2442
1 files changed, 0 insertions, 2442 deletions
diff --git a/winsup/cygwin/uinfo.cc b/winsup/cygwin/uinfo.cc
deleted file mode 100644
index 3a8c3954a..000000000
--- a/winsup/cygwin/uinfo.cc
+++ /dev/null
@@ -1,2442 +0,0 @@
-/* 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.
-
-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 <iptypes.h>
-#include <lm.h>
-#include <ntsecapi.h>
-#include <wininet.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <wchar.h>
-#include <sys/cygwin.h>
-#include "cygerrno.h"
-#include "pinfo.h"
-#include "path.h"
-#include "fhandler.h"
-#include "dtable.h"
-#include "cygheap.h"
-#include "shared_info.h"
-#include "registry.h"
-#include "child_info.h"
-#include "environ.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.
- Final initialization occurs in uinfo_init */
-void
-cygheap_user::init ()
-{
- WCHAR user_name[UNLEN + 1];
- DWORD user_name_len = UNLEN + 1;
-
- /* This code is only run if a Cygwin process gets started by a native
- Win32 process. We try to get the username from the environment,
- first USERNAME (Win32), then USER (POSIX). If that fails (which is
- very unlikely), it only has an impact if we don't have an entry in
- /etc/passwd for this user either. In that case the username sticks
- to "unknown". Since this is called early in initialization, and
- since we don't want pull in a dependency to any other DLL except
- ntdll and kernel32 at this early stage, don't call GetUserName,
- GetUserNameEx, NetWkstaUserGetInfo, etc. */
- if (GetEnvironmentVariableW (L"USERNAME", user_name, user_name_len)
- || GetEnvironmentVariableW (L"USER", user_name, user_name_len))
- {
- char mb_user_name[user_name_len = sys_wcstombs (NULL, 0, user_name)];
- sys_wcstombs (mb_user_name, user_name_len, user_name);
- set_name (mb_user_name);
- }
- else
- set_name ("unknown");
-
- NTSTATUS status;
- ULONG size;
- PSECURITY_DESCRIPTOR psd;
-
- status = NtQueryInformationToken (hProcToken, TokenPrimaryGroup,
- &groups.pgsid, sizeof (cygsid), &size);
- if (!NT_SUCCESS (status))
- system_printf ("NtQueryInformationToken (TokenPrimaryGroup), %y", status);
-
- /* Get the SID from current process and store it in effec_cygsid */
- status = NtQueryInformationToken (hProcToken, TokenUser, &effec_cygsid,
- sizeof (cygsid), &size);
- if (!NT_SUCCESS (status))
- {
- system_printf ("NtQueryInformationToken (TokenUser), %y", status);
- return;
- }
-
- /* Set token owner to the same value as token user */
- status = NtSetInformationToken (hProcToken, TokenOwner, &effec_cygsid,
- sizeof (cygsid));
- if (!NT_SUCCESS (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);
- psd = (PSECURITY_DESCRIPTOR)
- (sec_user_nih (sa_buf, sid()))->lpSecurityDescriptor;
-
- BOOLEAN acl_exists, dummy;
- TOKEN_DEFAULT_DACL dacl;
-
- status = RtlGetDaclSecurityDescriptor (psd, &acl_exists, &dacl.DefaultDacl,
- &dummy);
- if (NT_SUCCESS (status) && acl_exists && dacl.DefaultDacl)
- {
-
- /* Set the default DACL and the process DACL */
- status = NtSetInformationToken (hProcToken, TokenDefaultDacl, &dacl,
- sizeof (dacl));
- if (!NT_SUCCESS (status))
- system_printf ("NtSetInformationToken (TokenDefaultDacl), %y", status);
- if ((status = NtSetSecurityObject (NtCurrentProcess (),
- DACL_SECURITY_INFORMATION, psd)))
- system_printf ("NtSetSecurityObject, %y", status);
- }
- else
- system_printf("Cannot get dacl, %E");
-}
-
-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");
- 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)
- {
- if (gsid.getfromgr (grp = internal_getgrgid (pwd->pw_gid, &cldap)))
- {
- /* 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");
- }
- }
- cygheap->user.ontherange (CH_HOME, pwd);
-}
-
-void
-uinfo_init ()
-{
- if (child_proc_info && !cygheap->user.has_impersonation_tokens ())
- return;
-
- if (!child_proc_info)
- internal_getlogin (cygheap->user); /* Set the cygheap->user. */
- /* Conditions must match those in spawn to allow starting child
- processes with ruid != euid and rgid != egid. */
- else if (cygheap->user.issetuid ()
- && cygheap->user.saved_uid == cygheap->user.real_uid
- && cygheap->user.saved_gid == cygheap->user.real_gid
- && !cygheap->user.groups.issetgroups ()
- && !cygheap->user.setuid_to_restricted)
- {
- cygheap->user.reimpersonate ();
- return;
- }
- else
- cygheap->user.close_impersonation_tokens ();
-
- cygheap->user.saved_uid = cygheap->user.real_uid = myself->uid;
- cygheap->user.saved_gid = cygheap->user.real_gid = myself->gid;
- cygheap->user.external_token = NO_IMPERSONATION;
- cygheap->user.internal_token = NO_IMPERSONATION;
- cygheap->user.curr_primary_token = NO_IMPERSONATION;
- cygheap->user.curr_imp_token = NO_IMPERSONATION;
- cygheap->user.ext_token_is_restricted = false;
- cygheap->user.curr_token_is_restricted = false;
- cygheap->user.setuid_to_restricted = false;
- cygheap->user.set_saved_sid (); /* Update the original sid */
- cygheap->user.deimpersonate ();
-}
-
-extern "C" int
-getlogin_r (char *name, size_t namesize)
-{
- const char *login = cygheap->user.name ();
- size_t len = strlen (login) + 1;
- if (len > namesize)
- return ERANGE;
- __try
- {
- strncpy (name, login, len);
- }
- __except (NO_ERROR)
- {
- return EFAULT;
- }
- __endtry
- return 0;
-}
-
-extern "C" char *
-getlogin (void)
-{
- static char username[UNLEN];
- int ret = getlogin_r (username, UNLEN);
- if (ret)
- {
- set_errno (ret);
- return NULL;
- }
- return username;
-}
-
-extern "C" uid_t
-getuid32 (void)
-{
- return cygheap->user.real_uid;
-}
-
-#ifdef __x86_64__
-EXPORT_ALIAS (getuid32, getuid)
-#else
-extern "C" __uid16_t
-getuid (void)
-{
- return cygheap->user.real_uid;
-}
-#endif
-
-extern "C" gid_t
-getgid32 (void)
-{
- return cygheap->user.real_gid;
-}
-
-#ifdef __x86_64__
-EXPORT_ALIAS (getgid32, getgid)
-#else
-extern "C" __gid16_t
-getgid (void)
-{
- return cygheap->user.real_gid;
-}
-#endif
-
-extern "C" uid_t
-geteuid32 (void)
-{
- return myself->uid;
-}
-
-#ifdef __x86_64__
-EXPORT_ALIAS (geteuid32, geteuid)
-#else
-extern "C" uid_t
-geteuid (void)
-{
- return myself->uid;
-}
-#endif
-
-extern "C" gid_t
-getegid32 (void)
-{
- return myself->gid;
-}
-
-#ifdef __x86_64__
-EXPORT_ALIAS (getegid32, getegid)
-#else
-extern "C" __gid16_t
-getegid (void)
-{
- return myself->gid;
-}
-#endif
-
-/* Not quite right - cuserid can change, getlogin can't */
-extern "C" char *
-cuserid (char *src)
-{
- if (!src)
- return getlogin ();
-
- strcpy (src, getlogin ());
- return src;
-}
-
-const char *
-cygheap_user::ontherange (homebodies what, struct passwd *pw)
-{
- char homedrive_env_buf[3];
- char *newhomedrive = NULL;
- char *newhomepath = NULL;
- tmp_pathbuf tp;
-
- debug_printf ("what %d, pw %p", what, pw);
- if (what == CH_HOME)
- {
- char *p;
-
- if ((p = getenv ("HOME")))
- debug_printf ("HOME is already in the environment %s", p);
- else
- {
- if (pw && pw->pw_dir && *pw->pw_dir)
- {
- debug_printf ("Set HOME (from account db) to %s", pw->pw_dir);
- setenv ("HOME", pw->pw_dir, 1);
- }
- else
- {
- char home[strlen (name ()) + 8];
-
- debug_printf ("Set HOME to default /home/USER");
- __small_sprintf (home, "/home/%s", name ());
- setenv ("HOME", home, 1);
- }
- }
- }
-
- if (what != CH_HOME && homepath == NULL)
- {
- WCHAR wuser[UNLEN + 1];
- WCHAR wlogsrv[INTERNET_MAX_HOST_NAME_LENGTH + 3];
- PUSER_INFO_3 ui = NULL;
- char *homepath_env_buf = tp.c_get ();
- WCHAR profile[MAX_PATH];
- WCHAR win_id[UNLEN + 1]; /* Large enough for SID */
-
- homepath_env_buf[0] = homepath_env_buf[1] = '\0';
- /* Use Cygwin home as HOMEDRIVE/HOMEPATH in the first place. This
- should cover it completely, in theory. Still, it might be the
- wrong choice in the long run, which might demand to set HOMEDRIVE/
- HOMEPATH to the correct values per Windows. Keep the entire rest
- of the code mainly for this reason, so switching is easy. */
- pw = internal_getpwsid (sid ());
- if (pw && pw->pw_dir && *pw->pw_dir)
- cygwin_conv_path (CCP_POSIX_TO_WIN_A, pw->pw_dir, homepath_env_buf,
- NT_MAX_PATH);
- /* First fallback: Windows path per Windows user DB. */
- else if (logsrv ())
- {
- sys_mbstowcs (wlogsrv, sizeof (wlogsrv) / sizeof (*wlogsrv),
- logsrv ());
- sys_mbstowcs (wuser, sizeof wuser / sizeof *wuser, winname ());
- if (NetUserGetInfo (wlogsrv, wuser, 3, (LPBYTE *) &ui)
- == NERR_Success)
- {
- if (ui->usri3_home_dir_drive && *ui->usri3_home_dir_drive)
- {
- sys_wcstombs (homepath_env_buf, NT_MAX_PATH,
- ui->usri3_home_dir_drive);
- strcat (homepath_env_buf, "\\");
- }
- else if (ui->usri3_home_dir && *ui->usri3_home_dir)
- sys_wcstombs (homepath_env_buf, NT_MAX_PATH,
- ui->usri3_home_dir);
- }
- if (ui)
- NetApiBufferFree (ui);
- }
- /* Second fallback: Windows profile dir. */
- if (!homepath_env_buf[0]
- && get_user_profile_directory (get_windows_id (win_id),
- profile, MAX_PATH))
- sys_wcstombs (homepath_env_buf, NT_MAX_PATH, profile);
- /* Last fallback: Cygwin root dir. */
- if (!homepath_env_buf[0])
- cygwin_conv_path (CCP_POSIX_TO_WIN_A | CCP_ABSOLUTE,
- "/", homepath_env_buf, NT_MAX_PATH);
-
- if (homepath_env_buf[1] != ':')
- {
- newhomedrive = almost_null;
- newhomepath = homepath_env_buf;
- }
- else
- {
- homedrive_env_buf[0] = homepath_env_buf[0];
- homedrive_env_buf[1] = homepath_env_buf[1];
- homedrive_env_buf[2] = '\0';
- newhomedrive = homedrive_env_buf;
- newhomepath = homepath_env_buf + 2;
- }
- }
-
- if (newhomedrive && newhomedrive != homedrive)
- cfree_and_set (homedrive, (newhomedrive == almost_null)
- ? almost_null : cstrdup (newhomedrive));
-
- if (newhomepath && newhomepath != homepath)
- cfree_and_set (homepath, cstrdup (newhomepath));
-
- switch (what)
- {
- case CH_HOMEDRIVE:
- return homedrive;
- case CH_HOMEPATH:
- return homepath;
- default:
- return homepath;
- }
-}
-
-const char *
-cygheap_user::test_uid (char *&what, const char *name, size_t namelen)
-{
- if (!what && !issetuid ())
- what = getwinenveq (name, namelen, HEAP_STR);
- return what;
-}
-
-const char *
-cygheap_user::env_logsrv (const char *name, size_t namelen)
-{
- if (test_uid (plogsrv, name, namelen))
- return plogsrv;
-
- const char *mydomain = domain ();
- const char *myname = winname ();
- if (!mydomain || ascii_strcasematch (myname, "SYSTEM"))
- return almost_null;
-
- WCHAR wdomain[MAX_DOMAIN_NAME_LEN + 1];
- 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))
- sys_wcstombs_alloc (&plogsrv, HEAP_STR, wlogsrv);
- return plogsrv;
-}
-
-const char *
-cygheap_user::env_domain (const char *name, size_t namelen)
-{
- if (pwinname && test_uid (pdomain, name, namelen))
- return pdomain;
-
- DWORD ulen = UNLEN + 1;
- WCHAR username[ulen];
- DWORD dlen = MAX_DOMAIN_NAME_LEN + 1;
- WCHAR userdomain[dlen];
- SID_NAME_USE use;
-
- cfree_and_set (pwinname, almost_null);
- cfree_and_set (pdomain, almost_null);
- if (!LookupAccountSidW (NULL, sid (), username, &ulen,
- userdomain, &dlen, &use))
- __seterrno ();
- else
- {
- sys_wcstombs_alloc (&pwinname, HEAP_STR, username);
- sys_wcstombs_alloc (&pdomain, HEAP_STR, userdomain);
- }
- return pdomain;
-}
-
-const char *
-cygheap_user::env_userprofile (const char *name, size_t namelen)
-{
- if (test_uid (puserprof, name, namelen))
- return puserprof;
-
- /* User profile path is never longer than MAX_PATH. */
- WCHAR profile[MAX_PATH];
- WCHAR win_id[UNLEN + 1]; /* Large enough for SID */
-
- cfree_and_set (puserprof, almost_null);
- if (get_user_profile_directory (get_windows_id (win_id), profile, MAX_PATH))
- sys_wcstombs_alloc (&puserprof, HEAP_STR, profile);
-
- return puserprof;
-}
-
-const char *
-cygheap_user::env_homepath (const char *name, size_t namelen)
-{
- return ontherange (CH_HOMEPATH);
-}
-
-const char *
-cygheap_user::env_homedrive (const char *name, size_t namelen)
-{
- return ontherange (CH_HOMEDRIVE);
-}
-
-const char *
-cygheap_user::env_name (const char *name, size_t namelen)
-{
- if (!test_uid (pwinname, name, namelen))
- domain ();
- return pwinname;
-}
-
-const char *
-cygheap_user::env_systemroot (const char *name, size_t namelen)
-{
- if (!psystemroot)
- {
- int size = GetSystemWindowsDirectoryW (NULL, 0);
- if (size > 0)
- {
- WCHAR wsystemroot[size];
- size = GetSystemWindowsDirectoryW (wsystemroot, size);
- if (size > 0)
- sys_wcstombs_alloc (&psystemroot, HEAP_STR, wsystemroot);
- }
- if (size <= 0)
- debug_printf ("GetSystemWindowsDirectoryW(), %E");
- }
- return psystemroot;
-}
-
-char *
-pwdgrp::next_str (char c)
-{
- char *res = lptr;
- lptr = strchrnul (lptr, c);
- if (*lptr)
- *lptr++ = '\0';
- return res;
-}
-
-bool
-pwdgrp::next_num (unsigned long& n)
-{
- char *p = next_str (':');
- char *cp;
- n = strtoul (p, &cp, 10);
- return p != cp && !*cp;
-}
-
-char *
-pwdgrp::add_line (char *eptr)
-{
- if (eptr)
- {
- if (curr_lines >= max_lines)
- {
- max_lines += 10;
- pwdgrp_buf = crealloc_abort (pwdgrp_buf,
- max_lines * pwdgrp_buf_elem_size);
- }
- lptr = eptr;
- if ((this->*parse) ())
- curr_lines++;
- }
- return eptr;
-}
-
-void
-cygheap_pwdgrp::init ()
-{
- 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 DISABLED
- db_separator: + DISABLED
- db_home: cygwin desc
- db_shell: cygwin desc
- db_gecos: cygwin desc
- db_enum: cache builtin
- */
- pwd_src = (NSS_SRC_FILES | NSS_SRC_DB);
- grp_src = (NSS_SRC_FILES | NSS_SRC_DB);
- prefix = NSS_PFX_AUTO;
- separator[0] = L'+';
- home_scheme[0].method = NSS_SCHEME_CYGWIN;
- home_scheme[1].method = NSS_SCHEME_DESC;
- shell_scheme[0].method = NSS_SCHEME_CYGWIN;
- shell_scheme[1].method = NSS_SCHEME_DESC;
- gecos_scheme[0].method = NSS_SCHEME_CYGWIN;
- gecos_scheme[1].method = NSS_SCHEME_DESC;
- enums = (ENUM_CACHE | ENUM_BUILTIN);
- enum_tdoms = NULL;
- caching = true; /* INTERNAL ONLY */
-}
-
-#define NSS_NCMP(s) (!strncmp(c, (s), sizeof(s)-1))
-#define NSS_CMP(s) (!strncmp(c, (s), sizeof(s)-1) \
- && strchr (" \t", c[sizeof(s)-1]))
-
-/* 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");
- char *comment = strchr (c, '#');
- if (comment)
- *comment = '\0';
- switch (*c)
- {
- case 'p':
- case 'g':
- {
- uint32_t *src = NULL;
- if (NSS_NCMP ("passwd:"))
- src = &pwd_src;
- else if (NSS_NCMP ("group:"))
- src = &grp_src;
- c = strchr (c, ':') + 1;
- if (src)
- {
- *src = 0;
- while (*(c += strspn (c, " \t")))
- {
- if (NSS_CMP ("files"))
- {
- *src |= NSS_SRC_FILES;
- c += 5;
- }
- else if (NSS_CMP ("db"))
- {
- *src |= NSS_SRC_DB;
- c += 2;
- }
- else
- {
- c += strcspn (c, " \t");
- debug_printf ("Invalid nsswitch.conf content: %s", line);
- }
- }
- if (*src == 0)
- *src = (NSS_SRC_FILES | NSS_SRC_DB);
- }
- }
- break;
- case 'd':
- if (!NSS_NCMP ("db_"))
- {
- debug_printf ("Invalid nsswitch.conf content: %s", line);
- break;
- }
- c += 3;
-#if 0 /* Disable setting prefix and separator from nsswitch.conf for now.
- Remove if nobody complains too loudly. */
- if (NSS_NCMP ("prefix:"))
- {
- c = strchr (c, ':') + 1;
- c += strspn (c, " \t");
- if (NSS_CMP ("auto"))
- prefix = NSS_AUTO;
- else if (NSS_CMP ("primary"))
- prefix = NSS_PRIMARY;
- else if (NSS_CMP ("always"))
- prefix = NSS_ALWAYS;
- else
- debug_printf ("Invalid nsswitch.conf content: %s", line);
- }
- else if (NSS_NCMP ("separator:"))
- {
- c = strchr (c, ':') + 1;
- 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
-#endif
- if (NSS_NCMP ("enum:"))
- {
- tmp_pathbuf tp;
- char *tdoms = tp.c_get ();
- char *td = tdoms;
- int new_enums = ENUM_NONE;
-
- td[0] = '\0';
- c = strchr (c, ':') + 1;
- c += strspn (c, " \t");
- while (!strchr (" \t", *c))
- {
- const char *e = c + strcspn (c, " \t");
- if (NSS_CMP ("none"))
- new_enums = ENUM_NONE;
- else if (NSS_CMP ("builtin"))
- new_enums |= ENUM_BUILTIN;
- else if (NSS_CMP ("cache"))
- new_enums |= ENUM_CACHE;
- else if (NSS_CMP ("files"))
- new_enums |= ENUM_FILES;
- else if (NSS_CMP ("local"))
- new_enums |= ENUM_LOCAL;
- else if (NSS_CMP ("primary"))
- new_enums |= ENUM_PRIMARY;
- else if (NSS_CMP ("alltrusted"))
- new_enums |= ENUM_TDOMS | ENUM_TDOMS_ALL;
- else if (NSS_CMP ("all"))
- 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;
- }
- else
- {
- nss_scheme_t *scheme = NULL;
- if (NSS_NCMP ("home:"))
- scheme = home_scheme;
- else if (NSS_NCMP ("shell:"))
- scheme = shell_scheme;
- else if (NSS_NCMP ("gecos:"))
- scheme = gecos_scheme;
- if (scheme)
- {
- uint16_t idx = 0;
-
- scheme[0].method = scheme[1].method = NSS_SCHEME_FALLBACK;
- c = strchr (c, ':') + 1;
- c += strspn (c, " \t");
- while (*c && idx < NSS_SCHEME_MAX)
- {
- if (NSS_CMP ("windows"))
- scheme[idx].method = NSS_SCHEME_WINDOWS;
- else if (NSS_CMP ("cygwin"))
- scheme[idx].method = NSS_SCHEME_CYGWIN;
- else if (NSS_CMP ("unix"))
- scheme[idx].method = NSS_SCHEME_UNIX;
- else if (NSS_CMP ("desc"))
- scheme[idx].method = NSS_SCHEME_DESC;
- else if (NSS_NCMP ("/"))
- {
- const char *e = c + strcspn (c, " \t");
- scheme[idx].method = NSS_SCHEME_PATH;
- sys_mbstowcs_alloc (&scheme[idx].attrib, HEAP_STR,
- c, e - c);
- }
- else if (NSS_NCMP ("@") && isalnum ((unsigned) *++c))
- {
- const char *e = c + strcspn (c, " \t");
- scheme[idx].method = NSS_SCHEME_FREEATTR;
- sys_mbstowcs_alloc (&scheme[idx].attrib, HEAP_STR,
- c, e - c);
- }
- else
- debug_printf ("Invalid nsswitch.conf content: %s", line);
- c += strcspn (c, " \t");
- c += strspn (c, " \t");
- ++idx;
- }
- /* If nothing has been set, revert to default. */
- if (scheme[0].method == NSS_SCHEME_FALLBACK)
- {
- scheme[0].method = NSS_SCHEME_CYGWIN;
- scheme[1].method = NSS_SCHEME_DESC;
- }
- }
- }
- break;
- case '\0':
- break;
- default:
- debug_printf ("Invalid nsswitch.conf content: %s", line);
- break;
- }
-}
-
-static char *
-fetch_windows_home (PCWSTR home_from_db, cygpsid &sid)
-{
- char *home = NULL;
-
- if (home_from_db && *home_from_db)
- home = (char *) cygwin_create_path (CCP_WIN_W_TO_POSIX, home_from_db);
- else
- {
- /* The db fields are empty, so we have to evaluate the local profile
- path, which is the same thing as the default home directory on
- Windows. So what we do here is to try to find out if the user
- already has a profile on this machine.
- Note that we don't try to generate the profile if it doesn't exist.
- Think what would happen if we actually have the permissions to do
- so and call getpwent... in a domain environment. The problem is,
- of course, that we can't know the profile path, unless the OS
- created it.
- The only reason this could occur is if a user account, which never
- logged on to the machine before, tries to logon via a Cygwin service
- like sshd. */
- WCHAR profile[MAX_PATH];
- WCHAR sidstr[128];
-
- if (get_user_profile_directory (sid.string (sidstr), profile, MAX_PATH))
- home = (char *) cygwin_create_path (CCP_WIN_W_TO_POSIX, profile);
- }
- return home;
-}
-
-/* Local SAM accounts have only a handful attributes available to home users.
- Therefore, allow to fetch additional passwd/group attributes from the
- "Comment" field in XML short style. For symmetry, this is also allowed
- from the equivalent "description" AD attribute. */
-static char *
-fetch_from_description (PCWSTR desc, PCWSTR search, size_t len)
-{
- PWCHAR s, e;
- char *ret = NULL;
-
- if ((s = wcsstr (desc, L"<cygwin ")) && (e = wcsstr (s + 8, L"/>")))
- {
- s += 8;
- while (s && s < e)
- {
- while (*s == L' ')
- ++s;
- if (!wcsncmp (s, search, len)) /* Found what we're searching? */
- {
- s += len;
- if ((e = wcschr (s, L'"')))
- {
- sys_wcstombs_alloc (&ret, HEAP_NOTHEAP, s, e - s);
- s = e + 1;
- }
- break;
- }
- else /* Skip the current foo="bar" string. */
- if ((s = wcschr (s, L'"')) && (s = wcschr (s + 1, L'"')))
- ++s;
- }
- }
- return ret;
-}
-
-static char *
-fetch_from_path (PCWSTR str, PCWSTR dom, PCWSTR name, bool full_qualified)
-{
- tmp_pathbuf tp;
- PWCHAR wpath = tp.w_get ();
- PWCHAR w = wpath;
- PWCHAR we = wpath + NT_MAX_PATH - 1;
- char *ret = NULL;
-
- while (*str && w < we)
- {
- if (*str != L'%')
- *w++ = *str++;
- else
- {
- switch (*++str)
- {
- case L'u':
- if (full_qualified)
- {
- w = wcpncpy (w, dom, we - w);
- if (w < we)
- *w++ = cygheap->pg.nss_separator ()[0];
- }
- w = wcpncpy (w, name, we - w);
- break;
- case L'U':
- w = wcpncpy (w, name, we - w);
- break;
- case L'D':
- w = wcpncpy (w, dom, we - w);
- break;
- case L'_':
- *w++ = L' ';
- break;
- default:
- *w++ = *str;
- break;
- }
- ++str;
- }
- }
- *w = L'\0';
- sys_wcstombs_alloc (&ret, HEAP_NOTHEAP, wpath);
- return ret;
-}
-
-char *
-cygheap_pwdgrp::get_home (cyg_ldap *pldap, cygpsid &sid, PCWSTR dom,
- PCWSTR name, bool full_qualified)
-{
- PWCHAR val;
- char *home = NULL;
-
- for (uint16_t idx = 0; !home && idx < NSS_SCHEME_MAX; ++idx)
- {
- switch (home_scheme[idx].method)
- {
- case NSS_SCHEME_FALLBACK:
- return NULL;
- case NSS_SCHEME_WINDOWS:
- val = pldap->get_string_attribute (L"homeDrive");
- if (!val || !*val)
- val = pldap->get_string_attribute (L"homeDirectory");
- home = fetch_windows_home (val, sid);
- break;
- case NSS_SCHEME_CYGWIN:
- val = pldap->get_string_attribute (L"cygwinHome");
- if (val && *val)
- sys_wcstombs_alloc (&home, HEAP_NOTHEAP, val);
- break;
- case NSS_SCHEME_UNIX:
- val = pldap->get_string_attribute (L"unixHomeDirectory");
- if (val && *val)
- sys_wcstombs_alloc (&home, HEAP_NOTHEAP, val);
- break;
- case NSS_SCHEME_DESC:
- val = pldap->get_string_attribute (L"description");
- if (val && *val)
- home = fetch_from_description (val, L"home=\"", 6);
- break;
- case NSS_SCHEME_PATH:
- home = fetch_from_path (home_scheme[idx].attrib, dom, name,
- full_qualified);
- break;
- case NSS_SCHEME_FREEATTR:
- val = pldap->get_string_attribute (home_scheme[idx].attrib);
- if (val && *val)
- {
- if (isdrive (val) || *val == '\\')
- home = (char *) cygwin_create_path (CCP_WIN_W_TO_POSIX, val);
- else
- sys_wcstombs_alloc (&home, HEAP_NOTHEAP, val);
- }
- break;
- }
- }
- return home;
-}
-
-char *
-cygheap_pwdgrp::get_home (PUSER_INFO_3 ui, cygpsid &sid, PCWSTR dom,
- PCWSTR name, bool full_qualified)
-{
- PWCHAR val = NULL;
- char *home = NULL;
-
- for (uint16_t idx = 0; !home && idx < NSS_SCHEME_MAX; ++idx)
- {
- switch (home_scheme[idx].method)
- {
- case NSS_SCHEME_FALLBACK:
- return NULL;
- case NSS_SCHEME_WINDOWS:
- if (ui->usri3_home_dir_drive && *ui->usri3_home_dir_drive)
- val = ui->usri3_home_dir_drive;
- else if (ui->usri3_home_dir && *ui->usri3_home_dir)
- val = ui->usri3_home_dir;
- home = fetch_windows_home (val, sid);
- break;
- case NSS_SCHEME_CYGWIN:
- case NSS_SCHEME_UNIX:
- case NSS_SCHEME_FREEATTR:
- break;
- case NSS_SCHEME_DESC:
- home = fetch_from_description (ui->usri3_comment, L"home=\"", 6);
- break;
- case NSS_SCHEME_PATH:
- home = fetch_from_path (home_scheme[idx].attrib, dom, name,
- full_qualified);
- break;
- }
- }
- return home;
-}
-
-char *
-cygheap_pwdgrp::get_shell (cyg_ldap *pldap, PCWSTR dom, PCWSTR name,
- bool full_qualified)
-{
- PWCHAR val;
- char *shell = NULL;
-
- for (uint16_t idx = 0; !shell && idx < NSS_SCHEME_MAX; ++idx)
- {
- switch (shell_scheme[idx].method)
- {
- case NSS_SCHEME_FALLBACK:
- return NULL;
- case NSS_SCHEME_WINDOWS:
- break;
- case NSS_SCHEME_CYGWIN:
- val = pldap->get_string_attribute (L"cygwinShell");
- if (val && *val)
- sys_wcstombs_alloc (&shell, HEAP_NOTHEAP, val);
- break;
- case NSS_SCHEME_UNIX:
- val = pldap->get_string_attribute (L"loginShell");
- if (val && *val)
- sys_wcstombs_alloc (&shell, HEAP_NOTHEAP, val);
- break;
- case NSS_SCHEME_DESC:
- val = pldap->get_string_attribute (L"description");
- if (val && *val)
- shell = fetch_from_description (val, L"shell=\"", 7);
- break;
- case NSS_SCHEME_PATH:
- shell = fetch_from_path (shell_scheme[idx].attrib, dom, name,
- full_qualified);
- break;
- case NSS_SCHEME_FREEATTR:
- val = pldap->get_string_attribute (shell_scheme[idx].attrib);
- if (val && *val)
- {
- if (isdrive (val) || *val == '\\')
- shell = (char *) cygwin_create_path (CCP_WIN_W_TO_POSIX, val);
- else
- sys_wcstombs_alloc (&shell, HEAP_NOTHEAP, val);
- }
- break;
- }
- }
- return shell;
-}
-
-char *
-cygheap_pwdgrp::get_shell (PUSER_INFO_3 ui, PCWSTR dom, PCWSTR name,
- bool full_qualified)
-{
- char *shell = NULL;
-
- for (uint16_t idx = 0; !shell && idx < NSS_SCHEME_MAX; ++idx)
- {
- switch (shell_scheme[idx].method)
- {
- case NSS_SCHEME_FALLBACK:
- return NULL;
- case NSS_SCHEME_WINDOWS:
- case NSS_SCHEME_CYGWIN:
- case NSS_SCHEME_UNIX:
- case NSS_SCHEME_FREEATTR:
- break;
- case NSS_SCHEME_DESC:
- shell = fetch_from_description (ui->usri3_comment, L"shell=\"", 7);
- break;
- case NSS_SCHEME_PATH:
- shell = fetch_from_path (shell_scheme[idx].attrib, dom, name,
- full_qualified);
- break;
- }
- }
- return shell;
-}
-
-/* Helper function to replace colons with semicolons in pw_gecos field. */
-static inline void
-colon_to_semicolon (char *str)
-{
- char *cp = str;
- while ((cp = strchr (cp, L':')) != NULL)
- *cp++ = L';';
-}
-
-char *
-cygheap_pwdgrp::get_gecos (cyg_ldap *pldap, PCWSTR dom, PCWSTR name,
- bool full_qualified)
-{
- PWCHAR val;
- char *gecos = NULL;
-
- for (uint16_t idx = 0; !gecos && idx < NSS_SCHEME_MAX; ++idx)
- {
- switch (gecos_scheme[idx].method)
- {
- case NSS_SCHEME_FALLBACK:
- return NULL;
- case NSS_SCHEME_WINDOWS:
- val = pldap->get_string_attribute (L"displayName");
- if (val && *val)
- sys_wcstombs_alloc (&gecos, HEAP_NOTHEAP, val);
- break;
- case NSS_SCHEME_CYGWIN:
- val = pldap->get_string_attribute (L"cygwinGecos");
- if (val && *val)
- sys_wcstombs_alloc (&gecos, HEAP_NOTHEAP, val);
- break;
- case NSS_SCHEME_UNIX:
- val = pldap->get_string_attribute (L"gecos");
- if (val && *val)
- sys_wcstombs_alloc (&gecos, HEAP_NOTHEAP, val);
- break;
- case NSS_SCHEME_DESC:
- val = pldap->get_string_attribute (L"description");
- if (val && *val)
- gecos = fetch_from_description (val, L"gecos=\"", 7);
- break;
- case NSS_SCHEME_PATH:
- gecos = fetch_from_path (gecos_scheme[idx].attrib + 1, dom, name,
- full_qualified);
- break;
- case NSS_SCHEME_FREEATTR:
- val = pldap->get_string_attribute (gecos_scheme[idx].attrib);
- if (val && *val)
- sys_wcstombs_alloc (&gecos, HEAP_NOTHEAP, val);
- break;
- }
- }
- if (gecos)
- colon_to_semicolon (gecos);
- return gecos;
-}
-
-char *
-cygheap_pwdgrp::get_gecos (PUSER_INFO_3 ui, PCWSTR dom, PCWSTR name,
- bool full_qualified)
-{
- char *gecos = NULL;
-
- for (uint16_t idx = 0; !gecos && idx < NSS_SCHEME_MAX; ++idx)
- {
- switch (gecos_scheme[idx].method)
- {
- case NSS_SCHEME_FALLBACK:
- return NULL;
- case NSS_SCHEME_WINDOWS:
- if (ui->usri3_full_name && *ui->usri3_full_name)
- sys_wcstombs_alloc (&gecos, HEAP_NOTHEAP, ui->usri3_full_name);
- break;
- case NSS_SCHEME_CYGWIN:
- case NSS_SCHEME_UNIX:
- case NSS_SCHEME_FREEATTR:
- break;
- case NSS_SCHEME_DESC:
- gecos = fetch_from_description (ui->usri3_comment, L"gecos=\"", 7);
- break;
- case NSS_SCHEME_PATH:
- gecos = fetch_from_path (gecos_scheme[idx].attrib + 1, dom, name,
- full_qualified);
- break;
- }
- }
- if (gecos)
- colon_to_semicolon (gecos);
- return gecos;
-}
-
-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.Length = (wcslen (cygheap->installation_root) + wcslen (rel_path))
- * sizeof (WCHAR);
- path.MaximumLength = path.Length + sizeof (WCHAR);
- path.Buffer = (PWCHAR) alloca (path.MaximumLength);
- wcpcpy (wcpcpy (path.Buffer, cygheap->installation_root), rel_path);
- 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;
-}
-
-/* Per session, so it changes potentially when switching the user context. */
-static cygsid logon_sid ("");
-
-static void
-get_logon_sid ()
-{
- if (PSID (logon_sid) == NO_SID)
- {
- 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;
- }
- 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);
-}
-
-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);
-}
-
-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);
-}
-
-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.Length = (wcslen (cygheap->installation_root) + wcslen (rel_path))
- * sizeof (WCHAR);
- path.MaximumLength = path.Length + sizeof (WCHAR);
- path.Buffer = (PWCHAR) cmalloc_abort (HEAP_BUF, path.MaximumLength);
- wcpcpy (wcpcpy (path.Buffer, cygheap->installation_root), rel_path);
- 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);
- if (!NT_SUCCESS (status))
- {
- if (last_modified.QuadPart)
- last_modified.QuadPart = 0LL;
- else
- path.MaximumLength = 0;
- return false;
- }
- 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;
-}
-
-/* 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;
- char *shell = NULL;
- char *home = NULL;
- char *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:
- 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. */
- 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);
- }
- /* LookupAccountName doesn't find NT SERVICE accounts. Try just for
- kicks (and to make TrustedInstaller work here :-P */
- else if (!ret)
- {
- p = wcpcpy (name, L"NT SERVICE");
- *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;
- /* SECURITY_APP_PACKAGE_AUTHORITY */
- if (arg.id >= 0xf20 && arg.id <= 0xf3f)
- __small_swprintf (sidstr, L"S-1-15-%u-%u", (arg.id >> 4) & 0xf,
- arg.id & 0xf);
- else
- __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:
- /* Don't allow users as group. While this is technically possible,
- it doesn't make sense in a POSIX scenario. It *is* used for
- Microsoft Accounts, but those are converted to well-known groups
- above. */
- if (is_group ())
- return NULL;
- /*FALLTHRU*/
- 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 uid/gid values. */
- if (uid == ILLEGAL_UID)
- uid = posix_offset + sid_sub_auth_rid (sid);
- if (!is_group () && 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))
- {
- if ((id_val = cldap->get_primary_gid ()) != ILLEGAL_GID)
- gid = posix_offset + id_val;
- if (!is_group ())
- {
- home = cygheap->pg.get_home (cldap, sid, dom, name,
- fully_qualified_name);
- shell = cygheap->pg.get_shell (cldap, dom, name,
- fully_qualified_name);
- gecos = cygheap->pg.get_gecos (cldap, dom, name,
- fully_qualified_name);
- }
- /* 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_3 ui;
- PLOCALGROUP_INFO_1 gi;
- char *pgrp = NULL;
- char *uxid = NULL;
-
- if (acc_type == SidTypeUser)
- {
- nas = NetUserGetInfo (NULL, name, 3, (PBYTE *) &ui);
- if (nas != NERR_Success)
- {
- debug_printf ("NetUserGetInfo(%W) %u", name, nas);
- break;
- }
- /* 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);
- }
- }
- /* Fetch user attributes. */
- home = cygheap->pg.get_home (ui, sid, dom, name,
- fully_qualified_name);
- shell = cygheap->pg.get_shell (ui, dom, name,
- fully_qualified_name);
- gecos = cygheap->pg.get_gecos (ui, dom, name,
- fully_qualified_name);
- uxid = fetch_from_description (ui->usri3_comment,
- L"unix=\"", 6);
- pgrp = fetch_from_description (ui->usri3_comment,
- L"group=\"", 7);
- }
- else /* acc_type == SidTypeAlias */
- {
- nas = NetLocalGroupGetInfo (NULL, name, 1, (PBYTE *) &gi);
- if (nas != NERR_Success)
- {
- debug_printf ("NetLocalGroupGetInfo(%W) %u", name, nas);
- break;
- }
- /* Fetch unix gid from comment field. */
- uxid = fetch_from_description (gi->lgrpi1_comment,
- L"unix=\"", 6);
- }
-
- if (acc_type == SidTypeUser)
- NetApiBufferFree (ui);
- else
- NetApiBufferFree (gi);
- if (pgrp)
- {
- /* Set primary group from the "Description" field. Prepend
- account domain if this is a domain member machine or the
- db_prefix setting requires it. */
- char gname[2 * DNLEN + strlen (pgrp) + 1], *gp = gname;
- struct group *gr;
-
- if (cygheap->dom.member_machine ()
- || !cygheap->pg.nss_prefix_auto ())
- {
- gp = gname
- + sys_wcstombs (gname, sizeof gname,
- cygheap->dom.account_flat_name ());
- *gp++ = cygheap->pg.nss_separator ()[0];
- }
- stpcpy (gp, pgrp);
- if ((gr = internal_getgrnam (gname, cldap)))
- gid = gr->gr_gid;
- }
- char *e;
- if (!pldap && uxid && ((id_val = strtoul (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);
- }
- if (pgrp)
- free (pgrp);
- if (uxid)
- free (uxid);
- }
- break;
- case SidTypeWellKnownGroup:
- fully_qualified_name = (cygheap->pg.nss_prefix_always ()
- /* NT SERVICE Account */
- || (sid_id_auth (sid) == 5 /* SECURITY_NT_AUTHORITY */
- && sid_sub_auth (sid, 0) == SECURITY_SERVICE_ID_BASE_RID)
- /* 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) == 15 /* SECURITY_APP_PACKAGE_AUTHORITY */)
- uid = 0x10000 + 0x100 * sid_id_auth (sid)
- + 0x10 * sid_sub_auth (sid, 0)
- + (sid_sub_auth_rid (sid) & 0xf);
- 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 "NULL SID", S-1-0-0 and "Everyone", S-1-1-0.
- Never return "NULL SID" or Everyone as user or group. */
- if (uid == 0x10000 || uid == 0x10100)
- 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)
- {
- /* 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;
- }
- 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;
- char *linebuf = tp.c_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_sprintf (linebuf, "%W:%s:%u:",
- posix_name, sid.string ((char *) 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_sprintf (linebuf, "%W:*:%u:%u:U-%W\\%W,%s:/:/sbin/nologin",
- posix_name, uid, gid,
- dom, name,
- sid.string ((char *) sidstr));
- else
- __small_sprintf (linebuf, "%W:*:%u:%u:%s%sU-%W\\%W,%s:%s%W:%s",
- posix_name, uid, gid,
- gecos ?: "", gecos ? "," : "",
- dom, name,
- sid.string ((char *) sidstr),
- home ?: "/home/", home ? L"" : name,
- shell ?: "/bin/bash");
- if (gecos)
- free (gecos);
- if (home)
- free (home);
- if (shell)
- free (shell);
- line = cstrdup (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)
- {
- 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 + 1;
- break;
- case ID_arg:
- _parameters.in.arg.id = arg.id;
- len = sizeof (uint32_t);
- }
- 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 ())
- {
- /* 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);
-}