mirror of
https://github.com/eclipse/mosquitto.git
synced 2025-05-08 16:52:13 +08:00
Warn on lax permissions on sensitive files.
- Broker will log warnings if sensitive files are world readable/writable, or if the owner/group is not the same as the user/group the broker is running as. In future versions the broker will refuse to open these files.
This commit is contained in:
parent
4093dad058
commit
4ca294fd9c
@ -11,6 +11,9 @@ Broker:
|
||||
on start after restoring from persistence. Closes #2634.
|
||||
- Fix connections being limited to 2048 on Windows. The limit is now 8192,
|
||||
where supported. Closes #2732.
|
||||
- Broker will log warnings if sensitive files are world readable/writable, or
|
||||
if the owner/group is not the same as the user/group the broker is running
|
||||
as. In future versions the broker will refuse to open these files.
|
||||
|
||||
Client library:
|
||||
- Use CLOCK_BOOTTIME when available, to keep track of time. This solves the
|
||||
|
@ -17,6 +17,7 @@ if (WITH_TLS AND CJSON_FOUND)
|
||||
dynsec_role.c
|
||||
../mosquitto_passwd/get_password.c ../mosquitto_passwd/get_password.h
|
||||
../../lib/memory_mosq.c ../../lib/memory_mosq.h
|
||||
../../lib/misc_mosq.c ../../lib/misc_mosq.h
|
||||
../../src/memory_public.c
|
||||
options.c
|
||||
../../src/password_mosq.c ../../src/password_mosq.h
|
||||
|
@ -23,6 +23,7 @@ OBJS= mosquitto_ctrl.o \
|
||||
get_password.o \
|
||||
memory_mosq.o \
|
||||
memory_public.o \
|
||||
misc_mosq.o \
|
||||
options.o \
|
||||
password_mosq.o
|
||||
|
||||
|
@ -30,6 +30,7 @@ Contributors:
|
||||
#include "mosquitto.h"
|
||||
#include "password_mosq.h"
|
||||
#include "get_password.h"
|
||||
#include "misc_mosq.h"
|
||||
|
||||
void dynsec__print_usage(void)
|
||||
{
|
||||
@ -738,7 +739,7 @@ static int dynsec_init(int argc, char *argv[])
|
||||
admin_password = password;
|
||||
}
|
||||
|
||||
fptr = fopen(filename, "rb");
|
||||
fptr = mosquitto__fopen(filename, "rb", true);
|
||||
if(fptr){
|
||||
fclose(fptr);
|
||||
fprintf(stderr, "dynsec init: '%s' already exists. Remove the file or use a different location..\n", filename);
|
||||
@ -753,7 +754,7 @@ static int dynsec_init(int argc, char *argv[])
|
||||
json_str = cJSON_Print(tree);
|
||||
cJSON_Delete(tree);
|
||||
|
||||
fptr = fopen(filename, "wb");
|
||||
fptr = mosquitto__fopen(filename, "wb", true);
|
||||
if(fptr){
|
||||
fprintf(fptr, "%s", json_str);
|
||||
free(json_str);
|
||||
|
@ -374,7 +374,7 @@ static int create_backup(const char *backup_file, FILE *fptr)
|
||||
{
|
||||
FILE *fbackup;
|
||||
|
||||
fbackup = fopen(backup_file, "wt");
|
||||
fbackup = mosquitto__fopen(backup_file, "wt", true);
|
||||
if(!fbackup){
|
||||
fprintf(stderr, "Error creating backup password file \"%s\", not continuing.\n", backup_file);
|
||||
return 1;
|
||||
@ -599,7 +599,7 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
password_cmd = password;
|
||||
}
|
||||
fptr = fopen(password_file, "wt");
|
||||
fptr = mosquitto__fopen(password_file, "wt", true);
|
||||
if(!fptr){
|
||||
fprintf(stderr, "Error: Unable to open file %s for writing. %s.\n", password_file, strerror(errno));
|
||||
free(password_file);
|
||||
@ -610,7 +610,7 @@ int main(int argc, char *argv[])
|
||||
fclose(fptr);
|
||||
return rc;
|
||||
}else{
|
||||
fptr = fopen(password_file, "r+t");
|
||||
fptr = mosquitto__fopen(password_file, "r+t", true);
|
||||
if(!fptr){
|
||||
fprintf(stderr, "Error: Unable to open password file %s. %s.\n", password_file, strerror(errno));
|
||||
free(password_file);
|
||||
|
@ -38,6 +38,8 @@ Contributors:
|
||||
# define PATH_MAX MAX_PATH
|
||||
#else
|
||||
# include <sys/stat.h>
|
||||
# include <pwd.h>
|
||||
# include <grp.h>
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
@ -149,6 +151,60 @@ FILE *mosquitto__fopen(const char *path, const char *mode, bool restrict_read)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if(restrict_read){
|
||||
if(statbuf.st_mode & S_IRWXO){
|
||||
#ifdef WITH_BROKER
|
||||
log__printf(NULL, MOSQ_LOG_WARNING,
|
||||
#else
|
||||
fprintf(stderr,
|
||||
#endif
|
||||
"Warning: File %s has world readable permissions. Future versions will refuse to load this file.",
|
||||
path);
|
||||
#if 0
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
if(statbuf.st_uid != getuid()){
|
||||
char buf[4096];
|
||||
struct passwd pw, *result;
|
||||
|
||||
getpwuid_r(getuid(), &pw, buf, sizeof(buf), &result);
|
||||
if(result){
|
||||
#ifdef WITH_BROKER
|
||||
log__printf(NULL, MOSQ_LOG_WARNING,
|
||||
#else
|
||||
fprintf(stderr,
|
||||
#endif
|
||||
"Warning: File %s owner is not %s. Future versions will refuse to load this file.",
|
||||
path, result->pw_name);
|
||||
}
|
||||
#if 0
|
||||
// Future version
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
if(statbuf.st_gid != getgid()){
|
||||
char buf[4096];
|
||||
struct group grp, *result;
|
||||
|
||||
getgrgid_r(getgid(), &grp, buf, sizeof(buf), &result);
|
||||
if(result){
|
||||
#ifdef WITH_BROKER
|
||||
log__printf(NULL, MOSQ_LOG_WARNING,
|
||||
#else
|
||||
fprintf(stderr,
|
||||
#endif
|
||||
"Warning: File %s group is not %s. Future versions will refuse to load this file.",
|
||||
path, result->gr_name);
|
||||
}
|
||||
#if 0
|
||||
// Future version
|
||||
return NULL
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if(!S_ISREG(statbuf.st_mode) && !S_ISLNK(statbuf.st_mode)){
|
||||
#ifdef WITH_BROKER
|
||||
log__printf(NULL, MOSQ_LOG_ERR, "Error: %s is not a file.", path);
|
||||
|
@ -41,6 +41,190 @@ static mosquitto_plugin_id_t *plg_id = NULL;
|
||||
static char *config_file = NULL;
|
||||
struct dynsec__acl_default_access default_access = {false, false, false, false};
|
||||
|
||||
#ifdef WIN32
|
||||
# include <winsock2.h>
|
||||
# include <aclapi.h>
|
||||
# include <io.h>
|
||||
# include <lmcons.h>
|
||||
# include <fcntl.h>
|
||||
# define PATH_MAX MAX_PATH
|
||||
#else
|
||||
# include <sys/stat.h>
|
||||
# include <pwd.h>
|
||||
# include <grp.h>
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
/* Temporary - remove in 2.1 */
|
||||
FILE *mosquitto__fopen(const char *path, const char *mode, bool restrict_read)
|
||||
{
|
||||
#ifdef WIN32
|
||||
char buf[4096];
|
||||
int rc;
|
||||
int flags = 0;
|
||||
|
||||
rc = ExpandEnvironmentStringsA(path, buf, 4096);
|
||||
if(rc == 0 || rc > 4096){
|
||||
return NULL;
|
||||
}else{
|
||||
if (restrict_read) {
|
||||
HANDLE hfile;
|
||||
SECURITY_ATTRIBUTES sec;
|
||||
EXPLICIT_ACCESS_A ea;
|
||||
PACL pacl = NULL;
|
||||
char username[UNLEN + 1];
|
||||
DWORD ulen = UNLEN;
|
||||
SECURITY_DESCRIPTOR sd;
|
||||
DWORD dwCreationDisposition;
|
||||
int fd;
|
||||
FILE *fptr;
|
||||
|
||||
switch(mode[0]){
|
||||
case 'a':
|
||||
dwCreationDisposition = OPEN_ALWAYS;
|
||||
flags = _O_APPEND;
|
||||
break;
|
||||
case 'r':
|
||||
dwCreationDisposition = OPEN_EXISTING;
|
||||
flags = _O_RDONLY;
|
||||
break;
|
||||
case 'w':
|
||||
dwCreationDisposition = CREATE_ALWAYS;
|
||||
break;
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
GetUserNameA(username, &ulen);
|
||||
if (!InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION)) {
|
||||
return NULL;
|
||||
}
|
||||
BuildExplicitAccessWithNameA(&ea, username, GENERIC_ALL, SET_ACCESS, NO_INHERITANCE);
|
||||
if (SetEntriesInAclA(1, &ea, NULL, &pacl) != ERROR_SUCCESS) {
|
||||
return NULL;
|
||||
}
|
||||
if (!SetSecurityDescriptorDacl(&sd, TRUE, pacl, FALSE)) {
|
||||
LocalFree(pacl);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset(&sec, 0, sizeof(sec));
|
||||
sec.nLength = sizeof(SECURITY_ATTRIBUTES);
|
||||
sec.bInheritHandle = FALSE;
|
||||
sec.lpSecurityDescriptor = &sd;
|
||||
|
||||
hfile = CreateFileA(buf, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ,
|
||||
&sec,
|
||||
dwCreationDisposition,
|
||||
FILE_ATTRIBUTE_NORMAL,
|
||||
NULL);
|
||||
|
||||
LocalFree(pacl);
|
||||
|
||||
fd = _open_osfhandle((intptr_t)hfile, flags);
|
||||
if (fd < 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
fptr = _fdopen(fd, mode);
|
||||
if (!fptr) {
|
||||
_close(fd);
|
||||
return NULL;
|
||||
}
|
||||
if(mode[0] == 'a'){
|
||||
fseek(fptr, 0, SEEK_END);
|
||||
}
|
||||
return fptr;
|
||||
|
||||
}else {
|
||||
return fopen(buf, mode);
|
||||
}
|
||||
}
|
||||
#else
|
||||
FILE *fptr;
|
||||
struct stat statbuf;
|
||||
|
||||
if (restrict_read) {
|
||||
mode_t old_mask;
|
||||
|
||||
old_mask = umask(0077);
|
||||
fptr = fopen(path, mode);
|
||||
umask(old_mask);
|
||||
}else{
|
||||
fptr = fopen(path, mode);
|
||||
}
|
||||
if(!fptr) return NULL;
|
||||
|
||||
if(fstat(fileno(fptr), &statbuf) < 0){
|
||||
fclose(fptr);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if(restrict_read){
|
||||
if(statbuf.st_mode & S_IRWXO){
|
||||
#ifdef WITH_BROKER
|
||||
log__printf(NULL, MOSQ_LOG_WARNING,
|
||||
#else
|
||||
fprintf(stderr,
|
||||
#endif
|
||||
"Warning: File %s has world readable permissions. Future versions will refuse to load this file.",
|
||||
path);
|
||||
#if 0
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
if(statbuf.st_uid != getuid()){
|
||||
char buf[4096];
|
||||
struct passwd pw, *result;
|
||||
|
||||
getpwuid_r(getuid(), &pw, buf, sizeof(buf), &result);
|
||||
if(result){
|
||||
#ifdef WITH_BROKER
|
||||
log__printf(NULL, MOSQ_LOG_WARNING,
|
||||
#else
|
||||
fprintf(stderr,
|
||||
#endif
|
||||
"Warning: File %s owner is not %s. Future versions will refuse to load this file.",
|
||||
path, result->pw_name);
|
||||
}
|
||||
#if 0
|
||||
// Future version
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
if(statbuf.st_gid != getgid()){
|
||||
char buf[4096];
|
||||
struct group grp, *result;
|
||||
|
||||
getgrgid_r(getgid(), &grp, buf, sizeof(buf), &result);
|
||||
if(result){
|
||||
#ifdef WITH_BROKER
|
||||
log__printf(NULL, MOSQ_LOG_WARNING,
|
||||
#else
|
||||
fprintf(stderr,
|
||||
#endif
|
||||
"Warning: File %s group is not %s. Future versions will refuse to load this file.",
|
||||
path, result->gr_name);
|
||||
}
|
||||
#if 0
|
||||
// Future version
|
||||
return NULL
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if(!S_ISREG(statbuf.st_mode) && !S_ISLNK(statbuf.st_mode)){
|
||||
#ifdef WITH_BROKER
|
||||
log__printf(NULL, MOSQ_LOG_ERR, "Error: %s is not a file.", path);
|
||||
#endif
|
||||
fclose(fptr);
|
||||
return NULL;
|
||||
}
|
||||
return fptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void dynsec__command_reply(cJSON *j_responses, struct mosquitto *context, const char *command, const char *error, const char *correlation_data)
|
||||
{
|
||||
cJSON *j_response;
|
||||
@ -359,7 +543,7 @@ static int dynsec__config_load(void)
|
||||
|
||||
/* Load from file */
|
||||
errno = 0;
|
||||
fptr = fopen(config_file, "rb");
|
||||
fptr = mosquitto__fopen(config_file, "rb", true);
|
||||
if(fptr == NULL){
|
||||
mosquitto_log_printf(MOSQ_LOG_ERR, "Error loading Dynamic security plugin config: File is not readable - check permissions.\n");
|
||||
return MOSQ_ERR_ERRNO;
|
||||
@ -460,7 +644,7 @@ void dynsec__config_save(void)
|
||||
}
|
||||
snprintf(file_path, file_path_len, "%s.new", config_file);
|
||||
|
||||
fptr = fopen(file_path, "wt");
|
||||
fptr = mosquitto__fopen(file_path, "wt", true);
|
||||
if(fptr == NULL){
|
||||
mosquitto_free(json_str);
|
||||
mosquitto_free(file_path);
|
||||
|
@ -56,6 +56,7 @@ Contributors:
|
||||
#include "mosquitto_broker_internal.h"
|
||||
#include "mqtt_protocol.h"
|
||||
#include "memory_mosq.h"
|
||||
#include "misc_mosq.h"
|
||||
#include "net_mosq.h"
|
||||
#include "util_mosq.h"
|
||||
|
||||
@ -416,7 +417,7 @@ int net__tls_server_ctx(struct mosquitto__listener *listener)
|
||||
#endif
|
||||
|
||||
if(listener->dhparamfile){
|
||||
dhparamfile = fopen(listener->dhparamfile, "r");
|
||||
dhparamfile = mosquitto__fopen(listener->dhparamfile, "r", true);
|
||||
if(!dhparamfile){
|
||||
log__printf(NULL, MOSQ_LOG_ERR, "Error loading dhparamfile \"%s\".", listener->dhparamfile);
|
||||
return MOSQ_ERR_TLS;
|
||||
|
@ -427,7 +427,7 @@ int persist__restore(void)
|
||||
|
||||
db.msg_store_load = NULL;
|
||||
|
||||
fptr = mosquitto__fopen(db.config->persistence_filepath, "rb", false);
|
||||
fptr = mosquitto__fopen(db.config->persistence_filepath, "rb", true);
|
||||
if(fptr == NULL) return MOSQ_ERR_SUCCESS;
|
||||
rlen = fread(&header, 1, 15, fptr);
|
||||
if(rlen == 0){
|
||||
|
@ -530,7 +530,7 @@ static int aclfile__parse(struct mosquitto__security_options *security_opts)
|
||||
return MOSQ_ERR_NOMEM;
|
||||
}
|
||||
|
||||
aclfptr = mosquitto__fopen(security_opts->acl_file, "rt", false);
|
||||
aclfptr = mosquitto__fopen(security_opts->acl_file, "rt", true);
|
||||
if(!aclfptr){
|
||||
mosquitto__free(buf);
|
||||
log__printf(NULL, MOSQ_LOG_ERR, "Error: Unable to open acl_file \"%s\".", security_opts->acl_file);
|
||||
@ -755,7 +755,7 @@ static int pwfile__parse(const char *file, struct mosquitto__unpwd **root)
|
||||
return MOSQ_ERR_NOMEM;
|
||||
}
|
||||
|
||||
pwfile = mosquitto__fopen(file, "rt", false);
|
||||
pwfile = mosquitto__fopen(file, "rt", true);
|
||||
if(!pwfile){
|
||||
log__printf(NULL, MOSQ_LOG_ERR, "Error: Unable to open pwfile \"%s\".", file);
|
||||
mosquitto__free(buf);
|
||||
|
Loading…
x
Reference in New Issue
Block a user