diff --git a/ChangeLog.txt b/ChangeLog.txt index 6c639d86..452b378c 100644 --- a/ChangeLog.txt +++ b/ChangeLog.txt @@ -10,6 +10,7 @@ Broker: - Fix config->user not being freed on exit. Closes #1564. - Fix trailing whitespace not being trimmed on acl users. Closes #1539. - Fix `bind_interface` not working for the default listener. Closes #1533. +- Improve password file parsing in the broker and mosqitto_passwd. Closes #1584. Library: - Set minimum keepalive argument to `mosquitto_connect*()` to be 5 seconds. diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index e1521f12..7ce86e78 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -25,6 +25,7 @@ set(C_SRC loop.c memory_mosq.c memory_mosq.h messages_mosq.c messages_mosq.h + misc_mosq.c misc_mosq.h mosquitto.c mosquitto.h mosquitto_internal.h mqtt_protocol.h diff --git a/lib/Makefile b/lib/Makefile index eca694f0..2abffd7a 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -21,6 +21,7 @@ MOSQ_OBJS=mosquitto.o \ loop.o \ memory_mosq.o \ messages_mosq.o \ + misc_mosq.o \ net_mosq_ocsp.o \ net_mosq.o \ options.o \ @@ -153,6 +154,9 @@ messages_mosq.o : messages_mosq.c messages_mosq.h memory_mosq.o : memory_mosq.c memory_mosq.h ${CROSS_COMPILE}$(CC) $(LIB_CPPFLAGS) $(LIB_CFLAGS) -c $< -o $@ +misc_mosq.o : misc_mosq.c misc_mosq.h + ${CROSS_COMPILE}$(CC) $(LIB_CPPFLAGS) $(LIB_CFLAGS) -c $< -o $@ + net_mosq_ocsp.o : net_mosq_ocsp.c net_mosq.h ${CROSS_COMPILE}$(CC) $(LIB_CPPFLAGS) $(LIB_CFLAGS) -c $< -o $@ diff --git a/lib/misc_mosq.c b/lib/misc_mosq.c new file mode 100644 index 00000000..d4d5510e --- /dev/null +++ b/lib/misc_mosq.c @@ -0,0 +1,177 @@ +/* +Copyright (c) 2009-2019 Roger Light + +All rights reserved. This program and the accompanying materials +are made available under the terms of the Eclipse Public License v1.0 +and Eclipse Distribution License v1.0 which accompany this distribution. + +The Eclipse Public License is available at + http://www.eclipse.org/legal/epl-v10.html +and the Eclipse Distribution License is available at + http://www.eclipse.org/org/documents/edl-v10.php. + +Contributors: + Roger Light - initial implementation and documentation. +*/ + +/* This contains general purpose utility functions that are not specific to + * Mosquitto/MQTT features. */ + +#include "config.h" + +#include +#include +#include +#include +#include + +#ifdef WIN32 +# include +# include +# include +# include +#else +# include +#endif + + +FILE *mosquitto__fopen(const char *path, const char *mode, bool restrict_read) +{ +#ifdef WIN32 + char buf[4096]; + int rc; + rc = ExpandEnvironmentStrings(path, buf, 4096); + if(rc == 0 || rc > 4096){ + return NULL; + }else{ + if (restrict_read) { + HANDLE hfile; + SECURITY_ATTRIBUTES sec; + EXPLICIT_ACCESS ea; + PACL pacl = NULL; + char username[UNLEN + 1]; + int ulen = UNLEN; + SECURITY_DESCRIPTOR sd; + DWORD dwCreationDisposition; + + switch(mode[0]){ + case 'a': + dwCreationDisposition = OPEN_ALWAYS; + break; + case 'r': + dwCreationDisposition = OPEN_EXISTING; + break; + case 'w': + dwCreationDisposition = CREATE_ALWAYS; + break; + default: + return NULL; + } + + GetUserName(username, &ulen); + if (!InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION)) { + return NULL; + } + BuildExplicitAccessWithName(&ea, username, GENERIC_ALL, SET_ACCESS, NO_INHERITANCE); + if (SetEntriesInAcl(1, &ea, NULL, &pacl) != ERROR_SUCCESS) { + return NULL; + } + if (!SetSecurityDescriptorDacl(&sd, TRUE, pacl, FALSE)) { + LocalFree(pacl); + return NULL; + } + + sec.nLength = sizeof(SECURITY_ATTRIBUTES); + sec.bInheritHandle = FALSE; + sec.lpSecurityDescriptor = &sd; + + hfile = CreateFile(buf, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, + &sec, + dwCreationDisposition, + FILE_ATTRIBUTE_NORMAL, + NULL); + + LocalFree(pacl); + + int fd = _open_osfhandle((intptr_t)hfile, 0); + if (fd < 0) { + return NULL; + } + + FILE *fptr = _fdopen(fd, mode); + if (!fptr) { + _close(fd); + return NULL; + } + return fptr; + + }else { + return fopen(buf, mode); + } + } +#else + if (restrict_read) { + FILE *fptr; + mode_t old_mask; + + old_mask = umask(0077); + fptr = fopen(path, mode); + umask(old_mask); + + return fptr; + }else{ + return fopen(path, mode); + } +#endif +} + + +char *misc__trimblanks(char *str) +{ + char *endptr; + + if(str == NULL) return NULL; + + while(isspace(str[0])){ + str++; + } + endptr = &str[strlen(str)-1]; + while(endptr > str && isspace(endptr[0])){ + endptr[0] = '\0'; + endptr--; + } + return str; +} + + +char *fgets_extending(char **buf, int *buflen, FILE *stream) +{ + char *rc; + char endchar; + int offset = 0; + char *newbuf; + + if(stream == NULL || buf == NULL || buflen == NULL || *buflen < 1){ + return NULL; + } + + do{ + rc = fgets(&((*buf)[offset]), (*buflen)-offset, stream); + if(feof(stream)){ + return rc; + } + + endchar = (*buf)[strlen(*buf)-1]; + if(endchar == '\n'){ + return rc; + } + /* No EOL char found, so extend buffer */ + offset = (*buflen)-1; + *buflen += 1000; + newbuf = realloc(*buf, *buflen); + if(!newbuf){ + return NULL; + } + *buf = newbuf; + }while(1); +} diff --git a/lib/misc_mosq.h b/lib/misc_mosq.h new file mode 100644 index 00000000..c18bbbd1 --- /dev/null +++ b/lib/misc_mosq.h @@ -0,0 +1,25 @@ +/* +Copyright (c) 2009-2019 Roger Light + +All rights reserved. This program and the accompanying materials +are made available under the terms of the Eclipse Public License v1.0 +and Eclipse Distribution License v1.0 which accompany this distribution. + +The Eclipse Public License is available at + http://www.eclipse.org/legal/epl-v10.html +and the Eclipse Distribution License is available at + http://www.eclipse.org/org/documents/edl-v10.php. + +Contributors: + Roger Light - initial implementation and documentation. +*/ +#ifndef MISC_MOSQ_H +#define MISC_MOSQ_H + +#include + +FILE *mosquitto__fopen(const char *path, const char *mode, bool restrict_read); +char *misc__trimblanks(char *str); +char *fgets_extending(char **buf, int *buflen, FILE *stream); + +#endif diff --git a/lib/options.c b/lib/options.c index 4ab8897e..57a48fbd 100644 --- a/lib/options.c +++ b/lib/options.c @@ -32,6 +32,7 @@ Contributors: #include "mosquitto.h" #include "mosquitto_internal.h" #include "memory_mosq.h" +#include "misc_mosq.h" #include "mqtt_protocol.h" #include "util_mosq.h" #include "will_mosq.h" diff --git a/lib/util_mosq.c b/lib/util_mosq.c index 54a7e094..cae69c53 100644 --- a/lib/util_mosq.c +++ b/lib/util_mosq.c @@ -203,96 +203,6 @@ int mosquitto__hex2bin(const char *hex, unsigned char *bin, int bin_max_len) } #endif -FILE *mosquitto__fopen(const char *path, const char *mode, bool restrict_read) -{ -#ifdef WIN32 - char buf[4096]; - int rc; - rc = ExpandEnvironmentStrings(path, buf, 4096); - if(rc == 0 || rc > 4096){ - return NULL; - }else{ - if (restrict_read) { - HANDLE hfile; - SECURITY_ATTRIBUTES sec; - EXPLICIT_ACCESS ea; - PACL pacl = NULL; - char username[UNLEN + 1]; - int ulen = UNLEN; - SECURITY_DESCRIPTOR sd; - DWORD dwCreationDisposition; - - switch(mode[0]){ - case 'a': - dwCreationDisposition = OPEN_ALWAYS; - break; - case 'r': - dwCreationDisposition = OPEN_EXISTING; - break; - case 'w': - dwCreationDisposition = CREATE_ALWAYS; - break; - default: - return NULL; - } - - GetUserName(username, &ulen); - if (!InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION)) { - return NULL; - } - BuildExplicitAccessWithName(&ea, username, GENERIC_ALL, SET_ACCESS, NO_INHERITANCE); - if (SetEntriesInAcl(1, &ea, NULL, &pacl) != ERROR_SUCCESS) { - return NULL; - } - if (!SetSecurityDescriptorDacl(&sd, TRUE, pacl, FALSE)) { - LocalFree(pacl); - return NULL; - } - - sec.nLength = sizeof(SECURITY_ATTRIBUTES); - sec.bInheritHandle = FALSE; - sec.lpSecurityDescriptor = &sd; - - hfile = CreateFile(buf, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, - &sec, - dwCreationDisposition, - FILE_ATTRIBUTE_NORMAL, - NULL); - - LocalFree(pacl); - - int fd = _open_osfhandle((intptr_t)hfile, 0); - if (fd < 0) { - return NULL; - } - - FILE *fptr = _fdopen(fd, mode); - if (!fptr) { - _close(fd); - return NULL; - } - return fptr; - - }else { - return fopen(buf, mode); - } - } -#else - if (restrict_read) { - FILE *fptr; - mode_t old_mask; - - old_mask = umask(0077); - fptr = fopen(path, mode); - umask(old_mask); - - return fptr; - }else{ - return fopen(path, mode); - } -#endif -} - void util__increment_receive_quota(struct mosquitto *mosq) { if(mosq->msgs_in.inflight_quota < mosq->msgs_in.inflight_maximum){ @@ -383,21 +293,3 @@ enum mosquitto_client_state mosquitto__get_state(struct mosquitto *mosq) return state; } - - -char *util__trimblanks(char *str) -{ - char *endptr; - - if(str == NULL) return NULL; - - while(isspace(str[0])){ - str++; - } - endptr = &str[strlen(str)-1]; - while(endptr > str && isspace(endptr[0])){ - endptr[0] = '\0'; - endptr--; - } - return str; -} diff --git a/lib/util_mosq.h b/lib/util_mosq.h index db621d0b..3d61ec17 100644 --- a/lib/util_mosq.h +++ b/lib/util_mosq.h @@ -31,7 +31,6 @@ int mosquitto__check_keepalive(struct mosquitto_db *db, struct mosquitto *mosq); int mosquitto__check_keepalive(struct mosquitto *mosq); #endif uint16_t mosquitto__mid_generate(struct mosquitto *mosq); -FILE *mosquitto__fopen(const char *path, const char *mode, bool restrict_read); int mosquitto__set_state(struct mosquitto *mosq, enum mosquitto_client_state state); enum mosquitto_client_state mosquitto__get_state(struct mosquitto *mosq); @@ -49,5 +48,4 @@ void util__decrement_receive_quota(struct mosquitto *mosq); void util__decrement_send_quota(struct mosquitto *mosq); -char *util__trimblanks(char *str); #endif diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 7898ff5b..773054c4 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -27,6 +27,7 @@ set (MOSQ_SRCS ../lib/memory_mosq.c ../lib/memory_mosq.h mosquitto.c mosquitto_broker.h mosquitto_broker_internal.h + ../lib/misc_mosq.c ../lib/misc_mosq.h net.c ../lib/net_mosq_ocsp.c ../lib/net_mosq.c ../lib/net_mosq.h ../lib/packet_datatypes.c @@ -188,7 +189,7 @@ install(TARGETS mosquitto RUNTIME DESTINATION "${CMAKE_INSTALL_SBINDIR}") install(FILES mosquitto_broker.h mosquitto_plugin.h DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}") if (WITH_TLS) - add_executable(mosquitto_passwd mosquitto_passwd.c) + add_executable(mosquitto_passwd mosquitto_passwd.c ../lib/misc_mosq.c) target_link_libraries(mosquitto_passwd ${OPENSSL_LIBRARIES}) install(TARGETS mosquitto_passwd RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}") endif (WITH_TLS) diff --git a/src/Makefile b/src/Makefile index 2517cb65..d90c864d 100644 --- a/src/Makefile +++ b/src/Makefile @@ -31,6 +31,7 @@ OBJS= mosquitto.o \ logging.o \ loop.o \ memory_mosq.o \ + misc_mosq.o \ net.o \ net_mosq.o \ net_mosq_ocsp.o \ @@ -143,6 +144,9 @@ loop.o : loop.c mosquitto_broker_internal.h memory_mosq.o : ../lib/memory_mosq.c ../lib/memory_mosq.h ${CROSS_COMPILE}${CC} $(BROKER_CPPFLAGS) $(BROKER_CFLAGS) -c $< -o $@ +misc_mosq.o : ../lib/misc_mosq.c ../lib/misc_mosq.h + ${CROSS_COMPILE}${CC} $(BROKER_CPPFLAGS) $(BROKER_CFLAGS) -c $< -o $@ + net.o : net.c mosquitto_broker_internal.h ${CROSS_COMPILE}${CC} $(BROKER_CPPFLAGS) $(BROKER_CFLAGS) -c $< -o $@ @@ -260,11 +264,11 @@ will_delay.o : will_delay.c mosquitto_broker_internal.h will_mosq.o : ../lib/will_mosq.c ../lib/will_mosq.h ${CROSS_COMPILE}${CC} $(BROKER_CPPFLAGS) $(BROKER_CFLAGS) -c $< -o $@ -mosquitto_passwd : mosquitto_passwd.o +mosquitto_passwd : mosquitto_passwd.o misc_mosq.o ${CROSS_COMPILE}${CC} ${LDFLAGS} $^ -o $@ $(PASSWD_LDADD) mosquitto_passwd.o : mosquitto_passwd.c - ${CROSS_COMPILE}${CC} -I.. $(CPPFLAGS) $(CFLAGS) -c $< -o $@ + ${CROSS_COMPILE}${CC} -I.. -I../lib $(CPPFLAGS) $(CFLAGS) -c $< -o $@ plugin_defer.so : plugin_defer.c mosquitto_plugin.h mosquitto_broker.h mosquitto_broker_internal.h ${CROSS_COMPILE}${CC} -I. -I../lib -fPIC -shared $< -o $@ diff --git a/src/conf.c b/src/conf.c index 38075956..316ae2e7 100644 --- a/src/conf.c +++ b/src/conf.c @@ -42,6 +42,7 @@ Contributors: #include "mosquitto_broker_internal.h" #include "memory_mosq.h" +#include "misc_mosq.h" #include "tls_mosq.h" #include "util_mosq.h" #include "mqtt_protocol.h" @@ -71,35 +72,6 @@ static int config__read_file(struct mosquitto__config *config, bool reload, cons static int config__check(struct mosquitto__config *config); static void config__cleanup_plugins(struct mosquitto__config *config); -static char *fgets_extending(char **buf, int *buflen, FILE *stream) -{ - char *rc; - char endchar; - int offset = 0; - char *newbuf; - - do{ - rc = fgets(&((*buf)[offset]), *buflen-offset, stream); - if(feof(stream)){ - return rc; - } - - endchar = (*buf)[strlen(*buf)-1]; - if(endchar == '\n'){ - return rc; - } - /* No EOL char found, so extend buffer */ - offset = *buflen-1; - *buflen += 1000; - newbuf = realloc(*buf, *buflen); - if(!newbuf){ - return NULL; - } - *buf = newbuf; - }while(1); -} - - static void conf__set_cur_security_options(struct mosquitto__config *config, struct mosquitto__listener *cur_listener, struct mosquitto__security_options **security_options) { if(config->per_listener_settings){ @@ -2381,7 +2353,7 @@ static int conf__parse_string(char **token, const char *name, char **value, char return MOSQ_ERR_INVAL; } /* Deal with multiple spaces at the beginning of the string. */ - *token = util__trimblanks(*token); + *token = misc__trimblanks(*token); if(strlen(*token) == 0){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Empty %s value in configuration.", name); return MOSQ_ERR_INVAL; diff --git a/src/logging.c b/src/logging.c index ed244560..d290e6d6 100644 --- a/src/logging.c +++ b/src/logging.c @@ -29,6 +29,7 @@ Contributors: #include "mosquitto_broker_internal.h" #include "memory_mosq.h" +#include "misc_mosq.h" #include "util_mosq.h" extern struct mosquitto_db int_db; diff --git a/src/mosquitto.c b/src/mosquitto.c index 8969f03f..84eda2c2 100644 --- a/src/mosquitto.c +++ b/src/mosquitto.c @@ -51,6 +51,7 @@ Contributors: #include "mosquitto_broker_internal.h" #include "memory_mosq.h" +#include "misc_mosq.h" #include "util_mosq.h" struct mosquitto_db int_db; diff --git a/src/mosquitto_passwd.c b/src/mosquitto_passwd.c index 41d32adb..0a8f1900 100644 --- a/src/mosquitto_passwd.c +++ b/src/mosquitto_passwd.c @@ -49,9 +49,18 @@ Contributors: # include #endif -#define MAX_BUFFER_LEN 1024 +#define MAX_BUFFER_LEN 65536 #define SALT_LEN 12 +#include "misc_mosq.h" + +struct cb_helper { + const char *line; + const char *username; + const char *password; + bool found; +}; + #ifdef WIN32 static FILE *mpw_tmpfile(void) { @@ -199,93 +208,159 @@ int output_new_password(FILE *fptr, const char *username, const char *password) return 0; } -int delete_pwuser(FILE *fptr, FILE *ftmp, const char *username) -{ - char buf[MAX_BUFFER_LEN]; - char lbuf[MAX_BUFFER_LEN], *token; - bool found = false; - int line = 0; - while(!feof(fptr) && fgets(buf, MAX_BUFFER_LEN, fptr)){ +static int pwfile_iterate(FILE *fptr, FILE *ftmp, + int (*cb)(FILE *, FILE *, const char *, const char *, const char *, struct cb_helper *), + struct cb_helper *helper) +{ + char *buf; + int buflen = 1024; + char *lbuf; + int lbuflen; + int rc = 1; + int line = 0; + char *username, *password; + + buf = malloc(buflen); + if(buf == NULL){ + fprintf(stderr, "Error: Out of memory.\n"); + return 1; + } + lbuflen = buflen; + lbuf = malloc(lbuflen); + if(lbuf == NULL){ + fprintf(stderr, "Error: Out of memory.\n"); + free(buf); + return 1; + } + + while(!feof(fptr) && fgets_extending(&buf, &buflen, fptr)){ + if(lbuflen != buflen){ + free(lbuf); + lbuflen = buflen; + lbuf = malloc(lbuflen); + if(lbuf == NULL){ + fprintf(stderr, "Error: Out of memory.\n"); + free(buf); + return 1; + } + } + memcpy(lbuf, buf, buflen); line++; - memcpy(lbuf, buf, MAX_BUFFER_LEN); - token = strtok(lbuf, ":"); - if(!token){ + username = strtok(buf, ":"); + password = strtok(NULL, ":"); + if(username == NULL || password == NULL){ fprintf(stderr, "Error: Corrupt password file at line %d.\n", line); + free(lbuf); + free(buf); + return 1; + } + username = misc__trimblanks(username); + password = misc__trimblanks(password); + + if(strlen(username) == 0 || strlen(password) == 0){ + fprintf(stderr, "Error: Corrupt password file at line %d.\n", line); + free(lbuf); + free(buf); return 1; } - if(strcmp(username, token)){ - fprintf(ftmp, "%s", buf); - }else{ - found = true; + rc = cb(fptr, ftmp, username, password, lbuf, helper); + if(rc){ + break; } } - if(!found){ + free(lbuf); + free(buf); + + return rc; +} + + +/* ====================================================================== + * Delete a user from the password file + * ====================================================================== */ +static int delete_pwuser_cb(FILE *fptr, FILE *ftmp, const char *username, const char *password, const char *line, struct cb_helper *helper) +{ + if(strcmp(username, helper->username)){ + /* If this isn't the username to delete, write it to the new file */ + fprintf(ftmp, "%s", line); + }else{ + /* Don't write the matching username to the file. */ + helper->found = true; + } + return 0; +} + +int delete_pwuser(FILE *fptr, FILE *ftmp, const char *username) +{ + struct cb_helper helper; + int rc; + + memset(&helper, 0, sizeof(helper)); + helper.username = username; + rc = pwfile_iterate(fptr, ftmp, delete_pwuser_cb, &helper); + + if(helper.found == false){ fprintf(stderr, "Warning: User %s not found in password file.\n", username); return 1; } - return 0; + return rc; +} + + + +/* ====================================================================== + * Update a plain text password file to use hashes + * ====================================================================== */ +static int update_file_cb(FILE *fptr, FILE *ftmp, const char *username, const char *password, const char *line, struct cb_helper *helper) +{ + return output_new_password(ftmp, username, password); } int update_file(FILE *fptr, FILE *ftmp) { - char buf[MAX_BUFFER_LEN]; - char lbuf[MAX_BUFFER_LEN]; - char *username, *password; - int rc; - int len; + return pwfile_iterate(fptr, ftmp, update_file_cb, NULL); +} - while(!feof(fptr) && fgets(buf, MAX_BUFFER_LEN, fptr)){ - memcpy(lbuf, buf, MAX_BUFFER_LEN); - username = strtok(lbuf, ":"); - password = strtok(NULL, ":"); - if(password){ - len = strlen(password); - while(len && (password[len-1] == '\n' || password[len-1] == '\r')){ - password[len-1] = '\0'; - len = strlen(password); - } - rc = output_new_password(ftmp, username, password); - if(rc) return rc; - }else{ - fprintf(ftmp, "%s", username); - } + +/* ====================================================================== + * Update an existing user password / create a new password + * ====================================================================== */ +static int update_pwuser_cb(FILE *fptr, FILE *ftmp, const char *username, const char *password, const char *line, struct cb_helper *helper) +{ + int rc = 0; + + printf("%s\n", username); + if(strcmp(username, helper->username)){ + /* If this isn't the matching user, then writing out the exiting line */ + printf("%s\n", line); + fprintf(ftmp, "%s", line); + }else{ + /* Write out a new line for our matching username */ + helper->found = true; + rc = output_new_password(ftmp, username, password); } - return 0; + return rc; } int update_pwuser(FILE *fptr, FILE *ftmp, const char *username, const char *password) { - char buf[MAX_BUFFER_LEN]; - char lbuf[MAX_BUFFER_LEN], *token; - bool found = false; - int rc = 1; - int line = 0; + struct cb_helper helper; + int rc; - while(!feof(fptr) && fgets(buf, MAX_BUFFER_LEN, fptr)){ - line++; - memcpy(lbuf, buf, MAX_BUFFER_LEN); - token = strtok(lbuf, ":"); - if(!token){ - fprintf(stderr, "Error: Corrupt password file at line %d.\n", line); - return 1; - } + memset(&helper, 0, sizeof(helper)); + helper.username = username; + rc = pwfile_iterate(fptr, ftmp, update_pwuser_cb, &helper); - if(strcmp(username, token)){ - fprintf(ftmp, "%s", buf); - }else{ - rc = output_new_password(ftmp, username, password); - found = true; - } - } - if(found){ + if(helper.found){ return rc; }else{ return output_new_password(ftmp, username, password); } } + int gets_quiet(char *s, int len) { #ifdef WIN32 @@ -505,6 +580,14 @@ int main(int argc, char *argv[]) print_usage(); return 1; } + if(username && strlen(username) > 65535){ + fprintf(stderr, "Error: Username must be less than 65536 characters long.\n"); + return 1; + } + if(password_cmd && strlen(password_cmd) > 65535){ + fprintf(stderr, "Error: Password must be less than 65536 characters long.\n"); + return 1; + } #ifdef WIN32 password_file = _fullpath(NULL, password_file_tmp, 0); diff --git a/src/persist_read.c b/src/persist_read.c index 22dd5c5d..d5585752 100644 --- a/src/persist_read.c +++ b/src/persist_read.c @@ -34,6 +34,7 @@ Contributors: #include "memory_mosq.h" #include "persist.h" #include "time_mosq.h" +#include "misc_mosq.h" #include "util_mosq.h" static uint32_t db_version; diff --git a/src/persist_write.c b/src/persist_write.c index c24a24d2..f6c11d60 100644 --- a/src/persist_write.c +++ b/src/persist_write.c @@ -33,6 +33,7 @@ Contributors: #include "memory_mosq.h" #include "persist.h" #include "time_mosq.h" +#include "misc_mosq.h" #include "util_mosq.h" static int persist__client_messages_save(struct mosquitto_db *db, FILE *db_fptr, struct mosquitto *context, struct mosquitto_client_msg *queue) diff --git a/src/security_default.c b/src/security_default.c index e0c57e1b..a876df6d 100644 --- a/src/security_default.c +++ b/src/security_default.c @@ -24,6 +24,7 @@ Contributors: #include "memory_mosq.h" #include "mqtt_protocol.h" #include "send_mosq.h" +#include "misc_mosq.h" #include "util_mosq.h" static int aclfile__parse(struct mosquitto_db *db, struct mosquitto__security_options *security_opts); @@ -434,24 +435,32 @@ int mosquitto_acl_check_default(struct mosquitto_db *db, struct mosquitto *conte static int aclfile__parse(struct mosquitto_db *db, struct mosquitto__security_options *security_opts) { - FILE *aclfptr; - char buf[1024]; + FILE *aclfptr = NULL; char *token; char *user = NULL; char *topic; char *access_s; int access; - int rc; + int rc = MOSQ_ERR_SUCCESS; int slen; int topic_pattern; char *saveptr = NULL; + char *buf = NULL; + int buflen = 256; if(!db || !db->config) return MOSQ_ERR_INVAL; if(!security_opts) return MOSQ_ERR_INVAL; if(!security_opts->acl_file) return MOSQ_ERR_SUCCESS; + buf = mosquitto__malloc(buflen); + if(buf == NULL){ + log__printf(NULL, MOSQ_LOG_ERR, "Error: Out of memory."); + return 1; + } + aclfptr = mosquitto__fopen(security_opts->acl_file, "rt", false); if(!aclfptr){ + mosquitto__free(buf); log__printf(NULL, MOSQ_LOG_ERR, "Error: Unable to open acl_file \"%s\".", security_opts->acl_file); return 1; } @@ -459,7 +468,7 @@ static int aclfile__parse(struct mosquitto_db *db, struct mosquitto__security_op // topic [read|write] // user - while(fgets(buf, 1024, aclfptr)){ + while(fgets_extending(&buf, &buflen, aclfptr)){ slen = strlen(buf); while(slen > 0 && isspace(buf[slen-1])){ buf[slen-1] = '\0'; @@ -480,13 +489,12 @@ static int aclfile__parse(struct mosquitto_db *db, struct mosquitto__security_op access_s = strtok_r(NULL, " ", &saveptr); if(!access_s){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Empty topic in acl_file \"%s\".", security_opts->acl_file); - mosquitto__free(user); - fclose(aclfptr); - return MOSQ_ERR_INVAL; + rc = MOSQ_ERR_INVAL; + break; } token = strtok_r(NULL, "", &saveptr); if(token){ - topic = util__trimblanks(token); + topic = misc__trimblanks(token); }else{ topic = access_s; access_s = NULL; @@ -500,9 +508,8 @@ static int aclfile__parse(struct mosquitto_db *db, struct mosquitto__security_op access = MOSQ_ACL_READ | MOSQ_ACL_WRITE; }else{ log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid topic access type \"%s\" in acl_file \"%s\".", access_s, security_opts->acl_file); - mosquitto__free(user); - fclose(aclfptr); - return MOSQ_ERR_INVAL; + rc = MOSQ_ERR_INVAL; + break; } }else{ access = MOSQ_ACL_READ | MOSQ_ACL_WRITE; @@ -510,9 +517,8 @@ static int aclfile__parse(struct mosquitto_db *db, struct mosquitto__security_op rc = mosquitto_sub_topic_check(topic); if(rc != MOSQ_ERR_SUCCESS){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid ACL topic \"%s\" in acl_file \"%s\".", topic, security_opts->acl_file); - mosquitto__free(user); - fclose(aclfptr); - return rc; + rc = MOSQ_ERR_INVAL; + break; } if(topic_pattern == 0){ @@ -521,44 +527,41 @@ static int aclfile__parse(struct mosquitto_db *db, struct mosquitto__security_op rc = add__acl_pattern(security_opts, topic, access); } if(rc){ - mosquitto__free(user); - fclose(aclfptr); - return rc; + break; } }else if(!strcmp(token, "user")){ token = strtok_r(NULL, "", &saveptr); if(token){ - token = util__trimblanks(token); + token = misc__trimblanks(token); if(slen == 0){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Missing username in acl_file \"%s\".", security_opts->acl_file); - mosquitto__free(user); - fclose(aclfptr); - return 1; + rc = MOSQ_ERR_INVAL; + break; } mosquitto__free(user); user = mosquitto__strdup(token); if(!user){ - fclose(aclfptr); - return MOSQ_ERR_NOMEM; + rc = MOSQ_ERR_NOMEM; + break; } }else{ log__printf(NULL, MOSQ_LOG_ERR, "Error: Missing username in acl_file \"%s\".", security_opts->acl_file); - mosquitto__free(user); - fclose(aclfptr); - return 1; + rc = MOSQ_ERR_INVAL; + break; } }else{ log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid line in acl_file \"%s\": %s.", security_opts->acl_file, buf); - fclose(aclfptr); - return 1; + rc = MOSQ_ERR_INVAL; + break; } } } + mosquitto__free(buf); mosquitto__free(user); fclose(aclfptr); - return MOSQ_ERR_SUCCESS; + return rc; } static void free__acl(struct mosquitto__acl *acl) @@ -668,18 +671,26 @@ static int pwfile__parse(const char *file, struct mosquitto__unpwd **root) { FILE *pwfile; struct mosquitto__unpwd *unpwd; - char buf[256]; char *username, *password; char *saveptr = NULL; + char *buf; + int buflen = 256; + buf = mosquitto__malloc(buflen); + if(buf == NULL){ + log__printf(NULL, MOSQ_LOG_ERR, "Error: Out of memory."); + return 1; + } + pwfile = mosquitto__fopen(file, "rt", false); if(!pwfile){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Unable to open pwfile \"%s\".", file); + mosquitto__free(buf); return 1; } while(!feof(pwfile)){ - if(fgets(buf, 256, pwfile)){ + if(fgets_extending(&buf, &buflen, pwfile)){ if(buf[0] == '#') continue; if(!strchr(buf, ':')) continue; @@ -688,23 +699,40 @@ static int pwfile__parse(const char *file, struct mosquitto__unpwd **root) unpwd = mosquitto__calloc(1, sizeof(struct mosquitto__unpwd)); if(!unpwd){ fclose(pwfile); + mosquitto__free(buf); return MOSQ_ERR_NOMEM; } - username = util__trimblanks(username); + username = misc__trimblanks(username); + if(strlen(username) > 65535){ + log__printf(NULL, MOSQ_LOG_NOTICE, "Warning: Invalid line in password file '%s', username too long.", file); + mosquitto__free(unpwd); + continue; + } + unpwd->username = mosquitto__strdup(username); if(!unpwd->username){ mosquitto__free(unpwd); + mosquitto__free(buf); fclose(pwfile); return MOSQ_ERR_NOMEM; } password = strtok_r(NULL, ":", &saveptr); if(password){ - password = util__trimblanks(password); + password = misc__trimblanks(password); + + if(strlen(password) > 65535){ + log__printf(NULL, MOSQ_LOG_NOTICE, "Warning: Invalid line in password file '%s', password too long.", file); + mosquitto__free(unpwd->username); + mosquitto__free(unpwd); + continue; + } + unpwd->password = mosquitto__strdup(password); if(!unpwd->password){ fclose(pwfile); mosquitto__free(unpwd->username); mosquitto__free(unpwd); + mosquitto__free(buf); return MOSQ_ERR_NOMEM; } @@ -718,6 +746,7 @@ static int pwfile__parse(const char *file, struct mosquitto__unpwd **root) } } fclose(pwfile); + mosquitto__free(buf); return MOSQ_ERR_SUCCESS; } diff --git a/test/unit/Makefile b/test/unit/Makefile index 6d8c934d..0a461b1c 100644 --- a/test/unit/Makefile +++ b/test/unit/Makefile @@ -10,16 +10,17 @@ LDADD:=$(LDADD) -lcunit TEST_OBJS = test.o \ datatype_read.o \ datatype_write.o \ + misc_trim_test.o \ property_add.o \ property_read.o \ property_user_read.o \ property_write.o \ stubs.o \ util_topic_test.o \ - util_trim_test.o \ utf8.o LIB_OBJS = memory_mosq.o \ + misc_mosq.o \ packet_datatypes.o \ property_mosq.o \ util_mosq.o \ @@ -32,6 +33,7 @@ PERSIST_READ_TEST_OBJS = \ PERSIST_READ_OBJS = \ memory_mosq.o \ + misc_mosq.o \ packet_datatypes.o \ persist_read.o \ persist_read_v234.o \ @@ -47,6 +49,7 @@ PERSIST_WRITE_TEST_OBJS = \ PERSIST_WRITE_OBJS = \ database.o \ memory_mosq.o \ + misc_mosq.o \ packet_datatypes.o \ persist_read.o \ persist_read_v234.o \ @@ -78,6 +81,9 @@ database.o : ../../src/database.c memory_mosq.o : ../../lib/memory_mosq.c $(CROSS_COMPILE)$(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $^ +misc_mosq.o : ../../lib/misc_mosq.c + $(CROSS_COMPILE)$(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $^ + packet_datatypes.o : ../../lib/packet_datatypes.c $(CROSS_COMPILE)$(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $^ diff --git a/test/unit/util_trim_test.c b/test/unit/misc_trim_test.c similarity index 91% rename from test/unit/util_trim_test.c rename to test/unit/misc_trim_test.c index aaa0d646..2916bce9 100644 --- a/test/unit/util_trim_test.c +++ b/test/unit/misc_trim_test.c @@ -1,14 +1,14 @@ #include #include -#include +#include static void rtrim_helper(const char *expected, char *buf) { char *res; - res = util__trimblanks(buf); + res = misc__trimblanks(buf); CU_ASSERT_PTR_NOT_NULL(res); if(res){ CU_ASSERT_EQUAL(strlen(buf), strlen(res)); @@ -22,7 +22,7 @@ static void ltrim_helper(const char *expected, char *buf) { char *res; - res = util__trimblanks(buf); + res = misc__trimblanks(buf); CU_ASSERT_PTR_NOT_NULL(res); if(res){ CU_ASSERT_EQUAL(strlen(expected), strlen(res)); @@ -35,7 +35,7 @@ static void TEST_null_input(void) { char *res; - res = util__trimblanks(NULL); + res = misc__trimblanks(NULL); CU_ASSERT_PTR_NULL(res); } @@ -46,7 +46,7 @@ static void TEST_empty_input(void) char *res; memset(buf, 0, sizeof(buf)); - res = util__trimblanks(buf); + res = misc__trimblanks(buf); CU_ASSERT_PTR_NOT_NULL(res); if(res){ CU_ASSERT_STRING_EQUAL(res, ""); @@ -156,13 +156,13 @@ static void TEST_btrim(void) * TEST SUITE SETUP * ======================================================================== */ -int init_util_trim_tests(void) +int init_misc_trim_tests(void) { CU_pSuite test_suite = NULL; - test_suite = CU_add_suite("Util string trim", NULL, NULL); + test_suite = CU_add_suite("Misc string trim", NULL, NULL); if(!test_suite){ - printf("Error adding CUnit util string trim test suite.\n"); + printf("Error adding CUnit Misc string trim test suite.\n"); return 1; } @@ -175,7 +175,7 @@ int init_util_trim_tests(void) || !CU_add_test(test_suite, "Both trim", TEST_btrim) ){ - printf("Error adding util topic CUnit tests.\n"); + printf("Error adding Misc topic CUnit tests.\n"); return 1; } diff --git a/test/unit/test.c b/test/unit/test.c index 1b083963..97258c49 100644 --- a/test/unit/test.c +++ b/test/unit/test.c @@ -12,7 +12,7 @@ int init_property_user_read_tests(void); int init_property_write_tests(void); int init_utf8_tests(void); int init_util_topic_tests(void); -int init_util_trim_tests(void); +int init_misc_trim_tests(void); int main(int argc, char *argv[]) { @@ -32,7 +32,7 @@ int main(int argc, char *argv[]) || init_property_user_read_tests() || init_property_write_tests() || init_util_topic_tests() - || init_util_trim_tests() + || init_misc_trim_tests() ){ CU_cleanup_registry();