]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
[master] prevent reload failure due to LMDB database perms
authorEvan Hunt <each@isc.org>
Tue, 13 Jun 2017 17:15:34 +0000 (10:15 -0700)
committerEvan Hunt <each@isc.org>
Tue, 13 Jun 2017 17:15:34 +0000 (10:15 -0700)
4638. [bug] Reloading or reconfiguring named could fail on
some platforms when LMDB was in use. [RT #45203]

CHANGES
bin/named/server.c
bin/named/unix/include/named/os.h
bin/named/unix/os.c
bin/named/win32/include/named/os.h
bin/named/win32/os.c
doc/arm/notes.xml
lib/dns/include/dns/view.h
lib/dns/view.c

diff --git a/CHANGES b/CHANGES
index 8e4793e527f5c16d944a3211dcacd3cd387a0ca5..8b18cf2b26c6cecdf12e06648d872eba6ea2b0f8 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,6 @@
+4638.  [bug]           Reloading or reconfiguring named could fail on
+                       some platforms when LMDB was in use. [RT #45203]
+
 4637.  [func]          "nsec3hash -r" option ("rdata order") takes arguments
                        in the same order as they appear in NSEC3 or
                        NSEC3PARAM records, so that NSEC3 parameters can
index 97708a514ac2c438f0944ad06a41b2812b9942bf..e3ac7e56ba3e5b951ccc3873b05c72f9b7d6932d 100644 (file)
@@ -484,6 +484,12 @@ nzd_writable(dns_view_t *view);
 static isc_result_t
 nzd_open(dns_view_t *view, unsigned int flags, MDB_txn **txnp, MDB_dbi *dbi);
 
+static isc_result_t
+nzd_env_reopen(dns_view_t *view);
+
+static void
+nzd_env_close(dns_view_t *view);
+
 static isc_result_t
 nzd_close(MDB_txn **txnp, isc_boolean_t commit);
 
@@ -6727,12 +6733,21 @@ setup_newzones(dns_view_t *view, cfg_obj_t *config, cfg_obj_t *vconfig,
                return (ISC_R_NOMEMORY);
        }
 
+       /*
+        * We attach the parser that was used for config as well
+        * as the one that will be used for added zones, to avoid
+        * a shutdown race later.
+        */
        memset(nzcfg, 0, sizeof(*nzcfg));
+       cfg_parser_attach(conf_parser, &nzcfg->conf_parser);
+       cfg_parser_attach(ns_g_addparser, &nzcfg->add_parser);
+       isc_mem_attach(view->mctx, &nzcfg->mctx);
+       cfg_aclconfctx_attach(actx, &nzcfg->actx);
 
        result = dns_view_setnewzones(view, ISC_TRUE, nzcfg,
                                      newzone_cfgctx_destroy, mapsize);
        if (result != ISC_R_SUCCESS) {
-               isc_mem_put(view->mctx, nzcfg, sizeof(*nzcfg));
+               dns_view_setnewzones(view, ISC_FALSE, NULL, NULL, 0ULL);
                return (result);
        }
 
@@ -6740,16 +6755,6 @@ setup_newzones(dns_view_t *view, cfg_obj_t *config, cfg_obj_t *vconfig,
        if (vconfig != NULL)
                cfg_obj_attach(vconfig, &nzcfg->vconfig);
 
-       /*
-        * We attach the parser that was used for config as well
-        * as the one that will be used for added zones, to avoid
-        * a shutdown race later.
-        */
-       cfg_parser_attach(conf_parser, &nzcfg->conf_parser);
-       cfg_parser_attach(ns_g_addparser, &nzcfg->add_parser);
-       isc_mem_attach(view->mctx, &nzcfg->mctx);
-       cfg_aclconfctx_attach(actx, &nzcfg->actx);
-
        result = count_newzones(view, nzcfg, num_zones);
        return (result);
 }
@@ -7936,6 +7941,21 @@ load_configuration(const char *filename, ns_server_t *server,
                }
        }
 
+       /*
+        * If we're using LMDB, we may have created newzones databases
+        * as root, making it impossible to reopen them later after
+        * switching to a new userid. We close them now, and reopen
+        * after relinquishing privileges them.
+        */
+       if (first_time) {
+               for (view = ISC_LIST_HEAD(server->viewlist);
+                    view != NULL;
+                    view = ISC_LIST_NEXT(view, link))
+               {
+                       nzd_env_close(view);
+               }
+       }
+
        /*
         * Relinquish root privileges.
         */
@@ -7951,6 +7971,20 @@ load_configuration(const char *filename, ns_server_t *server,
                              "the working directory is not writable");
        }
 
+#ifdef HAVE_LMDB
+       /*
+        * Reopen NZD databases.
+        */
+       if (first_time) {
+               for (view = ISC_LIST_HEAD(server->viewlist);
+                    view != NULL;
+                    view = ISC_LIST_NEXT(view, link))
+               {
+                       nzd_env_reopen(view);
+               }
+       }
+#endif /* HAVE_LMDB */
+
        /*
         * Configure the logging system.
         *
@@ -11317,6 +11351,93 @@ nzd_open(dns_view_t *view, unsigned int flags, MDB_txn **txnp, MDB_dbi *dbi) {
        return (ISC_R_SUCCESS);
 }
 
+/*
+ * nzd_env_close() and nzd_env_reopen are a kluge to address the
+ * problem of an NZD file possibly being created before we drop
+ * root privileges.
+ */
+static void
+nzd_env_close(dns_view_t *view) {
+       if (view->new_zone_dbenv != NULL) {
+               const char *dbpath = NULL;
+               char lockpath[PATH_MAX];
+               int ret;
+
+               if (mdb_env_get_path(view->new_zone_dbenv, &dbpath) == 0) {
+                       snprintf(lockpath, sizeof(lockpath), "%s-lock",
+                                dbpath);
+               }
+
+               mdb_env_close((MDB_env *) view->new_zone_dbenv);
+               view->new_zone_dbenv = NULL;
+
+               /*
+                * Database files must be owned by the eventual user, not
+                * by root.
+                */
+               ret = chown(dbpath, ns_os_uid(), -1);
+               UNUSED(ret);
+
+                /*
+                 * Some platforms need the lockfile not to exist when we
+                 * reopen the environment.
+                */
+               (void) isc_file_remove(lockpath);
+       }
+}
+
+static isc_result_t
+nzd_env_reopen(dns_view_t *view) {
+       isc_result_t result;
+       MDB_env *env = NULL;
+       int status;
+
+       if (view->new_zone_db == NULL) {
+               return (ISC_R_SUCCESS);
+       }
+
+       nzd_env_close(view);
+
+       status = mdb_env_create(&env);
+       if (status != 0) {
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
+                             ISC_LOGMODULE_OTHER, ISC_LOG_ERROR,
+                             "mdb_env_create failed: %s",
+                             mdb_strerror(status));
+               CHECK(ISC_R_FAILURE);
+       }
+
+       if (view->new_zone_mapsize != 0ULL) {
+               status = mdb_env_set_mapsize(env, view->new_zone_mapsize);
+               if (status != 0) {
+                       isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
+                                     ISC_LOGMODULE_OTHER, ISC_LOG_ERROR,
+                                     "mdb_env_set_mapsize failed: %s",
+                                     mdb_strerror(status));
+                       CHECK(ISC_R_FAILURE);
+               }
+       }
+
+       status = mdb_env_open(env, view->new_zone_db,
+                             MDB_NOSUBDIR|MDB_CREATE, 0600);
+       if (status != 0) {
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
+                             ISC_LOGMODULE_OTHER, ISC_LOG_ERROR,
+                             "mdb_env_open of '%s' failed: %s",
+                             view->new_zone_db, mdb_strerror(status));
+               CHECK(ISC_R_FAILURE);
+       }
+
+       view->new_zone_dbenv = env;
+       env = NULL;
+
+ cleanup:
+       if (env != NULL) {
+               mdb_env_close(env);
+       }
+       return (result);
+}
+
 static isc_result_t
 nzd_close(MDB_txn **txnp, isc_boolean_t commit) {
        isc_result_t result = ISC_R_SUCCESS;
@@ -12659,7 +12780,6 @@ ns_server_showzone(ns_server_t *server, isc_lex_t *lex, isc_buffer_t **text) {
 static void
 newzone_cfgctx_destroy(void **cfgp) {
        ns_cfgctx_t *cfg;
-       isc_mem_t *mctx;
 
        REQUIRE(cfgp != NULL && *cfgp != NULL);
 
@@ -12681,8 +12801,7 @@ newzone_cfgctx_destroy(void **cfgp) {
        if (cfg->actx != NULL)
                cfg_aclconfctx_detach(&cfg->actx);
 
-       mctx = cfg->mctx;
-       isc_mem_putanddetach(&mctx, cfg, sizeof(*cfg));
+       isc_mem_putanddetach(&cfg->mctx, cfg, sizeof(*cfg));
        *cfgp = NULL;
 }
 
index c04354cc046e47aeb0ea226db9809228389a4837..26de282f99cd4c8937894a95363f3a8950a7f6f4 100644 (file)
@@ -11,6 +11,8 @@
 
 /*! \file */
 
+#include <pwd.h>
+
 #include <isc/types.h>
 
 void
@@ -34,6 +36,9 @@ ns_os_inituserinfo(const char *username);
 void
 ns_os_changeuser(void);
 
+uid_t
+ns_os_uid(void);
+
 void
 ns_os_adjustnofile(void);
 
index 27705c1cf4f0bd1092a39b3c61a584e56eb23da1..24066cb66d9ee911b7d46e09ad961a00a2afb144 100644 (file)
@@ -601,6 +601,13 @@ ns_os_changeuser(void) {
 #endif
 }
 
+uid_t
+ns_os_uid(void) {
+       if (runas_pw == NULL)
+               return (0);
+       return (runas_pw->pw_uid);
+}
+
 void
 ns_os_adjustnofile(void) {
 #ifdef HAVE_LINUXTHREADS
index af71da7458d9a53c932d7985a9382c3c20acb166..297b2d210897536a204dfecad35339aec3f1a32d 100644 (file)
@@ -32,6 +32,9 @@ ns_os_inituserinfo(const char *username);
 void
 ns_os_changeuser(void);
 
+unsigned int
+ns_os_uid(void);
+
 void
 ns_os_adjustnofile(void);
 
index 251cdd2e485046c34d95b22b23bb8bf107966a96..fc9b6f3eff3b8f4f1fc0fae046cb3bf130e7746d 100644 (file)
@@ -163,6 +163,11 @@ void
 ns_os_changeuser(void) {
 }
 
+unsigned int
+ns_os_uid(void) {
+       return (0);
+}
+
 void
 ns_os_adjustnofile(void) {
 }
index 02c5bdc5b5bd37862edc91d75efd0195c689edc0..ea87d05a86ead4faafb3131292f982e2e660f2ae 100644 (file)
 
   <section xml:id="relnotes_bugs"><info><title>Bug Fixes</title></info>
     <itemizedlist>
+      <listitem>
+       <para>
+         Reloading or reconfiguring <command>named</command> could
+         fail on some platforms when LMDB was in use. [RT #45203]
+       </para>
+      </listitem>
       <listitem>
        <para>
          Due to some incorrectly deleted code, when BIND was
          server restart. This has been corrected. [RT #45185]
        </para>
       </listitem>
-
       <listitem>
        <para>
          Semicolons are no longer escaped when printing CAA and
index e70b3f6f76cb8392a743a8057055d5b37f61b2b1..a586dc8add4b275ee083d1fd1c0774675f62fdec 100644 (file)
@@ -212,6 +212,7 @@ struct dns_view {
        char *                          new_zone_file;
        char *                          new_zone_db;
        void *                          new_zone_dbenv;
+       isc_uint64_t                    new_zone_mapsize;
        void *                          new_zone_config;
        void                            (*cfg_destroy)(void **);
        isc_mutex_t                     new_zone_lock;
index aa88a65d96605fcfc4fbf8c50a16999814c87e99..819484525d43094d25931fec8b243a098e0f72db 100644 (file)
@@ -236,6 +236,7 @@ dns_view_create(isc_mem_t *mctx, dns_rdataclass_t rdclass,
        view->new_zone_file = NULL;
        view->new_zone_db = NULL;
        view->new_zone_dbenv = NULL;
+       view->new_zone_mapsize = 0ULL;
        view->new_zone_config = NULL;
        view->cfg_destroy = NULL;
        view->fail_ttl = 0;
@@ -2038,7 +2039,7 @@ isc_result_t
 dns_view_setnewzones(dns_view_t *view, isc_boolean_t allow, void *cfgctx,
                     void (*cfg_destroy)(void **), isc_uint64_t mapsize)
 {
-       isc_result_t result;
+       isc_result_t result = ISC_R_SUCCESS;
        char buffer[1024];
 #ifdef HAVE_LMDB
        MDB_env *env = NULL;
@@ -2078,27 +2079,21 @@ dns_view_setnewzones(dns_view_t *view, isc_boolean_t allow, void *cfgctx,
                return (ISC_R_SUCCESS);
        }
 
-       result = nz_legacy(view->new_zone_dir, view->name, "nzf",
-                          buffer, sizeof(buffer));
-       if (result != ISC_R_SUCCESS)
-               goto out;
+       CHECK(nz_legacy(view->new_zone_dir, view->name, "nzf",
+                       buffer, sizeof(buffer)));
 
        view->new_zone_file = isc_mem_strdup(view->mctx, buffer);
        if (view->new_zone_file == NULL) {
-               result = ISC_R_NOMEMORY;
-               goto out;
+               CHECK(ISC_R_NOMEMORY);
        }
 
 #ifdef HAVE_LMDB
-       result = nz_legacy(view->new_zone_dir, view->name, "nzd",
-                          buffer, sizeof(buffer));
-       if (result != ISC_R_SUCCESS)
-               goto out;
+       CHECK(nz_legacy(view->new_zone_dir, view->name, "nzd",
+                       buffer, sizeof(buffer)));
 
        view->new_zone_db = isc_mem_strdup(view->mctx, buffer);
        if (view->new_zone_db == NULL) {
-               result = ISC_R_NOMEMORY;
-               goto out;
+               CHECK(ISC_R_NOMEMORY);
        }
 
        status = mdb_env_create(&env);
@@ -2107,19 +2102,18 @@ dns_view_setnewzones(dns_view_t *view, isc_boolean_t allow, void *cfgctx,
                              ISC_LOGMODULE_OTHER, ISC_LOG_ERROR,
                              "mdb_env_create failed: %s",
                              mdb_strerror(status));
-               result = ISC_R_FAILURE;
-               goto out;
+               CHECK(ISC_R_FAILURE);
        }
 
        if (mapsize != 0ULL) {
                status = mdb_env_set_mapsize(env, mapsize);
+               view->new_zone_mapsize = mapsize;
                if (status != 0) {
                        isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
                                      ISC_LOGMODULE_OTHER, ISC_LOG_ERROR,
                                      "mdb_env_set_mapsize failed: %s",
                                      mdb_strerror(status));
-                       result = ISC_R_FAILURE;
-                       goto out;
+                       CHECK(ISC_R_FAILURE);
                }
        }
 
@@ -2130,8 +2124,7 @@ dns_view_setnewzones(dns_view_t *view, isc_boolean_t allow, void *cfgctx,
                              ISC_LOGMODULE_OTHER, ISC_LOG_ERROR,
                              "mdb_env_open of '%s' failed: %s",
                              view->new_zone_db, mdb_strerror(status));
-               result = ISC_R_FAILURE;
-               goto out;
+               CHECK(ISC_R_FAILURE);
        }
 
        view->new_zone_dbenv = env;
@@ -2141,7 +2134,7 @@ dns_view_setnewzones(dns_view_t *view, isc_boolean_t allow, void *cfgctx,
        view->new_zone_config = cfgctx;
        view->cfg_destroy = cfg_destroy;
 
out:
cleanup:
        if (result != ISC_R_SUCCESS) {
                if (view->new_zone_file != NULL) {
                        isc_mem_free(view->mctx, view->new_zone_file);