mirror of
https://gitlab.isc.org/isc-projects/bind9
synced 2025-08-31 06:25:31 +00:00
Merge branch '3982-remove-isc_fsaccess-API' into 'main'
Replace isc_fsaccess API with more secure file creation Closes #3982 See merge request isc-projects/bind9!7766
This commit is contained in:
4
CHANGES
4
CHANGES
@@ -1,3 +1,7 @@
|
||||
6136. [cleanup] Remove the isc_fsaccess API in favor of creating
|
||||
temporary file first and atomically replace the key
|
||||
with non-truncated content. [GL #3982]
|
||||
|
||||
6135. [cleanup] Change isc_stdtime_get(&t) to t = isc_stdtime_now().
|
||||
[GL !7757]
|
||||
|
||||
|
@@ -29,18 +29,21 @@
|
||||
|
||||
/*! \file */
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <isc/buffer.h>
|
||||
#include <isc/dir.h>
|
||||
#include <isc/file.h>
|
||||
#include <isc/fsaccess.h>
|
||||
#include <isc/lex.h>
|
||||
#include <isc/mem.h>
|
||||
#include <isc/once.h>
|
||||
#include <isc/os.h>
|
||||
#include <isc/random.h>
|
||||
#include <isc/refcount.h>
|
||||
#include <isc/safe.h>
|
||||
@@ -893,6 +896,66 @@ out:
|
||||
return (result);
|
||||
}
|
||||
|
||||
FILE *
|
||||
dst_key_open(char *tmpname, mode_t mode) {
|
||||
/* Create public key file. */
|
||||
int fd = mkstemp(tmpname);
|
||||
if (fd == -1) {
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
if (fchmod(fd, mode & ~isc_os_umask()) != 0) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
FILE *fp = fdopen(fd, "w");
|
||||
if (fp == NULL) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
return (fp);
|
||||
error:
|
||||
(void)close(fd);
|
||||
(void)unlink(tmpname);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
isc_result_t
|
||||
dst_key_close(char *tmpname, FILE *fp, char *filename) {
|
||||
if ((fflush(fp) != 0) || (ferror(fp) != 0)) {
|
||||
return (dst_key_cleanup(tmpname, fp));
|
||||
}
|
||||
|
||||
if (rename(tmpname, filename) != 0) {
|
||||
return (dst_key_cleanup(tmpname, fp));
|
||||
}
|
||||
|
||||
if (fclose(fp) != 0) {
|
||||
/*
|
||||
* This is in fact error, but we don't care at this point,
|
||||
* as the file has been already flushed to disk.
|
||||
*/
|
||||
}
|
||||
|
||||
return (ISC_R_SUCCESS);
|
||||
}
|
||||
|
||||
isc_result_t
|
||||
dst_key_cleanup(char *tmpname, FILE *fp) {
|
||||
if (ftruncate(fileno(fp), 0) != 0) {
|
||||
/*
|
||||
* ftruncate() result can't be ignored, but we don't care, as
|
||||
* any sensitive data are protected by the permissions, and
|
||||
* unlinked in the next step, this is just a good practice.
|
||||
*/
|
||||
}
|
||||
|
||||
(void)unlink(tmpname);
|
||||
(void)fclose(fp);
|
||||
|
||||
return (DST_R_WRITEERROR);
|
||||
}
|
||||
|
||||
isc_result_t
|
||||
dst_key_buildinternal(const dns_name_t *name, unsigned int alg,
|
||||
unsigned int bits, unsigned int flags,
|
||||
@@ -1368,7 +1431,8 @@ dst_key_buildfilename(const dst_key_t *key, int type, const char *directory,
|
||||
isc_buffer_t *out) {
|
||||
REQUIRE(VALID_KEY(key));
|
||||
REQUIRE(type == DST_TYPE_PRIVATE || type == DST_TYPE_PUBLIC ||
|
||||
type == DST_TYPE_STATE || type == 0);
|
||||
type == DST_TYPE_STATE || type == DST_TYPE_TEMPLATE ||
|
||||
type == 0);
|
||||
|
||||
return (buildfilename(key->key_name, key->key_id, key->key_alg, type,
|
||||
directory, out));
|
||||
@@ -1988,9 +2052,10 @@ static isc_result_t
|
||||
write_key_state(const dst_key_t *key, int type, const char *directory) {
|
||||
FILE *fp;
|
||||
isc_buffer_t fileb;
|
||||
isc_buffer_t tmpb;
|
||||
char filename[NAME_MAX];
|
||||
isc_result_t ret;
|
||||
isc_fsaccess_t access;
|
||||
char tmpname[NAME_MAX];
|
||||
isc_result_t result;
|
||||
|
||||
REQUIRE(VALID_KEY(key));
|
||||
|
||||
@@ -1998,33 +2063,33 @@ write_key_state(const dst_key_t *key, int type, const char *directory) {
|
||||
* Make the filename.
|
||||
*/
|
||||
isc_buffer_init(&fileb, filename, sizeof(filename));
|
||||
ret = dst_key_buildfilename(key, DST_TYPE_STATE, directory, &fileb);
|
||||
if (ret != ISC_R_SUCCESS) {
|
||||
return (ret);
|
||||
result = dst_key_buildfilename(key, DST_TYPE_STATE, directory, &fileb);
|
||||
if (result != ISC_R_SUCCESS) {
|
||||
return (result);
|
||||
}
|
||||
|
||||
/*
|
||||
* Create public key file.
|
||||
*/
|
||||
if ((fp = fopen(filename, "w")) == NULL) {
|
||||
isc_buffer_init(&tmpb, tmpname, sizeof(tmpname));
|
||||
result = dst_key_buildfilename(key, DST_TYPE_TEMPLATE, directory,
|
||||
&tmpb);
|
||||
if (result != ISC_R_SUCCESS) {
|
||||
return (result);
|
||||
}
|
||||
|
||||
mode_t mode = issymmetric(key) ? S_IRUSR | S_IWUSR
|
||||
: S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
|
||||
|
||||
/* Create temporary public key file. */
|
||||
fp = dst_key_open(tmpname, mode);
|
||||
if (fp == NULL) {
|
||||
return (DST_R_WRITEERROR);
|
||||
}
|
||||
|
||||
if (issymmetric(key)) {
|
||||
access = 0;
|
||||
isc_fsaccess_add(ISC_FSACCESS_OWNER,
|
||||
ISC_FSACCESS_READ | ISC_FSACCESS_WRITE,
|
||||
&access);
|
||||
(void)isc_fsaccess_set(filename, access);
|
||||
}
|
||||
|
||||
/* Write key state */
|
||||
if ((type & DST_TYPE_KEY) == 0) {
|
||||
fprintf(fp, "; This is the state of key %d, for ", key->key_id);
|
||||
ret = dns_name_print(key->key_name, fp);
|
||||
if (ret != ISC_R_SUCCESS) {
|
||||
fclose(fp);
|
||||
return (ret);
|
||||
result = dns_name_print(key->key_name, fp);
|
||||
if (result != ISC_R_SUCCESS) {
|
||||
return (dst_key_cleanup(tmpname, fp));
|
||||
}
|
||||
fputc('\n', fp);
|
||||
|
||||
@@ -2064,13 +2129,11 @@ write_key_state(const dst_key_t *key, int type, const char *directory) {
|
||||
printstate(key, DST_KEY_GOAL, "GoalState", fp);
|
||||
}
|
||||
|
||||
fflush(fp);
|
||||
if (ferror(fp)) {
|
||||
ret = DST_R_WRITEERROR;
|
||||
if (result != ISC_R_SUCCESS) {
|
||||
return (dst_key_cleanup(tmpname, fp));
|
||||
}
|
||||
fclose(fp);
|
||||
|
||||
return (ret);
|
||||
return (dst_key_close(tmpname, fp, filename));
|
||||
}
|
||||
|
||||
/*%
|
||||
@@ -2079,15 +2142,15 @@ write_key_state(const dst_key_t *key, int type, const char *directory) {
|
||||
static isc_result_t
|
||||
write_public_key(const dst_key_t *key, int type, const char *directory) {
|
||||
FILE *fp;
|
||||
isc_buffer_t keyb, textb, fileb, classb;
|
||||
isc_buffer_t keyb, tmpb, textb, fileb, classb;
|
||||
isc_region_t r;
|
||||
char tmpname[NAME_MAX];
|
||||
char filename[NAME_MAX];
|
||||
unsigned char key_array[DST_KEY_MAXSIZE];
|
||||
char text_array[DST_KEY_MAXTEXTSIZE];
|
||||
char class_array[10];
|
||||
isc_result_t ret;
|
||||
dns_rdata_t rdata = DNS_RDATA_INIT;
|
||||
isc_fsaccess_t access;
|
||||
|
||||
REQUIRE(VALID_KEY(key));
|
||||
|
||||
@@ -2122,19 +2185,19 @@ write_public_key(const dst_key_t *key, int type, const char *directory) {
|
||||
return (ret);
|
||||
}
|
||||
|
||||
/*
|
||||
* Create public key file.
|
||||
*/
|
||||
if ((fp = fopen(filename, "w")) == NULL) {
|
||||
return (DST_R_WRITEERROR);
|
||||
isc_buffer_init(&tmpb, tmpname, sizeof(tmpname));
|
||||
ret = dst_key_buildfilename(key, DST_TYPE_TEMPLATE, directory, &tmpb);
|
||||
if (ret != ISC_R_SUCCESS) {
|
||||
return (ret);
|
||||
}
|
||||
|
||||
if (issymmetric(key)) {
|
||||
access = 0;
|
||||
isc_fsaccess_add(ISC_FSACCESS_OWNER,
|
||||
ISC_FSACCESS_READ | ISC_FSACCESS_WRITE,
|
||||
&access);
|
||||
(void)isc_fsaccess_set(filename, access);
|
||||
/* Create temporary public key file. */
|
||||
mode_t mode = issymmetric(key) ? S_IRUSR | S_IWUSR
|
||||
: S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
|
||||
|
||||
fp = dst_key_open(tmpname, mode);
|
||||
if (fp == NULL) {
|
||||
return (DST_R_WRITEERROR);
|
||||
}
|
||||
|
||||
/* Write key information in comments */
|
||||
@@ -2164,6 +2227,9 @@ write_public_key(const dst_key_t *key, int type, const char *directory) {
|
||||
|
||||
/* Now print the actual key */
|
||||
ret = dns_name_print(key->key_name, fp);
|
||||
if (ret != ISC_R_SUCCESS) {
|
||||
return (dst_key_cleanup(tmpname, fp));
|
||||
}
|
||||
fprintf(fp, " ");
|
||||
|
||||
if (key->key_ttl != 0) {
|
||||
@@ -2172,7 +2238,7 @@ write_public_key(const dst_key_t *key, int type, const char *directory) {
|
||||
|
||||
isc_buffer_usedregion(&classb, &r);
|
||||
if ((unsigned int)fwrite(r.base, 1, r.length, fp) != r.length) {
|
||||
ret = DST_R_WRITEERROR;
|
||||
return (dst_key_cleanup(tmpname, fp));
|
||||
}
|
||||
|
||||
if ((type & DST_TYPE_KEY) != 0) {
|
||||
@@ -2183,17 +2249,16 @@ write_public_key(const dst_key_t *key, int type, const char *directory) {
|
||||
|
||||
isc_buffer_usedregion(&textb, &r);
|
||||
if ((unsigned int)fwrite(r.base, 1, r.length, fp) != r.length) {
|
||||
ret = DST_R_WRITEERROR;
|
||||
return (dst_key_cleanup(tmpname, fp));
|
||||
}
|
||||
|
||||
fputc('\n', fp);
|
||||
fflush(fp);
|
||||
if (ferror(fp)) {
|
||||
ret = DST_R_WRITEERROR;
|
||||
}
|
||||
fclose(fp);
|
||||
|
||||
return (ret);
|
||||
if (ret != ISC_R_SUCCESS) {
|
||||
return (dst_key_cleanup(tmpname, fp));
|
||||
}
|
||||
|
||||
return (dst_key_close(tmpname, fp, filename));
|
||||
}
|
||||
|
||||
static isc_result_t
|
||||
@@ -2203,12 +2268,15 @@ buildfilename(dns_name_t *name, dns_keytag_t id, unsigned int alg,
|
||||
isc_result_t result;
|
||||
|
||||
REQUIRE(out != NULL);
|
||||
|
||||
if ((type & DST_TYPE_PRIVATE) != 0) {
|
||||
suffix = ".private";
|
||||
} else if ((type & DST_TYPE_PUBLIC) != 0) {
|
||||
suffix = ".key";
|
||||
} else if ((type & DST_TYPE_STATE) != 0) {
|
||||
suffix = ".state";
|
||||
} else if ((type & DST_TYPE_TEMPLATE) != 0) {
|
||||
suffix = ".XXXXXX";
|
||||
}
|
||||
|
||||
if (directory != NULL) {
|
||||
|
@@ -238,6 +238,16 @@ dst__mem_free(void *ptr);
|
||||
void *
|
||||
dst__mem_realloc(void *ptr, size_t size);
|
||||
|
||||
/*%
|
||||
* Secure private file handling
|
||||
*/
|
||||
FILE *
|
||||
dst_key_open(char *tmpname, mode_t mode);
|
||||
isc_result_t
|
||||
dst_key_close(char *tmpname, FILE *fp, char *filename);
|
||||
isc_result_t
|
||||
dst_key_cleanup(char *tmpname, FILE *fp);
|
||||
|
||||
ISC_LANG_ENDDECLS
|
||||
|
||||
/*! \file */
|
||||
|
@@ -30,11 +30,11 @@
|
||||
#include "dst_parse.h"
|
||||
#include <inttypes.h>
|
||||
#include <stdbool.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <isc/base64.h>
|
||||
#include <isc/dir.h>
|
||||
#include <isc/file.h>
|
||||
#include <isc/fsaccess.h>
|
||||
#include <isc/lex.h>
|
||||
#include <isc/mem.h>
|
||||
#include <isc/stdtime.h>
|
||||
@@ -593,11 +593,13 @@ dst__privstruct_writefile(const dst_key_t *key, const dst_private_t *priv,
|
||||
FILE *fp;
|
||||
isc_result_t result;
|
||||
char filename[NAME_MAX];
|
||||
char tmpname[NAME_MAX];
|
||||
char buffer[MAXFIELDSIZE * 2];
|
||||
isc_fsaccess_t access;
|
||||
isc_stdtime_t when;
|
||||
uint32_t value;
|
||||
isc_buffer_t b;
|
||||
isc_buffer_t fileb;
|
||||
isc_buffer_t tmpb;
|
||||
isc_region_t r;
|
||||
int major, minor;
|
||||
mode_t mode;
|
||||
@@ -612,14 +614,15 @@ dst__privstruct_writefile(const dst_key_t *key, const dst_private_t *priv,
|
||||
return (ret);
|
||||
}
|
||||
|
||||
isc_buffer_init(&b, filename, sizeof(filename));
|
||||
result = dst_key_buildfilename(key, DST_TYPE_PRIVATE, directory, &b);
|
||||
isc_buffer_init(&fileb, filename, sizeof(filename));
|
||||
result = dst_key_buildfilename(key, DST_TYPE_PRIVATE, directory,
|
||||
&fileb);
|
||||
if (result != ISC_R_SUCCESS) {
|
||||
return (result);
|
||||
}
|
||||
|
||||
result = isc_file_mode(filename, &mode);
|
||||
if (result == ISC_R_SUCCESS && mode != 0600) {
|
||||
if (result == ISC_R_SUCCESS && mode != (S_IRUSR | S_IWUSR)) {
|
||||
/* File exists; warn that we are changing its permissions */
|
||||
int level;
|
||||
|
||||
@@ -632,14 +635,17 @@ dst__privstruct_writefile(const dst_key_t *key, const dst_private_t *priv,
|
||||
filename, (unsigned int)mode);
|
||||
}
|
||||
|
||||
if ((fp = fopen(filename, "w")) == NULL) {
|
||||
return (DST_R_WRITEERROR);
|
||||
isc_buffer_init(&tmpb, tmpname, sizeof(tmpname));
|
||||
result = dst_key_buildfilename(key, DST_TYPE_TEMPLATE, directory,
|
||||
&tmpb);
|
||||
if (result != ISC_R_SUCCESS) {
|
||||
return (result);
|
||||
}
|
||||
|
||||
access = 0;
|
||||
isc_fsaccess_add(ISC_FSACCESS_OWNER,
|
||||
ISC_FSACCESS_READ | ISC_FSACCESS_WRITE, &access);
|
||||
(void)isc_fsaccess_set(filename, access);
|
||||
fp = dst_key_open(tmpname, S_IRUSR | S_IWUSR);
|
||||
if (fp == NULL) {
|
||||
return (DST_R_WRITEERROR);
|
||||
}
|
||||
|
||||
dst_key_getprivateformat(key, &major, &minor);
|
||||
if (major == 0 && minor == 0) {
|
||||
@@ -710,8 +716,7 @@ dst__privstruct_writefile(const dst_key_t *key, const dst_private_t *priv,
|
||||
isc_buffer_init(&b, buffer, sizeof(buffer));
|
||||
result = isc_base64_totext(&r, sizeof(buffer), "", &b);
|
||||
if (result != ISC_R_SUCCESS) {
|
||||
fclose(fp);
|
||||
return (DST_R_INVALIDPRIVATEKEY);
|
||||
return (dst_key_cleanup(tmpname, fp));
|
||||
}
|
||||
isc_buffer_usedregion(&b, &r);
|
||||
|
||||
@@ -742,8 +747,7 @@ dst__privstruct_writefile(const dst_key_t *key, const dst_private_t *priv,
|
||||
isc_buffer_init(&b, buffer, sizeof(buffer));
|
||||
result = dns_time32_totext(when, &b);
|
||||
if (result != ISC_R_SUCCESS) {
|
||||
fclose(fp);
|
||||
return (DST_R_INVALIDPRIVATEKEY);
|
||||
return (dst_key_cleanup(tmpname, fp));
|
||||
}
|
||||
|
||||
isc_buffer_usedregion(&b, &r);
|
||||
@@ -755,9 +759,7 @@ dst__privstruct_writefile(const dst_key_t *key, const dst_private_t *priv,
|
||||
}
|
||||
}
|
||||
|
||||
fflush(fp);
|
||||
result = ferror(fp) ? DST_R_WRITEERROR : ISC_R_SUCCESS;
|
||||
fclose(fp);
|
||||
result = dst_key_close(tmpname, fp, filename);
|
||||
return (result);
|
||||
}
|
||||
|
||||
|
@@ -119,10 +119,11 @@ typedef enum dst_algorithm {
|
||||
#define DST_KEY_MAXTEXTSIZE 2048
|
||||
|
||||
/*% 'Type' for dst_read_key() */
|
||||
#define DST_TYPE_KEY 0x1000000 /* KEY key */
|
||||
#define DST_TYPE_PRIVATE 0x2000000
|
||||
#define DST_TYPE_PUBLIC 0x4000000
|
||||
#define DST_TYPE_STATE 0x8000000
|
||||
#define DST_TYPE_KEY 0x1000000 /* KEY key */
|
||||
#define DST_TYPE_PRIVATE 0x2000000
|
||||
#define DST_TYPE_PUBLIC 0x4000000
|
||||
#define DST_TYPE_STATE 0x8000000
|
||||
#define DST_TYPE_TEMPLATE 0x10000000
|
||||
|
||||
/* Key timing metadata definitions */
|
||||
#define DST_TIME_CREATED 0
|
||||
@@ -772,11 +773,13 @@ dst_key_buildfilename(const dst_key_t *key, int type, const char *directory,
|
||||
/*%<
|
||||
* Generates the filename used by dst to store the specified key.
|
||||
* If directory is NULL, the current directory is assumed.
|
||||
* If tmp is not NULL, generates a template for mkstemp().
|
||||
*
|
||||
* Requires:
|
||||
*\li "key" is a valid key
|
||||
*\li "type" is either DST_TYPE_PUBLIC, DST_TYPE_PRIVATE, or 0 for no suffix.
|
||||
*\li "out" is a valid buffer
|
||||
*\li "tmp" is a valid buffer or NULL
|
||||
*
|
||||
* Ensures:
|
||||
*\li the file name will be written to "out", and the used pointer will
|
||||
|
@@ -29,7 +29,6 @@ libisc_la_HEADERS = \
|
||||
include/isc/error.h \
|
||||
include/isc/file.h \
|
||||
include/isc/formatcheck.h \
|
||||
include/isc/fsaccess.h \
|
||||
include/isc/fuzz.h \
|
||||
include/isc/getaddresses.h \
|
||||
include/isc/hash.h \
|
||||
@@ -130,9 +129,6 @@ libisc_la_SOURCES = \
|
||||
errno2result.h \
|
||||
error.c \
|
||||
file.c \
|
||||
fsaccess.c \
|
||||
fsaccess_common.c \
|
||||
fsaccess_common_p.h \
|
||||
getaddresses.c \
|
||||
hash.c \
|
||||
hashmap.c \
|
||||
|
@@ -1,86 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) Internet Systems Consortium, Inc. ("ISC")
|
||||
*
|
||||
* SPDX-License-Identifier: MPL-2.0
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* See the COPYRIGHT file distributed with this work for additional
|
||||
* information regarding copyright ownership.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdbool.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <isc/fsaccess.h>
|
||||
#include <isc/result.h>
|
||||
#include <isc/types.h>
|
||||
#include <isc/util.h>
|
||||
|
||||
#include "../fsaccess_common_p.h"
|
||||
#include "errno2result.h"
|
||||
|
||||
isc_result_t
|
||||
isc_fsaccess_set(const char *path, isc_fsaccess_t access) {
|
||||
struct stat statb;
|
||||
mode_t mode;
|
||||
bool is_dir = false;
|
||||
isc_fsaccess_t bits;
|
||||
isc_result_t result;
|
||||
|
||||
if (stat(path, &statb) != 0) {
|
||||
return (isc__errno2result(errno));
|
||||
}
|
||||
|
||||
if ((statb.st_mode & S_IFDIR) != 0) {
|
||||
is_dir = true;
|
||||
} else if ((statb.st_mode & S_IFREG) == 0) {
|
||||
return (ISC_R_INVALIDFILE);
|
||||
}
|
||||
|
||||
result = isc__fsaccess_check_bad_bits(access, is_dir);
|
||||
if (result != ISC_R_SUCCESS) {
|
||||
return (result);
|
||||
}
|
||||
|
||||
/*
|
||||
* Done with checking bad bits. Set mode_t.
|
||||
*/
|
||||
mode = 0;
|
||||
|
||||
#define SET_AND_CLEAR1(modebit) \
|
||||
if ((access & bits) != 0) { \
|
||||
mode |= modebit; \
|
||||
access &= ~bits; \
|
||||
}
|
||||
#define SET_AND_CLEAR(user, group, other) \
|
||||
SET_AND_CLEAR1(user); \
|
||||
bits <<= STEP; \
|
||||
SET_AND_CLEAR1(group); \
|
||||
bits <<= STEP; \
|
||||
SET_AND_CLEAR1(other);
|
||||
|
||||
bits = ISC_FSACCESS_READ | ISC_FSACCESS_LISTDIRECTORY;
|
||||
|
||||
SET_AND_CLEAR(S_IRUSR, S_IRGRP, S_IROTH);
|
||||
|
||||
bits = ISC_FSACCESS_WRITE | ISC_FSACCESS_CREATECHILD |
|
||||
ISC_FSACCESS_DELETECHILD;
|
||||
|
||||
SET_AND_CLEAR(S_IWUSR, S_IWGRP, S_IWOTH);
|
||||
|
||||
bits = ISC_FSACCESS_EXECUTE | ISC_FSACCESS_ACCESSCHILD;
|
||||
|
||||
SET_AND_CLEAR(S_IXUSR, S_IXGRP, S_IXOTH);
|
||||
|
||||
INSIST(access == 0);
|
||||
|
||||
if (chmod(path, mode) < 0) {
|
||||
return (isc__errno2result(errno));
|
||||
}
|
||||
|
||||
return (ISC_R_SUCCESS);
|
||||
}
|
@@ -1,96 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) Internet Systems Consortium, Inc. ("ISC")
|
||||
*
|
||||
* SPDX-License-Identifier: MPL-2.0
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* See the COPYRIGHT file distributed with this work for additional
|
||||
* information regarding copyright ownership.
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
* \brief
|
||||
* This file contains the OS-independent functionality of the API.
|
||||
*/
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <isc/fsaccess.h>
|
||||
#include <isc/result.h>
|
||||
#include <isc/types.h>
|
||||
#include <isc/util.h>
|
||||
|
||||
#include "fsaccess_common_p.h"
|
||||
|
||||
void
|
||||
isc_fsaccess_add(int trustee, int permission, isc_fsaccess_t *access) {
|
||||
REQUIRE(trustee <= 0x7);
|
||||
REQUIRE(permission <= 0xFF);
|
||||
|
||||
if ((trustee & ISC_FSACCESS_OWNER) != 0) {
|
||||
*access |= permission;
|
||||
}
|
||||
|
||||
if ((trustee & ISC_FSACCESS_GROUP) != 0) {
|
||||
*access |= (permission << GROUP);
|
||||
}
|
||||
|
||||
if ((trustee & ISC_FSACCESS_OTHER) != 0) {
|
||||
*access |= (permission << OTHER);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
isc_fsaccess_remove(int trustee, int permission, isc_fsaccess_t *access) {
|
||||
REQUIRE(trustee <= 0x7);
|
||||
REQUIRE(permission <= 0xFF);
|
||||
|
||||
if ((trustee & ISC_FSACCESS_OWNER) != 0) {
|
||||
*access &= ~permission;
|
||||
}
|
||||
|
||||
if ((trustee & ISC_FSACCESS_GROUP) != 0) {
|
||||
*access &= ~(permission << GROUP);
|
||||
}
|
||||
|
||||
if ((trustee & ISC_FSACCESS_OTHER) != 0) {
|
||||
*access &= ~(permission << OTHER);
|
||||
}
|
||||
}
|
||||
|
||||
isc_result_t
|
||||
isc__fsaccess_check_bad_bits(isc_fsaccess_t access, bool is_dir) {
|
||||
isc_fsaccess_t bits;
|
||||
|
||||
/*
|
||||
* Check for disallowed user bits.
|
||||
*/
|
||||
if (is_dir) {
|
||||
bits = ISC_FSACCESS_READ | ISC_FSACCESS_WRITE |
|
||||
ISC_FSACCESS_EXECUTE;
|
||||
} else {
|
||||
bits = ISC_FSACCESS_CREATECHILD | ISC_FSACCESS_ACCESSCHILD |
|
||||
ISC_FSACCESS_DELETECHILD | ISC_FSACCESS_LISTDIRECTORY;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set group bad bits.
|
||||
*/
|
||||
bits |= bits << STEP;
|
||||
/*
|
||||
* Set other bad bits.
|
||||
*/
|
||||
bits |= bits << STEP;
|
||||
|
||||
if ((access & bits) != 0) {
|
||||
if (is_dir) {
|
||||
return (ISC_R_NOTFILE);
|
||||
} else {
|
||||
return (ISC_R_NOTDIRECTORY);
|
||||
}
|
||||
}
|
||||
|
||||
return (ISC_R_SUCCESS);
|
||||
}
|
@@ -1,26 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) Internet Systems Consortium, Inc. ("ISC")
|
||||
*
|
||||
* SPDX-License-Identifier: MPL-2.0
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* See the COPYRIGHT file distributed with this work for additional
|
||||
* information regarding copyright ownership.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/*!
|
||||
* Shorthand. Maybe ISC__FSACCESS_PERMISSIONBITS should not even be in
|
||||
* <isc/fsaccess.h>. Could check consistency with sizeof(isc_fsaccess_t)
|
||||
* and the number of bits in each function.
|
||||
*/
|
||||
#define STEP (ISC__FSACCESS_PERMISSIONBITS)
|
||||
#define GROUP (STEP)
|
||||
#define OTHER (STEP * 2)
|
||||
|
||||
isc_result_t
|
||||
isc__fsaccess_check_bad_bits(isc_fsaccess_t access, bool is_dir);
|
@@ -1,171 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) Internet Systems Consortium, Inc. ("ISC")
|
||||
*
|
||||
* SPDX-License-Identifier: MPL-2.0
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* See the COPYRIGHT file distributed with this work for additional
|
||||
* information regarding copyright ownership.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/*! \file isc/fsaccess.h
|
||||
* \brief The ISC filesystem access module encapsulates the setting of file
|
||||
* and directory access permissions into one API that is meant to be
|
||||
* portable to multiple operating systems.
|
||||
*
|
||||
* The two primary operating system flavors that are initially accommodated
|
||||
* are POSIX and Windows NT 4.0 and later. The Windows NT access model is
|
||||
* considerable more flexible than POSIX's model (as much as I am loathe to
|
||||
* admit it), and so the ISC API has a higher degree of complexity than would
|
||||
* be needed to simply address POSIX's needs.
|
||||
*
|
||||
* The full breadth of NT's flexibility is not available either, for the
|
||||
* present time. Much of it is to provide compatibility with what Unix
|
||||
* programmers are expecting. This is also due to not yet really needing all
|
||||
* of the functionality of an NT system (or, for that matter, a POSIX system)
|
||||
* in BIND9, and so resolving how to handle the various incompatibilities has
|
||||
* been a purely theoretical exercise with no operational experience to
|
||||
* indicate how flawed the thinking may be.
|
||||
*
|
||||
* Some of the more notable dumbing down of NT for this API includes:
|
||||
*
|
||||
*\li Each of FILE_READ_DATA and FILE_READ_EA are set with #ISC_FSACCESS_READ.
|
||||
*
|
||||
* \li All of FILE_WRITE_DATA, FILE_WRITE_EA and FILE_APPEND_DATA are
|
||||
* set with #ISC_FSACCESS_WRITE. FILE_WRITE_ATTRIBUTES is not set
|
||||
* so as to be consistent with Unix, where only the owner of the file
|
||||
* or the superuser can change the attributes/mode of a file.
|
||||
*
|
||||
* \li Both of FILE_ADD_FILE and FILE_ADD_SUBDIRECTORY are set with
|
||||
* #ISC_FSACCESS_CREATECHILD. This is similar to setting the WRITE
|
||||
* permission on a Unix directory.
|
||||
*
|
||||
* \li SYNCHRONIZE is always set for files and directories, unless someone
|
||||
* can give me a reason why this is a bad idea.
|
||||
*
|
||||
* \li READ_CONTROL and FILE_READ_ATTRIBUTES are always set; this is
|
||||
* consistent with Unix, where any file or directory can be stat()'d
|
||||
* unless the directory path disallows complete access somewhere along
|
||||
* the way.
|
||||
*
|
||||
* \li WRITE_DAC is only set for the owner. This too is consistent with
|
||||
* Unix, and is tighter security than allowing anyone else to be
|
||||
* able to set permissions.
|
||||
*
|
||||
* \li DELETE is only set for the owner. On Unix the ability to delete
|
||||
* a file is controlled by the directory permissions, but it isn't
|
||||
* currently clear to me what happens on NT if the directory has
|
||||
* FILE_DELETE_CHILD set but a file within it does not have DELETE
|
||||
* set. Always setting DELETE on the file/directory for the owner
|
||||
* gives maximum flexibility to the owner without exposing the
|
||||
* file to deletion by others.
|
||||
*
|
||||
* \li WRITE_OWNER is never set. This too is consistent with Unix,
|
||||
* and is also tighter security than allowing anyone to change the
|
||||
* ownership of the file apart from the superu..ahem, Administrator.
|
||||
*
|
||||
* \li Inheritance is set to NO_INHERITANCE.
|
||||
*
|
||||
* Unix's dumbing down includes:
|
||||
*
|
||||
* \li The sticky bit cannot be set.
|
||||
*
|
||||
* \li setuid and setgid cannot be set.
|
||||
*
|
||||
* \li Only regular files and directories can be set.
|
||||
*
|
||||
* The rest of this comment discusses a few of the incompatibilities
|
||||
* between the two systems that need more thought if this API is to
|
||||
* be extended to accommodate them.
|
||||
*
|
||||
* The Windows standard access right "DELETE" doesn't have a direct
|
||||
* equivalent in the Unix world, so it isn't clear what should be done
|
||||
* with it.
|
||||
*
|
||||
* The Unix sticky bit is not supported. While NT does have a concept
|
||||
* of allowing users to create files in a directory but not delete or
|
||||
* rename them, it does not have a concept of allowing them to be deleted
|
||||
* if they are owned by the user trying to delete/rename. While it is
|
||||
* probable that something could be cobbled together in NT 5 with inheritance,
|
||||
* it can't really be done in NT 4 as a single property that you could
|
||||
* set on a directory. You'd need to coordinate something with file creation
|
||||
* so that every file created had DELETE set for the owner but no one else.
|
||||
*
|
||||
* On Unix systems, setting #ISC_FSACCESS_LISTDIRECTORY sets READ.
|
||||
* ... setting either #ISC_FSACCESS_CREATECHILD or #ISC_FSACCESS_DELETECHILD
|
||||
* sets WRITE.
|
||||
* ... setting #ISC_FSACCESS_ACCESSCHILD sets EXECUTE.
|
||||
*
|
||||
* On NT systems, setting #ISC_FSACCESS_LISTDIRECTORY sets FILE_LIST_DIRECTORY.
|
||||
* ... setting #ISC_FSACCESS_CREATECHILD sets FILE_CREATE_CHILD independently.
|
||||
* ... setting #ISC_FSACCESS_DELETECHILD sets FILE_DELETE_CHILD independently.
|
||||
* ... setting #ISC_FSACCESS_ACCESSCHILD sets FILE_TRAVERSE.
|
||||
*
|
||||
* Unresolved: XXXDCL
|
||||
* \li What NT access right controls the ability to rename a file?
|
||||
* \li How does DELETE work? If a directory has FILE_DELETE_CHILD but a
|
||||
* file or directory within it does not have DELETE, is that file
|
||||
* or directory deletable?
|
||||
* \li To implement isc_fsaccess_get(), mapping an existing Unix permission
|
||||
* mode_t back to an isc_fsaccess_t is pretty trivial; however, mapping
|
||||
* an NT DACL could be impossible to do in a responsible way.
|
||||
* \li Similarly, trying to implement the functionality of being able to
|
||||
* say "add group writability to whatever permissions already exist"
|
||||
* could be tricky on NT because of the order-of-entry issue combined
|
||||
* with possibly having one or more matching ACEs already explicitly
|
||||
* granting or denying access. Because this functionality is
|
||||
* not yet needed by the ISC, no code has been written to try to
|
||||
* solve this problem.
|
||||
*/
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
#include <isc/lang.h>
|
||||
#include <isc/types.h>
|
||||
|
||||
/*
|
||||
* Trustees.
|
||||
*/
|
||||
#define ISC_FSACCESS_OWNER 0x1 /*%< User account. */
|
||||
#define ISC_FSACCESS_GROUP 0x2 /*%< Primary group owner. */
|
||||
#define ISC_FSACCESS_OTHER 0x4 /*%< Not the owner or the group owner. */
|
||||
#define ISC_FSACCESS_WORLD 0x7 /*%< User, Group, Other. */
|
||||
|
||||
/*
|
||||
* Types of permission.
|
||||
*/
|
||||
#define ISC_FSACCESS_READ 0x00000001 /*%< File only. */
|
||||
#define ISC_FSACCESS_WRITE 0x00000002 /*%< File only. */
|
||||
#define ISC_FSACCESS_EXECUTE 0x00000004 /*%< File only. */
|
||||
#define ISC_FSACCESS_CREATECHILD 0x00000008 /*%< Dir only. */
|
||||
#define ISC_FSACCESS_DELETECHILD 0x00000010 /*%< Dir only. */
|
||||
#define ISC_FSACCESS_LISTDIRECTORY 0x00000020 /*%< Dir only. */
|
||||
#define ISC_FSACCESS_ACCESSCHILD 0x00000040 /*%< Dir only. */
|
||||
|
||||
/*%
|
||||
* Adding any permission bits beyond 0x200 would mean typedef'ing
|
||||
* isc_fsaccess_t as uint64_t, and redefining this value to
|
||||
* reflect the new range of permission types, Probably to 21 for
|
||||
* maximum flexibility. The number of bits has to accommodate all of
|
||||
* the permission types, and three full sets of them have to fit
|
||||
* within an isc_fsaccess_t.
|
||||
*/
|
||||
#define ISC__FSACCESS_PERMISSIONBITS 10
|
||||
|
||||
ISC_LANG_BEGINDECLS
|
||||
|
||||
void
|
||||
isc_fsaccess_add(int trustee, int permission, isc_fsaccess_t *access);
|
||||
|
||||
void
|
||||
isc_fsaccess_remove(int trustee, int permission, isc_fsaccess_t *access);
|
||||
|
||||
isc_result_t
|
||||
isc_fsaccess_set(const char *path, isc_fsaccess_t access);
|
||||
|
||||
ISC_LANG_ENDDECLS
|
@@ -14,6 +14,7 @@
|
||||
#pragma once
|
||||
|
||||
/*! \file isc/os.h */
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <isc/lang.h>
|
||||
#include <isc/types.h>
|
||||
@@ -37,9 +38,15 @@ isc_os_ncpus(void);
|
||||
unsigned long
|
||||
isc_os_cacheline(void);
|
||||
/*%<
|
||||
* Return L1 caheline size of the CPU.
|
||||
* Return L1 cacheline size of the CPU.
|
||||
* If L1 cache is greater than ISC_OS_CACHELINE_SIZE, ensure it is used
|
||||
* instead of constant. Is common on ppc64le architecture.
|
||||
*/
|
||||
|
||||
mode_t
|
||||
isc_os_umask(void);
|
||||
/*%<
|
||||
* Return umask of the current process as initialized at the program start
|
||||
*/
|
||||
|
||||
ISC_LANG_ENDDECLS
|
||||
|
@@ -38,7 +38,6 @@ typedef struct isc_constregion isc_constregion_t; /*%< Const region */
|
||||
typedef struct isc_consttextregion isc_consttextregion_t; /*%< Const Text Region
|
||||
*/
|
||||
typedef struct isc_counter isc_counter_t; /*%< Counter */
|
||||
typedef uint32_t isc_fsaccess_t; /*%< FS Access */
|
||||
typedef struct isc_hash isc_hash_t; /*%< Hash */
|
||||
typedef struct isc_httpd isc_httpd_t; /*%< HTTP client */
|
||||
typedef void(isc_httpdfree_t)(isc_buffer_t *, void *); /*%< HTTP free function
|
||||
|
14
lib/isc/os.c
14
lib/isc/os.c
@@ -12,6 +12,7 @@
|
||||
*/
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <isc/os.h>
|
||||
#include <isc/types.h>
|
||||
@@ -21,6 +22,7 @@
|
||||
|
||||
static unsigned int isc__os_ncpus = 0;
|
||||
static unsigned long isc__os_cacheline = ISC_OS_CACHELINE_SIZE;
|
||||
static mode_t isc__os_umask = 0;
|
||||
|
||||
#ifdef HAVE_SYSCONF
|
||||
|
||||
@@ -72,6 +74,12 @@ ncpus_initialize(void) {
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
umask_initialize(void) {
|
||||
isc__os_umask = umask(0);
|
||||
(void)umask(isc__os_umask);
|
||||
}
|
||||
|
||||
unsigned int
|
||||
isc_os_ncpus(void) {
|
||||
return (isc__os_ncpus);
|
||||
@@ -82,8 +90,14 @@ isc_os_cacheline(void) {
|
||||
return (isc__os_cacheline);
|
||||
}
|
||||
|
||||
mode_t
|
||||
isc_os_umask(void) {
|
||||
return (isc__os_umask);
|
||||
}
|
||||
|
||||
void
|
||||
isc__os_initialize(void) {
|
||||
umask_initialize();
|
||||
ncpus_initialize();
|
||||
#if defined(HAVE_SYSCONF) && defined(_SC_LEVEL1_DCACHE_LINESIZE)
|
||||
long s = sysconf(_SC_LEVEL1_DCACHE_LINESIZE);
|
||||
|
Reference in New Issue
Block a user