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 : return !!(capdata[ 0 ].effective & (1U << capability));
68 0 : }
69 :
70 : FD_FN_CONST static char const *
71 0 : cap_cstr( uint capability ) {
72 0 : switch( capability ) {
73 0 : case CAP_CHOWN: return "CAP_CHOWN";
74 0 : case CAP_DAC_OVERRIDE: return "CAP_DAC_OVERRIDE";
75 0 : case CAP_DAC_READ_SEARCH: return "CAP_DAC_READ_SEARCH";
76 0 : case CAP_FOWNER: return "CAP_FOWNER";
77 0 : case CAP_FSETID: return "CAP_FSETID";
78 0 : case CAP_KILL: return "CAP_KILL";
79 0 : case CAP_SETGID: return "CAP_SETGID";
80 0 : case CAP_SETUID: return "CAP_SETUID";
81 0 : case CAP_SETPCAP: return "CAP_SETPCAP";
82 0 : case CAP_LINUX_IMMUTABLE: return "CAP_LINUX_IMMUTABLE";
83 0 : case CAP_NET_BIND_SERVICE: return "CAP_NET_BIND_SERVICE";
84 0 : case CAP_NET_BROADCAST: return "CAP_NET_BROADCAST";
85 0 : case CAP_NET_ADMIN: return "CAP_NET_ADMIN";
86 0 : case CAP_NET_RAW: return "CAP_NET_RAW";
87 0 : case CAP_IPC_LOCK: return "CAP_IPC_LOCK";
88 0 : case CAP_IPC_OWNER: return "CAP_IPC_OWNER";
89 0 : case CAP_SYS_MODULE: return "CAP_SYS_MODULE";
90 0 : case CAP_SYS_RAWIO: return "CAP_SYS_RAWIO";
91 0 : case CAP_SYS_CHROOT: return "CAP_SYS_CHROOT";
92 0 : case CAP_SYS_PTRACE: return "CAP_SYS_PTRACE";
93 0 : case CAP_SYS_PACCT: return "CAP_SYS_PACCT";
94 0 : case CAP_SYS_ADMIN: return "CAP_SYS_ADMIN";
95 0 : case CAP_SYS_BOOT: return "CAP_SYS_BOOT";
96 0 : case CAP_SYS_NICE: return "CAP_SYS_NICE";
97 0 : case CAP_SYS_RESOURCE: return "CAP_SYS_RESOURCE";
98 0 : case CAP_SYS_TIME: return "CAP_SYS_TIME";
99 0 : case CAP_SYS_TTY_CONFIG: return "CAP_SYS_TTY_CONFIG";
100 0 : case CAP_MKNOD: return "CAP_MKNOD";
101 0 : case CAP_LEASE: return "CAP_LEASE";
102 0 : case CAP_AUDIT_WRITE: return "CAP_AUDIT_WRITE";
103 0 : case CAP_AUDIT_CONTROL: return "CAP_AUDIT_CONTROL";
104 0 : case CAP_SETFCAP: return "CAP_SETFCAP";
105 0 : case CAP_MAC_OVERRIDE: return "CAP_MAC_OVERRIDE";
106 0 : case CAP_MAC_ADMIN: return "CAP_MAC_ADMIN";
107 0 : case CAP_SYSLOG: return "CAP_SYSLOG";
108 0 : case CAP_WAKE_ALARM: return "CAP_WAKE_ALARM";
109 0 : case CAP_BLOCK_SUSPEND: return "CAP_BLOCK_SUSPEND";
110 0 : case CAP_AUDIT_READ: return "CAP_AUDIT_READ";
111 0 : #ifdef CAP_PERFMON
112 0 : case CAP_PERFMON: return "CAP_PERFMON";
113 0 : #endif
114 0 : #ifdef CAP_BPF
115 0 : case CAP_BPF: return "CAP_BPF";
116 0 : #endif
117 0 : #ifdef CAP_CHECKPOINT_RESTORE
118 0 : case CAP_CHECKPOINT_RESTORE: return "CAP_CHECKPOINT_RESTORE";
119 0 : #endif
120 0 : default: return "UNKNOWN";
121 0 : }
122 0 : }
123 :
124 : void
125 : fd_cap_chk_cap( fd_cap_chk_t * chk,
126 : char const * name,
127 : uint capability,
128 0 : char const * reason ) {
129 0 : if( FD_LIKELY( has_capability( capability ) ) ) return;
130 0 : fd_cap_chk_add_error( chk, "%s ... process requires capability `%s` to %s", name, cap_cstr( capability ), reason );
131 0 : }
132 :
133 : FD_FN_CONST static char *
134 0 : rlimit_cstr( int resource ) {
135 0 : switch( resource ) {
136 0 : case RLIMIT_CPU: return "RLIMIT_CPU";
137 0 : case RLIMIT_FSIZE: return "RLIMIT_FSIZE";
138 0 : case RLIMIT_DATA: return "RLIMIT_DATA";
139 0 : case RLIMIT_STACK: return "RLIMIT_STACK";
140 0 : case RLIMIT_CORE: return "RLIMIT_CORE";
141 0 : case RLIMIT_RSS: return "RLIMIT_RSS";
142 0 : case RLIMIT_NOFILE: return "RLIMIT_NOFILE";
143 0 : case RLIMIT_AS: return "RLIMIT_AS";
144 0 : case RLIMIT_NPROC: return "RLIMIT_NPROC";
145 0 : case RLIMIT_MEMLOCK: return "RLIMIT_MEMLOCK";
146 0 : case RLIMIT_LOCKS: return "RLIMIT_LOCKS";
147 0 : case RLIMIT_SIGPENDING: return "RLIMIT_SIGPENDING";
148 0 : case RLIMIT_MSGQUEUE: return "RLIMIT_MSGQUEUE";
149 0 : case RLIMIT_NICE: return "RLIMIT_NICE";
150 0 : case RLIMIT_RTPRIO: return "RLIMIT_RTPRIO";
151 0 : case RLIMIT_RTTIME: return "RLIMIT_RTTIME";
152 0 : case RLIMIT_NLIMITS: return "RLIMIT_NLIMITS";
153 0 : default: return "UNKNOWN";
154 0 : }
155 0 : }
156 :
157 : #ifdef __GLIBC__
158 : typedef __rlimit_resource_t fd_rlimit_res_t;
159 : #else /* non-glibc */
160 : typedef int fd_rlimit_res_t;
161 : #endif /* __GLIBC__ */
162 :
163 : void
164 : fd_cap_chk_raise_rlimit( fd_cap_chk_t * chk,
165 : char const * name,
166 : int _resource,
167 : ulong limit,
168 0 : char const * reason ) {
169 0 : fd_rlimit_res_t resource = (fd_rlimit_res_t)_resource;
170 :
171 0 : struct rlimit rlim;
172 0 : if( FD_UNLIKELY( getrlimit( resource, &rlim ) ) ) FD_LOG_ERR(( "getrlimit failed (%i-%s)", errno, fd_io_strerror( errno ) ));
173 0 : if( FD_LIKELY( rlim.rlim_cur>=limit ) ) return;
174 :
175 0 : if( FD_LIKELY( !has_capability( CAP_SYS_RESOURCE ) ) ) {
176 0 : if( FD_LIKELY( resource==RLIMIT_NICE && has_capability( CAP_SYS_NICE ) ) ) {
177 : /* Special case, if we have CAP_SYS_NICE we can set any nice
178 : value without raising the limit with CAP_SYS_RESOURCE. */
179 0 : return;
180 0 : }
181 :
182 0 : if( FD_UNLIKELY( resource==RLIMIT_NICE ) ) {
183 0 : fd_cap_chk_add_error( chk,
184 0 : "%s ... process requires capability `%s` or `%s` to %s",
185 0 : name,
186 0 : cap_cstr( CAP_SYS_RESOURCE ),
187 0 : cap_cstr( CAP_SYS_NICE ),
188 0 : reason );
189 0 : } else {
190 0 : fd_cap_chk_add_error( chk,
191 0 : "%s ... process requires capability `%s` to %s",
192 0 : name,
193 0 : cap_cstr( CAP_SYS_RESOURCE ),
194 0 : reason );
195 0 : }
196 0 : } else {
197 0 : if( FD_UNLIKELY( resource==RLIMIT_NOFILE ) ) {
198 : /* If we have CAP_SYS_RESOURCE, it may not be enough to increase
199 : RLIMIT_NOFILE. Will still result in EPERM if /proc/sys/fs/nr_open
200 : is below the desired number. */
201 0 : uint file_nr;
202 0 : if( FD_UNLIKELY( -1==fd_file_util_read_uint( "/proc/sys/fs/nr_open", &file_nr ) ) ) {
203 0 : FD_LOG_ERR(( "failed to read `/proc/sys/fs/nr_open` (%i-%s)", errno, fd_io_strerror( errno ) ));
204 0 : }
205 :
206 0 : if( FD_UNLIKELY( file_nr<limit ) )
207 0 : FD_LOG_ERR(( "Firedancer requires `/proc/sys/fs/nr_open` to be at least %lu "
208 0 : "to raise RLIMIT_NOFILE, but it is %u. Please either increase "
209 0 : "the sysctl or run `fdctl configure init sysctl` which will do "
210 0 : "it for you.", limit, file_nr ));
211 0 : }
212 0 : rlim.rlim_cur = limit;
213 0 : rlim.rlim_max = limit;
214 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 ) ));
215 0 : }
216 0 : }
217 :
218 : ulong
219 0 : fd_cap_chk_err_cnt( fd_cap_chk_t const * chk ) {
220 0 : return chk->err_cnt;
221 0 : }
222 :
223 : char const *
224 : fd_cap_chk_err( fd_cap_chk_t const * chk,
225 0 : ulong idx ) {
226 0 : return chk->err[ idx ];
227 0 : }
|