From 48d051e6ad302493f51c202134612049b966276c Mon Sep 17 00:00:00 2001 From: Thomas Walker Lynch Date: Mon, 22 Sep 2025 16:44:43 +0000 Subject: [PATCH] patch release.sh --- developer/tool/release | 3 +- release/shell/Man_In_Grey | 146 +++++++++++++------------------------- release/shell/diag_perm | 110 ++++++++++++++++++++++++++++ tool_shared/bespoke/vl | 18 +++++ 4 files changed, 179 insertions(+), 98 deletions(-) create mode 100755 release/shell/diag_perm create mode 100755 tool_shared/bespoke/vl diff --git a/developer/tool/release b/developer/tool/release index e5b4a8f..b2835c2 100755 --- a/developer/tool/release +++ b/developer/tool/release @@ -54,10 +54,11 @@ install -m 0755 "$PY_INNER_SRC" "${REL_PY}/executor_inner.py" install -m 0644 "$PY_PLANNER_SRC" "${REL_PY}/Planner.py" install -m 0755 "$WRAP_SRC" "${REL_SH}/Man_In_Grey" -install -m 0755 "$DIAG_PERM" "${REL_SH}/Man_In_Grey" +install -m 0755 "$DIAG_PERM" "${REL_SH}/diag_perm" echo "released to: ${REL_DIR}" echo " arch : ${REL_ARCH}/man_in_grey_apply" echo " py : ${REL_PY}/" echo " shell: ${REL_SH}/Man_In_Grey" +echo " shell: ${REL_SH}/diag_perm" diff --git a/release/shell/Man_In_Grey b/release/shell/Man_In_Grey index f73f61d..2ad1606 100755 --- a/release/shell/Man_In_Grey +++ b/release/shell/Man_In_Grey @@ -1,110 +1,62 @@ #!/usr/bin/env bash -# Diagnose Man_In_Grey privilege flow: user → gasket (setuid) → python inner +# man_in_grey — canonical entrypoint for Man_In_Grey +# - Resolves repo → release dirs +# - Requires the privileged gasket (single code path) +# - Always invokes the Python orchestrator with --apply-cmd set -euo pipefail -# --- locate repo root (best effort) --- -script_afp="$(realpath "${BASH_SOURCE[0]}")" -REPO_HOME="$(cd "$(dirname "$script_afp")/.." && pwd -P 2>/dev/null || pwd -P)" +# --- resolve this script’s absolute path (portable) --- +_this="${BASH_SOURCE[0]:-$0}" -# --- arch normalizer (same mapping we use elsewhere) --- -_arch_raw=$(uname -m | tr '[:upper:]' '[:lower:]') -case "$_arch_raw" in - amd64|x64) _arch="x86_64" ;; - x86_64) _arch="x86_64" ;; - i386|i486|i586|i686) _arch="i686" ;; - arm64|aarch64) _arch="aarch64" ;; - armv7l) _arch="armv7l" ;; - armv6l) _arch="armv6l" ;; - riscv64) _arch="riscv64" ;; - ppc64le|powerpc64le) _arch="ppc64le" ;; - s390x) _arch="s390x" ;; - *) _arch="$_arch_raw" ;; -esac - -# Allow overriding via env/args -REL_BASE="${REPO_HOME}/tool_shared/third_party/Man_In_Grey/release" -REL_BASE="${REL_BASE:-${REPO_HOME}/release}" -GASKET="${1:-${REL_BASE}/${_arch}/man_in_grey_apply}" -WRAP="${2:-${REL_BASE}/shell/Man_In_Grey}" - -echo "== who/where ==" -echo "user: $(id -un) (uid=$(id -u)) groups: $(id -nG)" -echo "pwd : $(pwd)" -echo - -echo "== paths ==" -echo "wrapper: $WRAP" -echo "gasket : $GASKET" -echo - -echo "== wrapper sanity ==" -if [[ -x "$WRAP" ]]; then - head -n 1 "$WRAP" | sed 's/^/shebang: /' - echo "exec path: $(command -v "$WRAP" || echo '(not in PATH)')" +if command -v realpath >/dev/null 2>&1; then + _this_abs="$(realpath -- "$_this")" else - echo "!! wrapper missing/non-exec" + if _tmp="$(readlink -f -- "$_this" 2>/dev/null)"; then + _this_abs="${_tmp}" + else + _dir="$(cd "$(dirname -- "$_this")" && pwd -P)" + _base="$(basename -- "$_this")" + _this_abs="${_dir}/${_base}" + fi fi -echo -echo "== gasket file sanity ==" -if [[ -e "$GASKET" ]]; then - ls -l "$GASKET" - # setuid bit present? - if [[ -u "$GASKET" ]]; then echo "setuid: YES"; else echo "setuid: NO"; fi - # mount options - mp="$(dirname "$(readlink -f "$GASKET")")" - echo "mount: $(findmnt -no TARGET,FSTYPE,OPTIONS "$mp" 2>/dev/null || echo 'findmnt not available')" +_shell_dir="$(cd "$(dirname -- "$_this_abs")" && pwd -P)" # .../release/shell +_release_dir="$(cd "${_shell_dir}/.." && pwd -P)" # .../release +_repo_root="$(cd "${_release_dir}/.." && pwd -P)" # repo root + +# --- orchestrator path (prefer released copy) --- +_py_release="${_release_dir}/python3/Man_In_Grey.py" +_py_dev="${_repo_root}/developer/source/Man_In_Grey.py" +if [[ -f "$_py_release" ]]; then + _py_entry="$_py_release" +elif [[ -f "$_py_dev" ]]; then + _py_entry="$_py_dev" else - echo "!! gasket not found" + echo "error: Man_In_Grey.py not found in release/python3/ or developer/source/" >&2 + exit 2 fi -echo -echo "== gasket self-report (--print-flags) ==" -if [[ -x "$GASKET" ]]; then - "$GASKET" --print-flags || true -else - echo "skip (no gasket)" -fi -echo - -echo "== python inner EUID test ==" -PYTEST="/tmp/mig_ids_$$.py" -cat >"$PYTEST"<<'PY' -import os, sys, grp, pwd -u = os.getuid(); e = os.geteuid() -name = pwd.getpwuid(u).pw_name if u>=0 else "?" -print(f"py.real_uid={u} ({name})") -print(f"py.effective_uid={e}") -print("py.groups=", os.getgroups()) -sys.exit(0) -PY -chmod 0644 "$PYTEST" - -if [[ -x "$GASKET" ]]; then - echo "running: $GASKET --inner $PYTEST" - "$GASKET" --inner "$PYTEST" || true -else - echo "skip (no gasket)" -fi -rm -f "$PYTEST" -echo +# --- arch normalize (uname -m → release/) --- +_arch_raw="$(uname -m | tr '[:upper:]' '[:lower:]')" +case "$_arch_raw" in + amd64|x64) _arch="x86_64" ;; + x86_64) _arch="x86_64" ;; + i386|i486|i586|i686) _arch="i686" ;; + arm64|aarch64) _arch="aarch64";; + armv7l) _arch="armv7l" ;; + armv6l) _arch="armv6l" ;; + riscv64) _arch="riscv64";; + ppc64le|powerpc64le) _arch="ppc64le";; + s390x) _arch="s390x" ;; + *) _arch="$_arch_raw" ;; +esac -echo "== wrapper execution trace (dry) ==" -if [[ -x "$WRAP" ]]; then - echo "+ bash -x \"$WRAP\" --phase-2-sanity2-then-stop --stage dummy (trace only)" - set +e - bash -x "$WRAP" --phase-2-sanity2-then-stop --stage dummy 1>/dev/null 2>&1 | sed 's/^/TRACE: /' || true - set -e -else - echo "skip (no wrapper)" +_gasket="${_release_dir}/${_arch}/man_in_grey_apply" +if [[ ! -x "$_gasket" ]]; then + echo "error: gasket missing: ${_gasket}" >&2 + echo "hint: from \$REPO_HOME/developer: run compile then release" >&2 + exit 2 fi -echo -echo "== verdict (rules of thumb) ==" -cat <<'EOT' -- If --print-flags shows "flag.this_process_privileged=0": the setuid bit is not taking effect (check mount nosuid, file perms, or wrong gasket path). -- If --print-flags is 1 but python euid is 0: privileges are flowing OK; problems are likely in the shell wrapper bypassing the gasket. -- If --print-flags is 1 but python euid is NOT 0: something is stripping EUID across exec (rare: no_new_privs or an LSM). Check `grep NoNewPrivs /proc/$$/status` from the caller shell and try running outside wrappers. -- If wrapper trace never execs the gasket: fix the wrapper to always call the gasket. -- If you are root and see a 'not in sudo' refusal: update gasket policy to allow root (special-case UID 0). -EOT +# --- run orchestrator (single code path via gasket) --- +exec "${PYTHON:-python3}" "$_py_entry" --apply-cmd "$_gasket" "$@" diff --git a/release/shell/diag_perm b/release/shell/diag_perm new file mode 100755 index 0000000..f73f61d --- /dev/null +++ b/release/shell/diag_perm @@ -0,0 +1,110 @@ +#!/usr/bin/env bash +# Diagnose Man_In_Grey privilege flow: user → gasket (setuid) → python inner +set -euo pipefail + +# --- locate repo root (best effort) --- +script_afp="$(realpath "${BASH_SOURCE[0]}")" +REPO_HOME="$(cd "$(dirname "$script_afp")/.." && pwd -P 2>/dev/null || pwd -P)" + +# --- arch normalizer (same mapping we use elsewhere) --- +_arch_raw=$(uname -m | tr '[:upper:]' '[:lower:]') +case "$_arch_raw" in + amd64|x64) _arch="x86_64" ;; + x86_64) _arch="x86_64" ;; + i386|i486|i586|i686) _arch="i686" ;; + arm64|aarch64) _arch="aarch64" ;; + armv7l) _arch="armv7l" ;; + armv6l) _arch="armv6l" ;; + riscv64) _arch="riscv64" ;; + ppc64le|powerpc64le) _arch="ppc64le" ;; + s390x) _arch="s390x" ;; + *) _arch="$_arch_raw" ;; +esac + +# Allow overriding via env/args +REL_BASE="${REPO_HOME}/tool_shared/third_party/Man_In_Grey/release" +REL_BASE="${REL_BASE:-${REPO_HOME}/release}" +GASKET="${1:-${REL_BASE}/${_arch}/man_in_grey_apply}" +WRAP="${2:-${REL_BASE}/shell/Man_In_Grey}" + +echo "== who/where ==" +echo "user: $(id -un) (uid=$(id -u)) groups: $(id -nG)" +echo "pwd : $(pwd)" +echo + +echo "== paths ==" +echo "wrapper: $WRAP" +echo "gasket : $GASKET" +echo + +echo "== wrapper sanity ==" +if [[ -x "$WRAP" ]]; then + head -n 1 "$WRAP" | sed 's/^/shebang: /' + echo "exec path: $(command -v "$WRAP" || echo '(not in PATH)')" +else + echo "!! wrapper missing/non-exec" +fi +echo + +echo "== gasket file sanity ==" +if [[ -e "$GASKET" ]]; then + ls -l "$GASKET" + # setuid bit present? + if [[ -u "$GASKET" ]]; then echo "setuid: YES"; else echo "setuid: NO"; fi + # mount options + mp="$(dirname "$(readlink -f "$GASKET")")" + echo "mount: $(findmnt -no TARGET,FSTYPE,OPTIONS "$mp" 2>/dev/null || echo 'findmnt not available')" +else + echo "!! gasket not found" +fi +echo + +echo "== gasket self-report (--print-flags) ==" +if [[ -x "$GASKET" ]]; then + "$GASKET" --print-flags || true +else + echo "skip (no gasket)" +fi +echo + +echo "== python inner EUID test ==" +PYTEST="/tmp/mig_ids_$$.py" +cat >"$PYTEST"<<'PY' +import os, sys, grp, pwd +u = os.getuid(); e = os.geteuid() +name = pwd.getpwuid(u).pw_name if u>=0 else "?" +print(f"py.real_uid={u} ({name})") +print(f"py.effective_uid={e}") +print("py.groups=", os.getgroups()) +sys.exit(0) +PY +chmod 0644 "$PYTEST" + +if [[ -x "$GASKET" ]]; then + echo "running: $GASKET --inner $PYTEST" + "$GASKET" --inner "$PYTEST" || true +else + echo "skip (no gasket)" +fi +rm -f "$PYTEST" +echo + +echo "== wrapper execution trace (dry) ==" +if [[ -x "$WRAP" ]]; then + echo "+ bash -x \"$WRAP\" --phase-2-sanity2-then-stop --stage dummy (trace only)" + set +e + bash -x "$WRAP" --phase-2-sanity2-then-stop --stage dummy 1>/dev/null 2>&1 | sed 's/^/TRACE: /' || true + set -e +else + echo "skip (no wrapper)" +fi + +echo +echo "== verdict (rules of thumb) ==" +cat <<'EOT' +- If --print-flags shows "flag.this_process_privileged=0": the setuid bit is not taking effect (check mount nosuid, file perms, or wrong gasket path). +- If --print-flags is 1 but python euid is 0: privileges are flowing OK; problems are likely in the shell wrapper bypassing the gasket. +- If --print-flags is 1 but python euid is NOT 0: something is stripping EUID across exec (rare: no_new_privs or an LSM). Check `grep NoNewPrivs /proc/$$/status` from the caller shell and try running outside wrappers. +- If wrapper trace never execs the gasket: fix the wrapper to always call the gasket. +- If you are root and see a 'not in sudo' refusal: update gasket policy to allow root (special-case UID 0). +EOT diff --git a/tool_shared/bespoke/vl b/tool_shared/bespoke/vl new file mode 100755 index 0000000..2c968d3 --- /dev/null +++ b/tool_shared/bespoke/vl @@ -0,0 +1,18 @@ +#!/usr/bin/env bash +script_afp=$(realpath "${BASH_SOURCE[0]}") +# vl 'vertical list' + +# Check if the command is provided +if [ -z "$1" ]; then + echo "Usage: vl [args...]" + exit 1 +fi + +# Capture the command and its arguments +cmd=$1 +shift + +# Run the command with the remaining arguments and replace colons or spaces with newlines +"$cmd" "$@" | tr ' :' '\n' + +exit 0 -- 2.20.1