Skip to content
Snippets Groups Projects
Commit b50346f6 authored by Martin Mareš's avatar Martin Mareš
Browse files

More setuid fun

parent c93baef6
No related branches found
No related tags found
No related merge requests found
...@@ -8,17 +8,15 @@ ...@@ -8,17 +8,15 @@
#define _GNU_SOURCE #define _GNU_SOURCE
#include <errno.h>
#include <fcntl.h> #include <fcntl.h>
#include <limits.h> #include <limits.h>
#include <pwd.h>
#include <grp.h> #include <grp.h>
#include <pwd.h>
#include <stdarg.h> #include <stdarg.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <syslog.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <unistd.h> #include <unistd.h>
...@@ -66,17 +64,17 @@ static void get_credentials(void) ...@@ -66,17 +64,17 @@ static void get_credentials(void)
struct passwd *pwd = getpwuid(uid); struct passwd *pwd = getpwuid(uid);
if (!pwd) if (!pwd)
die("You don't exist"); die("You don't exist");
int ulen = snprintf(as_user, sizeof(as_user), "%s", pwd->pw_name); int ulen = snprintf(as_user, sizeof(as_user), "%u", uid);
if (ulen >= (int) sizeof(as_user)) if (ulen >= (int) sizeof(as_user))
die("User name too long"); die("UID too long");
gid_t gid = getgid(); gid_t gid = getgid();
struct group *grp = getgrgid(gid); struct group *grp = getgrgid(gid);
if (!grp) if (!grp)
die("Your group does not exist"); die("Your group does not exist");
int glen = snprintf(as_groups, sizeof(as_groups), "%s", grp->gr_name); int glen = snprintf(as_groups, sizeof(as_groups), "%u", gid);
if (glen >= (int) sizeof(as_groups)) if (glen >= (int) sizeof(as_groups))
die("Group name too long"); die("GID too long");
int max_groups = getgroups(0, NULL); int max_groups = getgroups(0, NULL);
if (max_groups < 0) if (max_groups < 0)
...@@ -91,10 +89,7 @@ static void get_credentials(void) ...@@ -91,10 +89,7 @@ static void get_credentials(void)
{ {
if (groups[i] == gid) if (groups[i] == gid)
continue; continue;
struct group *sgrp = getgrgid(groups[i]); glen += snprintf(as_groups + glen, (int) sizeof(as_groups) - glen, ",%u", groups[i]);
if (!sgrp)
die("Secondary group %u does not exist", groups[i]);
glen += snprintf(as_groups + glen, (int) sizeof(as_groups) - glen, ",%s", sgrp->gr_name);
if (glen >= (int) sizeof(as_groups)) if (glen >= (int) sizeof(as_groups))
die("Group list too long"); die("Group list too long");
} }
......
...@@ -10,8 +10,10 @@ import subprocess ...@@ -10,8 +10,10 @@ import subprocess
import sys import sys
from typing import NoReturn, List, Optional from typing import NoReturn, List, Optional
from shipcat.config import GlobalConfig, ContainerConfig, ConfigError # FIXME
sys.path.append('/tmp/shc/usr/lib/python')
from shipcat.config import GlobalConfig, ContainerConfig, ConfigError
def die(msg: str) -> NoReturn: def die(msg: str) -> NoReturn:
print(msg, file=sys.stderr) print(msg, file=sys.stderr)
...@@ -22,22 +24,41 @@ def progress(msg: str) -> None: ...@@ -22,22 +24,41 @@ def progress(msg: str) -> None:
print(msg, file=sys.stderr, end="", flush=True) print(msg, file=sys.stderr, end="", flush=True)
def load_container_config(name: str) -> ContainerConfig: def setup_container(args: argparse.Namespace, require_root: bool) -> ContainerConfig:
if os.geteuid() != 0:
die('This program must be run setuid root')
try: try:
cc = ContainerConfig.load(config, name) cc = ContainerConfig.load(config, args.name)
except ConfigError as e: except ConfigError as e:
die(str(e)) die(str(e))
if args.as_user is None:
uid = 0
else:
uid = args.as_user
if require_root and uid != 0:
die('This operation must be performed by root')
if not check_rights(cc, uid, args.as_groups):
die('You do not have permission to operate this container')
return cc return cc
def is_root() -> bool: def check_rights(cc: ContainerConfig, uid: int, gids: List[int]) -> bool:
return os.geteuid() == 0 if uid == 0:
return True
if uid in cc.allowed_users:
return True
def assert_root() -> None: for gid in gids:
if not is_root(): if gid in cc.allowed_groups:
die('This operation must be performed by root') return True
return False
@dataclass(frozen=True, order=True) @dataclass(frozen=True, order=True)
...@@ -85,8 +106,7 @@ class SubIds: ...@@ -85,8 +106,7 @@ class SubIds:
def do_init(args: argparse.Namespace) -> None: def do_init(args: argparse.Namespace) -> None:
name = args.name name = args.name
cc = load_container_config(name) cc = setup_container(args, True)
assert_root()
progress(f'User {cc.user_name}: ') progress(f'User {cc.user_name}: ')
try: try:
...@@ -157,8 +177,7 @@ def do_init(args: argparse.Namespace) -> None: ...@@ -157,8 +177,7 @@ def do_init(args: argparse.Namespace) -> None:
def do_create(args: argparse.Namespace) -> None: def do_create(args: argparse.Namespace) -> None:
name = args.name name = args.name
cc = load_container_config(name) cc = setup_container(args, False)
assert_root()
data_dir = Path(cc.root_dir) / 'data' data_dir = Path(cc.root_dir) / 'data'
ip = socket.gethostbyname(name) ip = socket.gethostbyname(name)
...@@ -197,19 +216,25 @@ def do_start(args: argparse.Namespace) -> None: ...@@ -197,19 +216,25 @@ def do_start(args: argparse.Namespace) -> None:
pass pass
def parse_int_list(s: str) -> List[int]:
return list(map(int, s.split(',')))
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(
description='FIXME', description='FIXME',
) )
parser.add_argument('--as-user', type=int, metavar='UID', help='user ID of requesting user')
parser.add_argument('--as-groups', type=parse_int_list, metavar='GID,...', help='group IDs of requesting user (primary first)')
subparsers = parser.add_subparsers(help='action to perform', dest='action', required=True, metavar='ACTION') subparsers = parser.add_subparsers(help='action to perform', dest='action', required=True, metavar='ACTION')
init_parser = subparsers.add_parser('init', help='initialize a new container', description='Initialize a new container. Should be called by root.') init_parser = subparsers.add_parser('init', help='initialize a new container', description='Initialize a new container. Should be called by root.')
init_parser.add_argument('name', help='Name of the container') init_parser.add_argument('name', help='name of the container')
create_parser = subparsers.add_parser('create', help='create a container from an image', description='Create a container from an image.') create_parser = subparsers.add_parser('create', help='create a container from an image', description='Create a container from an image.')
create_parser.add_argument('name', help='Name of the container') 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 = subparsers.add_parser('start', help='start a container', description='Start a container')
start_parser.add_argument('name', help='Name of the container') start_parser.add_argument('name', help='name of the container')
args = parser.parse_args() args = parser.parse_args()
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment