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