LCOV - code coverage report
Current view: top level - app/shared - fd_cap_chk.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 155 0.0 %
Date: 2025-03-20 12:08:36 Functions: 0 11 0.0 %

          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 : }

Generated by: LCOV version 1.14