Line data Source code
1 : /* This is a pretty strange wrapper...
2 :
3 : We want to support "F5" debugging from, eg, VS code. To do that, VS
4 : code has an agent on our development box running as the user. It is
5 : not root and has no capabilities. That agent forks a child process
6 : that then exec's "gdb", which then loads and runs our program.
7 :
8 : We need to run our program as root sometimes. You might imagine a few
9 : ways to do that,
10 :
11 : (a) Run the agent as root? Not supported by VS code. It's probably
12 : feasible to make changes to the agent code itself to get this
13 : working but it seems difficult.
14 : (b) Run gdb as root? The agent won't launch it as root, but we
15 : could for example setuid the gdb binary itself. This doesn't
16 : work quite right because it prevents VS code from communicating
17 : with GDB, eg, to pause execution. See this bug
18 : https://github.com/microsoft/vscode-cpptools/issues/4243
19 : (c) Run the program as root, eg, using setuid? This isn't really
20 : possible. A non-root GDB will not attach to a root program for
21 : security reasons.
22 :
23 : So we can't really get the program running as root in any scenario.
24 : What else can we do? Just run it with all capabilities... that's what
25 : this program does.
26 :
27 : The sequence here is that, VS code sets this binary as it's "gdb"
28 : path, then, when launched this program,
29 :
30 : (1) Fork off a sudo process which `setcaps` the current binary to
31 : have all capabilities.
32 : (2) The parent waits for the sudo process, then reruns itself
33 : again to have all capabilities
34 : (3) Now running with capabilities, we do a `CAP_AMBIENT_RAISE` so
35 : that capabilities survive a `/bin/gdb` invocation which would
36 : normally strip them for security reasons.
37 : (4) Now we finally exec `/bin/gdb` with the arguments provided by
38 : the IDE. */
39 : #define _GNU_SOURCE
40 :
41 : #include "../../util/fd_util.h"
42 :
43 : #include <stdlib.h>
44 : #include <unistd.h>
45 : #include <stdlib.h>
46 : #include <string.h>
47 : #include <stdio.h>
48 : #include <sys/types.h>
49 : #include <sys/prctl.h>
50 : #include <sys/syscall.h>
51 : #include <sys/xattr.h>
52 : #include <sys/wait.h>
53 : #include <linux/limits.h>
54 : #include <linux/capability.h>
55 :
56 : static int
57 0 : has_all_capabilities( void ) {
58 0 : struct __user_cap_header_struct capheader;
59 0 : struct __user_cap_data_struct capdata[2];
60 :
61 0 : capheader.version = _LINUX_CAPABILITY_VERSION_3;
62 0 : capheader.pid = 0;
63 0 : FD_TEST( syscall( SYS_capget, &capheader, &capdata ) >= 0 );
64 0 : return
65 0 : capdata[0].permitted == 0xFFFFFFFF &&
66 0 : ( capdata[1].permitted & 0x000001FF ) == 0x000001FF;
67 0 : }
68 :
69 : static void
70 0 : raise_all_capabilities( void ) {
71 0 : struct __user_cap_header_struct capheader;
72 0 : struct __user_cap_data_struct capdata[2];
73 :
74 0 : capheader.version = _LINUX_CAPABILITY_VERSION_3;
75 0 : capheader.pid = 0;
76 0 : FD_TEST( syscall( SYS_capget, &capheader, &capdata ) >= 0 );
77 :
78 0 : capdata[0].effective = 0xFFFFFFFF;
79 0 : capdata[0].inheritable = 0xFFFFFFFF;
80 0 : capdata[1].effective = 0xFFFFFFFF;
81 0 : capdata[1].inheritable = 0xFFFFFFFF;
82 0 : FD_TEST( syscall(SYS_capset, &capheader, &capdata) >= 0 );
83 :
84 0 : for ( int cap = 0; cap <= CAP_LAST_CAP; cap++ )
85 0 : FD_TEST( !prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, cap, 0, 0) );
86 0 : }
87 :
88 : static void
89 0 : self_exe( char * path ) {
90 0 : long count = readlink( "/proc/self/exe", path, PATH_MAX );
91 0 : FD_TEST( count >= 0 && count < PATH_MAX );
92 0 : path[ count ] = '\0';
93 0 : }
94 :
95 : int
96 : main( int argc,
97 : char ** argv ) {
98 : fd_boot( &argc, &argv );
99 :
100 : if( FD_UNLIKELY( argc > 1 && !strcmp( argv[1], "--setcap" ) ) ) {
101 : struct vfs_cap_data {
102 : __le32 magic_etc;
103 : struct {
104 : __le32 permitted;
105 : __le32 inheritable;
106 : } data[2];
107 : } cap_data;
108 :
109 : cap_data.magic_etc = VFS_CAP_REVISION_2;
110 : cap_data.data[0].permitted = 0xFFFFFFFF;
111 : cap_data.data[0].inheritable = 0xFFFFFFFF;
112 : cap_data.data[1].permitted = 0xFFFFFFFF;
113 : cap_data.data[1].inheritable = 0xFFFFFFFF;
114 :
115 : char self_path[ PATH_MAX ];
116 : self_exe( self_path );
117 : FD_TEST( !setxattr( self_path, "security.capability", &cap_data, sizeof(cap_data), 0) );
118 : } else {
119 : if( FD_UNLIKELY( !has_all_capabilities() ) ) {
120 : if ( FD_UNLIKELY( argc > 1 && !strcmp( argv[1], "--withcap" ) ) ) FD_LOG_ERR(( "missing capabilities" ));
121 :
122 : char self_path[ PATH_MAX ];
123 : self_exe( self_path );
124 :
125 : pid_t child = fork();
126 : FD_TEST( child >= 0 );
127 : if( child == 0 ) execv( "/bin/sudo", (char *[]){ "sudo", self_path, "--setcap", NULL } );
128 : else {
129 : int wstatus;
130 : FD_TEST( waitpid( child, &wstatus, 0 ) >= 0 );
131 : FD_TEST( WIFEXITED( wstatus ) && !WEXITSTATUS( wstatus ) );
132 : char self_path[ PATH_MAX ];
133 : self_exe( self_path );
134 : FD_TEST( execv( self_path, (char *[]){ "fddbg", "--withcap", NULL } ) >= 0 );
135 : }
136 : } else {
137 : raise_all_capabilities();
138 :
139 : char * args[ 32 ];
140 : int start = argc > 1 && !strcmp( argv[1], "--withcap" ) ? 2 : 1;
141 : args[0] = "/bin/gdb";
142 : for( int i=start; i<argc; i++ ) args[i-start+1] = argv[i];
143 : args[ argc-start+1 ] = NULL;
144 :
145 : FD_TEST( execv( "/bin/gdb", args ) >= 0 );
146 : }
147 : }
148 : }
|