Line data Source code
1 : #define _GNU_SOURCE
2 : #include "fd_cap_chk.h"
3 :
4 : #include "fd_file_util.h"
5 : #include "../../util/fd_util.h"
6 :
7 : #include <unistd.h>
8 : #include <stdarg.h>
9 : #include <stdio.h>
10 : #include <errno.h>
11 : #include <sys/syscall.h>
12 : #include <sys/resource.h>
13 : #include <linux/capability.h>
14 :
15 : #define MAX_ERROR_ENTRIES (16UL)
16 0 : #define MAX_ERROR_MSG_LEN (256UL)
17 :
18 : struct fd_cap_chk_private {
19 : ulong err_cnt;
20 : char err[ MAX_ERROR_ENTRIES ][ MAX_ERROR_MSG_LEN ];
21 : };
22 :
23 : __attribute__ ((format (printf, 2, 3)))
24 : static void
25 : fd_cap_chk_add_error( fd_cap_chk_t * chk,
26 : char const * fmt,
27 0 : ... ) {
28 0 : if( FD_UNLIKELY( chk->err_cnt>=MAX_ERROR_ENTRIES ) ) FD_LOG_ERR(( "too many capability checks failed" ));
29 :
30 0 : va_list ap;
31 0 : va_start( ap, fmt );
32 0 : int result = vsnprintf( chk->err[ chk->err_cnt++ ], MAX_ERROR_MSG_LEN, fmt, ap );
33 0 : if( FD_UNLIKELY( result<0 ) ) FD_LOG_ERR(( "vsnprintf failed (%i-%s)", errno, fd_io_strerror( errno ) ));
34 0 : else if ( FD_UNLIKELY( (ulong)result>=MAX_ERROR_MSG_LEN ) ) FD_LOG_ERR(( "vsnprintf truncated message" ));
35 0 : va_end( ap );
36 0 : }
37 :
38 : void *
39 0 : fd_cap_chk_new( void * shmem ) {
40 0 : fd_cap_chk_t * chk = (fd_cap_chk_t *)shmem;
41 0 : chk->err_cnt = 0UL;
42 0 : return chk;
43 0 : }
44 :
45 : fd_cap_chk_t *
46 0 : fd_cap_chk_join( void * shchk ) {
47 0 : return (fd_cap_chk_t *)shchk;
48 0 : }
49 :
50 : void
51 : fd_cap_chk_root( fd_cap_chk_t * chk,
52 : char const * name,
53 0 : char const * reason ) {
54 0 : if( FD_LIKELY( !getuid() ) ) return;
55 0 : fd_cap_chk_add_error( chk, "%s ... process requires root to %s", name, reason );
56 0 : }
57 :
58 : static int
59 0 : has_capability( uint capability ) {
60 0 : struct __user_cap_data_struct capdata[2];
61 0 : struct __user_cap_header_struct capheader = {
62 0 : .pid = 0,
63 0 : .version = _LINUX_CAPABILITY_VERSION_3
64 0 : };
65 :
66 0 : if( FD_UNLIKELY( syscall( SYS_capget, &capheader, capdata ) ) ) FD_LOG_ERR(( "capget syscall failed (%i-%s)", errno, fd_io_strerror( errno ) ) );
67 0 : fd_msan_unpoison( capdata, sizeof(capdata) );
68 0 : return !!(capdata[ 0 ].effective & (1U << capability));
69 0 : }
70 :
71 : FD_FN_CONST static char const *
72 0 : cap_cstr( uint capability ) {
73 0 : switch( capability ) {
74 0 : case CAP_CHOWN: return "CAP_CHOWN";
75 0 : case CAP_DAC_OVERRIDE: return "CAP_DAC_OVERRIDE";
76 0 : case CAP_DAC_READ_SEARCH: return "CAP_DAC_READ_SEARCH";
77 0 : case CAP_FOWNER: return "CAP_FOWNER";
78 0 : case CAP_FSETID: return "CAP_FSETID";
79 0 : case CAP_KILL: return "CAP_KILL";
80 0 : case CAP_SETGID: return "CAP_SETGID";
81 0 : case CAP_SETUID: return "CAP_SETUID";
82 0 : case CAP_SETPCAP: return "CAP_SETPCAP";
83 0 : case CAP_LINUX_IMMUTABLE: return "CAP_LINUX_IMMUTABLE";
84 0 : case CAP_NET_BIND_SERVICE: return "CAP_NET_BIND_SERVICE";
85 0 : case CAP_NET_BROADCAST: return "CAP_NET_BROADCAST";
86 0 : case CAP_NET_ADMIN: return "CAP_NET_ADMIN";
87 0 : case CAP_NET_RAW: return "CAP_NET_RAW";
88 0 : case CAP_IPC_LOCK: return "CAP_IPC_LOCK";
89 0 : case CAP_IPC_OWNER: return "CAP_IPC_OWNER";
90 0 : case CAP_SYS_MODULE: return "CAP_SYS_MODULE";
91 0 : case CAP_SYS_RAWIO: return "CAP_SYS_RAWIO";
92 0 : case CAP_SYS_CHROOT: return "CAP_SYS_CHROOT";
93 0 : case CAP_SYS_PTRACE: return "CAP_SYS_PTRACE";
94 0 : case CAP_SYS_PACCT: return "CAP_SYS_PACCT";
95 0 : case CAP_SYS_ADMIN: return "CAP_SYS_ADMIN";
96 0 : case CAP_SYS_BOOT: return "CAP_SYS_BOOT";
97 0 : case CAP_SYS_NICE: return "CAP_SYS_NICE";
98 0 : case CAP_SYS_RESOURCE: return "CAP_SYS_RESOURCE";
99 0 : case CAP_SYS_TIME: return "CAP_SYS_TIME";
100 0 : case CAP_SYS_TTY_CONFIG: return "CAP_SYS_TTY_CONFIG";
101 0 : case CAP_MKNOD: return "CAP_MKNOD";
102 0 : case CAP_LEASE: return "CAP_LEASE";
103 0 : case CAP_AUDIT_WRITE: return "CAP_AUDIT_WRITE";
104 0 : case CAP_AUDIT_CONTROL: return "CAP_AUDIT_CONTROL";
105 0 : case CAP_SETFCAP: return "CAP_SETFCAP";
106 0 : case CAP_MAC_OVERRIDE: return "CAP_MAC_OVERRIDE";
107 0 : case CAP_MAC_ADMIN: return "CAP_MAC_ADMIN";
108 0 : case CAP_SYSLOG: return "CAP_SYSLOG";
109 0 : case CAP_WAKE_ALARM: return "CAP_WAKE_ALARM";
110 0 : case CAP_BLOCK_SUSPEND: return "CAP_BLOCK_SUSPEND";
111 0 : case CAP_AUDIT_READ: return "CAP_AUDIT_READ";
112 0 : #ifdef CAP_PERFMON
113 0 : case CAP_PERFMON: return "CAP_PERFMON";
114 0 : #endif
115 0 : #ifdef CAP_BPF
116 0 : case CAP_BPF: return "CAP_BPF";
117 0 : #endif
118 0 : #ifdef CAP_CHECKPOINT_RESTORE
119 0 : case CAP_CHECKPOINT_RESTORE: return "CAP_CHECKPOINT_RESTORE";
120 0 : #endif
121 0 : default: return "UNKNOWN";
122 0 : }
123 0 : }
124 :
125 : void
126 : fd_cap_chk_cap( fd_cap_chk_t * chk,
127 : char const * name,
128 : uint capability,
129 0 : char const * reason ) {
130 0 : if( FD_LIKELY( has_capability( capability ) ) ) return;
131 0 : fd_cap_chk_add_error( chk, "%s ... process requires capability `%s` to %s", name, cap_cstr( capability ), reason );
132 0 : }
133 :
134 : FD_FN_CONST static char *
135 0 : rlimit_cstr( int resource ) {
136 0 : switch( resource ) {
137 0 : case RLIMIT_CPU: return "RLIMIT_CPU";
138 0 : case RLIMIT_FSIZE: return "RLIMIT_FSIZE";
139 0 : case RLIMIT_DATA: return "RLIMIT_DATA";
140 0 : case RLIMIT_STACK: return "RLIMIT_STACK";
141 0 : case RLIMIT_CORE: return "RLIMIT_CORE";
142 0 : case RLIMIT_RSS: return "RLIMIT_RSS";
143 0 : case RLIMIT_NOFILE: return "RLIMIT_NOFILE";
144 0 : case RLIMIT_AS: return "RLIMIT_AS";
145 0 : case RLIMIT_NPROC: return "RLIMIT_NPROC";
146 0 : case RLIMIT_MEMLOCK: return "RLIMIT_MEMLOCK";
147 0 : case RLIMIT_LOCKS: return "RLIMIT_LOCKS";
148 0 : case RLIMIT_SIGPENDING: return "RLIMIT_SIGPENDING";
149 0 : case RLIMIT_MSGQUEUE: return "RLIMIT_MSGQUEUE";
150 0 : case RLIMIT_NICE: return "RLIMIT_NICE";
151 0 : case RLIMIT_RTPRIO: return "RLIMIT_RTPRIO";
152 0 : case RLIMIT_RTTIME: return "RLIMIT_RTTIME";
153 0 : case RLIMIT_NLIMITS: return "RLIMIT_NLIMITS";
154 0 : default: return "UNKNOWN";
155 0 : }
156 0 : }
157 :
158 : #ifdef __GLIBC__
159 : typedef __rlimit_resource_t fd_rlimit_res_t;
160 : #else /* non-glibc */
161 : typedef int fd_rlimit_res_t;
162 : #endif /* __GLIBC__ */
163 :
164 : void
165 : fd_cap_chk_raise_rlimit( fd_cap_chk_t * chk,
166 : char const * name,
167 : int _resource,
168 : ulong limit,
169 0 : char const * reason ) {
170 0 : fd_rlimit_res_t resource = (fd_rlimit_res_t)_resource;
171 :
172 0 : struct rlimit rlim;
173 0 : if( FD_UNLIKELY( getrlimit( resource, &rlim ) ) ) FD_LOG_ERR(( "getrlimit failed (%i-%s)", errno, fd_io_strerror( errno ) ));
174 0 : if( FD_LIKELY( rlim.rlim_cur>=limit ) ) return;
175 :
176 0 : if( FD_LIKELY( !has_capability( CAP_SYS_RESOURCE ) ) ) {
177 0 : if( FD_LIKELY( resource==RLIMIT_NICE && has_capability( CAP_SYS_NICE ) ) ) {
178 : /* Special case, if we have CAP_SYS_NICE we can set any nice
179 : value without raising the limit with CAP_SYS_RESOURCE. */
180 0 : return;
181 0 : }
182 :
183 0 : if( FD_UNLIKELY( resource==RLIMIT_NICE ) ) {
184 0 : fd_cap_chk_add_error( chk,
185 0 : "%s ... process requires capability `%s` or `%s` to %s",
186 0 : name,
187 0 : cap_cstr( CAP_SYS_RESOURCE ),
188 0 : cap_cstr( CAP_SYS_NICE ),
189 0 : reason );
190 0 : } else {
191 0 : fd_cap_chk_add_error( chk,
192 0 : "%s ... process requires capability `%s` to %s",
193 0 : name,
194 0 : cap_cstr( CAP_SYS_RESOURCE ),
195 0 : reason );
196 0 : }
197 0 : } else {
198 0 : if( FD_UNLIKELY( resource==RLIMIT_NOFILE ) ) {
199 : /* If we have CAP_SYS_RESOURCE, it may not be enough to increase
200 : RLIMIT_NOFILE. Will still result in EPERM if /proc/sys/fs/nr_open
201 : is below the desired number. */
202 0 : uint file_nr;
203 0 : if( FD_UNLIKELY( -1==fd_file_util_read_uint( "/proc/sys/fs/nr_open", &file_nr ) ) ) {
204 0 : FD_LOG_ERR(( "failed to read `/proc/sys/fs/nr_open` (%i-%s)", errno, fd_io_strerror( errno ) ));
205 0 : }
206 :
207 0 : if( FD_UNLIKELY( file_nr<limit ) )
208 0 : FD_LOG_ERR(( "Firedancer requires `/proc/sys/fs/nr_open` to be at least %lu "
209 0 : "to raise RLIMIT_NOFILE, but it is %u. Please either increase "
210 0 : "the sysctl or run `fdctl configure init sysctl` which will do "
211 0 : "it for you.", limit, file_nr ));
212 0 : }
213 0 : rlim.rlim_cur = limit;
214 0 : rlim.rlim_max = limit;
215 0 : if( FD_UNLIKELY( setrlimit( resource, &rlim ) ) ) FD_LOG_ERR(( "setrlimit failed (%i-%s) for resource %s", errno, fd_io_strerror( errno ), rlimit_cstr( _resource ) ));
216 0 : }
217 0 : }
218 :
219 : ulong
220 0 : fd_cap_chk_err_cnt( fd_cap_chk_t const * chk ) {
221 0 : return chk->err_cnt;
222 0 : }
223 :
224 : char const *
225 : fd_cap_chk_err( fd_cap_chk_t const * chk,
226 0 : ulong idx ) {
227 0 : return chk->err[ idx ];
228 0 : }
|