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>
This commit is contained in:
Corinna Vinschen 2023-01-14 17:46:55 +01:00
parent 73e4ded2fe
commit 8bd56ec873
3 changed files with 213 additions and 40 deletions

View File

@ -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>

View File

@ -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

View File

@ -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]))
{
char *e = NULL;
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;
}
/* Check if we have to regenerate a stock environment */
if (argv[1][0] == '-')
{
new_child_env = true;
--argc;
++argv;
}
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;
}