2013-08-08 15:36:31 +00:00
|
|
|
/*
|
2019-08-01 09:57:39 +00:00
|
|
|
* virt-login-shell-helper.c: a shell to connect to a container
|
2013-08-08 15:36:31 +00:00
|
|
|
*
|
2013-12-23 17:15:48 +00:00
|
|
|
* Copyright (C) 2013-2014 Red Hat, Inc.
|
2013-08-08 15:36:31 +00:00
|
|
|
*
|
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
* License as published by the Free Software Foundation; either
|
|
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This library is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* Lesser General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
|
|
* License along with this library. If not, see
|
|
|
|
* <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
#include <config.h>
|
|
|
|
|
2013-12-24 04:07:01 +00:00
|
|
|
#include <getopt.h>
|
|
|
|
#include <signal.h>
|
|
|
|
#include <stdarg.h>
|
2015-12-20 07:27:21 +00:00
|
|
|
#include <unistd.h>
|
2013-08-08 15:36:31 +00:00
|
|
|
|
|
|
|
#include "internal.h"
|
|
|
|
#include "virerror.h"
|
|
|
|
#include "virconf.h"
|
|
|
|
#include "virutil.h"
|
|
|
|
#include "virfile.h"
|
|
|
|
#include "virprocess.h"
|
|
|
|
#include "configmake.h"
|
|
|
|
#include "virstring.h"
|
|
|
|
#include "viralloc.h"
|
|
|
|
#include "vircommand.h"
|
2016-04-12 22:29:52 +00:00
|
|
|
#include "virgettext.h"
|
2013-08-08 15:36:31 +00:00
|
|
|
#define VIR_FROM_THIS VIR_FROM_NONE
|
|
|
|
|
|
|
|
static const char *conf_file = SYSCONFDIR "/libvirt/virt-login-shell.conf";
|
|
|
|
|
|
|
|
static int virLoginShellAllowedUser(virConfPtr conf,
|
|
|
|
const char *name,
|
2016-06-17 12:50:44 +00:00
|
|
|
gid_t *groups,
|
|
|
|
size_t ngroups)
|
2013-08-08 15:36:31 +00:00
|
|
|
{
|
|
|
|
int ret = -1;
|
|
|
|
size_t i;
|
|
|
|
char *gname = NULL;
|
2016-07-08 12:52:42 +00:00
|
|
|
char **users = NULL, **entries;
|
2013-08-08 15:36:31 +00:00
|
|
|
|
2016-07-08 12:52:42 +00:00
|
|
|
if (virConfGetValueStringList(conf, "allowed_users", false, &users) < 0)
|
|
|
|
goto cleanup;
|
2013-08-08 15:36:31 +00:00
|
|
|
|
2016-07-08 12:52:42 +00:00
|
|
|
|
2019-08-01 11:07:47 +00:00
|
|
|
for (entries = users; entries && *entries; entries++) {
|
2016-07-08 12:52:42 +00:00
|
|
|
char *entry = *entries;
|
|
|
|
/*
|
|
|
|
If string begins with a % this indicates a linux group.
|
|
|
|
Check to see if the user is in the Linux Group.
|
|
|
|
*/
|
|
|
|
if (entry[0] == '%') {
|
|
|
|
entry++;
|
|
|
|
if (!*entry)
|
|
|
|
continue;
|
|
|
|
for (i = 0; i < ngroups; i++) {
|
|
|
|
if (!(gname = virGetGroupName(groups[i])))
|
2013-08-08 15:36:31 +00:00
|
|
|
continue;
|
2019-12-20 16:02:49 +00:00
|
|
|
if (g_pattern_match_simple(entry, gname)) {
|
2013-08-08 15:36:31 +00:00
|
|
|
ret = 0;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
2016-07-08 12:52:42 +00:00
|
|
|
VIR_FREE(gname);
|
|
|
|
}
|
|
|
|
} else {
|
2019-12-20 16:02:49 +00:00
|
|
|
if (g_pattern_match_simple(entry, name)) {
|
2016-07-08 12:52:42 +00:00
|
|
|
ret = 0;
|
|
|
|
goto cleanup;
|
2013-08-08 15:36:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2013-08-13 23:28:06 +00:00
|
|
|
virReportSystemError(EPERM,
|
|
|
|
_("%s not matched against 'allowed_users' in %s"),
|
|
|
|
name, conf_file);
|
2014-03-25 06:53:59 +00:00
|
|
|
cleanup:
|
2013-08-08 15:36:31 +00:00
|
|
|
VIR_FREE(gname);
|
2016-11-25 08:18:35 +00:00
|
|
|
virStringListFree(users);
|
2013-08-08 15:36:31 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2016-04-12 16:01:39 +00:00
|
|
|
|
2016-04-12 14:18:04 +00:00
|
|
|
static int virLoginShellGetShellArgv(virConfPtr conf,
|
2016-07-08 12:52:42 +00:00
|
|
|
char ***shargv,
|
|
|
|
size_t *shargvlen)
|
2013-08-08 15:36:31 +00:00
|
|
|
{
|
2016-07-18 18:07:42 +00:00
|
|
|
int rv;
|
|
|
|
|
|
|
|
if ((rv = virConfGetValueStringList(conf, "shell", true, shargv)) < 0)
|
2016-07-08 12:52:42 +00:00
|
|
|
return -1;
|
2016-04-12 14:18:04 +00:00
|
|
|
|
2016-07-18 18:07:42 +00:00
|
|
|
if (rv == 0) {
|
2016-07-08 12:52:42 +00:00
|
|
|
if (VIR_ALLOC_N(*shargv, 2) < 0)
|
|
|
|
return -1;
|
2019-10-20 11:49:46 +00:00
|
|
|
(*shargv)[0] = g_strdup("/bin/sh");
|
2016-07-08 12:52:42 +00:00
|
|
|
*shargvlen = 1;
|
|
|
|
} else {
|
|
|
|
*shargvlen = virStringListLength((const char *const *)shargv);
|
2013-08-08 15:36:31 +00:00
|
|
|
}
|
2016-04-12 14:18:04 +00:00
|
|
|
return 0;
|
2013-08-08 15:36:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static char *progname;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Print usage
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
usage(void)
|
|
|
|
{
|
2013-08-13 11:13:33 +00:00
|
|
|
fprintf(stdout,
|
|
|
|
_("\n"
|
|
|
|
"Usage:\n"
|
2013-12-24 04:26:27 +00:00
|
|
|
" %s [option]\n\n"
|
2013-08-13 11:13:33 +00:00
|
|
|
"Options:\n"
|
2013-12-24 04:26:27 +00:00
|
|
|
" -h | --help Display program help\n"
|
|
|
|
" -V | --version Display program version\n"
|
2016-04-12 14:18:04 +00:00
|
|
|
" -c CMD Run CMD via shell\n"
|
2013-08-13 11:13:33 +00:00
|
|
|
"\n"
|
|
|
|
"libvirt login shell\n"),
|
|
|
|
progname);
|
2013-08-08 15:36:31 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-08-13 11:13:33 +00:00
|
|
|
/* Display version information. */
|
|
|
|
static void
|
|
|
|
show_version(void)
|
|
|
|
{
|
|
|
|
printf("%s (%s) %s\n", progname, PACKAGE_NAME, PACKAGE_VERSION);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-08-01 12:02:57 +00:00
|
|
|
static void
|
2019-10-14 12:44:29 +00:00
|
|
|
hideErrorFunc(void *opaque G_GNUC_UNUSED,
|
|
|
|
virErrorPtr err G_GNUC_UNUSED)
|
2019-08-01 12:02:57 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2013-08-08 15:36:31 +00:00
|
|
|
int
|
|
|
|
main(int argc, char **argv)
|
|
|
|
{
|
2019-10-15 12:47:50 +00:00
|
|
|
g_autoptr(virConf) conf = NULL;
|
2013-08-08 15:36:31 +00:00
|
|
|
const char *login_shell_path = conf_file;
|
2013-12-24 04:07:01 +00:00
|
|
|
pid_t cpid = -1;
|
|
|
|
int ret = EXIT_CANCELED;
|
2013-08-08 15:36:31 +00:00
|
|
|
int status;
|
tools: split virt-login-shell into two binaries
The virt-login-shell binary is a setuid program that takes
no arguments. When invoked it looks at the invoking uid,
resolves it to a username, and finds an LXC guest with the
same name. It then starts the guest and runs the shell in
side the namespaces of the container.
Given this set of tasks the virt-login-shell binary needs
to connect to libvirtd, make various other libvirt API calls.
This is a problem for setuid binaries as various libraries
that libvirt.so links to are not safe. For example, they have
constructor functions which execute an unknown amount of code
that can be influenced by env variables.
For this reason virt-login-shell doesn't use libvirt.so,
but instead links to a custom, cut down, set of source files
sufficient to be a local client only.
This introduces a problem for integrating glib2 into libvirt
though, as once integrated, there would be no way to build
virt-login-shell without an external dependancy on glib2 and
this is definitely not setuid safe.
To resolve this problem, we split the virt-login-shell binary
into two parts. The first part is setuid and does almost
nothing. It simply records the original uid+gid, and then
invokes the virt-login-shell-helper binary. Crucially when
it does this it completes scrubs all environment variables.
It is thus safe for virt-login-shell-helper to link to the
normal libvirt.so. Any things that constructor functions
do cannot be influenced by user control env vars or cli
args.
Reviewed-by: Michal Privoznik <mprivozn@redhat.com>
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
2019-08-01 09:58:31 +00:00
|
|
|
unsigned long long uidval;
|
|
|
|
unsigned long long gidval;
|
|
|
|
uid_t uid;
|
|
|
|
gid_t gid;
|
2013-08-08 15:36:31 +00:00
|
|
|
char *name = NULL;
|
|
|
|
char **shargv = NULL;
|
2016-04-12 14:18:04 +00:00
|
|
|
size_t shargvlen = 0;
|
2016-04-12 14:45:48 +00:00
|
|
|
char *shcmd = NULL;
|
2013-08-08 15:36:31 +00:00
|
|
|
virSecurityModelPtr secmodel = NULL;
|
|
|
|
virSecurityLabelPtr seclabel = NULL;
|
|
|
|
virDomainPtr dom = NULL;
|
|
|
|
virConnectPtr conn = NULL;
|
|
|
|
char *homedir = NULL;
|
|
|
|
int arg;
|
|
|
|
int longindex = -1;
|
|
|
|
int ngroups;
|
|
|
|
gid_t *groups = NULL;
|
virt-login-shell: use single instead of double fork
Note that 'virsh lxc-enter-namespace' must double-fork, for two
reasons: some namespaces can only be done from a single thread,
while virsh is multithreaded; and because virsh can be run in
batch mode where we must not corrupt the namespace of that
execution upon return from the subsidiary command.
When virt-login-shell was first written, it blindly copied from
'virsh lxc-enter-namespace', including the double-fork. But
neither of the reasons for double forking apply to
virt-login-shell (we are single-threaded, and we have nothing to
do after the child completes that would require us to preserve a
namespace), so we can simplify life by using a single fork.
In turn, this will make it easier for a future patch to pass the
child's exit status on to the invoking shell.
In flattening to a single fork, note that closing the fds must
be done after fork, because the parent process still needs to
use fds to control the virConnectPtr; meanwhile, chdir can be
done prior to forking (in fact, it's easier to report errors
on anything attempted before forking).
* tools/virt-login-shell.c (main): Single rather than double fork.
(virLoginShellFini): Delete, by inlining actions instead.
Signed-off-by: Eric Blake <eblake@redhat.com>
2013-12-23 16:15:16 +00:00
|
|
|
ssize_t nfdlist = 0;
|
|
|
|
int *fdlist = NULL;
|
|
|
|
int openmax;
|
|
|
|
size_t i;
|
2016-04-12 14:18:04 +00:00
|
|
|
const char *cmdstr = NULL;
|
2016-04-12 14:45:48 +00:00
|
|
|
char *tmp;
|
2016-04-12 15:52:58 +00:00
|
|
|
char *term = NULL;
|
2016-04-12 15:48:19 +00:00
|
|
|
virErrorPtr saved_err = NULL;
|
2016-04-12 16:01:39 +00:00
|
|
|
bool autoshell = false;
|
2013-08-08 15:36:31 +00:00
|
|
|
|
|
|
|
struct option opt[] = {
|
|
|
|
{"help", no_argument, NULL, 'h'},
|
2013-08-13 11:13:33 +00:00
|
|
|
{"version", optional_argument, NULL, 'V'},
|
2013-08-08 15:36:31 +00:00
|
|
|
{NULL, 0, NULL, 0}
|
|
|
|
};
|
|
|
|
if (virInitialize() < 0) {
|
2013-12-24 04:07:01 +00:00
|
|
|
fprintf(stderr, _("Failed to initialize libvirt error handling"));
|
|
|
|
return EXIT_CANCELED;
|
2013-08-08 15:36:31 +00:00
|
|
|
}
|
|
|
|
|
2019-08-01 12:02:57 +00:00
|
|
|
virSetErrorFunc(NULL, hideErrorFunc);
|
2013-08-08 15:36:31 +00:00
|
|
|
virSetErrorLogPriorityFunc(NULL);
|
|
|
|
|
|
|
|
progname = argv[0];
|
2016-04-12 22:29:52 +00:00
|
|
|
if (virGettextInitialize() < 0)
|
2013-08-08 15:36:31 +00:00
|
|
|
return ret;
|
|
|
|
|
tools: split virt-login-shell into two binaries
The virt-login-shell binary is a setuid program that takes
no arguments. When invoked it looks at the invoking uid,
resolves it to a username, and finds an LXC guest with the
same name. It then starts the guest and runs the shell in
side the namespaces of the container.
Given this set of tasks the virt-login-shell binary needs
to connect to libvirtd, make various other libvirt API calls.
This is a problem for setuid binaries as various libraries
that libvirt.so links to are not safe. For example, they have
constructor functions which execute an unknown amount of code
that can be influenced by env variables.
For this reason virt-login-shell doesn't use libvirt.so,
but instead links to a custom, cut down, set of source files
sufficient to be a local client only.
This introduces a problem for integrating glib2 into libvirt
though, as once integrated, there would be no way to build
virt-login-shell without an external dependancy on glib2 and
this is definitely not setuid safe.
To resolve this problem, we split the virt-login-shell binary
into two parts. The first part is setuid and does almost
nothing. It simply records the original uid+gid, and then
invokes the virt-login-shell-helper binary. Crucially when
it does this it completes scrubs all environment variables.
It is thus safe for virt-login-shell-helper to link to the
normal libvirt.so. Any things that constructor functions
do cannot be influenced by user control env vars or cli
args.
Reviewed-by: Michal Privoznik <mprivozn@redhat.com>
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
2019-08-01 09:58:31 +00:00
|
|
|
if (geteuid() != 0) {
|
|
|
|
fprintf(stderr, _("%s: must be run as root\n"), argv[0]);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (getuid() != 0) {
|
|
|
|
fprintf(stderr, _("%s: must not be run setuid root\n"), argv[0]);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2016-04-12 14:18:04 +00:00
|
|
|
while ((arg = getopt_long(argc, argv, "hVc:", opt, &longindex)) != -1) {
|
2013-08-08 15:36:31 +00:00
|
|
|
switch (arg) {
|
|
|
|
case 'h':
|
|
|
|
usage();
|
|
|
|
exit(EXIT_SUCCESS);
|
2013-08-13 11:13:33 +00:00
|
|
|
|
|
|
|
case 'V':
|
|
|
|
show_version();
|
|
|
|
exit(EXIT_SUCCESS);
|
|
|
|
|
2016-04-12 14:18:04 +00:00
|
|
|
case 'c':
|
|
|
|
cmdstr = optarg;
|
|
|
|
break;
|
|
|
|
|
2013-08-13 11:13:33 +00:00
|
|
|
case '?':
|
|
|
|
default:
|
|
|
|
usage();
|
2013-12-24 04:07:01 +00:00
|
|
|
exit(EXIT_CANCELED);
|
2013-08-08 15:36:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
tools: split virt-login-shell into two binaries
The virt-login-shell binary is a setuid program that takes
no arguments. When invoked it looks at the invoking uid,
resolves it to a username, and finds an LXC guest with the
same name. It then starts the guest and runs the shell in
side the namespaces of the container.
Given this set of tasks the virt-login-shell binary needs
to connect to libvirtd, make various other libvirt API calls.
This is a problem for setuid binaries as various libraries
that libvirt.so links to are not safe. For example, they have
constructor functions which execute an unknown amount of code
that can be influenced by env variables.
For this reason virt-login-shell doesn't use libvirt.so,
but instead links to a custom, cut down, set of source files
sufficient to be a local client only.
This introduces a problem for integrating glib2 into libvirt
though, as once integrated, there would be no way to build
virt-login-shell without an external dependancy on glib2 and
this is definitely not setuid safe.
To resolve this problem, we split the virt-login-shell binary
into two parts. The first part is setuid and does almost
nothing. It simply records the original uid+gid, and then
invokes the virt-login-shell-helper binary. Crucially when
it does this it completes scrubs all environment variables.
It is thus safe for virt-login-shell-helper to link to the
normal libvirt.so. Any things that constructor functions
do cannot be influenced by user control env vars or cli
args.
Reviewed-by: Michal Privoznik <mprivozn@redhat.com>
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
2019-08-01 09:58:31 +00:00
|
|
|
if (optind != (argc - 2)) {
|
|
|
|
virReportSystemError(EINVAL, _("%s expects UID and GID parameters"), progname);
|
2013-08-08 15:36:31 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
tools: split virt-login-shell into two binaries
The virt-login-shell binary is a setuid program that takes
no arguments. When invoked it looks at the invoking uid,
resolves it to a username, and finds an LXC guest with the
same name. It then starts the guest and runs the shell in
side the namespaces of the container.
Given this set of tasks the virt-login-shell binary needs
to connect to libvirtd, make various other libvirt API calls.
This is a problem for setuid binaries as various libraries
that libvirt.so links to are not safe. For example, they have
constructor functions which execute an unknown amount of code
that can be influenced by env variables.
For this reason virt-login-shell doesn't use libvirt.so,
but instead links to a custom, cut down, set of source files
sufficient to be a local client only.
This introduces a problem for integrating glib2 into libvirt
though, as once integrated, there would be no way to build
virt-login-shell without an external dependancy on glib2 and
this is definitely not setuid safe.
To resolve this problem, we split the virt-login-shell binary
into two parts. The first part is setuid and does almost
nothing. It simply records the original uid+gid, and then
invokes the virt-login-shell-helper binary. Crucially when
it does this it completes scrubs all environment variables.
It is thus safe for virt-login-shell-helper to link to the
normal libvirt.so. Any things that constructor functions
do cannot be influenced by user control env vars or cli
args.
Reviewed-by: Michal Privoznik <mprivozn@redhat.com>
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
2019-08-01 09:58:31 +00:00
|
|
|
if (virStrToLong_ull(argv[optind], NULL, 10, &uidval) < 0 ||
|
|
|
|
((uid_t)uidval) != uidval) {
|
|
|
|
virReportSystemError(EINVAL, _("%s cannot parse UID '%s'"),
|
|
|
|
progname, argv[optind]);
|
2013-08-08 15:36:31 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
tools: split virt-login-shell into two binaries
The virt-login-shell binary is a setuid program that takes
no arguments. When invoked it looks at the invoking uid,
resolves it to a username, and finds an LXC guest with the
same name. It then starts the guest and runs the shell in
side the namespaces of the container.
Given this set of tasks the virt-login-shell binary needs
to connect to libvirtd, make various other libvirt API calls.
This is a problem for setuid binaries as various libraries
that libvirt.so links to are not safe. For example, they have
constructor functions which execute an unknown amount of code
that can be influenced by env variables.
For this reason virt-login-shell doesn't use libvirt.so,
but instead links to a custom, cut down, set of source files
sufficient to be a local client only.
This introduces a problem for integrating glib2 into libvirt
though, as once integrated, there would be no way to build
virt-login-shell without an external dependancy on glib2 and
this is definitely not setuid safe.
To resolve this problem, we split the virt-login-shell binary
into two parts. The first part is setuid and does almost
nothing. It simply records the original uid+gid, and then
invokes the virt-login-shell-helper binary. Crucially when
it does this it completes scrubs all environment variables.
It is thus safe for virt-login-shell-helper to link to the
normal libvirt.so. Any things that constructor functions
do cannot be influenced by user control env vars or cli
args.
Reviewed-by: Michal Privoznik <mprivozn@redhat.com>
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
2019-08-01 09:58:31 +00:00
|
|
|
optind++;
|
|
|
|
if (virStrToLong_ull(argv[optind], NULL, 10, &gidval) < 0 ||
|
|
|
|
((gid_t)gidval) != gidval) {
|
|
|
|
virReportSystemError(EINVAL, _("%s cannot parse GID '%s'"),
|
|
|
|
progname, argv[optind]);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
uid = (uid_t)uidval;
|
|
|
|
gid = (gid_t)gidval;
|
|
|
|
|
2013-08-08 15:36:31 +00:00
|
|
|
name = virGetUserName(uid);
|
|
|
|
if (!name)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
homedir = virGetUserDirectoryByUID(uid);
|
|
|
|
if (!homedir)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (!(conf = virConfReadFile(login_shell_path, 0)))
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if ((ngroups = virGetGroupList(uid, gid, &groups)) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
2016-06-17 12:50:44 +00:00
|
|
|
if (virLoginShellAllowedUser(conf, name, groups, ngroups) < 0)
|
2013-08-08 15:36:31 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
2016-04-12 14:18:04 +00:00
|
|
|
if (virLoginShellGetShellArgv(conf, &shargv, &shargvlen) < 0)
|
2013-08-08 15:36:31 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
2016-07-08 12:52:42 +00:00
|
|
|
if (virConfGetValueBool(conf, "auto_shell", &autoshell) < 0)
|
2016-04-12 16:01:39 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
2018-03-27 13:32:07 +00:00
|
|
|
conn = virConnectOpen("lxc:///system");
|
2013-08-08 15:36:31 +00:00
|
|
|
if (!conn)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
dom = virDomainLookupByName(conn, name);
|
|
|
|
if (!dom)
|
|
|
|
goto cleanup;
|
|
|
|
|
2014-09-12 12:16:07 +00:00
|
|
|
if (!virDomainIsActive(dom) && virDomainCreate(dom) < 0) {
|
2013-08-08 15:36:31 +00:00
|
|
|
virErrorPtr last_error;
|
|
|
|
last_error = virGetLastError();
|
|
|
|
if (last_error->code != VIR_ERR_OPERATION_INVALID) {
|
2013-08-13 11:13:33 +00:00
|
|
|
virReportSystemError(last_error->code,
|
|
|
|
_("Can't create %s container: %s"),
|
|
|
|
name, last_error->message);
|
2013-08-08 15:36:31 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
virt-login-shell: use single instead of double fork
Note that 'virsh lxc-enter-namespace' must double-fork, for two
reasons: some namespaces can only be done from a single thread,
while virsh is multithreaded; and because virsh can be run in
batch mode where we must not corrupt the namespace of that
execution upon return from the subsidiary command.
When virt-login-shell was first written, it blindly copied from
'virsh lxc-enter-namespace', including the double-fork. But
neither of the reasons for double forking apply to
virt-login-shell (we are single-threaded, and we have nothing to
do after the child completes that would require us to preserve a
namespace), so we can simplify life by using a single fork.
In turn, this will make it easier for a future patch to pass the
child's exit status on to the invoking shell.
In flattening to a single fork, note that closing the fds must
be done after fork, because the parent process still needs to
use fds to control the virConnectPtr; meanwhile, chdir can be
done prior to forking (in fact, it's easier to report errors
on anything attempted before forking).
* tools/virt-login-shell.c (main): Single rather than double fork.
(virLoginShellFini): Delete, by inlining actions instead.
Signed-off-by: Eric Blake <eblake@redhat.com>
2013-12-23 16:15:16 +00:00
|
|
|
openmax = sysconf(_SC_OPEN_MAX);
|
|
|
|
if (openmax < 0) {
|
|
|
|
virReportSystemError(errno, "%s",
|
|
|
|
_("sysconf(_SC_OPEN_MAX) failed"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2013-08-08 15:36:31 +00:00
|
|
|
if ((nfdlist = virDomainLxcOpenNamespace(dom, &fdlist, 0)) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
if (VIR_ALLOC(secmodel) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
if (VIR_ALLOC(seclabel) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
if (virNodeGetSecurityModel(conn, secmodel) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
if (virDomainGetSecurityLabel(dom, seclabel) < 0)
|
|
|
|
goto cleanup;
|
virt-login-shell: use single instead of double fork
Note that 'virsh lxc-enter-namespace' must double-fork, for two
reasons: some namespaces can only be done from a single thread,
while virsh is multithreaded; and because virsh can be run in
batch mode where we must not corrupt the namespace of that
execution upon return from the subsidiary command.
When virt-login-shell was first written, it blindly copied from
'virsh lxc-enter-namespace', including the double-fork. But
neither of the reasons for double forking apply to
virt-login-shell (we are single-threaded, and we have nothing to
do after the child completes that would require us to preserve a
namespace), so we can simplify life by using a single fork.
In turn, this will make it easier for a future patch to pass the
child's exit status on to the invoking shell.
In flattening to a single fork, note that closing the fds must
be done after fork, because the parent process still needs to
use fds to control the virConnectPtr; meanwhile, chdir can be
done prior to forking (in fact, it's easier to report errors
on anything attempted before forking).
* tools/virt-login-shell.c (main): Single rather than double fork.
(virLoginShellFini): Delete, by inlining actions instead.
Signed-off-by: Eric Blake <eblake@redhat.com>
2013-12-23 16:15:16 +00:00
|
|
|
if (virSetUIDGID(0, 0, NULL, 0) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
if (virDomainLxcEnterSecurityLabel(secmodel, seclabel, NULL, 0) < 0)
|
|
|
|
goto cleanup;
|
2016-04-13 10:00:11 +00:00
|
|
|
if (virDomainLxcEnterCGroup(dom, 0) < 0)
|
|
|
|
goto cleanup;
|
virt-login-shell: use single instead of double fork
Note that 'virsh lxc-enter-namespace' must double-fork, for two
reasons: some namespaces can only be done from a single thread,
while virsh is multithreaded; and because virsh can be run in
batch mode where we must not corrupt the namespace of that
execution upon return from the subsidiary command.
When virt-login-shell was first written, it blindly copied from
'virsh lxc-enter-namespace', including the double-fork. But
neither of the reasons for double forking apply to
virt-login-shell (we are single-threaded, and we have nothing to
do after the child completes that would require us to preserve a
namespace), so we can simplify life by using a single fork.
In turn, this will make it easier for a future patch to pass the
child's exit status on to the invoking shell.
In flattening to a single fork, note that closing the fds must
be done after fork, because the parent process still needs to
use fds to control the virConnectPtr; meanwhile, chdir can be
done prior to forking (in fact, it's easier to report errors
on anything attempted before forking).
* tools/virt-login-shell.c (main): Single rather than double fork.
(virLoginShellFini): Delete, by inlining actions instead.
Signed-off-by: Eric Blake <eblake@redhat.com>
2013-12-23 16:15:16 +00:00
|
|
|
if (nfdlist > 0 &&
|
|
|
|
virDomainLxcEnterNamespace(dom, nfdlist, fdlist, NULL, NULL, 0) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
if (virSetUIDGID(uid, gid, groups, ngroups) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
if (chdir(homedir) < 0) {
|
|
|
|
virReportSystemError(errno, _("Unable to chdir(%s)"), homedir);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
2013-08-08 15:36:31 +00:00
|
|
|
|
2016-04-12 16:01:39 +00:00
|
|
|
if (autoshell) {
|
|
|
|
tmp = virGetUserShell(uid);
|
|
|
|
if (tmp) {
|
2016-11-25 08:18:35 +00:00
|
|
|
virStringListFree(shargv);
|
2016-04-12 16:01:39 +00:00
|
|
|
shargvlen = 1;
|
|
|
|
if (VIR_ALLOC_N(shargv[0], shargvlen + 1) < 0) {
|
|
|
|
VIR_FREE(tmp);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
shargv[0] = tmp;
|
|
|
|
shargv[1] = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-04-12 14:18:04 +00:00
|
|
|
if (cmdstr) {
|
|
|
|
if (VIR_REALLOC_N(shargv, shargvlen + 3) < 0)
|
|
|
|
goto cleanup;
|
2019-10-20 11:49:46 +00:00
|
|
|
shargv[shargvlen++] = g_strdup("-c");
|
|
|
|
shargv[shargvlen++] = g_strdup(cmdstr);
|
2016-04-12 14:18:04 +00:00
|
|
|
shargv[shargvlen] = NULL;
|
|
|
|
}
|
|
|
|
|
2016-04-12 14:45:48 +00:00
|
|
|
/* We need to modify the first elementin shargv
|
|
|
|
* so that it has the relative filename and has
|
|
|
|
* a leading '-' to indicate it is a login shell
|
|
|
|
*/
|
|
|
|
shcmd = shargv[0];
|
|
|
|
if (shcmd[0] != '/') {
|
|
|
|
virReportSystemError(errno,
|
|
|
|
_("Shell '%s' should have absolute path"),
|
|
|
|
shcmd);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
tmp = strrchr(shcmd, '/');
|
2019-10-20 11:49:46 +00:00
|
|
|
shargv[0] = g_strdup(tmp);
|
2016-04-12 14:45:48 +00:00
|
|
|
shargv[0][0] = '-';
|
|
|
|
|
2016-04-12 15:52:58 +00:00
|
|
|
/* We're duping the string because the clearenv()
|
|
|
|
* call will shortly release the pointer we get
|
2019-08-01 12:35:56 +00:00
|
|
|
* back from getenv() right here */
|
2019-10-20 11:49:46 +00:00
|
|
|
term = g_strdup(getenv("TERM"));
|
2016-04-12 15:52:58 +00:00
|
|
|
|
virt-login-shell: use single instead of double fork
Note that 'virsh lxc-enter-namespace' must double-fork, for two
reasons: some namespaces can only be done from a single thread,
while virsh is multithreaded; and because virsh can be run in
batch mode where we must not corrupt the namespace of that
execution upon return from the subsidiary command.
When virt-login-shell was first written, it blindly copied from
'virsh lxc-enter-namespace', including the double-fork. But
neither of the reasons for double forking apply to
virt-login-shell (we are single-threaded, and we have nothing to
do after the child completes that would require us to preserve a
namespace), so we can simplify life by using a single fork.
In turn, this will make it easier for a future patch to pass the
child's exit status on to the invoking shell.
In flattening to a single fork, note that closing the fds must
be done after fork, because the parent process still needs to
use fds to control the virConnectPtr; meanwhile, chdir can be
done prior to forking (in fact, it's easier to report errors
on anything attempted before forking).
* tools/virt-login-shell.c (main): Single rather than double fork.
(virLoginShellFini): Delete, by inlining actions instead.
Signed-off-by: Eric Blake <eblake@redhat.com>
2013-12-23 16:15:16 +00:00
|
|
|
/* A fork is required to create new process in correct pid namespace. */
|
virFork: simplify semantics
The old semantics of virFork() violates the priciple of good
usability: it requires the caller to check the pid argument
after use, *even when virFork returned -1*, in order to properly
abort a child process that failed setup done immediately after
fork() - that is, the caller must call _exit() in the child.
While uses in virfile.c did this correctly, uses in 'virsh
lxc-enter-namespace' and 'virt-login-shell' would happily return
from the calling function in both the child and the parent,
leading to very confusing results. [Thankfully, I found the
problem by inspection, and can't actually trigger the double
return on error without an LD_PRELOAD library.]
It is much better if the semantics of virFork are impossible
to abuse. Looking at virFork(), the parent could only ever
return -1 with a non-negative pid if it misused pthread_sigmask,
but this never happens. Up until this patch series, the child
could return -1 with non-negative pid if it fails to set up
signals correctly, but we recently fixed that to make the child
call _exit() at that point instead of forcing the caller to do
it. Thus, the return value and contents of the pid argument are
now redundant (a -1 return now happens only for failure to fork,
a child 0 return only happens for a successful 0 pid, and a
parent 0 return only happens for a successful non-zero pid),
so we might as well return the pid directly rather than an
integer of whether it succeeded or failed; this is also good
from the interface design perspective as users are already
familiar with fork() semantics.
One last change in this patch: before returning the pid directly,
I found cases where using virProcessWait unconditionally on a
cleanup path of a virFork's -1 pid return would be nicer if there
were a way to avoid it overwriting an earlier message. While
such paths are a bit harder to come by with my change to a direct
pid return, I decided to keep the virProcessWait change in this
patch.
* src/util/vircommand.h (virFork): Change signature.
* src/util/vircommand.c (virFork): Guarantee that child will only
return on success, to simplify callers. Return pid rather than
status, now that the situations are always the same.
(virExec): Adjust caller, also avoid open-coding process death.
* src/util/virprocess.c (virProcessWait): Tweak semantics when pid
is -1.
(virProcessRunInMountNamespace): Adjust caller.
* src/util/virfile.c (virFileAccessibleAs, virFileOpenForked)
(virDirCreate): Likewise.
* tools/virt-login-shell.c (main): Likewise.
* tools/virsh-domain.c (cmdLxcEnterNamespace): Likewise.
* tests/commandtest.c (test23): Likewise.
Signed-off-by: Eric Blake <eblake@redhat.com>
2013-12-22 00:54:33 +00:00
|
|
|
if ((cpid = virFork()) < 0)
|
2013-08-08 15:36:31 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (cpid == 0) {
|
virt-login-shell: use single instead of double fork
Note that 'virsh lxc-enter-namespace' must double-fork, for two
reasons: some namespaces can only be done from a single thread,
while virsh is multithreaded; and because virsh can be run in
batch mode where we must not corrupt the namespace of that
execution upon return from the subsidiary command.
When virt-login-shell was first written, it blindly copied from
'virsh lxc-enter-namespace', including the double-fork. But
neither of the reasons for double forking apply to
virt-login-shell (we are single-threaded, and we have nothing to
do after the child completes that would require us to preserve a
namespace), so we can simplify life by using a single fork.
In turn, this will make it easier for a future patch to pass the
child's exit status on to the invoking shell.
In flattening to a single fork, note that closing the fds must
be done after fork, because the parent process still needs to
use fds to control the virConnectPtr; meanwhile, chdir can be
done prior to forking (in fact, it's easier to report errors
on anything attempted before forking).
* tools/virt-login-shell.c (main): Single rather than double fork.
(virLoginShellFini): Delete, by inlining actions instead.
Signed-off-by: Eric Blake <eblake@redhat.com>
2013-12-23 16:15:16 +00:00
|
|
|
int tmpfd;
|
2013-10-09 14:14:34 +00:00
|
|
|
|
virt-login-shell: use single instead of double fork
Note that 'virsh lxc-enter-namespace' must double-fork, for two
reasons: some namespaces can only be done from a single thread,
while virsh is multithreaded; and because virsh can be run in
batch mode where we must not corrupt the namespace of that
execution upon return from the subsidiary command.
When virt-login-shell was first written, it blindly copied from
'virsh lxc-enter-namespace', including the double-fork. But
neither of the reasons for double forking apply to
virt-login-shell (we are single-threaded, and we have nothing to
do after the child completes that would require us to preserve a
namespace), so we can simplify life by using a single fork.
In turn, this will make it easier for a future patch to pass the
child's exit status on to the invoking shell.
In flattening to a single fork, note that closing the fds must
be done after fork, because the parent process still needs to
use fds to control the virConnectPtr; meanwhile, chdir can be
done prior to forking (in fact, it's easier to report errors
on anything attempted before forking).
* tools/virt-login-shell.c (main): Single rather than double fork.
(virLoginShellFini): Delete, by inlining actions instead.
Signed-off-by: Eric Blake <eblake@redhat.com>
2013-12-23 16:15:16 +00:00
|
|
|
for (i = 3; i < openmax; i++) {
|
|
|
|
tmpfd = i;
|
2013-12-23 17:15:48 +00:00
|
|
|
VIR_MASS_CLOSE(tmpfd);
|
|
|
|
}
|
2016-04-12 15:52:58 +00:00
|
|
|
|
|
|
|
clearenv();
|
2019-12-18 17:16:19 +00:00
|
|
|
g_setenv("PATH", "/bin:/usr/bin", TRUE);
|
|
|
|
g_setenv("SHELL", shcmd, TRUE);
|
|
|
|
g_setenv("USER", name, TRUE);
|
|
|
|
g_setenv("LOGNAME", name, TRUE);
|
|
|
|
g_setenv("HOME", homedir, TRUE);
|
2016-04-12 15:52:58 +00:00
|
|
|
if (term)
|
2019-12-18 17:16:19 +00:00
|
|
|
g_setenv("TERM", term, TRUE);
|
2016-04-12 15:52:58 +00:00
|
|
|
|
2016-04-12 14:45:48 +00:00
|
|
|
if (execv(shcmd, (char *const*) shargv) < 0) {
|
virt-login-shell: use single instead of double fork
Note that 'virsh lxc-enter-namespace' must double-fork, for two
reasons: some namespaces can only be done from a single thread,
while virsh is multithreaded; and because virsh can be run in
batch mode where we must not corrupt the namespace of that
execution upon return from the subsidiary command.
When virt-login-shell was first written, it blindly copied from
'virsh lxc-enter-namespace', including the double-fork. But
neither of the reasons for double forking apply to
virt-login-shell (we are single-threaded, and we have nothing to
do after the child completes that would require us to preserve a
namespace), so we can simplify life by using a single fork.
In turn, this will make it easier for a future patch to pass the
child's exit status on to the invoking shell.
In flattening to a single fork, note that closing the fds must
be done after fork, because the parent process still needs to
use fds to control the virConnectPtr; meanwhile, chdir can be
done prior to forking (in fact, it's easier to report errors
on anything attempted before forking).
* tools/virt-login-shell.c (main): Single rather than double fork.
(virLoginShellFini): Delete, by inlining actions instead.
Signed-off-by: Eric Blake <eblake@redhat.com>
2013-12-23 16:15:16 +00:00
|
|
|
virReportSystemError(errno, _("Unable to exec shell %s"),
|
2016-04-12 14:45:48 +00:00
|
|
|
shcmd);
|
2013-12-24 04:07:01 +00:00
|
|
|
virDispatchError(NULL);
|
|
|
|
return errno == ENOENT ? EXIT_ENOENT : EXIT_CANNOT_INVOKE;
|
2013-08-08 15:36:31 +00:00
|
|
|
}
|
|
|
|
}
|
virt-login-shell: use single instead of double fork
Note that 'virsh lxc-enter-namespace' must double-fork, for two
reasons: some namespaces can only be done from a single thread,
while virsh is multithreaded; and because virsh can be run in
batch mode where we must not corrupt the namespace of that
execution upon return from the subsidiary command.
When virt-login-shell was first written, it blindly copied from
'virsh lxc-enter-namespace', including the double-fork. But
neither of the reasons for double forking apply to
virt-login-shell (we are single-threaded, and we have nothing to
do after the child completes that would require us to preserve a
namespace), so we can simplify life by using a single fork.
In turn, this will make it easier for a future patch to pass the
child's exit status on to the invoking shell.
In flattening to a single fork, note that closing the fds must
be done after fork, because the parent process still needs to
use fds to control the virConnectPtr; meanwhile, chdir can be
done prior to forking (in fact, it's easier to report errors
on anything attempted before forking).
* tools/virt-login-shell.c (main): Single rather than double fork.
(virLoginShellFini): Delete, by inlining actions instead.
Signed-off-by: Eric Blake <eblake@redhat.com>
2013-12-23 16:15:16 +00:00
|
|
|
|
2013-12-24 04:07:01 +00:00
|
|
|
/* At this point, the parent is now waiting for the child to exit,
|
|
|
|
* but as that may take a long time, we release resources now. */
|
2014-03-25 06:53:59 +00:00
|
|
|
cleanup:
|
2016-04-12 15:48:19 +00:00
|
|
|
saved_err = virSaveLastError();
|
|
|
|
|
2014-03-05 18:55:27 +00:00
|
|
|
if (nfdlist > 0)
|
|
|
|
for (i = 0; i < nfdlist; i++)
|
|
|
|
VIR_FORCE_CLOSE(fdlist[i]);
|
virt-login-shell: use single instead of double fork
Note that 'virsh lxc-enter-namespace' must double-fork, for two
reasons: some namespaces can only be done from a single thread,
while virsh is multithreaded; and because virsh can be run in
batch mode where we must not corrupt the namespace of that
execution upon return from the subsidiary command.
When virt-login-shell was first written, it blindly copied from
'virsh lxc-enter-namespace', including the double-fork. But
neither of the reasons for double forking apply to
virt-login-shell (we are single-threaded, and we have nothing to
do after the child completes that would require us to preserve a
namespace), so we can simplify life by using a single fork.
In turn, this will make it easier for a future patch to pass the
child's exit status on to the invoking shell.
In flattening to a single fork, note that closing the fds must
be done after fork, because the parent process still needs to
use fds to control the virConnectPtr; meanwhile, chdir can be
done prior to forking (in fact, it's easier to report errors
on anything attempted before forking).
* tools/virt-login-shell.c (main): Single rather than double fork.
(virLoginShellFini): Delete, by inlining actions instead.
Signed-off-by: Eric Blake <eblake@redhat.com>
2013-12-23 16:15:16 +00:00
|
|
|
VIR_FREE(fdlist);
|
|
|
|
if (dom)
|
|
|
|
virDomainFree(dom);
|
|
|
|
if (conn)
|
|
|
|
virConnectClose(conn);
|
2016-11-25 08:18:35 +00:00
|
|
|
virStringListFree(shargv);
|
2016-04-12 14:45:48 +00:00
|
|
|
VIR_FREE(shcmd);
|
2016-04-12 15:52:58 +00:00
|
|
|
VIR_FREE(term);
|
2013-08-08 15:36:31 +00:00
|
|
|
VIR_FREE(name);
|
|
|
|
VIR_FREE(homedir);
|
|
|
|
VIR_FREE(seclabel);
|
|
|
|
VIR_FREE(secmodel);
|
|
|
|
VIR_FREE(groups);
|
2013-12-24 04:07:01 +00:00
|
|
|
|
|
|
|
if (virProcessWait(cpid, &status, true) == 0)
|
|
|
|
virProcessExitWithStatus(status);
|
|
|
|
|
2016-04-12 15:48:19 +00:00
|
|
|
if (saved_err) {
|
|
|
|
virSetError(saved_err);
|
2019-08-01 12:02:57 +00:00
|
|
|
fprintf(stderr, "%s: %s\n", argv[0], virGetLastErrorMessage());
|
2016-04-12 15:48:19 +00:00
|
|
|
}
|
2013-08-08 15:36:31 +00:00
|
|
|
return ret;
|
|
|
|
}
|