]> git.ipfire.org Git - thirdparty/freeswitch.git/commitdiff
add -u and -g command line args to set user and group.
authorMichael Jerris <mike@jerris.com>
Tue, 23 Oct 2007 15:56:23 +0000 (15:56 +0000)
committerMichael Jerris <mike@jerris.com>
Tue, 23 Oct 2007 15:56:23 +0000 (15:56 +0000)
properly handle portability for mlockall and setrlimit
Tested on linux, Freebsd, solaris, mac.
FSCORE-47

git-svn-id: http://svn.freeswitch.org/svn/freeswitch/trunk@6033 d0543943-73ff-0310-b7d9-9358b9ac24b2

configure.in
src/include/private/switch_core_pvt.h
src/include/switch_core.h
src/switch.c
src/switch_core.c

index bc11de57587b4574fceb091f10855ac461a294ad..fa94afec3f0bcb13aa0dfd6b854075d9857090b7 100644 (file)
@@ -170,7 +170,7 @@ AC_SUBST(DYNAMIC_LIB_EXTEN)
 # Checks for header files.
 AC_HEADER_DIRENT
 AC_HEADER_STDC
-AC_CHECK_HEADERS([sys/types.h])
+AC_CHECK_HEADERS([sys/types.h sys/resource.h sched.h])
 
 # Checks for typedefs, structures, and compiler characteristics.
 AC_C_CONST
@@ -185,6 +185,59 @@ AC_FUNC_MALLOC
 AC_TYPE_SIGNAL
 AC_FUNC_STRFTIME
 AC_CHECK_FUNCS([gethostname vasprintf mmap mlock mlockall usleep])
+AC_CHECK_FUNCS([sched_setscheduler setpriority setrlimit setgroups initgroups])
+
+AC_CHECK_DECL([RLIMIT_MEMLOCK],
+       [AC_DEFINE([HAVE_RLIMIT_MEMLOCK],[1],[RLIMIT_MEMLOCK constant for setrlimit])],,
+       [#ifdef HAVE_SYS_RESOURCE_H
+        #include <sys/resource.h>
+        #endif])
+
+AC_CHECK_DECL([SCHED_RR],
+       [AC_DEFINE([HAVE_SCHED_RR],[1],[SCHED_RR constant for sched_setscheduler])],,
+       [#ifdef HAVE_SCHED_H
+        #include <sched.h>
+        #endif])
+
+#
+# use mlockall only on linux (for now; if available)
+#
+if test "x${ac_cv_func_mlockall}" = "xyes"; then
+       AC_MSG_CHECKING([whether to use mlockall])
+       case "$host" in
+       *-linux-*)
+               AC_DEFINE([USE_MLOCKALL],[1],[Enable mlockall support])
+               AC_MSG_RESULT([yes])
+               USE_MLOCKALL=yes
+               ;;
+       *-freebsd*)
+               AC_MSG_RESULT([no, broken for non-root users])
+               ;;
+       *)
+               AC_MSG_RESULT([no])
+               ;;
+       esac
+
+       #
+       # setrlimit prerequisites
+       #
+       if test "x${USE_MLOCKALL}" = "xyes" -a \
+               "x${ac_cv_func_setrlimit}" = "xyes" -a \
+               "x${ac_cv_have_decl_RLIMIT_MEMLOCK}" = "xyes"
+       then
+               AC_DEFINE([USE_SETRLIMIT],[1],[Use setrlimit to disable mlock limit for non-root users])
+       fi
+fi
+
+#
+# sched_setcheduler + round-robin scheduler prerequisites
+#
+if test "x${ac_cv_func_sched_setscheduler}" = "xyes" -a \
+        "x${ac_cv_have_decl_SCHED_RR}" = "xyes"
+then
+       AC_DEFINE([USE_SCHED_SETSCHEDULER],[1],[Enable round-robin scheduler using sched_setscheduler])
+fi
+
 
 AC_C_BIGENDIAN(AC_DEFINE([SWITCH_BYTE_ORDER],__BIG_ENDIAN,[Big Endian]),AC_DEFINE([SWITCH_BYTE_ORDER],__LITTLE_ENDIAN,[Little Endian]))
 
index 3c61fb0d8dd4f0fb6cee423e0f26dae8aeb6c811..af75dc9d31ea1a5ad55f8a23c9248902ae9ddea0 100644 (file)
@@ -60,6 +60,19 @@ typedef apr_os_thread_t switch_thread_id_t;
 #include <sys/mman.h>
 #endif
 
+#ifndef WIN32
+/* setuid, setgid */
+#include <unistd.h>
+
+/* getgrnam, getpwnam */
+#include <pwd.h>
+#include <grp.h>
+
+#ifdef HAVE_SYS_RESOURCE_H
+#include <sys/resource.h>
+#endif
+#endif
+
 /* #define DEBUG_ALLOC */
 #define DO_EVENTS
 
index d4e32d34ab529d25f653b59b2cff429224fa7ae9..47a4210d288535201ac274280eb1bb794e4ec5d8 100644 (file)
@@ -1413,6 +1413,18 @@ SWITCH_DECLARE(switch_status_t) switch_core_management_exec(char *relative_oid,
 */
 SWITCH_DECLARE(int32_t) set_high_priority(void);
 
+/*! 
+  \brief Change user and/or group of the running process
+  \long Several possible combinations:
+  - user only (group NULL): switch to user and his primary group (and supplementary groups, if supported)
+  - user and group: switch to user and specified group (only)
+  - group only (user NULL): switch group only
+  \param user name of the user to switch to (or NULL)
+  \param group name of the group to switch to (or NULL)
+  \return 0 on success, -1 otherwise
+*/
+SWITCH_DECLARE(int32_t) change_user_group(const char *user, const char *group);
+
 /*! 
   \brief Run endlessly until the system is shutdown
   \param bg divert console to the background
index 193be311f23509d8be03e6c6eecf5231de95a327..21db4b95e012b1ed71e592d01a8a543003c30508 100644 (file)
@@ -205,6 +205,8 @@ int main(int argc, char *argv[])
        const char *err = NULL;         /* error value for return from freeswitch initialization */
 #ifndef WIN32
        int nf = 0;                                     /* TRUE if we are running in nofork mode */
+       char *runas_user = NULL;
+       char *runas_group = NULL;
 #endif
        int nc = 0;                                     /* TRUE if we are running in noconsole mode */
        FILE *f;                                        /* file handle to the pid file */
@@ -214,6 +216,7 @@ int main(int argc, char *argv[])
        char *usageDesc;
        int alt_dirs = 0;
        int known_opt;
+       int high_prio = 0;
        switch_core_flag_t flags = SCF_USE_SQL;
 
 #ifdef WIN32
@@ -229,6 +232,8 @@ int main(int argc, char *argv[])
                "\t-uninstall       -- remove freeswitch as a service\n"
 #else
                "\t-nf              -- no forking\n"
+               "\t-u [user]        -- specify user to switch to\n"
+               "\t-g [group]       -- specify group to switch to\n"
 #endif
                "\t-help            -- this message\n"
                "\t-hp              -- enable high priority settings\n"
@@ -280,6 +285,21 @@ int main(int argc, char *argv[])
                        }
                }
 #else
+               if (argv[x] && !strcmp(argv[x], "-u")) {
+                       x++;
+                       if (argv[x] && strlen(argv[x])) {
+                               runas_user = argv[x];
+                       }
+                       known_opt++;
+               }
+
+               if (argv[x] && !strcmp(argv[x], "-g")) {
+                       x++;
+                       if (argv[x] && strlen(argv[x])) {
+                               runas_group = argv[x];
+                       }
+                       known_opt++;
+               }
 
                if (argv[x] && !strcmp(argv[x], "-nf")) {
                        nf++;
@@ -287,7 +307,7 @@ int main(int argc, char *argv[])
                }
 #endif
                if (argv[x] && !strcmp(argv[x], "-hp")) {
-                       set_high_priority();
+                       high_prio++;
                        known_opt++;
                }
 
@@ -389,6 +409,19 @@ int main(int argc, char *argv[])
 #endif
        }
 
+       if (high_prio) {
+               set_high_priority();
+       }
+
+#ifndef WIN32
+       if (runas_user || runas_group) {
+               if(change_user_group(runas_user, runas_group) < 0) {
+                       fprintf(stderr, "Failed to switch user / group\n" );
+                       return 255;
+               }
+       }
+#endif
+
        if (switch_core_init_and_modload(nc ? lfile : NULL, flags, &err) != SWITCH_STATUS_SUCCESS) {
                fprintf(stderr, "Cannot Initilize [%s]\n", err);
                return 255;
index 801c39d14a821c7106d32a4de9b398a49cd1654c..25b96547d552cc15c03a12e7dfaa82095e50ce34 100644 (file)
@@ -360,7 +360,19 @@ SWITCH_DECLARE(void) switch_core_set_globals(void)
 
 SWITCH_DECLARE(int32_t) set_high_priority(void)
 {
-#ifdef __linux__
+#ifdef WIN32
+       SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS);
+#else
+
+#ifdef USE_SETRLIMIT
+       struct rlimit lim = { RLIM_INFINITY, RLIM_INFINITY };
+#endif
+
+#ifdef USE_SCHED_SETSCHEDULER
+       /*
+        * Try to use a round-robin scheduler
+        * with a fallback if that does not work
+        */
        struct sched_param sched = { 0 };
        sched.sched_priority = 1;
        if (sched_setscheduler(0, SCHED_RR, &sched)) {
@@ -371,19 +383,126 @@ SWITCH_DECLARE(int32_t) set_high_priority(void)
        }
 #endif
 
-#ifdef WIN32
-       SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS);
+#ifdef HAVE_SETPRIORITY
+       /*
+        * setpriority() works on FreeBSD (6.2), nice() doesn't
+        */
+       if (setpriority(PRIO_PROCESS, getpid(), -10) < 0) {
+               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Could not set nice level\n");
+       }
 #else
-       if(nice(-10)!= -10) {
+       if (nice(-10) != -10) {
                switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Could not set nice level\n");
        }
 #endif
 
-#define USE_MLOCKALL
-#ifdef HAVE_MLOCKALL
+#ifdef USE_SETRLIMIT
+       /*
+        * The amount of memory which can be mlocked is limited for non-root users.
+        * FS will segfault (= hitting the limit) soon after mlockall has been called
+        * and we've switched to a different user.
+        * So let's try to remove the mlock limit here...
+        */
+       if (setrlimit(RLIMIT_MEMLOCK, &lim) < 0) {
+               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT,
+                        "Failed to disable memlock limit, application may crash if run as non-root user!\n");
+       }
+#endif
+
 #ifdef USE_MLOCKALL
+       /*
+        * Pin memory pages to RAM to prevent being swapped to disk
+        */
        mlockall(MCL_CURRENT | MCL_FUTURE);
 #endif
+
+#endif
+       return 0;
+}
+
+SWITCH_DECLARE(int32_t) change_user_group(const char *user, const char *group)
+{
+#ifndef WIN32
+       uid_t runas_uid = 0;
+       gid_t runas_gid = 0;
+       struct passwd *runas_pw = NULL;
+
+       if (user) {
+               /*
+                * Lookup user information in the system's db
+                */
+               runas_pw = getpwnam( user );
+               if (!runas_pw) {
+                       switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Unknown user \"%s\"\n", user);
+                       return -1;
+               }
+               runas_uid = runas_pw->pw_uid;
+       }
+
+       if (group) {
+               struct group *gr = NULL;
+
+               /*
+                * Lookup group information in the system's db
+                */
+               gr = getgrnam( group );
+               if (!gr) {
+                       switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Unknown group \"%s\"\n", group);
+                       return -1;
+               }
+               runas_gid = gr->gr_gid;
+       }
+
+       if (runas_uid) {
+#ifdef HAVE_SETGROUPS
+               /*
+                * Drop all group memberships prior to changing anything
+                * or else we're going to inherit the parent's list of groups
+                * (which is not what we want...)
+                */
+               if (setgroups(0, NULL) < 0) {
+                       switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Failed to drop group access list\n");
+                       return -1;
+               }
+#endif
+               if (runas_gid) {
+                       /*
+                        * A group has been passed, switch to it
+                        * (without loading the user's other groups)
+                        */
+                       if (setgid(runas_gid) < 0) {
+                               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Failed to change gid!\n");
+                               return -1;
+                       }
+               } else {
+                       /*
+                        * No group has been passed, use the user's primary group in this case
+                        */
+                       if (setgid(runas_pw->pw_gid) < 0) {
+                               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Failed to change gid!\n");
+                               return -1;
+                       }
+
+#ifdef HAVE_INITGROUPS
+                       /*
+                        * Set all the other groups the user is a member of
+                        * (This can be really useful for fine-grained access control)
+                        */
+                       if (initgroups(runas_pw->pw_name, runas_pw->pw_gid) < 0) {
+                               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Failed to set group access list for user\n");
+                               return -1;
+                       }
+#endif
+               }
+
+               /*
+                * Finally drop all privileges by switching to the new userid
+                */
+               if (setuid(runas_uid) < 0) {
+                       switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Failed to change uid!\n");
+                       return -1;
+               }
+       }
 #endif
        return 0;
 }