From 600c0cd86b51f0715b2583baa09b2a0678000f9f Mon Sep 17 00:00:00 2001
From: Martin Mares <mj@ucw.cz>
Date: Wed, 5 Jun 2024 22:00:59 +0200
Subject: [PATCH] Wrapper: Fix array sizes
---
shipcat-wrapper.c | 74 ++++++++++++++++++++++++++++++----------
shipcat.py | 87 ++++++++++++++++++++++++++++++++---------------
2 files changed, 115 insertions(+), 46 deletions(-)
diff --git a/shipcat-wrapper.c b/shipcat-wrapper.c
index 66864ba..8083a64 100644
--- a/shipcat-wrapper.c
+++ b/shipcat-wrapper.c
@@ -22,6 +22,7 @@
#define NONRET __attribute__((noreturn))
#define UNUSED __attribute__((unused))
+#define ARRAY_SIZE(a) (int)(sizeof(a) / sizeof(*(a)))
static void NONRET die(const char *fmt, ...)
{
@@ -93,6 +94,8 @@ static void get_credentials(void)
if (glen >= (int) sizeof(as_groups))
die("Group list too long");
}
+
+ free(groups);
}
static void switch_ugid(void)
@@ -104,21 +107,10 @@ static void switch_ugid(void)
die("Failed to set user id: %m");
}
-int main(int argc UNUSED, char **argv UNUSED)
+static char **make_args(int argc, char **argv)
{
- sanitize_fds();
- umask(0077);
-
- if (geteuid())
- die("Must be run setuid");
-
- get_credentials();
- switch_ugid();
-
- char *program = DESTDIR "/usr/bin/shipcat";
-
char **args = xmalloc(sizeof(char *) * (5 + argc));
- args[0] = program;
+ args[0] = DESTDIR "/usr/bin/shipcat";
args[1] = "--as-user";
args[2] = as_user;
args[3] = "--as-groups";
@@ -126,14 +118,60 @@ int main(int argc UNUSED, char **argv UNUSED)
for (int i=1; i<argc; i++)
args[4+i] = argv[i];
args[4+argc] = NULL;
+ return args;
+}
- char * const env[] = {
- "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
- NULL
+static char **make_env(void)
+{
+ static char *set_env[] = {
+ "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
+ };
+
+ static char *inherit_env[] = {
+ "TERM=",
};
- if (execve(program, args, env) < 0)
- die("Cannot execute %s: %m", program);
+ char **env = xmalloc(sizeof(char *) * (ARRAY_SIZE(set_env) + ARRAY_SIZE(inherit_env) + 1));
+ char **ep = env;
+
+ for (int i=0; i < ARRAY_SIZE(set_env); i++)
+ *ep++ = set_env[i];
+
+ for (int i=0; environ[i]; i++)
+ {
+ char *e = environ[i];
+ int elen = strlen(e);
+ for (int j=0; j < ARRAY_SIZE(inherit_env); j++)
+ {
+ char *p = inherit_env[j];
+ int plen = strlen(p);
+ if (elen >= plen && !memcmp(e, p, plen))
+ {
+ *ep++ = e;
+ break;
+ }
+ }
+ }
+
+ *ep = NULL;
+ return env;
+}
+
+int main(int argc, char **argv)
+{
+ sanitize_fds();
+ umask(0077);
+
+ if (geteuid())
+ die("Must be run setuid");
+
+ get_credentials();
+ switch_ugid();
+
+ char **args = make_args(argc, argv);
+ char **env = make_env();
+ if (execve(args[0], args, env) < 0)
+ die("Cannot execute %s: %m", args[0]);
die("This must never happen");
}
diff --git a/shipcat.py b/shipcat.py
index 42baeb6..d9122cb 100755
--- a/shipcat.py
+++ b/shipcat.py
@@ -15,6 +15,7 @@ sys.path.append('/tmp/shc/usr/lib/python')
from shipcat.config import GlobalConfig, ContainerConfig, ConfigError
+
def die(msg: str) -> NoReturn:
print(msg, file=sys.stderr)
sys.exit(1)
@@ -24,6 +25,21 @@ def progress(msg: str) -> None:
print(msg, file=sys.stderr, end="", flush=True)
+def run_command(args: List[str], *rest, **kwargs) -> None:
+ res = subprocess.run(args, *rest, **kwargs)
+ if res.returncode != 0:
+ die('Command failed: ' + " ".join(args))
+
+
+def load_config() -> GlobalConfig:
+ try:
+ # FIXME: Path to config
+ return GlobalConfig.load('shipcat.toml')
+ except ConfigError as e:
+ print(e, file=sys.stderr)
+ sys.exit(1)
+
+
def setup_container(args: argparse.Namespace, require_root: bool) -> ContainerConfig:
if os.geteuid() != 0:
die('This program must be run setuid root')
@@ -104,7 +120,7 @@ class SubIds:
return SubIdRange(i, size, user)
-def do_init(args: argparse.Namespace) -> None:
+def cmd_init(args: argparse.Namespace) -> None:
name = args.name
cc = setup_container(args, True)
@@ -114,9 +130,8 @@ def do_init(args: argparse.Namespace) -> None:
progress('already exists\n')
except KeyError:
progress('creating\n')
- subprocess.run(
+ run_command(
['adduser', '--system', '--group', '--gecos', f'Container {name}', '--disabled-password', cc.user_name],
- check=True,
)
pwd = getpwnam(cc.user_name)
uid = pwd.pw_uid
@@ -129,9 +144,8 @@ def do_init(args: argparse.Namespace) -> None:
else:
progress('allocating\n')
sur = subuids.alloc_range(cc.user_name, 65536)
- subprocess.run(
+ run_command(
['usermod', '--add-subuids', f'{sur.first}-{sur.first + sur.count - 1}', cc.user_name],
- check=True,
)
progress('Subgid range: ')
@@ -142,9 +156,8 @@ def do_init(args: argparse.Namespace) -> None:
else:
progress('allocating\n')
sgr = subgids.alloc_range(cc.user_name, 65536)
- subprocess.run(
+ run_command(
['usermod', '--add-subgids', f'{sgr.first}-{sgr.first + sgr.count - 1}', cc.user_name],
- check=True,
)
progress(f'Using user {cc.user_name}, uid {uid}, subuids {sur.first}+{sur.count}, subgids {sgr.first}+{sgr.count}\n')
@@ -175,26 +188,30 @@ def do_init(args: argparse.Namespace) -> None:
progress(f'{ip}\n')
-def do_create(args: argparse.Namespace) -> None:
+def service_action(cc: ContainerConfig, action: str) -> None:
+ run_command(
+ ['systemctl', action, f'shc@{cc.container}.service'],
+ )
+
+
+def cmd_create(args: argparse.Namespace) -> None:
name = args.name
cc = setup_container(args, False)
data_dir = Path(cc.root_dir) / 'data'
ip = socket.gethostbyname(name)
- subprocess.run(
+ run_command(
['podman', 'pull', cc.image],
- check=True,
)
- # FIXME: If the container was running, stop it
+ service_action(cc, 'stop')
- subprocess.run(
+ run_command(
['podman', 'rm', '-if', name],
- check=True,
)
- subprocess.run(
+ run_command(
[
'podman', 'create',
'--name', name,
@@ -208,12 +225,22 @@ def do_create(args: argparse.Namespace) -> None:
'--subgidname', cc.user_name,
cc.image,
],
- check=True,
)
-def do_start(args: argparse.Namespace) -> None:
- pass
+def cmd_start(args: argparse.Namespace) -> None:
+ cc = setup_container(args, False)
+ service_action(cc, 'start')
+
+
+def cmd_stop(args: argparse.Namespace) -> None:
+ cc = setup_container(args, False)
+ service_action(cc, 'stop')
+
+
+def cmd_status(args: argparse.Namespace) -> None:
+ cc = setup_container(args, False)
+ service_action(cc, 'status')
def parse_int_list(s: str) -> List[int]:
@@ -236,17 +263,21 @@ create_parser.add_argument('name', help='name of the container')
start_parser = subparsers.add_parser('start', help='start a container', description='Start a container')
start_parser.add_argument('name', help='name of the container')
+status_parser = subparsers.add_parser('status', help='show container status', description='Show container status')
+status_parser.add_argument('name', help='name of the container')
+
+stop_parser = subparsers.add_parser('stop', help='stop a container', description='Stop a container')
+stop_parser.add_argument('name', help='name of the container')
+
args = parser.parse_args()
-try:
- config = GlobalConfig.load('shipcat.toml')
-except ConfigError as e:
- print(e, file=sys.stderr)
- sys.exit(1)
+actions = {
+ 'create': cmd_create,
+ 'init': cmd_init,
+ 'start': cmd_start,
+ 'status': cmd_status,
+ 'stop': cmd_stop,
+}
-if args.action == 'init':
- do_init(args)
-elif args.action == 'create':
- do_create(args)
-elif args.action == 'start':
- do_start(args)
+config = load_config()
+actions[args.action](args)
--
GitLab