home directory. Include longish comment to explain what we're doing. (cygheap_pwdgrp::get_home): Take additional sid parameter. In NSS_SCHEME_WINDOWS case, call fetch_windows_home to create home directory. (pwdgrp::fetch_account_from_windows): Call cygheap_pwdgrp::get_home with additional sid argument. * cygheap.h (cygheap_pwdgrp::get_home): Align declaration to above change.
		
			
				
	
	
		
			2443 lines
		
	
	
		
			68 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			2443 lines
		
	
	
		
			68 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/* 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);
 | 
						|
}
 |