From a37c847375678ad3e639248aa9c688b04c6df8e2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Petr=20Men=C5=A1=C3=ADk?= <pemensik@redhat.com>
Date: Thu, 9 Mar 2017 20:37:44 +0100
Subject: [PATCH] Add new-zones-directory option, write .nzf there

---
 bin/named/server.c     | 103 ++++++++++++++++++++++++++++++++++++++++++-------
 lib/dns/view.c         |  21 +++-------
 lib/isccfg/namedconf.c |   1 +
 3 files changed, 97 insertions(+), 28 deletions(-)

diff --git a/bin/named/server.c b/bin/named/server.c
index 5d97a6b..0a34c4c 100644
--- a/bin/named/server.c
+++ b/bin/named/server.c
@@ -4069,21 +4069,64 @@ configure_zone(const cfg_obj_t *config, const cfg_obj_t *zconfig,
 }
 
 /*
+ * Create filename in directory and with name derived from viewname.
+ * Sets str to allocated copy of path on success.
+ *
+ * \param str Allocated copy of name
+ * \param directory Directory path, may be null
+ * \param viewname Name of view
+ * \param suffix Suffix of created file
+ * \param fileoverride If not null, uses fixed name instead of hash+suffix
+ * \param hashlen How many characters should be used from digest
+ */
+static isc_result_t
+hashed_filename(isc_mem_t *mctx, char **filepath,
+	const char *directory, const char *viewname,
+	const char *suffix, const char *fileoverride, unsigned hashlen)
+{
+	char filename[PATH_MAX];
+	char buffer[ISC_SHA256_DIGESTSTRINGLENGTH + 1];
+	int n;
+	isc_result_t result;
+
+	REQUIRE(hashlen <= ISC_SHA256_DIGESTSTRINGLENGTH);
+	REQUIRE(filepath != NULL && *filepath == NULL);
+	isc_sha256_data((const isc_uint8_t *)viewname, strlen(viewname), buffer);
+	buffer[hashlen] = '\0';
+	if (fileoverride != NULL)
+		suffix = "";
+	else
+		fileoverride = buffer;
+
+	n = snprintf(filename, sizeof(filename), "%s%s%s%s",
+		     directory ? directory : "", directory ? "/" : "",
+		     fileoverride, suffix);
+	if (n < 0) {
+		result = ISC_R_FAILURE;
+	} else if ((size_t)n >= sizeof(filename)) {
+		result = ISC_R_NOSPACE;
+	} else {
+		*filepath = isc_mem_strdup(mctx, filename);
+		result = ISC_R_SUCCESS;
+	}
+	return result;
+}
+
+/*
  * Configure built-in zone for storing managed-key data.
  */
 
 #define KEYZONE "managed-keys.bind"
 #define MKEYS ".mkeys"
 
+// FIXME: useful for nzf files, seems the same base is here
 static isc_result_t
 add_keydata_zone(dns_view_t *view, const char *directory, isc_mem_t *mctx) {
 	isc_result_t result;
 	dns_view_t *pview = NULL;
 	dns_zone_t *zone = NULL;
 	dns_acl_t *none = NULL;
-	char filename[PATH_MAX];
-	char buffer[ISC_SHA256_DIGESTSTRINGLENGTH + sizeof(MKEYS)];
-	int n;
+	char *filename = NULL;
 
 	REQUIRE(view != NULL);
 
@@ -4107,15 +4150,10 @@ add_keydata_zone(dns_view_t *view, const char *directory, isc_mem_t *mctx) {
 	CHECK(dns_zonemgr_createzone(ns_g_server->zonemgr, &zone));
 	CHECK(dns_zone_setorigin(zone, dns_rootname));
 
-	isc_sha256_data((void *)view->name, strlen(view->name), buffer);
-	strcat(buffer, MKEYS);
-	n = snprintf(filename, sizeof(filename), "%s%s%s",
-		     directory ? directory : "", directory ? "/" : "",
-		     strcmp(view->name, "_default") == 0 ? KEYZONE : buffer);
-	if (n < 0 || (size_t)n >= sizeof(filename)) {
-		result = (n < 0) ? ISC_R_FAILURE : ISC_R_NOSPACE;
-		goto cleanup;
-	}
+	CHECK(hashed_filename(view->mctx, &filename, directory,
+		     view->name, MKEYS,
+		     strcmp(view->name, "_default") == 0 ? KEYZONE : NULL,
+		     ISC_SHA256_DIGESTSTRINGLENGTH));
 	CHECK(dns_zone_setfile(zone, filename));
 
 	dns_zone_setview(zone, view);
@@ -4154,6 +4192,8 @@ cleanup:
 		dns_zone_detach(&zone);
 	if (none != NULL)
 		dns_acl_detach(&none);
+	if (filename != NULL)
+		isc_mem_free(view->mctx, filename);
 
 	return (result);
 }
@@ -4770,6 +4810,28 @@ configure_session_key(const cfg_obj_t **maps, ns_server_t *server,
 	return (result);
 }
 
+#define NZF ".nzf"
+
+/*
+ * Called only when new-zones is enabled.
+ * Moved here from dns_view_setnewzones.
+ */
+static isc_result_t
+allow_setnewzones(dns_view_t *view,
+		     const char *directory, void *cfgctx,
+		     void (*cfg_destroy)(void **))
+{
+	REQUIRE(DNS_VIEW_VALID(view));
+	REQUIRE(cfgctx != NULL && cfg_destroy != NULL);
+
+	dns_view_setnewzones(view, ISC_FALSE, NULL, NULL);
+	view->new_zone_config = cfgctx;
+	view->cfg_destroy = cfg_destroy;
+	return hashed_filename(view->mctx, &view->new_zone_file,
+			directory, view->name, NZF, NULL, 16);
+}
+
+
 static isc_result_t
 setup_newzones(dns_view_t *view, cfg_obj_t *config, cfg_obj_t *vconfig,
 	       cfg_parser_t *parser, cfg_aclconfctx_t *actx)
@@ -4782,7 +4844,9 @@ 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;
 	int i = 0;
+	const char *directory = NULL;
 
 	REQUIRE (config != NULL);
 
@@ -4799,6 +4863,18 @@ setup_newzones(dns_view_t *view, cfg_obj_t *config, cfg_obj_t *vconfig,
 	result = ns_config_get(maps, "allow-new-zones", &nz);
 	if (result == ISC_R_SUCCESS)
 		allow = cfg_obj_asboolean(nz);
+	result = ns_config_get(maps, "new-zones-directory", &nzdir);
+	if (result == ISC_R_SUCCESS)
+		directory = cfg_obj_asstring(nzdir);
+	if (directory != NULL)
+		result = isc_file_isdirectory(directory);
+	if (result != ISC_R_SUCCESS) {
+		isc_log_write(ns_g_lctx, DNS_LOGCATEGORY_SECURITY,
+			      NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
+			      "invalid new-zones-directory %s: %s",
+			      directory, isc_result_totext(result));
+		return result;
+	}
 
 	if (!allow) {
 		dns_view_setnewzones(view, ISC_FALSE, NULL, NULL);
@@ -4811,7 +4887,8 @@ setup_newzones(dns_view_t *view, cfg_obj_t *config, cfg_obj_t *vconfig,
 		return (ISC_R_NOMEMORY);
 	}
 
-	dns_view_setnewzones(view, allow, nzcfg, newzone_cfgctx_destroy);
+	(void)allow_setnewzones(view, directory, nzcfg,
+			newzone_cfgctx_destroy);
 
 	memset(nzcfg, 0, sizeof(*nzcfg));
 	isc_mem_attach(view->mctx, &nzcfg->mctx);
diff --git a/lib/dns/view.c b/lib/dns/view.c
index 142b09e..f768143 100644
--- a/lib/dns/view.c
+++ b/lib/dns/view.c
@@ -1843,14 +1843,15 @@ dns_view_untrust(dns_view_t *view, dns_name_t *keyname,
 	dst_key_free(&key);
 }
 
-#define NZF ".nzf"
-
 void
-dns_view_setnewzones(dns_view_t *view, isc_boolean_t allow, void *cfgctx,
+dns_view_setnewzones(dns_view_t *view, isc_boolean_t allow,
+		     void *cfgctx,
 		     void (*cfg_destroy)(void **))
 {
 	REQUIRE(DNS_VIEW_VALID(view));
-	REQUIRE((cfgctx != NULL && cfg_destroy != NULL) || !allow);
+	REQUIRE(!allow);
+	UNUSED(cfgctx);
+	UNUSED(cfg_destroy);
 
 #ifdef BIND9
 	if (view->new_zone_file != NULL) {
@@ -1863,18 +1864,8 @@ dns_view_setnewzones(dns_view_t *view, isc_boolean_t allow, void *cfgctx,
 		view->cfg_destroy = NULL;
 	}
 
-	if (allow) {
-		char buffer[ISC_SHA256_DIGESTSTRINGLENGTH + sizeof(NZF)];
-		isc_sha256_data((void *)view->name, strlen(view->name), buffer);
-		/* Truncate the hash at 16 chars; full length is overkill */
-		isc_string_printf(buffer + 16, sizeof(NZF), "%s", NZF);
-		view->new_zone_file = isc_mem_strdup(view->mctx, buffer);
-		view->new_zone_config = cfgctx;
-		view->cfg_destroy = cfg_destroy;
-	}
+	// allow code moved to bin/named/server.c
 #else
 	UNUSED(allow);
-	UNUSED(cfgctx);
-	UNUSED(cfg_destroy);
 #endif
 }
diff --git a/lib/isccfg/namedconf.c b/lib/isccfg/namedconf.c
index be28ea7..0659ce1 100644
--- a/lib/isccfg/namedconf.c
+++ b/lib/isccfg/namedconf.c
@@ -948,6 +948,7 @@ options_clauses[] = {
 	{ "memstatistics", &cfg_type_boolean, 0 },
 	{ "multiple-cnames", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE },
 	{ "named-xfer", &cfg_type_qstring, CFG_CLAUSEFLAG_OBSOLETE },
+	{ "new-zones-directory", &cfg_type_qstring, 0 },
 	{ "pid-file", &cfg_type_qstringornone, 0 },
 	{ "port", &cfg_type_uint32, 0 },
 	{ "querylog", &cfg_type_boolean, 0 },
-- 
2.9.3

