Reorganizing internal_getlogin()

Pierre A. Humblet Pierre.Humblet@ieee.org
Sun Jun 9 20:15:00 GMT 2002


Corinna,

internal_getlogin() has evolved over time. Currently it has two 
purposes:
1) set Cygwin variables (e.g. cygheap->user, HOME, prgpsid,...)
2) set traditional Windows environment entries (e.g. HOMEPATH)

It is called in 3 cases:
a) Entry from Windows
b) From seteuid()
c) After CreateProcessAsUser()

The purpose in cases a) and c) is 1 above.
The purpose in case  b) is mainly 2. 

I propose to reorganize the code by breaking 
internal_getlogin() in two functions, one that does 1) and 
another that does 2). The main purpose is to increase the efficiency,
as explained below, as well as fixing some nits, e.g. 
LOGONSERVER is never updated.

The first function would be called in cases a) and c), although
it would do very little in case c).
The second one would be called in spawn_guts(), just before
the CreateProcessAsUser() [when the environment is copied to
the cygheap]. 
There would be no call to internal_getlogin() from seteuid(). 
The few Cygwin fields that need updating in seteuid() 
[e.g.user.name] would be handled in seteuid() itself, where
the info is readily available.
These changes would improve the performances of servers [such as 
mail servers] that setuid() repeatedly but exec() only rarely,
in particular avoiding lookups over the network. 

As a first step in this process, the attached patches contain the 
modifications that add function 2). They can already be applied
although internal_getlogin() is not touched. It will be simplified
in a second phase. This will allow you to more easily check what's
going on.  

I worry (because I can't test) that this might break applications 
using sexecXX calls, although I don't see how it would. Are there 
any still around? It would be easy to bypass the new code for sexecXX 
calls, if needed.

2002-06-09  Pierre Humblet <pierre.humblet@ieee.org>

	* environ.cc (addWinDefEnv): New.
	(inWinDefEnv): New.
	(writeWinDefEnv): New.
	* spawn.cc (spawn_guts): Call functions above to set
	traditional Windows environment variables when copying the
	environment to the cygheap, before CreateProcessAsUser().
	Define sec_attribs and call sec_user_nih() only once.
	* environ.h: Declare inWinDefEnv() and addWinDefEnv(), and 
	define WINDEFENVC.

Pierre
-------------- next part --------------
--- spawn.cc.orig	2002-06-06 21:02:22.000000000 -0400
+++ spawn.cc	2002-06-08 15:00:32.000000000 -0400
@@ -567,13 +567,7 @@
   ciresrv.moreinfo->argv = newargv;

   ciresrv.moreinfo->envc = envsize (envp, 1);
-  ciresrv.moreinfo->envp = (char **) cmalloc (HEAP_1_ARGV, ciresrv.moreinfo->envc);
   ciresrv.hexec_proc = hexec_proc;
-  char **c;
-  const char * const *e;
-  for (c = ciresrv.moreinfo->envp, e = envp; *e;)
-    *c++ = cstrdup1 (*e++);
-  *c = NULL;
   if (mode != _P_OVERLAY ||
       !DuplicateHandle (hMainProc, myself.shared_handle (), hMainProc,
 			&ciresrv.moreinfo->myself_pinfo, 0,
@@ -604,14 +598,6 @@
   if (cygheap->fdtab.need_fixup_before ())
     flags |= CREATE_SUSPENDED;

-
-  /* Build windows style environment list */
-  char *envblock;
-  if (real_path.iscygexec ())
-    envblock = NULL;
-  else
-    envblock = winenv (envp, 0);
-
   /* Preallocated buffer for `sec_user' call */
   char sa_buf[1024];

@@ -624,9 +610,22 @@
   syscall_printf ("spawn_guts null_app_name %d (%s, %.132s)", null_app_name, runpath, one_line.buf);

   void *newheap;
+  char *envblock;
   cygbench ("spawn-guts");
   if (!hToken)
     {
+      ciresrv.moreinfo->envp = (char **) cmalloc (HEAP_1_ARGV, ciresrv.moreinfo->envc);
+      char **c;
+      const char * const *e;
+      for (c = ciresrv.moreinfo->envp, e = envp; *e;)
+	*c++ = cstrdup1 (*e++);
+      *c = NULL;
+      /* Build windows style environment list */
+      if (real_path.iscygexec ())
+	envblock = NULL;
+      else
+	envblock = winenv (envp, 0);
+      PSECURITY_ATTRIBUTES sec_attribs = sec_user_nih (sa_buf);
       ciresrv.moreinfo->uid = getuid32 ();
       /* FIXME: This leaks a handle in the CreateProcessAsUser case since the
 	 child process doesn't know about cygwin_mount_h. */
@@ -634,10 +633,8 @@
       newheap = cygheap_setup_for_child (&ciresrv, cygheap->fdtab.need_fixup_before ());
       rc = CreateProcess (runpath,	/* image name - with full path */
 			  one_line.buf,	/* what was passed to exec */
-					  /* process security attrs */
-			  sec_user_nih (sa_buf),
-					  /* thread security attrs */
-			  sec_user_nih (sa_buf),
+			  sec_attribs,  /* process security attrs */
+			  sec_attribs,  /* thread security attrs */
 			  TRUE,	/* inherit handles from parent */
 			  flags,
 			  envblock,/* environment */
@@ -659,10 +656,25 @@
       PSECURITY_ATTRIBUTES sec_attribs = sec_user_nih (sa_buf, sid);

       /* Remove impersonation */
-      if (cygheap->user.impersonated
-	  && cygheap->user.token != INVALID_HANDLE_VALUE)
+      if (cygheap->user.impersonated &&
+	  cygheap->user.token != INVALID_HANDLE_VALUE)
 	RevertToSelf ();

+      ciresrv.moreinfo->envp = (char **) cmalloc (HEAP_1_ARGV,
+						  ciresrv.moreinfo->envc +
+						  WINDEFENVC);
+      char **c;
+      const char * const *e;
+      for (c = ciresrv.moreinfo->envp, e = envp; *e; e++)
+	if (!inWinDefEnv(*e)) *c++ = cstrdup1 (*e);
+      ciresrv.moreinfo->envc = (1 + buildWinDefEnv((char **) c, sid) -
+				ciresrv.moreinfo->envp) * sizeof (char *);
+      /* Build windows style environment list */
+      if (real_path.iscygexec ())
+	envblock = NULL;
+      else
+	envblock = winenv (ciresrv.moreinfo->envp, 0);
+
       /* Load users registry hive. */
       load_registry_hive (sid);

-------------- next part --------------
--- environ.h.orig	2001-10-30 19:55:32.000000000 -0500
+++ environ.h	2002-06-08 13:31:02.000000000 -0400
@@ -8,6 +8,9 @@
 Cygwin license.  Please consult the file "CYGWIN_LICENSE" for
 details. */
 
+/* Space for Windows default variables (HOMEPATH, ..) */
+#define WINDEFENVC (6 * sizeof (char *))
+
 /* Initialize the environment */
 void environ_init (char **, int);
 
@@ -39,3 +42,5 @@
 extern char **__cygwin_environ, ***main_environ;
 extern "C" char __stdcall **cur_environ ();
 int __stdcall envsize (const char * const *, int debug_print = 0);
+extern char ** buildWinDefEnv(char **, PSID);
+extern BOOL inWinDefEnv(const char *);
-------------- next part --------------
--- environ.cc.orig	2002-06-06 21:02:20.000000000 -0400
+++ environ.cc	2002-06-08 13:50:20.000000000 -0400
@@ -14,6 +14,9 @@
 #include <ctype.h>
 #include <sys/cygwin.h>
 #include <cygwin/version.h>
+#include <winnls.h>
+#include <wininet.h>
+#include <lm.h>
 #include "pinfo.h"
 #include "perprocess.h"
 #include "security.h"
@@ -867,3 +870,136 @@

   return __cygwin_environ;
 }
+
+/* The default Windows environment variables */
+static NO_COPY struct _WinDefEnv {
+    const char * name;
+    const size_t namelen;
+} WinDefEnv[] = {
+  { "HOMEDRIVE=", sizeof("HOMEDRIVE=") - 1},
+  { "HOMEPATH=", sizeof("HOMEPATH=") - 1},
+  { "HOMESHARE=", sizeof("HOMESHARE=") - 1},     /* Clear only */
+  { "LOGONSERVER=", sizeof("LOGONSERVER=") - 1},
+  { "USERDOMAIN=", sizeof("USERDOMAIN=") - 1},
+  { "USERNAME=", sizeof("USERNAME=") - 1},
+  { "USERPROFILE=", sizeof("USERPROFILE=") - 1}
+};
+/* Must be in same order as above */
+enum {HD, HP, HS, LS, UD, UN, UP };
+
+/* True if name matches one of the names above */
+BOOL
+inWinDefEnv(const char * name)
+{
+  struct _WinDefEnv * ptr = NULL;
+  if (name[0] == 'H')
+    {
+      if (name[4] == 'D') ptr = &WinDefEnv[HD];
+      else if (name[4] == 'P') ptr = &WinDefEnv[HP];
+      else if (name[4] == 'S') ptr = &WinDefEnv[HS];
+    }
+  else if (name[0] == 'L') ptr = &WinDefEnv[LS];
+  else if (name[0] == 'U')
+    {
+      if (name[4] == 'D') ptr = &WinDefEnv[UD];
+      if (name[4] == 'N') ptr = &WinDefEnv[UN];
+      if (name[4] == 'P') ptr = &WinDefEnv[UP];
+    }
+  if (ptr)
+    return !strncmp(name, ptr->name, ptr->namelen);
+  else return FALSE;
+}
+
+static BOOL
+writeWinDefEnv(char *** pwhere, int offset, char * value)
+{
+  if (value[0])
+    {
+      **pwhere = (char *) cmalloc (HEAP_1_STR, strlen(value) +
+				   WinDefEnv[offset].namelen + 1);
+      if (!**pwhere) return FALSE;
+      strcpy(**pwhere, WinDefEnv[offset].name);
+      strcpy(**pwhere + WinDefEnv[offset].namelen, value);
+      (*pwhere)++;
+    }
+  return TRUE;
+}
+
+/* Build default Windows environment variables */
+char **
+buildWinDefEnv(char ** ptr, PSID pusersid)
+{
+  char username[UNLEN + 1] = {0};
+  DWORD ulen = sizeof (username);
+  char userdomain[DNLEN + 1] = {0};
+  DWORD dlen = sizeof (userdomain);
+  char userprofile[MAX_PATH +1] = {0};
+  char logsrv[INTERNET_MAX_HOST_NAME_LENGTH + 3] = {0}; /* With leading \\ */
+  WCHAR wlogsrv[INTERNET_MAX_HOST_NAME_LENGTH + 3]; /* With leading \\ */
+  char homepath[MAX_PATH + 1] = {0, 0}; /* Used for drive + path */
+  LPUSER_INFO_3 ui = NULL;
+
+  if (!pusersid)
+    goto out;
+  /*  Warning: the info is incorrect
+      if the pusersid is impersonated. */
+  SID_NAME_USE use;
+  if (!LookupAccountSid (NULL, pusersid, username, &ulen,
+                         userdomain, &dlen, &use))
+    {
+      __seterrno ();
+      goto out;
+    }
+  if (!writeWinDefEnv(&ptr, UD, userdomain) ||
+      !writeWinDefEnv(&ptr, UN, username))
+    goto out;
+  if (!strcasematch (username, "SYSTEM"))
+    {
+      if (get_logon_server (userdomain, logsrv, wlogsrv))
+        {
+          if (!writeWinDefEnv(&ptr, LS, logsrv))
+            goto out;
+
+          WCHAR wuser[UNLEN + 1];
+          NET_API_STATUS ret;
+          sys_mbstowcs (wuser, username, sizeof (wuser) / sizeof (*wuser));
+          if ((ret = NetUserGetInfo (wlogsrv, wuser, 3,(LPBYTE *)&ui)))
+            debug_printf("NetUserGetInfo: %d", ret);
+          else
+            {
+              sys_wcstombs (homepath, ui->usri3_home_dir, sizeof (homepath));
+              debug_printf("homepath(1) %s\n", homepath);
+              if (homepath[1] != ':')
+                {
+                  sys_wcstombs (homepath, ui->usri3_home_dir_drive, sizeof (homepath));
+                  debug_printf("homepath(2) %s", homepath);
+                  homepath[2] = '\\'; homepath[3] = 0;
+                }
+//            sys_wcstombs (userprofile, ui->usri3_profile, sizeof (userprofile));
+//            debug_printf("userprofile(1) %s\n", userprofile);
+              NetApiBufferFree (ui);
+            }
+        }
+      if (get_registry_hive_path (pusersid, userprofile))
+        {
+          debug_printf("userprofile(2) %s\n", userprofile);
+          if (!writeWinDefEnv(&ptr, UP, userprofile))
+            goto out;
+        }
+    }
+  if (homepath[1] != ':')
+    {
+      GetSystemDirectoryA (homepath, sizeof (homepath));
+      homepath[3] = 0;
+    }
+  if (homepath[1] == ':')
+    {
+      if (!writeWinDefEnv(&ptr, HP, &homepath[2]) ||
+          (homepath[2] = 0) ||
+          !writeWinDefEnv(&ptr, HD, homepath))
+        goto out;
+    }
+ out:
+  *ptr = NULL;
+  return ptr;
+}


More information about the Cygwin-patches mailing list