newlib/winsup/cygwin/uinfo.cc
Corinna Vinschen 526b0fbca3 * cygprops.h: New file.
* dtable.cc (handle_to_fn): Add check for correct installation_key
	string in object name for pipes and ttys.
	* external.cc (cygwin_internal): Add CW_GET_INSTKEY to allow fetching
	the installation_key from cygserver.
	* fhandler_fifo.cc (fhandler_fifo::fifo_name): Add installation_key
	to fifo name.
	* globals.cc: Include cygprops.h.
	(_RDATA): Move slightly and add comment.
	(cygwin_props): Define.
	* mount.cc (mount_info::init): Accommodate the fact that
	installation_root is now a global variable in DLL common shared memory,
	rather than a member of cygwin_shared.
	* pipe.cc (fhandler_pipe::create_selectable): Add installation_key to
	pipe name.
	* shared.cc (installation_root): Define here for storage in DLL
	common shared memory.
	(installation_key): Ditto.
	(installation_key_buf): Ditto.
	(init_installation_root): Convert from shared_info method to ordinary
	function.  Add initializing installation_key.  Invalidate
	installation_key depending of value of disable_key property.  Add
	comment to explain.
	(get_shared_parent_dir): Add installation_key to directory name.
	(get_session_parent_dir): Ditto.
	(shared_info::initialize): Move call to init_installation_root from
	here...
	(memory_init): ...to here.  Add debug output to print installation root
	and installation key.  Add comment to explain why.
	* shared_info.h (SHARED_INFO_CB): Recalculate.
	(CURR_SHARED_MAGIC): Ditto.
	(class shared_info): Remove definition of installation_root and
	declaration of init_installation_root.
	(init_installation_root): Declare.
	(installation_root): Declare.
	(installation_key): Declare.
	* uinfo.cc (pwdgrp::load): Accommodate the fact that installation_root
	is now a global variable in DLL common shared memory.
	* include/cygwin/version.h: Bump API minor number.
	(CYGWIN_INFO_INSTALLATIONS_NAME): Add.
	* include/sys/cygwin.h (cygwin_getinfo_types): Add CW_GET_INSTKEY.
2009-10-31 13:24:06 +00:00

582 lines
14 KiB
C++

/* uinfo.cc: user info (uid, gid, etc...)
Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
2006, 2007, 2008 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 <unistd.h>
#include <wininet.h>
#include <stdlib.h>
#include <wchar.h>
#include <lm.h>
#include <iptypes.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 "pwdgrp.h"
#include "tls_pbuf.h"
#include "ntdll.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 ()
{
char user_name[UNLEN + 1];
DWORD user_name_len = UNLEN + 1;
set_name (GetUserName (user_name, &user_name_len) ? user_name : "unknown");
DWORD siz;
PSECURITY_DESCRIPTOR psd;
if (!GetTokenInformation (hProcToken, TokenPrimaryGroup,
&groups.pgsid, sizeof (cygsid), &siz))
system_printf ("GetTokenInformation (TokenPrimaryGroup), %E");
/* Get the SID from current process and store it in effec_cygsid */
if (!GetTokenInformation (hProcToken, TokenUser, &effec_cygsid,
sizeof (cygsid), &siz))
{
system_printf ("GetTokenInformation (TokenUser), %E");
return;
}
/* Set token owner to the same value as token user */
if (!SetTokenInformation (hProcToken, TokenOwner, &effec_cygsid,
sizeof (cygsid)))
debug_printf ("SetTokenInformation(TokenOwner), %E");
/* 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;
BOOL acl_exists, dummy;
TOKEN_DEFAULT_DACL dacl;
if (GetSecurityDescriptorDacl (psd, &acl_exists, &dacl.DefaultDacl, &dummy)
&& acl_exists && dacl.DefaultDacl)
{
NTSTATUS status;
/* Set the default DACL and the process DACL */
if (!SetTokenInformation (hProcToken, TokenDefaultDacl, &dacl,
sizeof (dacl)))
system_printf ("SetTokenInformation (TokenDefaultDacl), %E");
if ((status = NtSetSecurityObject (hMainProc, DACL_SECURITY_INFORMATION,
psd)))
system_printf ("NtSetSecurityObject, %lx", status);
}
else
system_printf("Cannot get dacl, %E");
}
void
internal_getlogin (cygheap_user &user)
{
struct passwd *pw = NULL;
cygpsid psid = user.sid ();
pw = internal_getpwsid (psid);
if (!pw && !(pw = internal_getpwnam (user.name ()))
&& !(pw = internal_getpwuid (DEFAULT_UID)))
debug_printf ("user not found in augmented /etc/passwd");
else
{
cygsid gsid;
myself->uid = pw->pw_uid;
myself->gid = pw->pw_gid;
user.set_name (pw->pw_name);
if (gsid.getfromgr (internal_getgrgid (pw->pw_gid)))
{
if (gsid != user.groups.pgsid)
{
/* Set primary group to the group in /etc/passwd. */
if (!SetTokenInformation (hProcToken, TokenPrimaryGroup,
&gsid, sizeof gsid))
debug_printf ("SetTokenInformation(TokenPrimaryGroup), %E");
else
user.groups.pgsid = gsid;
clear_procimptoken ();
}
}
else
debug_printf ("gsid not found in augmented /etc/group");
}
cygheap->user.ontherange (CH_HOME, pw);
}
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)
{
char *login = getlogin ();
size_t len = strlen (login) + 1;
if (len > namesize)
return ERANGE;
myfault efault;
if (efault.faulted ())
return EFAULT;
strncpy (name, login, len);
return 0;
}
extern "C" char *
getlogin (void)
{
return strcpy (_my_tls.locals.username, cygheap->user.name ());
}
extern "C" __uid32_t
getuid32 (void)
{
return cygheap->user.real_uid;
}
extern "C" __uid16_t
getuid (void)
{
return cygheap->user.real_uid;
}
extern "C" __gid32_t
getgid32 (void)
{
return cygheap->user.real_gid;
}
extern "C" __gid16_t
getgid (void)
{
return cygheap->user.real_gid;
}
extern "C" __uid32_t
geteuid32 (void)
{
return myself->uid;
}
extern "C" __uid16_t
geteuid (void)
{
return myself->uid;
}
extern "C" __gid32_t
getegid32 (void)
{
return myself->gid;
}
extern "C" __gid16_t
getegid (void)
{
return myself->gid;
}
/* 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)
{
LPUSER_INFO_3 ui = NULL;
WCHAR wuser[UNLEN + 1];
NET_API_STATUS ret;
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 /etc/passwd) 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 && newhomepath == NULL)
{
char *homepath_env_buf = tp.c_get ();
if (!pw)
pw = internal_getpwnam (name ());
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);
else
{
homepath_env_buf[0] = homepath_env_buf[1] = '\0';
if (logsrv ())
{
WCHAR wlogsrv[INTERNET_MAX_HOST_NAME_LENGTH + 3];
sys_mbstowcs (wlogsrv, sizeof (wlogsrv) / sizeof (*wlogsrv),
logsrv ());
sys_mbstowcs (wuser, sizeof (wuser) / sizeof (*wuser), winname ());
if (!(ret = NetUserGetInfo (wlogsrv, wuser, 3, (LPBYTE *) &ui)))
{
sys_wcstombs (homepath_env_buf, NT_MAX_PATH,
ui->usri3_home_dir);
if (!homepath_env_buf[0])
{
sys_wcstombs (homepath_env_buf, NT_MAX_PATH,
ui->usri3_home_dir_drive);
if (homepath_env_buf[0])
strcat (homepath_env_buf, "\\");
else
cygwin_conv_path (CCP_POSIX_TO_WIN_A | CCP_ABSOLUTE,
"/", homepath_env_buf, NT_MAX_PATH);
}
}
}
if (ui)
NetApiBufferFree (ui);
}
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, false))
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;
char username[UNLEN + 1];
DWORD ulen = sizeof (username);
char userdomain[DNLEN + 1];
DWORD dlen = sizeof (userdomain);
SID_NAME_USE use;
cfree_and_set (pwinname, almost_null);
cfree_and_set (pdomain, almost_null);
if (!LookupAccountSid (NULL, sid (), username, &ulen,
userdomain, &dlen, &use))
__seterrno ();
else
{
pwinname = cstrdup (username);
pdomain = cstrdup (userdomain);
}
return pdomain;
}
const char *
cygheap_user::env_userprofile (const char *name, size_t namelen)
{
if (test_uid (puserprof, name, namelen))
return puserprof;
WCHAR userprofile_env_buf[NT_MAX_PATH];
WCHAR win_id[UNLEN + 1]; /* Large enough for SID */
cfree_and_set (puserprof, almost_null);
if (get_registry_hive_path (get_windows_id (win_id), userprofile_env_buf))
sys_wcstombs_alloc (&puserprof, HEAP_STR, userprofile_env_buf);
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 = GetWindowsDirectory (NULL, 0);
if (size > 0)
{
psystemroot = (char *) cmalloc_abort (HEAP_STR, ++size);
size = GetWindowsDirectory (psystemroot, size);
if (size <= 0)
{
cfree (psystemroot);
psystemroot = NULL;
}
}
if (size <= 0)
debug_printf ("GetWindowsDirectory(), %E");
}
return psystemroot;
}
char *
pwdgrp::next_str (char c)
{
char *res = lptr;
lptr = strechr (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)
{
lptr = eptr;
eptr = strchr (lptr, '\n');
if (eptr)
{
if (eptr > lptr && eptr[-1] == '\r')
eptr[-1] = '\0';
else
*eptr = '\0';
eptr++;
}
if (curr_lines >= max_lines)
{
max_lines += 10;
*pwdgrp_buf = realloc (*pwdgrp_buf, max_lines * pwdgrp_buf_elem_size);
}
if ((this->*parse) ())
curr_lines++;
}
return eptr;
}
void
pwdgrp::load (const wchar_t *rel_path)
{
static const char failed[] = "failed";
static const char succeeded[] = "succeeded";
const char *res = failed;
HANDLE fh = NULL;
NTSTATUS status;
OBJECT_ATTRIBUTES attr;
IO_STATUS_BLOCK io;
FILE_STANDARD_INFORMATION fsi;
if (buf)
free (buf);
buf = NULL;
curr_lines = 0;
if (!path &&
!(path = (PWCHAR) malloc ((wcslen (installation_root)
+ wcslen (rel_path) + 1) * sizeof (WCHAR))))
{
paranoid_printf ("malloc (%W) failed", rel_path);
goto out;
}
wcpcpy (wcpcpy (path, installation_root), rel_path);
RtlInitUnicodeString (&upath, path);
InitializeObjectAttributes (&attr, &upath, OBJ_CASE_INSENSITIVE, NULL, NULL);
etc_ix = etc::init (etc_ix, &attr);
paranoid_printf ("%S", &upath);
status = NtOpenFile (&fh, SYNCHRONIZE | FILE_READ_DATA, &attr, &io,
FILE_SHARE_VALID_FLAGS,
FILE_SYNCHRONOUS_IO_NONALERT
| FILE_OPEN_FOR_BACKUP_INTENT);
if (!NT_SUCCESS (status))
{
paranoid_printf ("NtOpenFile(%S) failed, status %p", &upath, status);
goto out;
}
status = NtQueryInformationFile (fh, &io, &fsi, sizeof fsi,
FileStandardInformation);
if (!NT_SUCCESS (status))
{
paranoid_printf ("NtQueryInformationFile(%S) failed, status %p",
&upath, status);
goto out;
}
/* FIXME: Should we test for HighPart set? If so, the
passwd or group file is way beyond what we can handle. */
/* FIXME 2: It's still ugly that we keep the file in memory.
Big organizations have naturally large passwd files. */
buf = (char *) malloc (fsi.EndOfFile.LowPart + 1);
if (!buf)
{
paranoid_printf ("malloc (%d) failed", fsi.EndOfFile.LowPart);
goto out;
}
status = NtReadFile (fh, NULL, NULL, NULL, &io, buf, fsi.EndOfFile.LowPart,
NULL, NULL);
if (!NT_SUCCESS (status))
{
paranoid_printf ("NtReadFile(%S) failed, status %p", &upath, status);
free (buf);
goto out;
}
buf[fsi.EndOfFile.LowPart] = '\0';
for (char *eptr = buf; (eptr = add_line (eptr)); )
continue;
debug_printf ("%W curr_lines %d", rel_path, curr_lines);
res = succeeded;
out:
if (fh)
NtClose (fh);
debug_printf ("%W load %s", rel_path, res);
initialized = true;
}