From 7dfe354ac826add4edeed17fb2338838f55e0868 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Petr=20Men=C5=A1=C3=ADk?= <pemensik@redhat.com>
Date: Mon, 9 Oct 2017 18:06:43 +0200
Subject: [PATCH 1/3] Allow named to save *.nta files into specified directory.
 Adds databases-directory option support.

---
 bin/named/server.c         | 110 ++++++++++++++++++++++++++++++---------------
 bin/named/unix/os.c        |   1 -
 doc/arm/Bv9ARM-book.xml    |  15 +++++++
 lib/dns/include/dns/view.h |  26 +++++++++++
 lib/dns/view.c             |  83 +++++++++++++++++++++++-----------
 lib/isccfg/namedconf.c     |   1 +
 6 files changed, 175 insertions(+), 61 deletions(-)

diff --git a/bin/named/server.c b/bin/named/server.c
index 7835f73..9379e93 100644
--- a/bin/named/server.c
+++ b/bin/named/server.c
@@ -675,6 +675,37 @@ configure_view_nametable(const cfg_obj_t *vconfig, const cfg_obj_t *config,
 }
 
 static isc_result_t
+directory_fromconfig(const cfg_obj_t * const *maps,
+		     const char *optionname,
+		     const char **directory)
+{
+	isc_result_t result;
+	const cfg_obj_t *obj = NULL;
+
+	REQUIRE(directory != NULL);
+
+	result = named_config_get(maps, optionname, &obj);
+	if (result == ISC_R_SUCCESS) {
+		const char *dir;
+
+		dir = cfg_obj_asstring(obj);
+		if (dir != NULL) {
+			result = isc_file_isdirectory(dir);
+		}
+		if (result == ISC_R_SUCCESS)
+			*directory = dir;
+		else {
+			isc_log_write(named_g_lctx, DNS_LOGCATEGORY_SECURITY,
+				      NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR,
+				      "invalid %s %s: %s", optionname,
+				      dir, isc_result_totext(result));
+		}
+		return (result);
+	}
+	return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
 dstkey_fromconfig(const cfg_obj_t *vconfig, const cfg_obj_t *key,
 		  isc_boolean_t managed, dst_key_t **target, isc_mem_t *mctx)
 {
@@ -894,8 +925,7 @@ configure_view_dnsseckeys(dns_view_t *view, const cfg_obj_t *vconfig,
 	const cfg_obj_t *maps[4];
 	const cfg_obj_t *voptions = NULL;
 	const cfg_obj_t *options = NULL;
-	const cfg_obj_t *obj = NULL;
-	const char *directory;
+	const char *directory = NULL;
 	int i = 0;
 
 	/* We don't need trust anchors for the _bind view */
@@ -1020,19 +1050,10 @@ configure_view_dnsseckeys(dns_view_t *view, const cfg_obj_t *vconfig,
 	/*
 	 * Add key zone for managed-keys.
 	 */
-	obj = NULL;
-	(void)named_config_get(maps, "managed-keys-directory", &obj);
-	directory = (obj != NULL ? cfg_obj_asstring(obj) : NULL);
-	if (directory != NULL)
-		result = isc_file_isdirectory(directory);
-	if (result != ISC_R_SUCCESS) {
-		isc_log_write(named_g_lctx, DNS_LOGCATEGORY_SECURITY,
-			      NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR,
-			      "invalid managed-keys-directory %s: %s",
-			      directory, isc_result_totext(result));
-		goto cleanup;
+	CHECK(directory_fromconfig(maps, "managed-keys-directory", &directory));
+	if (!directory)
+		directory = dns_view_getdbdir(view);
 
-	}
 	CHECK(add_keydata_zone(view, directory, named_g_mctx));
 
   cleanup:
@@ -6903,6 +6924,32 @@ configure_session_key(const cfg_obj_t **maps, named_server_t *server,
 	return (result);
 }
 
+static isc_result_t
+setup_dbdir(dns_view_t *view, const cfg_obj_t *config, const cfg_obj_t *voptions)
+{
+	isc_result_t result;
+	const cfg_obj_t *maps[4];
+	const cfg_obj_t *options = NULL;
+	int i = 0;
+	const char *dir = NULL;
+
+	REQUIRE(config != NULL);
+
+	if (voptions != NULL)
+		maps[i++] = voptions;
+	result = cfg_map_get(config, "options", &options);
+	if (result == ISC_R_SUCCESS)
+		maps[i++] = options;
+	maps[i++] = named_g_defaults;
+	maps[i] = NULL;
+
+	CHECK(directory_fromconfig(maps, "databases-directory", &dir));
+	dns_view_setdbdir(view, dir);
+	CHECK(dns_view_ntapermanent(view));
+cleanup:
+	return (result);
+}
+
 #ifndef HAVE_LMDB
 static isc_result_t
 count_newzones(dns_view_t *view, ns_cfgctx_t *nzcfg, int *num_zonesp) {
@@ -6997,7 +7044,6 @@ setup_newzones(dns_view_t *view, cfg_obj_t *config, cfg_obj_t *vconfig,
 	const cfg_obj_t *maps[4];
 	const cfg_obj_t *options = NULL, *voptions = NULL;
 	const cfg_obj_t *nz = NULL;
-	const cfg_obj_t *nzdir = NULL;
 	const char *dir = NULL;
 	const cfg_obj_t *obj = NULL;
 	int i = 0;
@@ -7018,22 +7064,10 @@ setup_newzones(dns_view_t *view, cfg_obj_t *config, cfg_obj_t *vconfig,
 	result = named_config_get(maps, "allow-new-zones", &nz);
 	if (result == ISC_R_SUCCESS)
 		allow = cfg_obj_asboolean(nz);
-	result = named_config_get(maps, "new-zones-directory", &nzdir);
-	if (result == ISC_R_SUCCESS) {
-		dir = cfg_obj_asstring(nzdir);
-		if (dir != NULL) {
-			result = isc_file_isdirectory(dir);
-		}
-		if (result != ISC_R_SUCCESS) {
-			isc_log_write(named_g_lctx, DNS_LOGCATEGORY_SECURITY,
-				      NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR,
-				      "invalid new-zones-directory %s: %s",
-				      dir, isc_result_totext(result));
-			return (result);
-		}
-
-		dns_view_setnewzonedir(view, dir);
-	}
+	CHECK(directory_fromconfig(maps, "new-zones-directory", &dir));
+	if (!dir)
+		dir = dns_view_getdbdir(view);
+	dns_view_setnewzonedir(view, dir);
 
 #ifdef HAVE_LMDB
 	result = named_config_get(maps, "lmdb-mapsize", &obj);
@@ -7111,6 +7145,8 @@ setup_newzones(dns_view_t *view, cfg_obj_t *config, cfg_obj_t *vconfig,
 		cfg_obj_attach(vconfig, &nzcfg->vconfig);
 
 	result = count_newzones(view, nzcfg, num_zones);
+
+cleanup:
 	return (result);
 }
 
@@ -8231,6 +8267,7 @@ load_configuration(const char *filename, named_server_t *server,
 
 		num_zones += count_zones(voptions);
 
+		CHECK(setup_dbdir(view, config, voptions));
 		CHECK(setup_newzones(view, config, vconfig, conf_parser,
 				     named_g_aclconfctx, &nzf_num_zones));
 		num_zones += nzf_num_zones;
@@ -8250,6 +8287,7 @@ load_configuration(const char *filename, named_server_t *server,
 
 		num_zones = count_zones(config);
 
+		CHECK(setup_dbdir(view, config, NULL));
 		CHECK(setup_newzones(view, config, NULL, conf_parser,
 				     named_g_aclconfctx, &nzf_num_zones));
 		num_zones += nzf_num_zones;
@@ -14185,9 +14223,10 @@ named_server_saventa(named_server_t *server) {
 		if (result != ISC_R_SUCCESS) {
 			isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
 				      NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR,
-				      "error writing NTA file "
+				      "error writing NTA file '%s' "
 				      "for view '%s': %s",
-				      view->name, isc_result_totext(result));
+				      view->nta_file, view->name,
+				      isc_result_totext(result));
 		}
 	}
 
@@ -14210,9 +14249,10 @@ named_server_loadnta(named_server_t *server) {
 		{
 			isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
 				      NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR,
-				      "error loading NTA file "
+				      "error loading NTA file '%s' "
 				      "for view '%s': %s",
-				      view->name, isc_result_totext(result));
+				      view->nta_file, view->name,
+				      isc_result_totext(result));
 		}
 	}
 
diff --git a/bin/named/unix/os.c b/bin/named/unix/os.c
index 56f52c4..f6e6da3 100644
--- a/bin/named/unix/os.c
+++ b/bin/named/unix/os.c
@@ -1013,7 +1013,6 @@ next_token(char **stringp, const char *delim) {
 void
 named_os_shutdownmsg(char *command, isc_buffer_t *text) {
 	char *input, *ptr;
-	unsigned int n;
 	pid_t pid;
 
 	input = command;
diff --git a/doc/arm/Bv9ARM-book.xml b/doc/arm/Bv9ARM-book.xml
index b5399b7..3fbc77b 100644
--- a/doc/arm/Bv9ARM-book.xml
+++ b/doc/arm/Bv9ARM-book.xml
@@ -4263,6 +4263,7 @@ badresp:1,adberr:0,findfail:0,valfail:0]
   [ <command>key-directory</command> <replaceable>path_name</replaceable> ; ]
   [ <command>managed-keys-directory</command> <replaceable>path_name</replaceable> ; ]
   [ <command>new-zones-directory</command> <replaceable>path_name</replaceable> ; ]
+  [ <command>databases-directory</command> <replaceable>path_name</replaceable> ; ]
   [ <command>named-xfer</command> <replaceable>path_name</replaceable> ; ]
   [ <command>tkey-gssapi-keytab</command> <replaceable>path_name</replaceable> ; ]
   [ <command>tkey-gssapi-credential</command> <replaceable>principal</replaceable> ; ]
@@ -4992,6 +4993,20 @@ badresp:1,adberr:0,findfail:0,valfail:0]
 	  </varlistentry>
 
 	  <varlistentry>
+	    <term><command>databases-directory</command></term>
+	    <listitem>
+	      <para>
+		Specifies the directory in which to store various server managed
+		databases. It includes negative trust
+		anchors added via <command>rndc nta</command>, configuration
+		parameters for zones added via <command>rndc addzone</command>
+		and the files that track managed DNSSEC keys.
+		By default, this is the working directory.
+	      </para>
+	    </listitem>
+	  </varlistentry>
+
+	  <varlistentry>
 	    <term><command>named-xfer</command></term>
 	    <listitem>
 	      <para>
diff --git a/lib/dns/include/dns/view.h b/lib/dns/include/dns/view.h
index 2a04120..bbc4c1a 100644
--- a/lib/dns/include/dns/view.h
+++ b/lib/dns/include/dns/view.h
@@ -149,6 +149,7 @@ struct dns_view {
 	isc_uint32_t			nta_lifetime;
 	isc_uint32_t			nta_recheck;
 	char				*nta_file;
+	char				*db_dir;
 	dns_ttl_t			prefetch_trigger;
 	dns_ttl_t			prefetch_eligible;
 	in_port_t			dstport;
@@ -1302,6 +1303,31 @@ dns_view_setfailttl(dns_view_t *view, isc_uint32_t failttl);
  *\li	'view' to be valid.
  */
 
+const char *
+dns_view_getdbdir(dns_view_t *view);
+void
+dns_view_setdbdir(dns_view_t *view, const char *dir);
+/*%<
+ * Set/get the path to the directory in which managed files should
+ * be stored. Managed files are mkeys, nzf, nzd, nta.
+ * If the path was previously set to a non-NULL value,
+ * the previous value is freed.
+ *
+ * Requires:
+ * \li 'view' is valid.
+ */
+
+isc_result_t
+dns_view_ntapermanent(dns_view_t *view);
+/*%<
+ * Initialize NTA file path in this view.
+ * Required before dns_view_loadnta and dns_view_saventa.
+ *
+ * Requires:
+ * \li	'view' to be valid.
+ */
+
+
 isc_result_t
 dns_view_saventa(dns_view_t *view);
 /*%<
diff --git a/lib/dns/view.c b/lib/dns/view.c
index 8742006..08d3ac0 100644
--- a/lib/dns/view.c
+++ b/lib/dns/view.c
@@ -67,6 +67,10 @@
 #define DNS_VIEW_DELONLYHASH 111
 #define DNS_VIEW_FAILCACHESIZE 1021
 
+#ifndef PATH_MAX
+#define PATH_MAX 1024
+#endif
+
 static void resolver_shutdown(isc_task_t *task, isc_event_t *event);
 static void adb_shutdown(isc_task_t *task, isc_event_t *event);
 static void req_shutdown(isc_task_t *task, isc_event_t *event);
@@ -77,7 +81,6 @@ dns_view_create(isc_mem_t *mctx, dns_rdataclass_t rdclass,
 {
 	dns_view_t *view;
 	isc_result_t result;
-	char buffer[1024];
 
 	/*
 	 * Create a view.
@@ -90,7 +93,6 @@ dns_view_create(isc_mem_t *mctx, dns_rdataclass_t rdclass,
 	if (view == NULL)
 		return (ISC_R_NOMEMORY);
 
-	view->nta_file = NULL;
 	view->mctx = NULL;
 	isc_mem_attach(mctx, &view->mctx);
 	view->name = isc_mem_strdup(mctx, name);
@@ -99,15 +101,8 @@ dns_view_create(isc_mem_t *mctx, dns_rdataclass_t rdclass,
 		goto cleanup_view;
 	}
 
-	result = isc_file_sanitize(NULL, view->name, "nta",
-				   buffer, sizeof(buffer));
-	if (result != ISC_R_SUCCESS)
-		goto cleanup_name;
-	view->nta_file = isc_mem_strdup(mctx, buffer);
-	if (view->nta_file == NULL) {
-		result = ISC_R_NOMEMORY;
-		goto cleanup_name;
-	}
+	view->db_dir = NULL;
+	view->nta_file = NULL;
 
 	result = isc_mutex_init(&view->lock);
 	if (result != ISC_R_SUCCESS)
@@ -316,9 +311,6 @@ dns_view_create(isc_mem_t *mctx, dns_rdataclass_t rdclass,
  cleanup_mutex:
 	DESTROYLOCK(&view->lock);
 
-	if (view->nta_file != NULL)
-		isc_mem_free(mctx, view->nta_file);
-
  cleanup_name:
 	isc_mem_free(mctx, view->name);
 
@@ -533,7 +525,10 @@ destroy(dns_view_t *view) {
 	DESTROYLOCK(&view->new_zone_lock);
 	DESTROYLOCK(&view->lock);
 	isc_refcount_destroy(&view->references);
-	isc_mem_free(view->mctx, view->nta_file);
+	if (view->nta_file != NULL)
+		isc_mem_free(view->mctx, view->nta_file);
+	if (view->db_dir != NULL)
+		isc_mem_free(view->mctx, view->db_dir);
 	isc_mem_free(view->mctx, view->name);
 	isc_mem_putanddetach(&view->mctx, view, sizeof(*view));
 }
@@ -2002,10 +1997,6 @@ dns_view_untrust(dns_view_t *view, const dns_name_t *keyname,
  * fit in 'buflen'.
  */
 
-#ifndef PATH_MAX
-#define PATH_MAX 1024
-#endif
-
 static isc_result_t
 nz_legacy(const char *directory, const char *viewname,
 	  const char *suffix, char *buffer, size_t buflen)
@@ -2045,7 +2036,7 @@ 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_R_SUCCESS;
-	char buffer[1024];
+	char buffer[PATH_MAX];
 #ifdef HAVE_LMDB
 	MDB_env *env = NULL;
 	int status;
@@ -2283,6 +2274,46 @@ dns_view_setfailttl(dns_view_t *view, isc_uint32_t fail_ttl) {
 	view->fail_ttl = fail_ttl;
 }
 
+const char *
+dns_view_getdbdir(dns_view_t *view) {
+	REQUIRE(DNS_VIEW_VALID(view));
+
+	return (view->db_dir);
+}
+
+void
+dns_view_setdbdir(dns_view_t *view, const char *dir) {
+	REQUIRE(DNS_VIEW_VALID(view));
+
+	if (view->db_dir != NULL) {
+		isc_mem_free(view->mctx, view->db_dir);
+		view->db_dir = NULL;
+	}
+
+	if (dir != NULL)
+		view->db_dir = isc_mem_strdup(view->mctx, dir);
+}
+
+isc_result_t
+dns_view_ntapermanent(dns_view_t *view) {
+	isc_result_t result;
+	char buffer[PATH_MAX];
+
+	REQUIRE(view != NULL);
+	REQUIRE(view->name != NULL);
+	REQUIRE(view->mctx != NULL);
+
+	if (view->nta_file != NULL)
+		isc_mem_free(view->mctx, view->nta_file);
+	CHECK(isc_file_sanitize(view->db_dir, view->name, "nta",
+				   buffer, sizeof(buffer)));
+	view->nta_file = isc_mem_strdup(view->mctx, buffer);
+	if (view->nta_file == NULL)
+		result = ISC_R_NOMEMORY;
+cleanup:
+	return (result);
+}
+
 isc_result_t
 dns_view_saventa(dns_view_t *view) {
 	isc_result_t result;
@@ -2295,17 +2326,17 @@ dns_view_saventa(dns_view_t *view) {
 	if (view->nta_lifetime == 0)
 		return (ISC_R_SUCCESS);
 
-	/* Open NTA save file for overwrite. */
-	CHECK(isc_stdio_open(view->nta_file, "w", &fp));
-
 	result = dns_view_getntatable(view, &ntatable);
-	if (result == ISC_R_NOTFOUND) {
+	if (result == ISC_R_NOTFOUND || view->nta_file == NULL) {
 		removefile = ISC_TRUE;
 		result = ISC_R_SUCCESS;
 		goto cleanup;
 	} else
 		CHECK(result);
 
+	/* Open NTA save file for overwrite. */
+	CHECK(isc_stdio_open(view->nta_file, "w", &fp));
+
 	result = dns_ntatable_save(ntatable, fp);
 	if (result == ISC_R_NOTFOUND) {
 		removefile = ISC_TRUE;
@@ -2323,7 +2354,7 @@ dns_view_saventa(dns_view_t *view) {
 		(void)isc_stdio_close(fp);
 
 	/* Don't leave half-baked NTA save files lying around. */
-	if (result != ISC_R_SUCCESS || removefile)
+	if ((result != ISC_R_SUCCESS || removefile) && view->nta_file != NULL)
 		(void) isc_file_remove(view->nta_file);
 
 	return (result);
@@ -2344,6 +2375,8 @@ dns_view_loadnta(dns_view_t *view) {
 
 	if (view->nta_lifetime == 0)
 		return (ISC_R_SUCCESS);
+	if (view->nta_file == NULL)
+		return (ISC_R_NOTFOUND);
 
 	CHECK(isc_lex_create(view->mctx, 1025, &lex));
 	CHECK(isc_lex_openfile(lex, view->nta_file));
diff --git a/lib/isccfg/namedconf.c b/lib/isccfg/namedconf.c
index 8522860..51f7180 100644
--- a/lib/isccfg/namedconf.c
+++ b/lib/isccfg/namedconf.c
@@ -1857,6 +1857,7 @@ view_clauses[] = {
 	{ "check-names", &cfg_type_checknames, CFG_CLAUSEFLAG_MULTI },
 	{ "cleaning-interval", &cfg_type_uint32, 0 },
 	{ "clients-per-query", &cfg_type_uint32, 0 },
+	{ "databases-directory", &cfg_type_qstring, 0 },
 	{ "deny-answer-addresses", &cfg_type_denyaddresses, 0 },
 	{ "deny-answer-aliases", &cfg_type_denyaliases, 0 },
 	{ "disable-algorithms", &cfg_type_disablealgorithm,
-- 
2.9.5

