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.
|
on start after restoring from persistence. Closes #2634.
|
||||||
- Fix connections being limited to 2048 on Windows. The limit is now 8192,
|
- Fix connections being limited to 2048 on Windows. The limit is now 8192,
|
||||||
where supported. Closes #2732.
|
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:
|
Client library:
|
||||||
- Use CLOCK_BOOTTIME when available, to keep track of time. This solves the
|
- 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
|
dynsec_role.c
|
||||||
../mosquitto_passwd/get_password.c ../mosquitto_passwd/get_password.h
|
../mosquitto_passwd/get_password.c ../mosquitto_passwd/get_password.h
|
||||||
../../lib/memory_mosq.c ../../lib/memory_mosq.h
|
../../lib/memory_mosq.c ../../lib/memory_mosq.h
|
||||||
|
../../lib/misc_mosq.c ../../lib/misc_mosq.h
|
||||||
../../src/memory_public.c
|
../../src/memory_public.c
|
||||||
options.c
|
options.c
|
||||||
../../src/password_mosq.c ../../src/password_mosq.h
|
../../src/password_mosq.c ../../src/password_mosq.h
|
||||||
|
@ -23,6 +23,7 @@ OBJS= mosquitto_ctrl.o \
|
|||||||
get_password.o \
|
get_password.o \
|
||||||
memory_mosq.o \
|
memory_mosq.o \
|
||||||
memory_public.o \
|
memory_public.o \
|
||||||
|
misc_mosq.o \
|
||||||
options.o \
|
options.o \
|
||||||
password_mosq.o
|
password_mosq.o
|
||||||
|
|
||||||
|
@ -30,6 +30,7 @@ Contributors:
|
|||||||
#include "mosquitto.h"
|
#include "mosquitto.h"
|
||||||
#include "password_mosq.h"
|
#include "password_mosq.h"
|
||||||
#include "get_password.h"
|
#include "get_password.h"
|
||||||
|
#include "misc_mosq.h"
|
||||||
|
|
||||||
void dynsec__print_usage(void)
|
void dynsec__print_usage(void)
|
||||||
{
|
{
|
||||||
@ -738,7 +739,7 @@ static int dynsec_init(int argc, char *argv[])
|
|||||||
admin_password = password;
|
admin_password = password;
|
||||||
}
|
}
|
||||||
|
|
||||||
fptr = fopen(filename, "rb");
|
fptr = mosquitto__fopen(filename, "rb", true);
|
||||||
if(fptr){
|
if(fptr){
|
||||||
fclose(fptr);
|
fclose(fptr);
|
||||||
fprintf(stderr, "dynsec init: '%s' already exists. Remove the file or use a different location..\n", filename);
|
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);
|
json_str = cJSON_Print(tree);
|
||||||
cJSON_Delete(tree);
|
cJSON_Delete(tree);
|
||||||
|
|
||||||
fptr = fopen(filename, "wb");
|
fptr = mosquitto__fopen(filename, "wb", true);
|
||||||
if(fptr){
|
if(fptr){
|
||||||
fprintf(fptr, "%s", json_str);
|
fprintf(fptr, "%s", json_str);
|
||||||
free(json_str);
|
free(json_str);
|
||||||
|
@ -374,7 +374,7 @@ static int create_backup(const char *backup_file, FILE *fptr)
|
|||||||
{
|
{
|
||||||
FILE *fbackup;
|
FILE *fbackup;
|
||||||
|
|
||||||
fbackup = fopen(backup_file, "wt");
|
fbackup = mosquitto__fopen(backup_file, "wt", true);
|
||||||
if(!fbackup){
|
if(!fbackup){
|
||||||
fprintf(stderr, "Error creating backup password file \"%s\", not continuing.\n", backup_file);
|
fprintf(stderr, "Error creating backup password file \"%s\", not continuing.\n", backup_file);
|
||||||
return 1;
|
return 1;
|
||||||
@ -599,7 +599,7 @@ int main(int argc, char *argv[])
|
|||||||
}
|
}
|
||||||
password_cmd = password;
|
password_cmd = password;
|
||||||
}
|
}
|
||||||
fptr = fopen(password_file, "wt");
|
fptr = mosquitto__fopen(password_file, "wt", true);
|
||||||
if(!fptr){
|
if(!fptr){
|
||||||
fprintf(stderr, "Error: Unable to open file %s for writing. %s.\n", password_file, strerror(errno));
|
fprintf(stderr, "Error: Unable to open file %s for writing. %s.\n", password_file, strerror(errno));
|
||||||
free(password_file);
|
free(password_file);
|
||||||
@ -610,7 +610,7 @@ int main(int argc, char *argv[])
|
|||||||
fclose(fptr);
|
fclose(fptr);
|
||||||
return rc;
|
return rc;
|
||||||
}else{
|
}else{
|
||||||
fptr = fopen(password_file, "r+t");
|
fptr = mosquitto__fopen(password_file, "r+t", true);
|
||||||
if(!fptr){
|
if(!fptr){
|
||||||
fprintf(stderr, "Error: Unable to open password file %s. %s.\n", password_file, strerror(errno));
|
fprintf(stderr, "Error: Unable to open password file %s. %s.\n", password_file, strerror(errno));
|
||||||
free(password_file);
|
free(password_file);
|
||||||
|
@ -38,6 +38,8 @@ Contributors:
|
|||||||
# define PATH_MAX MAX_PATH
|
# define PATH_MAX MAX_PATH
|
||||||
#else
|
#else
|
||||||
# include <sys/stat.h>
|
# include <sys/stat.h>
|
||||||
|
# include <pwd.h>
|
||||||
|
# include <grp.h>
|
||||||
# include <unistd.h>
|
# include <unistd.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -149,6 +151,60 @@ FILE *mosquitto__fopen(const char *path, const char *mode, bool restrict_read)
|
|||||||
return NULL;
|
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)){
|
if(!S_ISREG(statbuf.st_mode) && !S_ISLNK(statbuf.st_mode)){
|
||||||
#ifdef WITH_BROKER
|
#ifdef WITH_BROKER
|
||||||
log__printf(NULL, MOSQ_LOG_ERR, "Error: %s is not a file.", path);
|
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;
|
static char *config_file = NULL;
|
||||||
struct dynsec__acl_default_access default_access = {false, false, false, false};
|
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)
|
void dynsec__command_reply(cJSON *j_responses, struct mosquitto *context, const char *command, const char *error, const char *correlation_data)
|
||||||
{
|
{
|
||||||
cJSON *j_response;
|
cJSON *j_response;
|
||||||
@ -359,7 +543,7 @@ static int dynsec__config_load(void)
|
|||||||
|
|
||||||
/* Load from file */
|
/* Load from file */
|
||||||
errno = 0;
|
errno = 0;
|
||||||
fptr = fopen(config_file, "rb");
|
fptr = mosquitto__fopen(config_file, "rb", true);
|
||||||
if(fptr == NULL){
|
if(fptr == NULL){
|
||||||
mosquitto_log_printf(MOSQ_LOG_ERR, "Error loading Dynamic security plugin config: File is not readable - check permissions.\n");
|
mosquitto_log_printf(MOSQ_LOG_ERR, "Error loading Dynamic security plugin config: File is not readable - check permissions.\n");
|
||||||
return MOSQ_ERR_ERRNO;
|
return MOSQ_ERR_ERRNO;
|
||||||
@ -460,7 +644,7 @@ void dynsec__config_save(void)
|
|||||||
}
|
}
|
||||||
snprintf(file_path, file_path_len, "%s.new", config_file);
|
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){
|
if(fptr == NULL){
|
||||||
mosquitto_free(json_str);
|
mosquitto_free(json_str);
|
||||||
mosquitto_free(file_path);
|
mosquitto_free(file_path);
|
||||||
|
@ -56,6 +56,7 @@ Contributors:
|
|||||||
#include "mosquitto_broker_internal.h"
|
#include "mosquitto_broker_internal.h"
|
||||||
#include "mqtt_protocol.h"
|
#include "mqtt_protocol.h"
|
||||||
#include "memory_mosq.h"
|
#include "memory_mosq.h"
|
||||||
|
#include "misc_mosq.h"
|
||||||
#include "net_mosq.h"
|
#include "net_mosq.h"
|
||||||
#include "util_mosq.h"
|
#include "util_mosq.h"
|
||||||
|
|
||||||
@ -416,7 +417,7 @@ int net__tls_server_ctx(struct mosquitto__listener *listener)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
if(listener->dhparamfile){
|
if(listener->dhparamfile){
|
||||||
dhparamfile = fopen(listener->dhparamfile, "r");
|
dhparamfile = mosquitto__fopen(listener->dhparamfile, "r", true);
|
||||||
if(!dhparamfile){
|
if(!dhparamfile){
|
||||||
log__printf(NULL, MOSQ_LOG_ERR, "Error loading dhparamfile \"%s\".", listener->dhparamfile);
|
log__printf(NULL, MOSQ_LOG_ERR, "Error loading dhparamfile \"%s\".", listener->dhparamfile);
|
||||||
return MOSQ_ERR_TLS;
|
return MOSQ_ERR_TLS;
|
||||||
|
@ -427,7 +427,7 @@ int persist__restore(void)
|
|||||||
|
|
||||||
db.msg_store_load = NULL;
|
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;
|
if(fptr == NULL) return MOSQ_ERR_SUCCESS;
|
||||||
rlen = fread(&header, 1, 15, fptr);
|
rlen = fread(&header, 1, 15, fptr);
|
||||||
if(rlen == 0){
|
if(rlen == 0){
|
||||||
|
@ -530,7 +530,7 @@ static int aclfile__parse(struct mosquitto__security_options *security_opts)
|
|||||||
return MOSQ_ERR_NOMEM;
|
return MOSQ_ERR_NOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
aclfptr = mosquitto__fopen(security_opts->acl_file, "rt", false);
|
aclfptr = mosquitto__fopen(security_opts->acl_file, "rt", true);
|
||||||
if(!aclfptr){
|
if(!aclfptr){
|
||||||
mosquitto__free(buf);
|
mosquitto__free(buf);
|
||||||
log__printf(NULL, MOSQ_LOG_ERR, "Error: Unable to open acl_file \"%s\".", security_opts->acl_file);
|
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;
|
return MOSQ_ERR_NOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
pwfile = mosquitto__fopen(file, "rt", false);
|
pwfile = mosquitto__fopen(file, "rt", true);
|
||||||
if(!pwfile){
|
if(!pwfile){
|
||||||
log__printf(NULL, MOSQ_LOG_ERR, "Error: Unable to open pwfile \"%s\".", file);
|
log__printf(NULL, MOSQ_LOG_ERR, "Error: Unable to open pwfile \"%s\".", file);
|
||||||
mosquitto__free(buf);
|
mosquitto__free(buf);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user