1010#include < signal.h>
1111#include < stdlib.h>
1212#include < string.h>
13+ #include < sys/capability.h>
1314#include < sys/ioctl.h>
15+ #include < sys/prctl.h>
1416#include < sys/stat.h>
1517#include < sys/types.h>
1618#include < sys/wait.h>
@@ -97,12 +99,33 @@ static void set_selinux_context(uid_t uid, const char* seinfo, const char* pkgna
9799 }
98100}
99101
102+ static void retain_ptrace_capability () {
103+ __user_cap_header_struct hdr = {};
104+ hdr.version = _LINUX_CAPABILITY_VERSION_3;
105+ hdr.pid = 0 ;
106+ __user_cap_data_struct data[2 ] = {};
107+ const unsigned int idx = CAP_SYS_PTRACE >> 5 ;
108+ const unsigned int bit = 1u << (CAP_SYS_PTRACE & 31 );
109+ data[idx].effective = bit;
110+ data[idx].permitted = bit;
111+ data[idx].inheritable = bit;
112+ if (capset (&hdr, data) != 0 ) error (1 , errno, " capset(CAP_SYS_PTRACE) failed" );
113+ if (prctl (PR_CAP_AMBIENT , PR_CAP_AMBIENT_RAISE , CAP_SYS_PTRACE , 0 , 0 ) != 0 ) {
114+ error (1 , errno, " PR_CAP_AMBIENT_RAISE(CAP_SYS_PTRACE) failed" );
115+ }
116+ }
117+
100118static void become_app_and_exec (uid_t app_id, const char * seinfo, const char * pkgname,
101- const std::string& data_dir, char ** cmd_argv) {
119+ const std::string& data_dir, char ** cmd_argv,
120+ bool keep_ptrace) {
102121 std::vector<gid_t > gids = get_supplementary_gids (app_id);
103122 if (setgroups (gids.size (), gids.data ()) == -1 ) error (1 , errno, " setgroups failed" );
123+ if (keep_ptrace && prctl (PR_SET_KEEPCAPS , 1 , 0 , 0 , 0 ) != 0 ) {
124+ error (1 , errno, " PR_SET_KEEPCAPS failed" );
125+ }
104126 if (setresgid (app_id, app_id, app_id) == -1 ) error (1 , errno, " setresgid failed" );
105127 if (setresuid (app_id, app_id, app_id) == -1 ) error (1 , errno, " setresuid failed" );
128+ if (keep_ptrace) retain_ptrace_capability ();
106129
107130 set_selinux_context (app_id, seinfo, pkgname);
108131
@@ -145,7 +168,7 @@ static void write_all(int fd, const char* buf, size_t len) {
145168}
146169
147170static int run_on_pty (uid_t app_id, const char * seinfo, const char * pkgname,
148- const std::string& data_dir, char ** cmd_argv) {
171+ const std::string& data_dir, char ** cmd_argv, bool keep_ptrace ) {
149172 int ref_tty = isatty (STDOUT_FILENO ) ? STDOUT_FILENO : STDIN_FILENO ;
150173 struct termios ref_attr;
151174 bool have_attr = (tcgetattr (ref_tty, &ref_attr) == 0 );
@@ -169,7 +192,7 @@ static int run_on_pty(uid_t app_id, const char* seinfo, const char* pkgname,
169192 dup2 (slave, STDOUT_FILENO );
170193 dup2 (slave, STDERR_FILENO );
171194 if (slave > STDERR_FILENO ) close (slave);
172- become_app_and_exec (app_id, seinfo, pkgname, data_dir, cmd_argv);
195+ become_app_and_exec (app_id, seinfo, pkgname, data_dir, cmd_argv, keep_ptrace );
173196 _exit (127 );
174197 }
175198
@@ -228,18 +251,24 @@ static int run_on_pty(uid_t app_id, const char* seinfo, const char* pkgname,
228251}
229252
230253int main (int argc, char * argv[]) {
231- if (argc < 2 ) {
232- error (1 , 0 , " usage: jrunas <package-name> [--user <id>] [<command> [<args>]]" );
254+ const char * usage =
255+ " usage: jrunas [--ptrace] <package-name> [--user <id>] [<command> [<args>]]" ;
256+
257+ int argi = 1 ;
258+ bool keep_ptrace = false ;
259+ if (argi < argc && strcmp (argv[argi], " --ptrace" ) == 0 ) {
260+ keep_ptrace = true ;
261+ argi++;
233262 }
234263
235- char * pkgname = argv[ 1 ] ;
236- int cmd_argv_offset = 2 ;
264+ if (argi >= argc) error ( 1 , 0 , " %s " , usage) ;
265+ char * pkgname = argv[argi++] ;
237266
238267 int userId = 0 ;
239- if (argc >= 4 && strcmp (argv[2 ], " --user" ) == 0 ) {
240- userId = atoi (argv[3 ]);
268+ if (argc - argi >= 2 && strcmp (argv[argi ], " --user" ) == 0 ) {
269+ userId = atoi (argv[argi + 1 ]);
241270 if (userId < 0 ) error (1 , 0 , " negative user id: %d" , userId);
242- cmd_argv_offset += 2 ;
271+ argi += 2 ;
243272 }
244273
245274 bool is_root = (getuid () == AID_ROOT );
@@ -264,12 +293,12 @@ int main(int argc, char* argv[]) {
264293 std::string data_dir = " /data/user/" + std::to_string (userId) + " /" + pkgname;
265294 check_data_path (pkgname, data_dir.c_str (), userAppId);
266295
267- char ** cmd_argv = argv + cmd_argv_offset ;
296+ char ** cmd_argv = argv + argi ;
268297
269298 if (isatty (STDIN_FILENO ) || isatty (STDOUT_FILENO )) {
270- return run_on_pty (userAppId, info.seinfo .c_str (), pkgname, data_dir, cmd_argv);
299+ return run_on_pty (userAppId, info.seinfo .c_str (), pkgname, data_dir, cmd_argv, keep_ptrace );
271300 }
272301
273- become_app_and_exec (userAppId, info.seinfo .c_str (), pkgname, data_dir, cmd_argv);
302+ become_app_and_exec (userAppId, info.seinfo .c_str (), pkgname, data_dir, cmd_argv, keep_ptrace );
274303 return 1 ;
275304}
0 commit comments