Browse Source

merged trac268: -u for b10-auth

git-svn-id: svn://bind10.isc.org/svn/bind10/trunk@2675 e5f2f494-b856-4b98-b285-d166d9295462
JINMEI Tatuya 14 years ago
parent
commit
64b6d5f0f0

+ 1 - 0
src/bin/auth/Makefile.am

@@ -47,6 +47,7 @@ libasio_link_a_CPPFLAGS = $(AM_CPPFLAGS)
 BUILT_SOURCES = spec_config.h 
 pkglibexec_PROGRAMS = b10-auth
 b10_auth_SOURCES = auth_srv.cc auth_srv.h
+b10_auth_SOURCES += change_user.cc change_user.h
 b10_auth_SOURCES += common.h
 b10_auth_SOURCES += main.cc
 b10_auth_LDADD =  $(top_builddir)/src/lib/datasrc/.libs/libdatasrc.a

+ 15 - 0
src/bin/auth/b10-auth.xml

@@ -48,6 +48,7 @@
       <arg><option>-4</option></arg>
       <arg><option>-6</option></arg>
       <arg><option>-p <replaceable>number</replaceable></option></arg>
+      <arg><option>-u <replaceable>username</replaceable></option></arg>
       <arg><option>-v</option></arg>
     </cmdsynopsis>
   </refsynopsisdiv>
@@ -120,6 +121,20 @@
       </varlistentry>
 
       <varlistentry>
+        <term><option>-u <replaceable>username</replaceable></option></term>
+        <listitem>
+	  <para>
+	    The user name of the <command>b10-auth</command> daemon.
+	    If specified, the daemon changes the process owner to the
+	    specified user.
+	    The <replaceable>username</replaceable> must be either a
+	    valid numeric user ID or a valid user name.
+	    By default the daemon runs as the user who invokes it.
+	  </para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
         <term><option>-v</option></term>
         <listitem><para>
           Enabled verbose mode. This enables diagnostic messages to

+ 55 - 0
src/bin/auth/change_user.cc

@@ -0,0 +1,55 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+
+#include <errno.h>
+#include <string.h>
+#include <pwd.h>
+#include <unistd.h>
+
+#include <boost/lexical_cast.hpp>
+
+#include <exceptions/exceptions.h>
+
+#include <auth/common.h>
+
+using namespace boost;
+
+void
+changeUser(const char* const username) {
+    const struct passwd *runas_pw = NULL;
+
+    runas_pw = getpwnam(username);
+    endpwent();
+    if (runas_pw == NULL) {
+        try {
+            runas_pw = getpwuid(lexical_cast<uid_t>(username));
+            endpwent();
+        } catch (const bad_lexical_cast&) {
+            ;                   // fall through to isc_throw below.
+        }
+    }
+    if (runas_pw == NULL) {
+        isc_throw(FatalError, "Unknown user name or UID:" << username);
+    }
+
+    if (setgid(runas_pw->pw_gid) < 0) {
+        isc_throw(FatalError, "setgid() failed: " << strerror(errno));
+    }
+
+    if (setuid(runas_pw->pw_uid) < 0) {
+        isc_throw(FatalError, "setuid() failed: " << strerror(errno));
+    }
+}

+ 59 - 0
src/bin/auth/change_user.h

@@ -0,0 +1,59 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+
+#ifndef __CHANGE_USER_H
+#define __CHANGE_USER_H 1
+
+/// \brief Change the run time user.
+///
+/// This function changes the user and its group of the authoritative server
+/// process.
+///
+/// On success the user ID of the process is changed to the specified user,
+/// and the group is changed to that of the new user.
+///
+/// This is considered a short term workaround until we develop clearer
+/// privilege separation, where the server won't even have to open privileged
+/// ports and can be started by a non privileged user from the beginning.
+/// This function therefore ignores some corner case problems (see below)
+/// which we would address otherwise.
+///
+/// \c username can be either a textual user name or its numeric ID.
+/// If the specified user name (or ID) doesn't specify a local user ID
+/// or the user originally starting the process doesn't have a permission
+/// of changing the user to \c username, this function throws an exception
+/// of class \c FatalError.
+///
+/// This function internally uses system libraries that do not guarantee
+/// reentrancy.  In fact, it doesn't even expect to be called more than once.
+/// The behavior is undefined if this function is called from multiple threads
+/// simultaneously or more generally called multiple times.
+///
+/// This function only offers the basic exception guarantee, that is, if
+/// an exception is thrown from this function, it's possible that an exception
+/// is thrown after changing the group ID.  This function doesn't recover
+/// from that situation.  In practice, the process is expected to consider
+/// this event a fatal error and will immediately exit, and shouldn't cause
+/// a real trouble.
+///
+/// \param username User name or ID of the new effective user.
+void changeUser(const char* const username);
+
+#endif // __CHANGE_USER_H
+
+// Local Variables:
+// mode: c++
+// End:

+ 10 - 1
src/bin/auth/main.cc

@@ -41,6 +41,7 @@
 
 #include <auth/spec_config.h>
 #include <auth/common.h>
+#include <auth/change_user.h>
 #include <auth/auth_srv.h>
 #include <auth/asio_link.h>
 
@@ -97,9 +98,10 @@ main(int argc, char* argv[]) {
     int ch;
     const char* port = DNSPORT;
     const char* address = NULL;
+    const char* uid = NULL;
     bool use_ipv4 = true, use_ipv6 = true, cache = true;
 
-    while ((ch = getopt(argc, argv, "46a:np:v")) != -1) {
+    while ((ch = getopt(argc, argv, "46a:np:u:v")) != -1) {
         switch (ch) {
         case '4':
             // Note that -4 means "ipv4 only", we need to set "use_ipv6" here,
@@ -121,6 +123,9 @@ main(int argc, char* argv[]) {
         case 'p':
             port = optarg;
             break;
+        case 'u':
+            uid = optarg;
+            break;
         case 'v':
             verbose_mode = true;
             break;
@@ -188,6 +193,10 @@ main(int argc, char* argv[]) {
                                              my_command_handler);
         cout << "[b10-auth] Configuration channel established." << endl;
 
+        if (uid != NULL) {
+            changeUser(uid);
+        }
+
         xfrin_session = new Session(io_service->get_io_service());
         cout << "[b10-auth] Xfrin session channel created." << endl;
         xfrin_session->establish(NULL);

+ 2 - 0
src/bin/auth/tests/Makefile.am

@@ -13,7 +13,9 @@ TESTS += run_unittests
 run_unittests_SOURCES = $(top_srcdir)/src/lib/dns/tests/unittest_util.h
 run_unittests_SOURCES += $(top_srcdir)/src/lib/dns/tests/unittest_util.cc
 run_unittests_SOURCES += ../auth_srv.h ../auth_srv.cc
+run_unittests_SOURCES += ../change_user.h ../change_user.cc
 run_unittests_SOURCES += auth_srv_unittest.cc
+run_unittests_SOURCES += change_user_unittest.cc
 run_unittests_SOURCES += asio_link_unittest.cc
 run_unittests_SOURCES += run_unittests.cc
 run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)

+ 59 - 0
src/bin/auth/tests/change_user_unittest.cc

@@ -0,0 +1,59 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+
+#include <stdlib.h>
+#include <unistd.h>             // for getuid
+
+#include <string>
+
+#include <gtest/gtest.h>
+
+#include <auth/common.h>
+#include <auth/change_user.h>
+
+using namespace std;
+
+namespace {
+class ChangeUserTest : public ::testing::Test {
+protected:
+    // normally the USER environment variable should be set to the name
+    // of the local user running this test.  If we encounter a case where
+    // this doesn't hold, we'll need to add a prerequisite check in each
+    // test.  For now we assume this is valid for simplicity.
+    ChangeUserTest() : my_username(getenv("USER")) {}
+    const string my_username;
+};
+
+TEST_F(ChangeUserTest, changeToTheSameUser) {
+    // changing to the run time user should succeed.
+    EXPECT_NO_THROW(changeUser(my_username.c_str()));
+}
+
+TEST_F(ChangeUserTest, badUID) {
+    // -1 should be an invalid numeric UID, and (hopefully) shouldn't be
+    // a valid textual username.
+    EXPECT_THROW(changeUser("-1"), FatalError);
+}
+
+TEST_F(ChangeUserTest, promotionAttempt) {
+    // change to root should fail unless the running user is a super user.
+    if (getuid() == 0) {
+        cerr << "Already a super user, skipping the test" << endl;
+        return;
+    }
+    EXPECT_THROW(changeUser("root"), FatalError);
+}
+}

+ 2 - 0
src/bin/bind10/bind10.py.in

@@ -336,6 +336,8 @@ class BoB:
             authargs += ['-a', str(self.address)]
         if self.nocache:
             authargs += ['-n']
+        if self.uid:
+            authargs += ['-u', str(self.uid)]
         if self.verbose:
             authargs += ['-v']
             sys.stdout.write("Starting b10-auth using port %d" %