From ae30cf1e3a1216e80fc1fd97b6801cd572e5a017 Mon Sep 17 00:00:00 2001 From: Theodore Dubois Date: Sat, 8 Jun 2024 16:12:46 -0700 Subject: [PATCH] strat -n --- src/strat/strat-pivot.sh | 34 +++++++++++++ src/strat/strat.c | 103 ++++++++++++++++++++++++++++++++++++--- 2 files changed, 130 insertions(+), 7 deletions(-) create mode 100755 src/strat/strat-pivot.sh diff --git a/src/strat/strat-pivot.sh b/src/strat/strat-pivot.sh new file mode 100755 index 00000000..f18a2bc0 --- /dev/null +++ b/src/strat/strat-pivot.sh @@ -0,0 +1,34 @@ +#!/bedrock/libexec/busybox sh + +source /bedrock/share/common-code + +to="${1}" +shift +ensure_legal_stratum_name "${to}" +if ! is_stratum "${to}"; then + abort "no such stratum ${to}" +fi + +from=$(get_attr / stratum) +if [ "${to}" = "${from}" ]; then + abort "already there" +fi + +if [ $# = 0 ]; then + set -- sh +fi + +script=$(cat << 'END' +set -x +to=$1 +from=$2 +shift 2 +/bedrock/libexec/busybox pivot_root /bedrock/strata/${to} /bedrock/strata/${to}/bedrock/strata/${from} +/bedrock/libexec/busybox mount --move /bedrock/strata/${from}/bedrock /tmp +/bedrock/libexec/busybox mount --move /bedrock/strata/${from} /tmp/strata/${from} +/bedrock/libexec/busybox mount --move /bedrock /tmp/strata/${from}/bedrock +/tmp/libexec/busybox mount --move /tmp /bedrock +exec "$@" +END +) +exec unshare --mount /bedrock/libexec/busybox sh -c "$script" . "${to}" "${from}" "$@" diff --git a/src/strat/strat.c b/src/strat/strat.c index af4946d1..fa8cdf1b 100644 --- a/src/strat/strat.c +++ b/src/strat/strat.c @@ -17,12 +17,15 @@ #define _GNU_SOURCE #include +#include #include #include #include #include +#include #include #include +#include #include #include @@ -42,6 +45,11 @@ #endif #define MIN(x, y) (x < y ? x : y) +enum root_mode { + root_mode_chroot, + root_mode_namespace, +}; + /* * Check if this process has the proper CAP_SYS_CHROOT properties. */ @@ -79,11 +87,12 @@ int check_capsyschroot(void) } void parse_args(int argc, char *argv[], int *flag_help, int *flag_restrict, - int *flag_unrestrict, char **param_stratum, char **param_arg0, char ***param_arglist) + int *flag_unrestrict, int *flag_namespace, char **param_stratum, char **param_arg0, char ***param_arglist) { *flag_help = 0; *flag_restrict = 0; *flag_unrestrict = 0; + *flag_namespace = 0; *param_stratum = NULL; *param_arg0 = NULL; *param_arglist = NULL; @@ -103,6 +112,10 @@ void parse_args(int argc, char *argv[], int *flag_help, int *flag_restrict, *flag_unrestrict = 1; argv++; argc--; + } else if (argc > 0 && (strcmp(argv[0], "-n") == 0 || strcmp(argv[0], "--namespace") == 0)) { + *flag_namespace = 1; + argv++; + argc--; } else if (argc > 1 && (strcmp(argv[0], "-a") == 0 || strcmp(argv[0], "--arg0") == 0)) { *param_arg0 = argv[1]; argv += 2; @@ -132,6 +145,7 @@ void print_help(void) "Options:\n" " -r, --restrict disable cross-stratum hooks\n" " -u, --unrestrict do not disable cross-stratum hooks\n" + " -n, --namespace make a new mount namespace with the new stratum at the root, instead of using chroot\n" " -a, --arg0 specify arg0\n" " -h, --help print this message\n" "\n" @@ -375,6 +389,69 @@ int chroot_to_stratum(char *stratum_path) return chroot("."); } +int pivot_root_to_stratum(char *stratum_path, char *current_stratum) +{ + /* + * This moves around mounts in the namespace, ultimately making it + * look like the stratum we are switching to was the init stratum. + * + * This was originally written as a shell script. The shell commands + * are preserved here as comments. + */ + + char src_buf[strlen(stratum_path) + strlen("/bedrock/strata/") + strlen(current_stratum) + 1]; + char dst_buf[strlen(stratum_path) + strlen("/bedrock/strata/") + strlen(current_stratum) + 1]; + + /* unshare --mount */ + if (unshare(CLONE_NEWNS) != 0) { + perror("unshare(CLONE_NEWNS)"); + return -1; + } + if (mount(NULL, "/", NULL, MS_PRIVATE | MS_REC, NULL) != 0) { + perror("mount(/, MS_PRIVATE | MS_REC)"); + return -1; + } + + /* pivot_root /bedrock/strata/${to} /bedrock/strata/${to}/bedrock/strata/${from} */ + sprintf(dst_buf, "%s/bedrock/strata/%s", stratum_path, current_stratum); + if (syscall(SYS_pivot_root, stratum_path, dst_buf) != 0) { + perror("pivot_root"); + return -1; + } + + /* mount --move /bedrock/strata/${from}/bedrock /tmp */ + sprintf(src_buf, "/bedrock/strata/%s/bedrock", current_stratum); + sprintf(dst_buf, "/tmp"); + if (mount(src_buf, dst_buf, NULL, MS_MOVE, NULL) != 0) { + perror("move /bedrock/strata/current/bedrock -> /tmp"); + return -1; + } + + /* mount --move /bedrock/strata/${from} /tmp/strata/${from} */ + sprintf(src_buf, "/bedrock/strata/%s", current_stratum); + sprintf(dst_buf, "/tmp/strata/%s", current_stratum); + if (mount(src_buf, dst_buf, NULL, MS_MOVE, NULL) != 0) { + perror("move /bedrock/strata/current -> /tmp/strata/current"); + return -1; + } + + /* mount --move /bedrock /tmp/strata/${from}/bedrock */ + sprintf(src_buf, "/bedrock"); + sprintf(dst_buf, "/tmp/strata/%s/bedrock", current_stratum); + if (mount(src_buf, dst_buf, NULL, MS_MOVE, NULL) != 0) { + perror("move /bedrock -> /tmp/strata/current/bedrock"); + return -1; + } + + /* mount --move /tmp /bedrock */ + if (mount("/tmp", "/bedrock", NULL, MS_MOVE, NULL) != 0) { + perror("move /tmp -> /bedrock"); + return -1; + } + + return 0; +} + /* * Like execvp(), but skips certain $PATH entries */ @@ -437,7 +514,7 @@ void execv_skip(char *file, char *argv[], char *skip) return; } -int switch_stratum(const char *alias) +int switch_stratum(const char *alias, enum root_mode mode) { /* * local alias indicates no stratum change is needed. Hard code this @@ -530,9 +607,20 @@ int switch_stratum(const char *alias) strcpy(stratum_path, STRATA_ROOT); strcat(stratum_path, stratum); - if (chroot_to_stratum(stratum_path) < 0) { - fprintf(stderr, "strat: unable chroot() to %s\n", stratum_path); - return -1; + switch (mode) { + case root_mode_chroot: + if (chroot_to_stratum(stratum_path) < 0) { + fprintf(stderr, "strat: unable chroot() to %s\n", stratum_path); + return -1; + } + break; + + case root_mode_namespace: + if (pivot_root_to_stratum(stratum_path, current_stratum) < 0) { + fprintf(stderr, "strat: unable to create namespace for stratum %s\n", stratum); + return -1; + } + break; } /* @@ -564,11 +652,12 @@ int main(int argc, char *argv[]) int flag_help; int flag_restrict; int flag_unrestrict; + int flag_namespace; char *param_stratum; char *param_arg0; char **param_arglist; parse_args(argc, argv, &flag_help, &flag_restrict, &flag_unrestrict, - ¶m_stratum, ¶m_arg0, ¶m_arglist); + &flag_namespace, ¶m_stratum, ¶m_arg0, ¶m_arglist); if (flag_help) { print_help(); @@ -585,7 +674,7 @@ int main(int argc, char *argv[]) return 1; } - if (switch_stratum(param_stratum) < 0) { + if (switch_stratum(param_stratum, flag_namespace) < 0) { return 1; }