aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--winsup/doc/utils.xml50
-rw-r--r--winsup/utils/Makefile.am1
-rw-r--r--winsup/utils/newgrp.c200
3 files changed, 212 insertions, 39 deletions
diff --git a/winsup/doc/utils.xml b/winsup/doc/utils.xml
index 8959880..927dc7c 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 75da116..d4d5638 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 012c318..8858635 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;
}