]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
setpriv: Add --ptracer, which calls PR_SET_PTRACER
authorGeoffrey Thomas <geofft@ldpreload.com>
Mon, 12 Aug 2024 23:02:05 +0000 (23:02 +0000)
committerGeoffrey Thomas <geofft@ldpreload.com>
Tue, 13 Aug 2024 17:20:28 +0000 (13:20 -0400)
This makes it easier to use a debugger on systems with Yama configured
without making the debugger binary itself privileged.

Signed-off-by: Geoffrey Thomas <geofft@ldpreload.com>
sys-utils/setpriv.1.adoc
sys-utils/setpriv.c

index b281ccb34d32e8c0d48ab6ad5f878ea8564014c4..79c1ec2d07153321e45ba936af618b51cec9c62b 100644 (file)
@@ -78,6 +78,9 @@ Set or clear securebits. The argument is a comma-separated list. The valid secur
 **--pdeathsig keep**|**clear**|*<signal>*::
 Keep, clear or set the parent death signal. Some LSMs, most notably SELinux and AppArmor, clear the signal when the process' credentials change. Using *--pdeathsig keep* will restore the parent death signal after changing credentials to remedy that situation.
 
+*--ptracer* _pid_|**any**|**none**::
+When Yama's restricted ptrace mode is in effect (that is, when _/proc/sys/kernel/yama/ptrace_scope_ is set to 1), allow being traced via **ptrace**(2) by the process with the specified PID, or any process, or no process. See **PR_SET_PTRACER**(2const). (Note that this is not inherited by child processes, though it is preserved across **execve**(2).) This option has no effect when Yama is not enabled or is in a mode other than restricted ptrace.
+
 *--selinux-label* _label_::
 Request a particular SELinux transition (using a transition on exec, not dyntrans). This will fail and cause *setpriv* to abort if SELinux is not in use, and the transition may be ignored or cause *execve*(2) to fail at SELinux's whim. (In particular, this is unlikely to work in conjunction with _no_new_privs_.) This is similar to *runcon*(1).
 
index 4b05431012d1e6e28d2a355567d4c0925c4bffeb..bd188e4dd1dd46a7f5c9cd11076bbcf997a6ddc9 100644 (file)
@@ -87,7 +87,8 @@ struct privctx {
                clear_groups:1,         /* remove groups */
                init_groups:1,          /* initialize groups */
                reset_env:1,            /* reset environment */
-               have_securebits:1;      /* remove groups */
+               have_securebits:1,      /* remove groups */
+               have_ptracer:1;         /* modify ptracer */
 
        /* uids and gids */
        uid_t ruid, euid;
@@ -110,6 +111,9 @@ struct privctx {
        /* parent death signal (<0 clear, 0 nothing, >0 signal) */
        int pdeathsig;
 
+       /* permitted ptracer under Yama mode 1 */
+       long ptracer;
+
        /* LSMs */
        const char *selinux_label;
        const char *apparmor_profile;
@@ -146,6 +150,7 @@ static void __attribute__((__noreturn__)) usage(void)
        fputs(_(" --securebits <bits>         set securebits\n"), out);
        fputs(_(" --pdeathsig keep|clear|<signame>\n"
                "                             set or clear parent death signal\n"), out);
+       fputs(_(" --ptracer <pid>|any|none    allow ptracing from the given process\n"), out);
        fputs(_(" --selinux-label <label>     set SELinux label\n"), out);
        fputs(_(" --apparmor-profile <pr>     set AppArmor profile\n"), out);
        fputs(_(" --landlock-access <access>  add Landlock access\n"), out);
@@ -459,6 +464,17 @@ static void parse_pdeathsig(struct privctx *opts, const char *str)
        }
 }
 
+static void parse_ptracer(struct privctx *opts, const char *str)
+{
+       if (!strcmp(str, "any")) {
+               opts->ptracer = PR_SET_PTRACER_ANY;
+       } else if (!strcmp(str, "none")) {
+               opts->ptracer = 0;
+       } else {
+               opts->ptracer = strtopid_or_err(str, _("failed to parse ptracer pid"));
+       }
+}
+
 static void do_setresuid(const struct privctx *opts)
 {
        uid_t ruid, euid, suid;
@@ -801,6 +817,7 @@ int main(int argc, char **argv)
                CAPBSET,
                SECUREBITS,
                PDEATHSIG,
+               PTRACER,
                SELINUX_LABEL,
                APPARMOR_PROFILE,
                LANDLOCK_ACCESS,
@@ -829,6 +846,7 @@ int main(int argc, char **argv)
                { "bounding-set",     required_argument, NULL, CAPBSET          },
                { "securebits",       required_argument, NULL, SECUREBITS       },
                { "pdeathsig",        required_argument, NULL, PDEATHSIG,       },
+               { "ptracer",          required_argument, NULL, PTRACER,       },
                { "selinux-label",    required_argument, NULL, SELINUX_LABEL    },
                { "apparmor-profile", required_argument, NULL, APPARMOR_PROFILE },
                { "landlock-access",  required_argument, NULL, LANDLOCK_ACCESS  },
@@ -950,6 +968,13 @@ int main(int argc, char **argv)
                                     _("duplicate --keep-pdeathsig option"));
                        parse_pdeathsig(&opts, optarg);
                        break;
+               case PTRACER:
+                       if (opts.have_ptracer)
+                               errx(EXIT_FAILURE,
+                                    _("duplicate --ptracer option"));
+                       opts.have_ptracer = 1;
+                       parse_ptracer(&opts, optarg);
+                       break;
                case LISTCAPS:
                        list_caps = 1;
                        break;
@@ -1126,6 +1151,12 @@ int main(int argc, char **argv)
        if (opts.pdeathsig && prctl(PR_SET_PDEATHSIG, opts.pdeathsig < 0 ? 0 : opts.pdeathsig) != 0)
                err(SETPRIV_EXIT_PRIVERR, _("set parent death signal failed"));
 
+       if (opts.have_ptracer) {
+               if (prctl(PR_SET_PTRACER, opts.ptracer) < 0) {
+                       err(SETPRIV_EXIT_PRIVERR, _("set ptracer"));
+               }
+       }
+
        do_landlock(&opts.landlock);
 
        execvp(argv[optind], argv + optind);