mirror of
https://github.com/systemd/systemd
synced 2024-10-06 16:21:34 +00:00
run: when invoked as "uid0", expose some sudo-like behaviour
This turns "systemd-run" into a multi-call binary. When invoked under the name "uid0", then it behaves a bit more like traditional "sudo". This mostly means defaults appropriuate for that, for example a PAM stack, interactivity and more. Fixes: #29199
This commit is contained in:
parent
20f8b345db
commit
72eb3081b2
|
@ -1257,6 +1257,7 @@ manpages = [
|
||||||
''],
|
''],
|
||||||
['udev_new', '3', ['udev_ref', 'udev_unref'], ''],
|
['udev_new', '3', ['udev_ref', 'udev_unref'], ''],
|
||||||
['udevadm', '8', [], ''],
|
['udevadm', '8', [], ''],
|
||||||
|
['uid0', '1', [], ''],
|
||||||
['ukify', '1', [], 'ENABLE_UKIFY'],
|
['ukify', '1', [], 'ENABLE_UKIFY'],
|
||||||
['user@.service',
|
['user@.service',
|
||||||
'5',
|
'5',
|
||||||
|
|
|
@ -677,7 +677,8 @@ $ systemd-run --user --wait -p SuccessExitStatus=SIGUSR1 --expand-environment=no
|
||||||
<citerefentry><refentrytitle>systemd.resource-control</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
|
<citerefentry><refentrytitle>systemd.resource-control</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
|
||||||
<citerefentry><refentrytitle>systemd.timer</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
|
<citerefentry><refentrytitle>systemd.timer</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
|
||||||
<citerefentry><refentrytitle>systemd-mount</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
|
<citerefentry><refentrytitle>systemd-mount</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
|
||||||
<citerefentry><refentrytitle>machinectl</refentrytitle><manvolnum>1</manvolnum></citerefentry>
|
<citerefentry><refentrytitle>machinectl</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
|
||||||
|
<citerefentry><refentrytitle>uid0</refentrytitle><manvolnum>1</manvolnum></citerefentry>
|
||||||
</para>
|
</para>
|
||||||
</refsect1>
|
</refsect1>
|
||||||
|
|
||||||
|
|
214
man/uid0.xml
Normal file
214
man/uid0.xml
Normal file
|
@ -0,0 +1,214 @@
|
||||||
|
<?xml version='1.0'?>
|
||||||
|
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
|
||||||
|
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
|
||||||
|
<!-- SPDX-License-Identifier: LGPL-2.1-or-later -->
|
||||||
|
|
||||||
|
<refentry id="uid0"
|
||||||
|
xmlns:xi="http://www.w3.org/2001/XInclude">
|
||||||
|
|
||||||
|
<refentryinfo>
|
||||||
|
<title>uid0</title>
|
||||||
|
<productname>systemd</productname>
|
||||||
|
</refentryinfo>
|
||||||
|
|
||||||
|
<refmeta>
|
||||||
|
<refentrytitle>uid0</refentrytitle>
|
||||||
|
<manvolnum>1</manvolnum>
|
||||||
|
</refmeta>
|
||||||
|
|
||||||
|
<refnamediv>
|
||||||
|
<refname>uid0</refname>
|
||||||
|
<refpurpose>Elevate privileges</refpurpose>
|
||||||
|
</refnamediv>
|
||||||
|
|
||||||
|
<refsynopsisdiv>
|
||||||
|
<cmdsynopsis>
|
||||||
|
<command>uid0</command>
|
||||||
|
<arg choice="opt" rep="repeat">OPTIONS</arg>
|
||||||
|
<arg choice="opt" rep="repeat">COMMAND</arg>
|
||||||
|
</cmdsynopsis>
|
||||||
|
</refsynopsisdiv>
|
||||||
|
|
||||||
|
<refsect1>
|
||||||
|
<title>Description</title>
|
||||||
|
|
||||||
|
<para><command>uid0</command> may be used to temporarily and interactively acquire elavated or different
|
||||||
|
privileges. It serves a similar purpose as <citerefentry
|
||||||
|
project='man-pages'><refentrytitle>sudo</refentrytitle><manvolnum>8</manvolnum></citerefentry>, but
|
||||||
|
operates differently in a couple of key areas:</para>
|
||||||
|
|
||||||
|
<itemizedlist>
|
||||||
|
<listitem><para>No execution or security context credentials are inherited from the caller into the
|
||||||
|
invoked commands, as they are invoked from a fresh, isolated service forked off the service
|
||||||
|
manager.</para></listitem>
|
||||||
|
|
||||||
|
<listitem><para>Authentication takes place via <ulink
|
||||||
|
url="https://www.freedesktop.org/wiki/Software/polkit">polkit</ulink>, thus isolating the
|
||||||
|
authentication prompt from the terminal (if possible).</para></listitem>
|
||||||
|
|
||||||
|
<listitem><para>An independent pseudo-tty is allocated for the invoked command, detaching its lifecycle and
|
||||||
|
isolating it for security.</para></listitem>
|
||||||
|
|
||||||
|
<listitem><para>No SetUID/SetGID file access bit functionality is used for the implementation.</para></listitem>
|
||||||
|
</itemizedlist>
|
||||||
|
|
||||||
|
<para>Altogether this should provide a safer and more robust alternative to the <command>sudo</command>
|
||||||
|
mechanism, in particular in OS environments where SetUID/SetGID support is not available (for example by
|
||||||
|
setting the <varname>NoNewPrivileges=</varname> variable in
|
||||||
|
<citerefentry><refentrytitle>systemd-system.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>).</para>
|
||||||
|
|
||||||
|
<para>Any session invoked via <command>uid0</command> will run through the
|
||||||
|
<literal>systemd-uid0</literal> PAM stack.</para>
|
||||||
|
|
||||||
|
<para>Note that <command>uid0</command> is implemented as an alternative multi-call invocation of
|
||||||
|
<citerefentry><refentrytitle>systemd-run</refentrytitle><manvolnum>1</manvolnum></citerefentry>.</para>
|
||||||
|
</refsect1>
|
||||||
|
|
||||||
|
<refsect1>
|
||||||
|
<title>Options</title>
|
||||||
|
|
||||||
|
<para>The following options are understood:</para>
|
||||||
|
|
||||||
|
<variablelist>
|
||||||
|
<varlistentry>
|
||||||
|
<term><option>--no-ask-password</option></term>
|
||||||
|
|
||||||
|
<listitem><para>Do not query the user for authentication for privileged operations.</para>
|
||||||
|
|
||||||
|
<xi:include href="version-info.xml" xpointer="v256"/></listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term><option>--unit=</option></term>
|
||||||
|
|
||||||
|
<listitem><para>Use this unit name instead of an automatically generated one.</para>
|
||||||
|
|
||||||
|
<xi:include href="version-info.xml" xpointer="v256"/></listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term><option>--property=</option></term>
|
||||||
|
|
||||||
|
<listitem><para>Sets a property on the service unit that is created. This option takes an assignment
|
||||||
|
in the same format as
|
||||||
|
<citerefentry><refentrytitle>systemctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>'s
|
||||||
|
<command>set-property</command> command.</para>
|
||||||
|
|
||||||
|
<xi:include href="version-info.xml" xpointer="v256"/>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term><option>--description=</option></term>
|
||||||
|
|
||||||
|
<listitem><para>Provide a description for the service unit that is invoked. If not specified,
|
||||||
|
the command itself will be used as a description. See <varname>Description=</varname> in
|
||||||
|
<citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<xi:include href="version-info.xml" xpointer="v256"/></listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term><option>--slice=</option></term>
|
||||||
|
|
||||||
|
<listitem><para>Make the new <filename>.service</filename> unit part of the specified slice, instead
|
||||||
|
of <filename>user.slice</filename>.</para>
|
||||||
|
|
||||||
|
<xi:include href="version-info.xml" xpointer="v256"/>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term><option>--slice-inherit</option></term>
|
||||||
|
|
||||||
|
<listitem><para>Make the new <filename>.service</filename> unit part of the slice the
|
||||||
|
<command>uid0</command> itself has been invoked in. This option may be combined with
|
||||||
|
<option>--slice=</option>, in which case the slice specified via <option>--slice=</option> is placed
|
||||||
|
within the slice the <command>uid0</command> command is invoked in.</para>
|
||||||
|
|
||||||
|
<para>Example: consider <command>uid0</command> being invoked in the slice
|
||||||
|
<filename>foo.slice</filename>, and the <option>--slice=</option> argument is
|
||||||
|
<filename>bar</filename>. The unit will then be placed under
|
||||||
|
<filename>foo-bar.slice</filename>.</para>
|
||||||
|
|
||||||
|
<xi:include href="version-info.xml" xpointer="v256"/>
|
||||||
|
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term><option>--user=</option></term>
|
||||||
|
<term><option>-u</option></term>
|
||||||
|
<term><option>--group=</option></term>
|
||||||
|
<term><option>-g</option></term>
|
||||||
|
|
||||||
|
<listitem><para>Switches to the specified user/group instead of root.</para>
|
||||||
|
|
||||||
|
<xi:include href="version-info.xml" xpointer="v256"/>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term><option>--nice=</option></term>
|
||||||
|
|
||||||
|
<listitem><para>Runs the invoked session with the specified nice level.</para>
|
||||||
|
|
||||||
|
<xi:include href="version-info.xml" xpointer="v256"/>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term><option>--chdir=</option></term>
|
||||||
|
<term><option>-D</option></term>
|
||||||
|
|
||||||
|
<listitem><para>Runs the invoked session with the specified working directory. If not specified
|
||||||
|
defaults to the client's current working directory if switching to the root user, or the target
|
||||||
|
user's home directory otherwise.</para>
|
||||||
|
|
||||||
|
<xi:include href="version-info.xml" xpointer="v256"/>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term><option>--setenv=<replaceable>NAME</replaceable>[=<replaceable>VALUE</replaceable>]</option></term>
|
||||||
|
|
||||||
|
<listitem><para>Runs the invoked session with the specified environment variable set. This parameter
|
||||||
|
may be used more than once to set multiple variables. When <literal>=</literal> and
|
||||||
|
<replaceable>VALUE</replaceable> are omitted, the value of the variable with the same name in the
|
||||||
|
invoking environment will be used.</para>
|
||||||
|
|
||||||
|
<xi:include href="version-info.xml" xpointer="v256"/>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
<xi:include href="user-system-options.xml" xpointer="machine" />
|
||||||
|
<xi:include href="standard-options.xml" xpointer="help" />
|
||||||
|
<xi:include href="standard-options.xml" xpointer="version" />
|
||||||
|
</variablelist>
|
||||||
|
|
||||||
|
<para>All command line arguments after the first non-option argument become part of the command line of
|
||||||
|
the launched process. If no command line is specified an interactive shell is invoked. The shell to
|
||||||
|
invoke may be controlled via <option>--setenv=SHELL=…</option> and currently defaults to the
|
||||||
|
<emphasis>originating user's</emphasis> shell (i.e. not the target user's!) if operating locally, or
|
||||||
|
<filename>/bin/sh</filename> when operating with <option>--machine=</option>.</para>
|
||||||
|
</refsect1>
|
||||||
|
|
||||||
|
<refsect1>
|
||||||
|
<title>Exit status</title>
|
||||||
|
|
||||||
|
<para>On success, 0 is returned. If <command>uid0</command> failed to start the session or the specified command fails, a
|
||||||
|
non-zero return value will be returned.</para>
|
||||||
|
</refsect1>
|
||||||
|
|
||||||
|
<refsect1>
|
||||||
|
<title>See Also</title>
|
||||||
|
<para>
|
||||||
|
<citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
|
||||||
|
<citerefentry><refentrytitle>systemd-run</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
|
||||||
|
<citerefentry project='man-pages'><refentrytitle>sudo</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
|
||||||
|
<citerefentry><refentrytitle>machinectl</refentrytitle><manvolnum>1</manvolnum></citerefentry>
|
||||||
|
</para>
|
||||||
|
</refsect1>
|
||||||
|
|
||||||
|
</refentry>
|
|
@ -7,3 +7,17 @@ executables += [
|
||||||
'sources' : files('run.c'),
|
'sources' : files('run.c'),
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
install_emptydir(bindir)
|
||||||
|
|
||||||
|
meson.add_install_script(sh, '-c',
|
||||||
|
ln_s.format(bindir / 'systemd-run',
|
||||||
|
bindir / 'uid0'))
|
||||||
|
|
||||||
|
custom_target(
|
||||||
|
'systemd-uid0',
|
||||||
|
input : 'systemd-uid0.in',
|
||||||
|
output : 'systemd-uid0',
|
||||||
|
command : [jinja2_cmdline, '@INPUT@', '@OUTPUT@'],
|
||||||
|
install : pamconfdir != 'no',
|
||||||
|
install_dir : pamconfdir)
|
||||||
|
|
267
src/run/run.c
267
src/run/run.c
|
@ -73,6 +73,7 @@ static bool arg_aggressive_gc = false;
|
||||||
static char *arg_working_directory = NULL;
|
static char *arg_working_directory = NULL;
|
||||||
static bool arg_shell = false;
|
static bool arg_shell = false;
|
||||||
static char **arg_cmdline = NULL;
|
static char **arg_cmdline = NULL;
|
||||||
|
static char *arg_exec_path = NULL;
|
||||||
|
|
||||||
STATIC_DESTRUCTOR_REGISTER(arg_description, freep);
|
STATIC_DESTRUCTOR_REGISTER(arg_description, freep);
|
||||||
STATIC_DESTRUCTOR_REGISTER(arg_environment, strv_freep);
|
STATIC_DESTRUCTOR_REGISTER(arg_environment, strv_freep);
|
||||||
|
@ -82,6 +83,7 @@ STATIC_DESTRUCTOR_REGISTER(arg_socket_property, strv_freep);
|
||||||
STATIC_DESTRUCTOR_REGISTER(arg_timer_property, strv_freep);
|
STATIC_DESTRUCTOR_REGISTER(arg_timer_property, strv_freep);
|
||||||
STATIC_DESTRUCTOR_REGISTER(arg_working_directory, freep);
|
STATIC_DESTRUCTOR_REGISTER(arg_working_directory, freep);
|
||||||
STATIC_DESTRUCTOR_REGISTER(arg_cmdline, strv_freep);
|
STATIC_DESTRUCTOR_REGISTER(arg_cmdline, strv_freep);
|
||||||
|
STATIC_DESTRUCTOR_REGISTER(arg_exec_path, freep);
|
||||||
|
|
||||||
static int help(void) {
|
static int help(void) {
|
||||||
_cleanup_free_ char *link = NULL;
|
_cleanup_free_ char *link = NULL;
|
||||||
|
@ -146,6 +148,39 @@ static int help(void) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int help_sudo_mode(void) {
|
||||||
|
_cleanup_free_ char *link = NULL;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
r = terminal_urlify_man("uid0", "1", &link);
|
||||||
|
if (r < 0)
|
||||||
|
return log_oom();
|
||||||
|
|
||||||
|
printf("%s [OPTIONS...] COMMAND [ARGUMENTS...]\n"
|
||||||
|
"\n%sElevate privileges interactively.%s\n\n"
|
||||||
|
" -h --help Show this help\n"
|
||||||
|
" -V --version Show package version\n"
|
||||||
|
" --no-ask-password Do not prompt for password\n"
|
||||||
|
" --machine=CONTAINER Operate on local container\n"
|
||||||
|
" --unit=UNIT Run under the specified unit name\n"
|
||||||
|
" --property=NAME=VALUE Set service or scope unit property\n"
|
||||||
|
" --description=TEXT Description for unit\n"
|
||||||
|
" --slice=SLICE Run in the specified slice\n"
|
||||||
|
" --slice-inherit Inherit the slice\n"
|
||||||
|
" -u --user=USER Run as system user\n"
|
||||||
|
" -g --group=GROUP Run as system group\n"
|
||||||
|
" --nice=NICE Nice level\n"
|
||||||
|
" -D --chdir=PATH Set working directory\n"
|
||||||
|
" --setenv=NAME[=VALUE] Set environment variable\n"
|
||||||
|
"\nSee the %s for details.\n",
|
||||||
|
program_invocation_short_name,
|
||||||
|
ansi_highlight(),
|
||||||
|
ansi_normal(),
|
||||||
|
link);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int add_timer_property(const char *name, const char *val) {
|
static int add_timer_property(const char *name, const char *val) {
|
||||||
char *p;
|
char *p;
|
||||||
|
|
||||||
|
@ -162,6 +197,18 @@ static int add_timer_property(const char *name, const char *val) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static char **make_login_shell_cmdline(const char *shell) {
|
||||||
|
_cleanup_free_ char *argv0 = NULL;
|
||||||
|
|
||||||
|
assert(shell);
|
||||||
|
|
||||||
|
argv0 = strjoin("-", shell); /* The - is how shells determine if they shall be consider login shells */
|
||||||
|
if (!argv0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return strv_new(argv0);
|
||||||
|
}
|
||||||
|
|
||||||
static int parse_argv(int argc, char *argv[]) {
|
static int parse_argv(int argc, char *argv[]) {
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
|
@ -651,6 +698,219 @@ static int parse_argv(int argc, char *argv[]) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int parse_argv_sudo_mode(int argc, char *argv[]) {
|
||||||
|
|
||||||
|
enum {
|
||||||
|
ARG_NO_ASK_PASSWORD = 0x100,
|
||||||
|
ARG_HOST,
|
||||||
|
ARG_MACHINE,
|
||||||
|
ARG_UNIT,
|
||||||
|
ARG_PROPERTY,
|
||||||
|
ARG_DESCRIPTION,
|
||||||
|
ARG_SLICE,
|
||||||
|
ARG_SLICE_INHERIT,
|
||||||
|
ARG_NICE,
|
||||||
|
ARG_SETENV,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* If invoked as "uid0" binary, let's expose a more sudo-like interface. We add various extensions
|
||||||
|
* though (but limit the extension to long options). */
|
||||||
|
|
||||||
|
static const struct option options[] = {
|
||||||
|
{ "help", no_argument, NULL, 'h' },
|
||||||
|
{ "version", no_argument, NULL, 'V' },
|
||||||
|
{ "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
|
||||||
|
{ "machine", required_argument, NULL, ARG_MACHINE },
|
||||||
|
{ "unit", required_argument, NULL, ARG_UNIT },
|
||||||
|
{ "property", required_argument, NULL, ARG_PROPERTY },
|
||||||
|
{ "description", required_argument, NULL, ARG_DESCRIPTION },
|
||||||
|
{ "slice", required_argument, NULL, ARG_SLICE },
|
||||||
|
{ "slice-inherit", no_argument, NULL, ARG_SLICE_INHERIT },
|
||||||
|
{ "user", required_argument, NULL, 'u' },
|
||||||
|
{ "group", required_argument, NULL, 'g' },
|
||||||
|
{ "nice", required_argument, NULL, ARG_NICE },
|
||||||
|
{ "chdir", required_argument, NULL, 'D' },
|
||||||
|
{ "setenv", required_argument, NULL, ARG_SETENV },
|
||||||
|
{},
|
||||||
|
};
|
||||||
|
|
||||||
|
int r, c;
|
||||||
|
|
||||||
|
assert(argc >= 0);
|
||||||
|
assert(argv);
|
||||||
|
|
||||||
|
/* Resetting to 0 forces the invocation of an internal initialization routine of getopt_long()
|
||||||
|
* that checks for GNU extensions in optstring ('-' or '+' at the beginning). */
|
||||||
|
optind = 0;
|
||||||
|
while ((c = getopt_long(argc, argv, "+hVu:g:D:", options, NULL)) >= 0)
|
||||||
|
|
||||||
|
switch (c) {
|
||||||
|
|
||||||
|
case 'h':
|
||||||
|
return help_sudo_mode();
|
||||||
|
|
||||||
|
case 'V':
|
||||||
|
return version();
|
||||||
|
|
||||||
|
case ARG_NO_ASK_PASSWORD:
|
||||||
|
arg_ask_password = false;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ARG_MACHINE:
|
||||||
|
arg_transport = BUS_TRANSPORT_MACHINE;
|
||||||
|
arg_host = optarg;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ARG_UNIT:
|
||||||
|
arg_unit = optarg;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ARG_PROPERTY:
|
||||||
|
if (strv_extend(&arg_property, optarg) < 0)
|
||||||
|
return log_oom();
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ARG_DESCRIPTION:
|
||||||
|
r = free_and_strdup_warn(&arg_description, optarg);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ARG_SLICE:
|
||||||
|
arg_slice = optarg;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ARG_SLICE_INHERIT:
|
||||||
|
arg_slice_inherit = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'u':
|
||||||
|
arg_exec_user = optarg;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'g':
|
||||||
|
arg_exec_group = optarg;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ARG_NICE:
|
||||||
|
r = parse_nice(optarg, &arg_nice);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to parse nice value: %s", optarg);
|
||||||
|
|
||||||
|
arg_nice_set = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'D':
|
||||||
|
r = parse_path_argument(optarg, true, &arg_working_directory);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ARG_SETENV:
|
||||||
|
r = strv_env_replace_strdup_passthrough(&arg_environment, optarg);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Cannot assign environment variable %s: %m", optarg);
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '?':
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
default:
|
||||||
|
assert_not_reached();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!arg_working_directory) {
|
||||||
|
if (arg_exec_user) {
|
||||||
|
/* When switching to a specific user, also switch to its home directory. */
|
||||||
|
arg_working_directory = strdup("~");
|
||||||
|
if (!arg_working_directory)
|
||||||
|
return log_oom();
|
||||||
|
} else {
|
||||||
|
/* When switching to root without this being specified, then stay in the current directory */
|
||||||
|
r = safe_getcwd(&arg_working_directory);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to get current working directory: %m");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
arg_service_type = "exec";
|
||||||
|
arg_quiet = true;
|
||||||
|
arg_wait = true;
|
||||||
|
arg_aggressive_gc = true;
|
||||||
|
|
||||||
|
arg_stdio = isatty(STDIN_FILENO) && isatty(STDOUT_FILENO) && isatty(STDERR_FILENO) ? ARG_STDIO_PTY : ARG_STDIO_DIRECT;
|
||||||
|
arg_expand_environment = false;
|
||||||
|
arg_send_sighup = true;
|
||||||
|
|
||||||
|
_cleanup_strv_free_ char **l = NULL;
|
||||||
|
if (argc > optind)
|
||||||
|
l = strv_copy(argv + optind);
|
||||||
|
else {
|
||||||
|
const char *e;
|
||||||
|
|
||||||
|
e = strv_env_get(arg_environment, "SHELL");
|
||||||
|
if (e)
|
||||||
|
arg_exec_path = strdup(e);
|
||||||
|
else {
|
||||||
|
if (arg_transport == BUS_TRANSPORT_LOCAL) {
|
||||||
|
r = get_shell(&arg_exec_path);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to determine shell: %m");
|
||||||
|
} else
|
||||||
|
arg_exec_path = strdup("/bin/sh");
|
||||||
|
}
|
||||||
|
if (!arg_exec_path)
|
||||||
|
return log_oom();
|
||||||
|
|
||||||
|
l = make_login_shell_cmdline(arg_exec_path);
|
||||||
|
}
|
||||||
|
if (!l)
|
||||||
|
return log_oom();
|
||||||
|
|
||||||
|
strv_free_and_replace(arg_cmdline, l);
|
||||||
|
|
||||||
|
if (!arg_slice) {
|
||||||
|
arg_slice = strdup("user.slice");
|
||||||
|
if (!arg_slice)
|
||||||
|
return log_oom();
|
||||||
|
}
|
||||||
|
|
||||||
|
_cleanup_free_ char *un = NULL;
|
||||||
|
un = getusername_malloc();
|
||||||
|
if (!un)
|
||||||
|
return log_oom();
|
||||||
|
|
||||||
|
/* Set a bunch of environment variables in a roughly sudo-compatible way */
|
||||||
|
r = strv_env_assign(&arg_environment, "SUDO_USER", un);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to set $SUDO_USER environment variable: %m");
|
||||||
|
|
||||||
|
r = strv_env_assignf(&arg_environment, "SUDO_UID", UID_FMT, getuid());
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to set $SUDO_UID environment variable: %m");
|
||||||
|
|
||||||
|
r = strv_env_assignf(&arg_environment, "SUDO_GID", GID_FMT, getgid());
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to set $SUDO_GID environment variable: %m");
|
||||||
|
|
||||||
|
if (strv_extendf(&arg_property, "LogExtraFields=ELEVATED_UID=" UID_FMT, getuid()) < 0)
|
||||||
|
return log_oom();
|
||||||
|
|
||||||
|
if (strv_extendf(&arg_property, "LogExtraFields=ELEVATED_GID=" GID_FMT, getgid()) < 0)
|
||||||
|
return log_oom();
|
||||||
|
|
||||||
|
if (strv_extendf(&arg_property, "LogExtraFields=ELEVATED_USER=%s", un) < 0)
|
||||||
|
return log_oom();
|
||||||
|
|
||||||
|
if (strv_extend(&arg_property, "PAMName=systemd-uid0") < 0)
|
||||||
|
return log_oom();
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
static int transient_unit_set_properties(sd_bus_message *m, UnitType t, char **properties) {
|
static int transient_unit_set_properties(sd_bus_message *m, UnitType t, char **properties) {
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
|
@ -899,7 +1159,7 @@ static int transient_service_set_properties(sd_bus_message *m, const char *pty_p
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return bus_log_create_error(r);
|
return bus_log_create_error(r);
|
||||||
|
|
||||||
r = sd_bus_message_append(m, "s", arg_cmdline[0]);
|
r = sd_bus_message_append(m, "s", arg_exec_path ?: arg_cmdline[0]);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return bus_log_create_error(r);
|
return bus_log_create_error(r);
|
||||||
|
|
||||||
|
@ -1899,6 +2159,8 @@ static int start_transient_trigger(sd_bus *bus, const char *suffix) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool shall_make_executable_absolute(void) {
|
static bool shall_make_executable_absolute(void) {
|
||||||
|
if (arg_exec_path)
|
||||||
|
return false;
|
||||||
if (strv_isempty(arg_cmdline))
|
if (strv_isempty(arg_cmdline))
|
||||||
return false;
|
return false;
|
||||||
if (arg_transport != BUS_TRANSPORT_LOCAL)
|
if (arg_transport != BUS_TRANSPORT_LOCAL)
|
||||||
|
@ -1919,6 +2181,9 @@ static int run(int argc, char* argv[]) {
|
||||||
log_parse_environment();
|
log_parse_environment();
|
||||||
log_open();
|
log_open();
|
||||||
|
|
||||||
|
if (invoked_as(argv, "uid0"))
|
||||||
|
r = parse_argv_sudo_mode(argc, argv);
|
||||||
|
else
|
||||||
r = parse_argv(argc, argv);
|
r = parse_argv(argc, argv);
|
||||||
if (r <= 0)
|
if (r <= 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
23
src/run/systemd-uid0.in
Normal file
23
src/run/systemd-uid0.in
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
|
# This file is part of systemd.
|
||||||
|
#
|
||||||
|
# Used by uid0 sessions
|
||||||
|
|
||||||
|
{% if ENABLE_HOMED %}
|
||||||
|
-account sufficient pam_systemd_home.so
|
||||||
|
{% endif %}
|
||||||
|
account required pam_unix.so
|
||||||
|
|
||||||
|
{% if HAVE_SELINUX %}
|
||||||
|
session required pam_selinux.so close
|
||||||
|
session required pam_selinux.so open
|
||||||
|
{% endif %}
|
||||||
|
session required pam_loginuid.so
|
||||||
|
session optional pam_keyinit.so force revoke
|
||||||
|
session required pam_namespace.so
|
||||||
|
{% if ENABLE_HOMED %}
|
||||||
|
-session optional pam_systemd_home.so
|
||||||
|
{% endif %}
|
||||||
|
session optional pam_umask.so silent
|
||||||
|
session optional pam_systemd.so
|
||||||
|
session required pam_unix.so
|
Loading…
Reference in a new issue