LCOV - code coverage report
Current view: top level - lib/process - restrict.c (source / functions) Hit Total Coverage
Test: lcov.info Lines: 21 55 38.2 %
Date: 2021-11-24 03:28:48 Functions: 2 4 50.0 %

          Line data    Source code
       1             : /* Copyright (c) 2003-2004, Roger Dingledine
       2             :  * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
       3             :  * Copyright (c) 2007-2021, The Tor Project, Inc. */
       4             : /* See LICENSE for licensing information */
       5             : 
       6             : /**
       7             :  * \file restrict.c
       8             :  * \brief Drop privileges from the current process.
       9             :  **/
      10             : 
      11             : #include "orconfig.h"
      12             : #include "lib/process/restrict.h"
      13             : #include "lib/intmath/cmp.h"
      14             : #include "lib/log/log.h"
      15             : #include "lib/log/util_bug.h"
      16             : #include "lib/net/socket.h"
      17             : 
      18             : #ifdef HAVE_SYS_MMAN_H
      19             : #include <sys/mman.h>
      20             : #endif
      21             : #include <errno.h>
      22             : #include <stdlib.h>
      23             : #include <string.h>
      24             : 
      25             : /* We only use the linux prctl for now. There is no Win32 support; this may
      26             :  * also work on various BSD systems and Mac OS X - send testing feedback!
      27             :  *
      28             :  * On recent Gnu/Linux kernels it is possible to create a system-wide policy
      29             :  * that will prevent non-root processes from attaching to other processes
      30             :  * unless they are the parent process; thus gdb can attach to programs that
      31             :  * they execute but they cannot attach to other processes running as the same
      32             :  * user. The system wide policy may be set with the sysctl
      33             :  * kernel.yama.ptrace_scope or by inspecting
      34             :  * /proc/sys/kernel/yama/ptrace_scope and it is 1 by default on Ubuntu 11.04.
      35             :  *
      36             :  * This ptrace scope will be ignored on Gnu/Linux for users with
      37             :  * CAP_SYS_PTRACE and so it is very likely that root will still be able to
      38             :  * attach to the Tor process.
      39             :  */
      40             : /** Attempt to disable debugger attachment: return 1 on success, -1 on
      41             :  * failure, and 0 if we don't know how to try on this platform. */
      42             : int
      43           4 : tor_disable_debugger_attach(void)
      44             : {
      45           4 :   int r = -1;
      46           4 :   log_debug(LD_CONFIG,
      47             :             "Attempting to disable debugger attachment to Tor for "
      48             :             "unprivileged users.");
      49             : #if defined(__linux__) && defined(HAVE_SYS_PRCTL_H) \
      50             :   && defined(HAVE_PRCTL) && defined(PR_SET_DUMPABLE)
      51             : #define TRIED_TO_DISABLE
      52             :   r = prctl(PR_SET_DUMPABLE, 0);
      53             : #elif defined(__APPLE__) && defined(PT_DENY_ATTACH)
      54             : #define TRIED_TO_ATTACH
      55             :   r = ptrace(PT_DENY_ATTACH, 0, 0, 0);
      56             : #endif /* defined(__linux__) && defined(HAVE_SYS_PRCTL_H) ... || ... */
      57             : 
      58             :   // XXX: TODO - Mac OS X has dtrace and this may be disabled.
      59             :   // XXX: TODO - Windows probably has something similar
      60             : #ifdef TRIED_TO_DISABLE
      61             :   if (r == 0) {
      62             :     log_debug(LD_CONFIG,"Debugger attachment disabled for "
      63             :               "unprivileged users.");
      64             :     return 1;
      65             :   } else {
      66             :     log_warn(LD_CONFIG, "Unable to disable debugger attaching: %s",
      67             :              strerror(errno));
      68             :   }
      69             : #endif /* defined(TRIED_TO_DISABLE) */
      70             : #undef TRIED_TO_DISABLE
      71           4 :   return r;
      72             : }
      73             : 
      74             : #if defined(HAVE_MLOCKALL) && HAVE_DECL_MLOCKALL && defined(RLIMIT_MEMLOCK)
      75             : #define HAVE_UNIX_MLOCKALL
      76             : #endif
      77             : 
      78             : #ifdef HAVE_UNIX_MLOCKALL
      79             : /** Attempt to raise the current and max rlimit to infinity for our process.
      80             :  * This only needs to be done once and can probably only be done when we have
      81             :  * not already dropped privileges.
      82             :  */
      83             : static int
      84           0 : tor_set_max_memlock(void)
      85             : {
      86             :   /* Future consideration for Windows is probably SetProcessWorkingSetSize
      87             :    * This is similar to setting the memory rlimit of RLIMIT_MEMLOCK
      88             :    * https://msdn.microsoft.com/en-us/library/ms686234(VS.85).aspx
      89             :    */
      90             : 
      91           0 :   struct rlimit limit;
      92             : 
      93             :   /* RLIM_INFINITY is -1 on some platforms. */
      94           0 :   limit.rlim_cur = RLIM_INFINITY;
      95           0 :   limit.rlim_max = RLIM_INFINITY;
      96             : 
      97           0 :   if (setrlimit(RLIMIT_MEMLOCK, &limit) == -1) {
      98           0 :     if (errno == EPERM) {
      99           0 :       log_warn(LD_GENERAL, "You appear to lack permissions to change memory "
     100             :                            "limits. Are you root?");
     101             :     }
     102           0 :     log_warn(LD_GENERAL, "Unable to raise RLIMIT_MEMLOCK: %s",
     103             :              strerror(errno));
     104           0 :     return -1;
     105             :   }
     106             : 
     107             :   return 0;
     108             : }
     109             : #endif /* defined(HAVE_UNIX_MLOCKALL) */
     110             : 
     111             : /** Attempt to lock all current and all future memory pages.
     112             :  * This should only be called once and while we're privileged.
     113             :  * Like mlockall() we return 0 when we're successful and -1 when we're not.
     114             :  * Unlike mlockall() we return 1 if we've already attempted to lock memory.
     115             :  */
     116             : int
     117           0 : tor_mlockall(void)
     118             : {
     119           0 :   static int memory_lock_attempted = 0;
     120             : 
     121           0 :   if (memory_lock_attempted) {
     122             :     return 1;
     123             :   }
     124             : 
     125           0 :   memory_lock_attempted = 1;
     126             : 
     127             :   /*
     128             :    * Future consideration for Windows may be VirtualLock
     129             :    * VirtualLock appears to implement mlock() but not mlockall()
     130             :    *
     131             :    * https://msdn.microsoft.com/en-us/library/aa366895(VS.85).aspx
     132             :    */
     133             : 
     134             : #ifdef HAVE_UNIX_MLOCKALL
     135           0 :   if (tor_set_max_memlock() == 0) {
     136           0 :     log_debug(LD_GENERAL, "RLIMIT_MEMLOCK is now set to RLIM_INFINITY.");
     137             :   }
     138             : 
     139           0 :   if (mlockall(MCL_CURRENT|MCL_FUTURE) == 0) {
     140           0 :     log_info(LD_GENERAL, "Insecure OS paging is effectively disabled.");
     141           0 :     return 0;
     142             :   } else {
     143           0 :     if (errno == ENOSYS) {
     144             :       /* Apple - it's 2009! I'm looking at you. Grrr. */
     145           0 :       log_notice(LD_GENERAL, "It appears that mlockall() is not available on "
     146             :                              "your platform.");
     147           0 :     } else if (errno == EPERM) {
     148           0 :       log_notice(LD_GENERAL, "It appears that you lack the permissions to "
     149             :                              "lock memory. Are you root?");
     150             :     }
     151           0 :     log_notice(LD_GENERAL, "Unable to lock all current and future memory "
     152             :                            "pages: %s", strerror(errno));
     153           0 :     return -1;
     154             :   }
     155             : #else /* !defined(HAVE_UNIX_MLOCKALL) */
     156             :   log_warn(LD_GENERAL, "Unable to lock memory pages. mlockall() unsupported?");
     157             :   return -1;
     158             : #endif /* defined(HAVE_UNIX_MLOCKALL) */
     159             : }
     160             : 
     161             : /** Number of extra file descriptors to keep in reserve beyond those that we
     162             :  * tell Tor it's allowed to use. */
     163             : #define ULIMIT_BUFFER 32 /* keep 32 extra fd's beyond ConnLimit_ */
     164             : 
     165             : /** Learn the maximum allowed number of file descriptors, and tell the
     166             :  * system we want to use up to that number. (Some systems have a low soft
     167             :  * limit, and let us set it higher.)  We compute this by finding the largest
     168             :  * number that we can use.
     169             :  *
     170             :  * If the limit is below the reserved file descriptor value (ULIMIT_BUFFER),
     171             :  * return -1 and <b>max_out</b> is untouched.
     172             :  *
     173             :  * If we can't find a number greater than or equal to <b>limit</b>, then we
     174             :  * fail by returning -1 and <b>max_out</b> is untouched.
     175             :  *
     176             :  * If we are unable to set the limit value because of setrlimit() failing,
     177             :  * return 0 and <b>max_out</b> is set to the current maximum value returned
     178             :  * by getrlimit().
     179             :  *
     180             :  * Otherwise, return 0 and store the maximum we found inside <b>max_out</b>
     181             :  * and set <b>max_sockets</b> with that value as well.*/
     182             : int
     183           4 : set_max_file_descriptors(rlim_t limit, int *max_out)
     184             : {
     185           4 :   if (limit < ULIMIT_BUFFER) {
     186           0 :     log_warn(LD_CONFIG,
     187             :              "ConnLimit must be at least %d. Failing.", ULIMIT_BUFFER);
     188           0 :     return -1;
     189             :   }
     190             : 
     191             :   /* Define some maximum connections values for systems where we cannot
     192             :    * automatically determine a limit. Re Cygwin, see
     193             :    * https://archives.seul.org/or/talk/Aug-2006/msg00210.html
     194             :    * For an iPhone, 9999 should work. For Windows and all other unknown
     195             :    * systems we use 15000 as the default. */
     196             : #ifndef HAVE_GETRLIMIT
     197             : #if defined(CYGWIN) || defined(__CYGWIN__)
     198             :   const char *platform = "Cygwin";
     199             :   const unsigned long MAX_CONNECTIONS = 3200;
     200             : #elif defined(_WIN32)
     201             :   const char *platform = "Windows";
     202             :   const unsigned long MAX_CONNECTIONS = 15000;
     203             : #else
     204             :   const char *platform = "unknown platforms with no getrlimit()";
     205             :   const unsigned long MAX_CONNECTIONS = 15000;
     206             : #endif /* defined(CYGWIN) || defined(__CYGWIN__) || ... */
     207             :   log_fn(LOG_INFO, LD_NET,
     208             :          "This platform is missing getrlimit(). Proceeding.");
     209             :   if (limit > MAX_CONNECTIONS) {
     210             :     log_warn(LD_CONFIG,
     211             :              "We do not support more than %lu file descriptors "
     212             :              "on %s. Tried to raise to %lu.",
     213             :              (unsigned long)MAX_CONNECTIONS, platform, (unsigned long)limit);
     214             :     return -1;
     215             :   }
     216             :   limit = MAX_CONNECTIONS;
     217             : #else /* defined(HAVE_GETRLIMIT) */
     218           4 :   struct rlimit rlim;
     219             : 
     220           4 :   if (getrlimit(RLIMIT_NOFILE, &rlim) != 0) {
     221           0 :     log_warn(LD_NET, "Could not get maximum number of file descriptors: %s",
     222             :              strerror(errno));
     223           0 :     return -1;
     224             :   }
     225           4 :   if (rlim.rlim_max < limit) {
     226           0 :     log_warn(LD_CONFIG,"We need %lu file descriptors available, and we're "
     227             :              "limited to %lu. Please change your ulimit -n.",
     228             :              (unsigned long)limit, (unsigned long)rlim.rlim_max);
     229           0 :     return -1;
     230             :   }
     231             : 
     232           4 :   if (rlim.rlim_max > rlim.rlim_cur) {
     233           4 :     log_info(LD_NET,"Raising max file descriptors from %lu to %lu.",
     234             :              (unsigned long)rlim.rlim_cur, (unsigned long)rlim.rlim_max);
     235             :   }
     236             :   /* Set the current limit value so if the attempt to set the limit to the
     237             :    * max fails at least we'll have a valid value of maximum sockets. */
     238           4 :   *max_out = (int)rlim.rlim_cur - ULIMIT_BUFFER;
     239           4 :   set_max_sockets(*max_out);
     240           4 :   rlim.rlim_cur = rlim.rlim_max;
     241             : 
     242           4 :   if (setrlimit(RLIMIT_NOFILE, &rlim) != 0) {
     243           0 :     int couldnt_set = 1;
     244           0 :     const int setrlimit_errno = errno;
     245             : #ifdef OPEN_MAX
     246             :     uint64_t try_limit = OPEN_MAX - ULIMIT_BUFFER;
     247             :     if (errno == EINVAL && try_limit < (uint64_t) rlim.rlim_cur) {
     248             :       /* On some platforms, OPEN_MAX is the real limit, and getrlimit() is
     249             :        * full of nasty lies.  I'm looking at you, OSX 10.5.... */
     250             :       rlim.rlim_cur = MIN((rlim_t) try_limit, rlim.rlim_cur);
     251             :       if (setrlimit(RLIMIT_NOFILE, &rlim) == 0) {
     252             :         if (rlim.rlim_cur < (rlim_t)limit) {
     253             :           log_warn(LD_CONFIG, "We are limited to %lu file descriptors by "
     254             :                    "OPEN_MAX (%lu), and ConnLimit is %lu.  Changing "
     255             :                    "ConnLimit; sorry.",
     256             :                    (unsigned long)try_limit, (unsigned long)OPEN_MAX,
     257             :                    (unsigned long)limit);
     258             :         } else {
     259             :           log_info(LD_CONFIG, "Dropped connection limit to %lu based on "
     260             :                    "OPEN_MAX (%lu); Apparently, %lu was too high and rlimit "
     261             :                    "lied to us.",
     262             :                    (unsigned long)try_limit, (unsigned long)OPEN_MAX,
     263             :                    (unsigned long)rlim.rlim_max);
     264             :         }
     265             :         couldnt_set = 0;
     266             :       }
     267             :     }
     268             : #endif /* defined(OPEN_MAX) */
     269           0 :     if (couldnt_set) {
     270           0 :       log_warn(LD_CONFIG,"Couldn't set maximum number of file descriptors: %s",
     271             :                strerror(setrlimit_errno));
     272             :     }
     273             :   }
     274             :   /* leave some overhead for logs, etc, */
     275           4 :   limit = rlim.rlim_cur;
     276             : #endif /* !defined(HAVE_GETRLIMIT) */
     277             : 
     278           4 :   if (limit > INT_MAX)
     279             :     limit = INT_MAX;
     280           4 :   tor_assert(max_out);
     281           4 :   *max_out = (int)limit - ULIMIT_BUFFER;
     282           4 :   set_max_sockets(*max_out);
     283             : 
     284           4 :   return 0;
     285             : }

Generated by: LCOV version 1.14