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

(cherry picked from commit bf05e66bb38c44378a7873ff3701e6e596e70bf7)

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 58cf969842718b2422b059fbcf9927955b9dafdb..fa411735533cf47da6d2b3838ff94e861e2a1349 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]
+
 4636.  [bug]           Normalize rpz policy zone names when checking for
                        existence. [RT #45358]
 
index dbcd75996325ba4525fe2e6bc6c341a5f24e4f43..e7a51d8588c10bd6df2ef1f73e000ceb6c69f2ff 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);
 
@@ -6714,12 +6720,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, allow, 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);
        }
 
@@ -6727,16 +6742,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);
 }
@@ -7864,6 +7869,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.
         */
@@ -7879,6 +7899,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.
         *
@@ -11209,6 +11243,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;
@@ -12491,7 +12612,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);
 
@@ -12513,8 +12633,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 1b623584a626fc16c2c7a48273bff4db3fa511af..37cee0f8696fc9b8c98ae2516c83b9bba8d9c19b 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 d052317da54ffb7d772dfec06ccada35849cfd25..2e3e8c38af443889f12b4e8929ecdfbc8193215f 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 9bcf00db6503f50544eb703a493216e318f8e0d6..15fe681e2ef48bcee5361387d6ba7476e6d3c7d9 100644 (file)
@@ -237,6 +237,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;
@@ -1988,7 +1989,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;
@@ -2027,27 +2028,21 @@ dns_view_setnewzones(dns_view_t *view, isc_boolean_t allow, void *cfgctx,
        if (!allow)
                return (ISC_R_SUCCESS);
 
-       result = isc_file_sanitize(NULL, view->name, "nzf",
-                                  buffer, sizeof(buffer));
-       if (result != ISC_R_SUCCESS)
-               goto out;
+       CHECK(isc_file_sanitize(NULL, 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 = isc_file_sanitize(NULL, view->name, "nzd",
-                                  buffer, sizeof(buffer));
-       if (result != ISC_R_SUCCESS)
-               goto out;
+       CHECK(isc_file_sanitize(NULL, 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);
@@ -2056,19 +2051,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);
                }
        }
 
@@ -2079,8 +2073,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;
@@ -2090,7 +2083,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);