Commit ce96d432 authored by Evan Hunt's avatar Evan Hunt
Browse files

[master] new mkeys and nzf naming format

3999.	[func]		"mkeys" and "nzf" files are now named after
			their corresponding views, unless the view name
			contains characters that would be incompatible
			with use in a filename (i.e., slash, backslash,
			or capital letters). If a view name does contain
			these characters, the files will still be named
			using a cryptographic hash of the view name.
			Regardless of this, if a file using the old name
			format is found to exist, it will continue to be
			used. [RT #37704]
parent daf4204f
3999. [func] "mkeys" and "nzf" files are now named after
their corresponding views, unless the view name
contains characters that would be incompatible
with use in a filename (i.e., slash, backslash,
or capital letters). If a view name does contain
these characters, the files will still be named
using a cryptographic hash of the view name.
Regardless of this, if a file using the old name
format is found to exist, it will continue to be
used. [RT #37704]
3998. [bug] isc_radix_search was returning matches that were
too precise. [RT #37680]
......
......@@ -4569,9 +4569,6 @@ configure_zone(const cfg_obj_t *config, const cfg_obj_t *zconfig,
* Configure built-in zone for storing managed-key data.
*/
#define KEYZONE "managed-keys.bind"
#define MKEYS ".mkeys"
static isc_result_t
add_keydata_zone(dns_view_t *view, const char *directory, isc_mem_t *mctx) {
isc_result_t result;
......@@ -4579,8 +4576,7 @@ add_keydata_zone(dns_view_t *view, const char *directory, isc_mem_t *mctx) {
dns_zone_t *zone = NULL;
dns_acl_t *none = NULL;
char filename[PATH_MAX];
char buffer[ISC_SHA256_DIGESTSTRINGLENGTH + sizeof(MKEYS)];
int n;
isc_boolean_t defaultview;
REQUIRE(view != NULL);
......@@ -4604,15 +4600,11 @@ 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;
}
defaultview = ISC_TF(strcmp(view->name, "_default") == 0);
CHECK(isc_file_sanitize(directory,
defaultview ? "managed-keys" : view->name,
defaultview ? "bind" : "mkeys",
filename, sizeof(filename)));
CHECK(dns_zone_setfile(zone, filename));
dns_zone_setview(zone, view);
......
......@@ -699,10 +699,13 @@
</para>
<para>
The configuration is saved in a file called
<filename><replaceable>hash</replaceable>.nzf</filename>,
where <replaceable>hash</replaceable> is a
cryptographic hash generated from the name of
the view. When <command>named</command> is
<filename><replaceable>name</replaceable>.nzf</filename>,
where <replaceable>name</replaceable> is the
name of the view, or if it contains characters
that are incompatible with use as a file name, a
cryptographic hash generated from the name
of the view.
When <command>named</command> is
restarted, the file will be loaded into the view
configuration, so that zones that were added
can persist after a restart.
......
......@@ -261,7 +261,7 @@ status=`expr $status + $ret`
echo "I:checking new nzf file has comment ($n)"
ret=0
hcount=`grep "^# New zone file for view: external" ns2/3c4623849a49a539.nzf | wc -l`
hcount=`grep "^# New zone file for view: external" ns2/external.nzf | wc -l`
[ $hcount -eq 1 ] || ret=1
n=`expr $n + 1`
if [ $ret != 0 ]; then echo "I:failed"; fi
......
......@@ -5156,10 +5156,19 @@ badresp:1,adberr:0,findfail:0,valfail:0]
then managed keys for the server will be tracked in a single
file called <filename>managed-keys.bind</filename>.
Otherwise, managed keys will be tracked in separate files,
one file per view; each file name will be the SHA256 hash
of the view name, followed by the extension
one file per view; each file name will be the view name
(or, if it contains characters that are incompatible with
use as a file name, the SHA256 hash of the view name),
followed by the extension
<filename>.mkeys</filename>.
</para>
<para>
(Note: in previous releases, file names for views
always used the SHA256 hash of the view name. To ensure
compatibility after upgrade, if a file using the old
name format is found to exist, it will be used instead
of the new format.)
</para>
</listitem>
</varlistentry>
 
......
......@@ -259,6 +259,22 @@
Added support for OPENPGPKEY type.
</para>
</listitem>
<listitem>
<para>
The names of the files used to store managed keys and added
zones for each view are no longer based on the SHA256 hash
of the view name, except when this is necessary because the
view name contains characters that would be incompatible with use
as a file name. For views whose names do not contain forward
slashes ('/'), backslashes ('\'), or capital letters - which
could potentially cause namespace collision problems on
case-insensitive filesystems - files will now be named
after the view (for example, <filename>internal.mkeys</filename>
or <filename>external.nzf</filename>). However, to ensure
consistent behavior when upgrading, if a file using the old
name format is found to exist, it will continue to be used.
</para>
</listitem>
</itemizedlist>
</sect2>
<sect2 id="relnotes_bugs">
......
......@@ -1906,8 +1906,6 @@ 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,
void (*cfg_destroy)(void **))
......@@ -1926,13 +1924,17 @@ dns_view_setnewzones(dns_view_t *view, isc_boolean_t allow, void *cfgctx,
}
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;
isc_result_t result;
char buffer[1024];
result = isc_file_sanitize(NULL, view->name, "nzf",
buffer, sizeof(buffer));
if (result == ISC_R_SUCCESS) {
view->new_zone_file = isc_mem_strdup(view->mctx,
buffer);
view->new_zone_config = cfgctx;
view->cfg_destroy = cfg_destroy;
}
}
}
......
......@@ -365,6 +365,35 @@ isc_file_munmap(void *addr, size_t len);
* this platform, then we simply free the memory.
*/
isc_result_t
isc_file_sanitize(const char *dir, const char *base, const char *ext,
char *path, size_t length);
/*%<
* Generate a sanitized filename, such as for MKEYS or NZF files.
*
* Historically, MKEYS and NZF files used SHA256 hashes of the view
* name for the filename; this was to deal with the possibility of
* forbidden characters such as "/" being in a view name, and to
* avoid problems with case-insensitive file systems.
*
* Given a basename 'base' and an extension 'ext', this function checks
* for the existence of file using the old-style name format in directory
* 'dir'. If found, it returns the path to that file. If there is no
* file already in place, a new pathname is generated; if the basename
* contains any excluded characters, then a truncated SHA256 hash is
* used, otherwise the basename is used. The path name is copied
* into 'path', which must point to a buffer of at least 'length'
* bytes.
*
* Requires:
* - base != NULL
* - path != NULL
*
* Returns:
* - ISC_R_SUCCESS on success
* - ISC_R_NOSPACE if the resulting path would be longer than 'length'
*/
ISC_LANG_ENDDECLS
#endif /* ISC_FILE_H */
......@@ -39,7 +39,8 @@ SRCS = isctest.c taskpool_test.c socket_test.c hash_test.c \
lex_test.c radix_test.c random_test.c \
sockaddr_test.c symtab_test.c task_test.c queue_test.c \
parse_test.c pool_test.c print_test.c regex_test.c \
socket_test.c safe_test.c time_test.c aes_test.c
socket_test.c safe_test.c time_test.c aes_test.c \
file_test.c
SUBDIRS =
TARGETS = taskpool_test@EXEEXT@ socket_test@EXEEXT@ hash_test@EXEEXT@ \
......@@ -47,7 +48,8 @@ TARGETS = taskpool_test@EXEEXT@ socket_test@EXEEXT@ hash_test@EXEEXT@ \
sockaddr_test@EXEEXT@ symtab_test@EXEEXT@ task_test@EXEEXT@ \
queue_test@EXEEXT@ parse_test@EXEEXT@ pool_test@EXEEXT@ \
print_test@EXEEXT@ regex_test@EXEEXT@ socket_test@EXEEXT@ \
safe_test@EXEEXT@ time_test@EXEEXT@ aes_test@EXEEXT@
safe_test@EXEEXT@ time_test@EXEEXT@ aes_test@EXEEXT@ \
file_test@EXEEXT@
@BIND9_MAKE_RULES@
......@@ -119,6 +121,10 @@ aes_test@EXEEXT@: aes_test.@O@ ${ISCDEPLIBS}
${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ \
aes_test.@O@ ${ISCLIBS} ${LIBS}
file_test@EXEEXT@: file_test.@O@ ${ISCDEPLIBS}
${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ \
file_test.@O@ ${ISCLIBS} ${LIBS}
unit::
sh ${top_srcdir}/unit/unittest.sh
......
/*
* Copyright (C) 2014 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.
*/
#include <config.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <atf-c.h>
#include <isc/file.h>
#include <isc/result.h>
ATF_TC(isc_file_sanitize);
ATF_TC_HEAD(isc_file_sanitize, tc) {
atf_tc_set_md_var(tc, "descr", "sanitized filenames");
}
#define NAME "internal"
#define SHA "3bed2cb3a3acf7b6a8ef408420cc682d5520e26976d354254f528c965612054f"
#define TRUNC_SHA "3bed2cb3a3acf7b6"
#define BAD1 "in/internal"
#define BADHASH1 "8bbb97a888791399"
#define BAD2 "Internal"
#define BADHASH2 "2ea1842b445b0c81"
#define F(x) "testdata/file/" x ".test"
ATF_TC_BODY(isc_file_sanitize, tc) {
isc_result_t result;
char buf[1024];
unlink(F(TRUNC_SHA));
unlink(F(SHA));
result = isc_file_sanitize("testdata/file", NAME, "test", buf, 1024);
ATF_CHECK_EQ(result, ISC_R_SUCCESS);
ATF_CHECK(strcmp(buf, F(NAME)) == 0);
creat(F(TRUNC_SHA), 0644);
result = isc_file_sanitize("testdata/file", NAME, "test", buf, 1024);
ATF_CHECK_EQ(result, ISC_R_SUCCESS);
ATF_CHECK(strcmp(buf, F(TRUNC_SHA)) == 0);
creat(F(SHA), 0644);
result = isc_file_sanitize("testdata/file", NAME, "test", buf, 1024);
ATF_CHECK_EQ(result, ISC_R_SUCCESS);
ATF_CHECK(strcmp(buf, F(SHA)) == 0);
result = isc_file_sanitize("testdata/file", BAD1, "test", buf, 1024);
ATF_CHECK_EQ(result, ISC_R_SUCCESS);
ATF_CHECK(strcmp(buf, F(BADHASH1)) == 0);
result = isc_file_sanitize("testdata/file", BAD2, "test", buf, 1024);
ATF_CHECK_EQ(result, ISC_R_SUCCESS);
ATF_CHECK(strcmp(buf, F(BADHASH2)) == 0);
unlink(F(TRUNC_SHA));
unlink(F(SHA));
}
/*
* Main
*/
ATF_TP_ADD_TCS(tp) {
ATF_TP_ADD_TC(tp, isc_file_sanitize);
return (atf_no_error());
}
......@@ -70,6 +70,7 @@
#include <isc/log.h>
#include <isc/mem.h>
#include <isc/random.h>
#include <isc/sha2.h>
#include <isc/string.h>
#include <isc/time.h>
#include <isc/util.h>
......@@ -695,3 +696,72 @@ isc_file_munmap(void *addr, size_t len) {
return (0);
#endif
}
#define DISALLOW "\\/ABCDEFGHIJKLMNOPQRSTUVWXYZ"
#ifndef PATH_MAX
#define PATH_MAX 1024
#endif
isc_result_t
isc_file_sanitize(const char *dir, const char *base, const char *ext,
char *path, size_t length)
{
char buf[PATH_MAX], hash[PATH_MAX];
size_t l = 0;
REQUIRE(base != NULL);
REQUIRE(path != NULL);
l = strlen(base) + 1;
/*
* allow room for a full sha256 hash (64 chars
* plus null terminator)
*/
if (l < 65)
l = 65;
if (dir != NULL)
l += strlen(dir) + 1;
if (ext != NULL)
l += strlen(ext) + 1;
if (l > length || l > PATH_MAX)
return (ISC_R_NOSPACE);
/* Check whether the full-length SHA256 hash filename exists */
isc_sha256_data((const void *) base, strlen(base), hash);
snprintf(buf, sizeof(buf), "%s%s%s%s%s",
dir != NULL ? dir : "", dir != NULL ? "/" : "",
hash, ext != NULL ? "." : "", ext != NULL ? ext : "");
if (isc_file_exists(buf)) {
strlcpy(path, buf, length);
return (ISC_R_SUCCESS);
}
/* Check for a truncated SHA256 hash filename */
hash[16] = '\0';
snprintf(buf, sizeof(buf), "%s%s%s%s%s",
dir != NULL ? dir : "", dir != NULL ? "/" : "",
hash, ext != NULL ? "." : "", ext != NULL ? ext : "");
if (isc_file_exists(buf)) {
strlcpy(path, buf, length);
return (ISC_R_SUCCESS);
}
/*
* If neither hash filename already exists, then we'll use
* the original base name if it has no disallowed characters,
* or the truncated hash name if it does.
*/
if (strpbrk(base, DISALLOW) != NULL) {
strlcpy(path, buf, length);
return (ISC_R_SUCCESS);
}
snprintf(buf, sizeof(buf), "%s%s%s%s%s",
dir != NULL ? dir : "", dir != NULL ? "/" : "",
base, ext != NULL ? "." : "", ext != NULL ? ext : "");
strlcpy(path, buf, length);
return (ISC_R_SUCCESS);
}
......@@ -766,3 +766,72 @@ isc_file_munmap(void *addr, size_t len) {
free(addr);
return (0);
}
#define DISALLOW "\\/:ABCDEFGHIJKLMNOPQRSTUVWXYZ"
#ifndef PATH_MAX
#define PATH_MAX 1024
#endif
isc_result_t
isc_file_sanitize(const char *dir, const char *base, const char *ext,
char *path, size_t length)
{
char buf[PATH_MAX], hash[PATH_MAX];
size_t l = 0;
REQUIRE(base != NULL);
REQUIRE(path != NULL);
l = strlen(base) + 1;
/*
* allow room for a full sha256 hash (64 chars
* plus null terminator)
*/
if (l < 65)
l = 65;
if (dir != NULL)
l += strlen(dir) + 1;
if (ext != NULL)
l += strlen(ext) + 1;
if (l > length || l > PATH_MAX)
return (ISC_R_NOSPACE);
/* Check whether the full-length SHA256 hash filename exists */
isc_sha256_data((const void *) base, strlen(base), hash);
snprintf(buf, sizeof(buf), "%s%s%s%s%s",
dir != NULL ? dir : "", dir != NULL ? "/" : "",
hash, ext != NULL ? "." : "", ext != NULL ? ext : "");
if (isc_file_exists(buf)) {
strlcpy(path, buf, length);
return (ISC_R_SUCCESS);
}
/* Check for a truncated SHA256 hash filename */
hash[16] = '\0';
snprintf(buf, sizeof(buf), "%s%s%s%s%s",
dir != NULL ? dir : "", dir != NULL ? "/" : "",
hash, ext != NULL ? "." : "", ext != NULL ? ext : "");
if (isc_file_exists(buf)) {
strlcpy(path, buf, length);
return (ISC_R_SUCCESS);
}
/*
* If neither hash filename already exists, then we'll use
* the original base name if it has no disallowed characters,
* or the truncated hash name if it does.
*/
if (strpbrk(base, DISALLOW) != NULL) {
strlcpy(path, buf, length);
return (ISC_R_SUCCESS);
}
snprintf(buf, sizeof(buf), "%s%s%s%s%s",
dir != NULL ? dir : "", dir != NULL ? "/" : "",
base, ext != NULL ? "." : "", ext != NULL ? ext : "");
strlcpy(path, buf, length);
return (ISC_R_SUCCESS);
}
......@@ -230,6 +230,7 @@ isc_file_rename
isc_file_renameunique
isc_file_safecreate
isc_file_safemovefile
isc_file_sanitize
isc_file_settime
isc_file_splitpath
isc_file_template
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment