public inbox for cygwin-cvs@sourceware.org
help / color / mirror / Atom feed
* [newlib-cygwin/main] Cygwin: newgrp: first full version
@ 2023-01-14 16:49 Corinna Vinschen
  0 siblings, 0 replies; only message in thread
From: Corinna Vinschen @ 2023-01-14 16:49 UTC (permalink / raw)
  To: cygwin-cvs

https://sourceware.org/git/gitweb.cgi?p=newlib-cygwin.git;h=8bd56ec8734539ba014c9ed993c3c9e19173ed82

commit 8bd56ec8734539ba014c9ed993c3c9e19173ed82
Author:     Corinna Vinschen <corinna@vinschen.de>
AuthorDate: Sat Jan 14 17:46:55 2023 +0100
Commit:     Corinna Vinschen <corinna@vinschen.de>
CommitDate: Sat Jan 14 17:47:48 2023 +0100

    Cygwin: newgrp: first full version
    
    - add '-' option
    - make group argument optional
    - drop ability to take a numerical group argument
    - simplify usage output to bare minimum
    - Add manpage and documentation
    
    Signed-off-by: Corinna Vinschen <corinna@vinschen.de>

Diff:
---
 winsup/doc/utils.xml     |  50 ++++++++++++
 winsup/utils/Makefile.am |   1 +
 winsup/utils/newgrp.c    | 200 ++++++++++++++++++++++++++++++++++++++---------
 3 files changed, 212 insertions(+), 39 deletions(-)

diff --git a/winsup/doc/utils.xml b/winsup/doc/utils.xml
index 895988037904..927dc7c6b85e 100644
--- a/winsup/doc/utils.xml
+++ b/winsup/doc/utils.xml
@@ -1939,6 +1939,56 @@ D: on /d type fat (binary,user,noumount)
     </refsect1>
   </refentry>
 
+  <refentry id="newgrp">
+    <refmeta>
+      <refentrytitle>newgrp</refentrytitle>
+      <manvolnum>1</manvolnum>
+      <refmiscinfo class="manual">Cygwin Utilities</refmiscinfo>
+    </refmeta>
+
+    <refnamediv>
+      <refname>newgrp</refname>
+      <refpurpose>change primary group for a command</refpurpose>
+    </refnamediv>
+
+    <refsynopsisdiv>
+      <cmdsynopsis>
+	<command>newgrp</command>
+	<arg choice="opt">-</arg>
+	<arg choice="opt"><replaceable>group</replaceable></arg>
+	<arg><replaceable>command</replaceable>
+	<arg rep="repeat"><replaceable>args</replaceable></arg>
+	</arg>
+      </cmdsynopsis>
+    </refsynopsisdiv>
+
+    <refsect1 id="newgrp-desc">
+      <title>Description</title>
+      <para><command>newgrp</command> changes the primary group for a
+        command.</para>
+
+      <para>If the '-' flag is given as first argument, the user's environment
+        will be reinitialized as though the user had logged in, otherwise the
+	current environment, including current working directory, remains
+	unchanged.</para>
+
+      <para><command>newgrp</command> changes the current primary group to the
+        named group, or to the default group listed in /etc/passwd if no group
+	name is given.</para>
+
+      <para>By default, the user's standard shell is started, called as login
+        shell if the '-' flag has been specified.  If a group has been given
+	as argument, a command and its arguments can be specified on the
+	command line.</para>
+
+      <para>Please note that setting the primary group to any arbitrary group
+        is no privileged operation on Windows.  However, if this group is not
+	in your current user token, or if the group is in your user token but
+	marked as <literal>deny-only</literal>, no additional permissions can
+	be obtained by setting this group as primary group.</para>
+    </refsect1>
+  </refentry>
+
   <refentry id="passwd">
     <refmeta>
       <refentrytitle>passwd</refentrytitle>
diff --git a/winsup/utils/Makefile.am b/winsup/utils/Makefile.am
index 75da1163b629..d4d56386f72e 100644
--- a/winsup/utils/Makefile.am
+++ b/winsup/utils/Makefile.am
@@ -87,6 +87,7 @@ pldd_LDADD = $(LDADD) -lpsapi
 profiler_CXXFLAGS = -I$(srcdir) -idirafter ${top_srcdir}/cygwin/local_includes -idirafter ${top_srcdir}/cygwin/include $(AM_CXXFLAGS)
 profiler_LDADD = $(LDADD) -lntdll
 cygps_LDADD = $(LDADD) -lpsapi -lntdll
+newgrp_LDADD = $(LDADD) -luserenv
 
 if CROSS_BOOTSTRAP
 SUBDIRS = mingw
diff --git a/winsup/utils/newgrp.c b/winsup/utils/newgrp.c
index 012c3188f477..88586352e108 100644
--- a/winsup/utils/newgrp.c
+++ b/winsup/utils/newgrp.c
@@ -6,86 +6,204 @@ This software is a copyrighted work licensed under the terms of the
 Cygwin license.  Please consult the file "CYGWIN_LICENSE" for
 details. */
 
+#define _GNU_SOURCE 1
 #include <stdio.h>
 #include <stdlib.h>
+#include <stdbool.h>
 #include <unistd.h>
 #include <errno.h>
 #include <ctype.h>
 #include <string.h>
+#include <wchar.h>
+#include <locale.h>
 #include <grp.h>
 #include <pwd.h>
 
+#include <sys/cygwin.h>
+#include <w32api/windows.h>
+#include <w32api/userenv.h>
+
+#define PATH_PREFIX	"PATH=/usr/bin:"
+
+char *
+create_env_var (const char *name, const char *val)
+{
+  char *var, *cp;
+
+  var = (char *) calloc (strlen (name) + strlen (val) + 2, sizeof (char *));
+  cp = stpcpy (var, name);
+  *cp++ = '=';
+  stpcpy (cp, val);
+  return var;
+}
+
+char **
+create_child_env (struct passwd *pw)
+{
+  char **posix_env, *cp;
+  wchar_t *win_env, *wep;
+  size_t max_cnt = 0;
+  size_t idx = 0;
+  HANDLE token;
+
+  /* Fecth Windows default environment of current user */
+  if (!OpenProcessToken (GetCurrentProcess (),
+			 TOKEN_QUERY | TOKEN_DUPLICATE, &token))
+    {
+      fprintf (stderr, "%s: creating environment failed with error %u "
+		       "(OpenProcessToken)\n",
+	       program_invocation_short_name, GetLastError ());
+      return NULL;
+    }
+  if (!CreateEnvironmentBlock ((PVOID *) &win_env, token, FALSE))
+    {
+      fprintf (stderr, "%s: creating environment failed with error %u "
+		       "(CreateEnvironmentBlock)\n",
+	       program_invocation_short_name, GetLastError ());
+      CloseHandle (token);
+      return NULL;
+    }
+  CloseHandle (token);
+  /* Convert to Posix env */
+  for (wep = win_env; *wep; wep = wcschr (wep, '\0') + 1)
+    ++max_cnt;
+  posix_env = (char **) calloc (max_cnt + 6, sizeof (char *));
+  if (!posix_env)
+    {
+      fprintf (stderr, "%s: allocating environment failed: %s\n",
+	       program_invocation_short_name, strerror (errno));
+      return NULL;
+    }
+  for (wep = win_env; *wep; ++idx, wep = wcschr (wep, '\0') + 1)
+    {
+      /* For $PATH we must prepend /usr/bin to the converted POSIX path list */
+      if (!wcsncasecmp (wep, L"PATH=", 5))
+	{
+	  size_t len = cygwin_conv_path_list (CCP_WIN_W_TO_POSIX,
+					      wep + 5, NULL, 0);
+	  posix_env[idx] = (char *) calloc (sizeof (PATH_PREFIX) + len,
+					    sizeof (char *));
+	  if (!posix_env[idx])
+	    {
+	      fprintf (stderr, "%s: allocating environment failed: %s\n",
+		       program_invocation_short_name, strerror (errno));
+	      return NULL;
+	    }
+	  cp = stpcpy (posix_env[idx], PATH_PREFIX);
+	  cygwin_conv_path_list (CCP_WIN_W_TO_POSIX, wep + 5, cp, len);
+	}
+      else
+	{
+	  size_t len = wcstombs (NULL, wep, 0);
+
+	  if (len == (size_t) -1)
+	    {
+	      fprintf (stderr,
+		       "%s: invalid char in environment variable: %ls\n",
+		       program_invocation_short_name, wep);
+	      return NULL;
+	    }
+	  posix_env[idx] = (char *) calloc (len + 1, sizeof (char *));
+	  if (!posix_env[idx])
+	    {
+	      fprintf (stderr, "%s: allocating environment failed: %s\n",
+		       program_invocation_short_name, strerror (errno));
+	      return NULL;
+	    }
+	  wcstombs (posix_env[idx], wep, len + 1);
+	}
+    }
+  DestroyEnvironmentBlock (win_env);
+  /* Add USER, LOGNAME, HOME, LANG, just like sshd */
+  posix_env[idx++] = create_env_var ("USER", pw->pw_name);
+  posix_env[idx++] = create_env_var ("LOGNAME", pw->pw_name);
+  posix_env[idx++] = create_env_var ("HOME", pw->pw_dir);
+  cp = getenv("LANG");
+  if (cp)
+    posix_env[idx] = create_env_var ("LANG", cp);
+  cp = getenv("TERM");
+  if (cp)
+    posix_env[idx] = create_env_var ("TERM", cp);
+  return posix_env;
+}
+
 int
 main (int argc, const char **argv)
 {
+  const char *cmd, **cmd_av, *fake_av[2];
+  struct passwd *pw;
   struct group *gr;
+  char **child_env;
+  bool new_child_env = false;
   gid_t gid;
-  const char *cmd;
-  const char **cmd_av;
-  const char *fake_av[2];
 
-  /* TODO: Implement '-' option */
-  /* TODO: Add command description to documentation */
+  setlocale (LC_ALL, "");
 
-  if (argc < 2 || argv[1][0] == '-')
+  if (argc < 2 || (argv[1][0] == '-' && argv[1][1]))
     {
-      fprintf (stderr,
-	       "Usage: %1$s group [command [args...]]\n"
-	       "\n"
-	       "%1$s changes the current primary group for a command.\n"
-	       "The primary group must be member of the supplementary group\n"
-	       "list of the user.\n"
-	       "The command and its arguments are specified on the command\n"
-	       "line.  Default is the user's standard shell.\n",
+      fprintf (stderr, "Usage: %s [-] [group] [command [args...]]\n",
 	       program_invocation_short_name);
       return 1;
     }
-  if (isdigit ((int) argv[1][0]))
+
+  /* Check if we have to regenerate a stock environment */
+  if (argv[1][0] == '-')
     {
-      char *e = NULL;
+      new_child_env = true;
+      --argc;
+      ++argv;
+    }
 
-      gid = strtol (argv[1], &e, 10);
-      if (e && *e != '\0')
-	{
-	  fprintf (stderr, "%s: invalid gid `%s'\n",
-		   program_invocation_short_name, argv[1]);
-	  return 2;
-	}
-      gr = getgrgid (gid);
-      if (!gr)
-	{
-	  fprintf (stderr, "%s: unknown group gid `%u'\n",
-		   program_invocation_short_name, gid);
-	  return 2;
-	}
+  pw = getpwuid (getuid ());
+
+  /* Fetch group */
+  if (argv[1] == NULL)
+    {
+      gid = pw->pw_gid;
     }
   else
     {
       gr = getgrnam (argv[1]);
       if (!gr)
 	{
-	  fprintf (stderr, "%s: unknown group name `%s'\n",
+	  fprintf (stderr, "%s: group '%s' does not exist\n",
 		   program_invocation_short_name, argv[1]);
 	  return 2;
 	}
       gid = gr->gr_gid;
+      --argc;
+      ++argv;
     }
+
+  /* Set primary group */
   if (setgid (gid) != 0)
     {
-      fprintf (stderr, "%s: can't switch primary group to `%s'\n",
+      fprintf (stderr, "%s: can't switch primary group to '%s'\n",
 	       program_invocation_short_name, argv[1]);
       return 2;
     }
-  argc -= 2;
-  argv += 2;
+
+  /* Maybe generate stock child environment */
+  if (!new_child_env)
+    child_env = environ;
+  else
+    {
+      child_env = create_child_env (pw);
+      if (!child_env)
+	return 3;
+      chdir (pw->pw_dir);
+    }
+
+  /* Set argc/argv for execvpe */
+  --argc;
+  ++argv;
   if (argc < 1)
     {
-      struct passwd *pw = getpwuid (getuid ());
       if (!pw)
 	cmd = "/usr/bin/bash";
       else
 	cmd = pw->pw_shell;
-      fake_av[0] = cmd;
+      fake_av[0] = new_child_env ? "-" : cmd;
       fake_av[1] = NULL;
       cmd_av = fake_av;
     }
@@ -94,8 +212,12 @@ main (int argc, const char **argv)
       cmd = argv[0];
       cmd_av = argv;
     }
-  execvp (cmd, (char **) cmd_av);
-  fprintf (stderr, "%s: failed to start `%s': %s\n",
+
+  /* Exec child process */
+  execvpe (cmd, (char **) cmd_av, child_env);
+
+  /* Oops */
+  fprintf (stderr, "%s: failed to start '%s': %s\n",
 	   program_invocation_short_name, cmd, strerror (errno));
-  return 3;
+  return 4;
 }

^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2023-01-14 16:49 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-01-14 16:49 [newlib-cygwin/main] Cygwin: newgrp: first full version Corinna Vinschen

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).