Refactor password code

This commit is contained in:
Roger A. Light
2023-11-20 22:58:42 +00:00
committed by Roger Light
parent 7b21f596d4
commit b6710f9364
16 changed files with 642 additions and 487 deletions
-1
View File
@@ -5,7 +5,6 @@ if(WITH_TLS AND CJSON_FOUND)
broker.c
client.c
dynsec.c
../../plugins/dynamic-security/hash.c
dynsec_client.c
dynsec_group.c
dynsec_role.c
-4
View File
@@ -25,7 +25,6 @@ OBJS= \
OBJS_EXTERNAL= \
base64_mosq.o \
dynsec_hash.o \
get_password.o \
json_help.o \
memory_mosq.o \
@@ -54,9 +53,6 @@ ${OBJS} : %.o: %.c mosquitto_ctrl.h
base64_mosq.o : ${R}/common/base64_mosq.c ${R}/common/base64_mosq.h
${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(LOCAL_CFLAGS) -c $< -o $@
dynsec_hash.o : ${R}/plugins/dynamic-security/hash.c ${R}/plugins/dynamic-security/dynamic_security.h
${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(LOCAL_CFLAGS) -c $< -o $@
example.o : example.c mosquitto_ctrl.h
${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(LOCAL_CFLAGS) -fPIC -c $< -o $@
+31 -20
View File
@@ -564,47 +564,58 @@ static cJSON *init_add_client(const char *username, const char *password, const
{
cJSON *j_client, *j_roles, *j_role;
struct mosquitto_pw pw;
char *salt64 = NULL, *hash64 = NULL;
char buf[10];
memset(&pw, 0, sizeof(pw));
pw.hashtype = pw_sha512_pbkdf2;
if(pw__hash(password, &pw, true, PW_DEFAULT_ITERATIONS) != 0){
return NULL;
}
if(base64__encode(pw.salt, (unsigned int)pw.salt_len, &salt64)
|| base64__encode(pw.password_hash, sizeof(pw.password_hash), &hash64)
){
fprintf(stderr, "dynsec init: Internal error while encoding password.\n");
free(salt64);
free(hash64);
if(pw__create(&pw, password) != MOSQ_ERR_SUCCESS){
return NULL;
}
j_client = cJSON_CreateObject();
if(j_client == NULL){
free(salt64);
free(hash64);
return NULL;
}
snprintf(buf, sizeof(buf), "%d", PW_DEFAULT_ITERATIONS);
if(cJSON_AddStringToObject(j_client, "username", username) == NULL
|| cJSON_AddStringToObject(j_client, "textName", "Dynsec admin user") == NULL
|| cJSON_AddStringToObject(j_client, "password", hash64) == NULL
|| cJSON_AddStringToObject(j_client, "salt", salt64) == NULL
|| cJSON_AddRawToObject(j_client, "iterations", buf) == NULL
){
free(salt64);
free(hash64);
cJSON_Delete(j_client);
return NULL;
}
free(salt64);
free(hash64);
if(pw.hashtype == pw_sha512_pbkdf2){
char *salt_b64 = NULL, *password_b64 = NULL;
if(base64__encode(pw.params.sha512_pbkdf2.salt, pw.params.sha512_pbkdf2.salt_len, &salt_b64)
|| base64__encode(pw.params.sha512_pbkdf2.password_hash, sizeof(pw.params.sha512_pbkdf2.password_hash), &password_b64)
|| cJSON_AddStringToObject(j_client, "salt", salt_b64) == NULL
|| cJSON_AddStringToObject(j_client, "password", password_b64) == NULL
|| cJSON_AddNumberToObject(j_client, "iterations", pw.params.sha512_pbkdf2.iterations) == NULL){
cJSON_Delete(j_client);
free(password_b64);
free(salt_b64);
return NULL;
}
free(password_b64);
free(salt_b64);
}else{
if(pw__encode(&pw) != MOSQ_ERR_SUCCESS){
cJSON_Delete(j_client);
return NULL;
}
if(cJSON_AddStringToObject(j_client, "encoded_password", pw.encoded_password)){
free(pw.encoded_password);
cJSON_Delete(j_client);
return NULL;
}
free(pw.encoded_password);
}
j_roles = cJSON_CreateArray();
if(j_roles == NULL){
+41 -39
View File
@@ -158,12 +158,9 @@ int dynsec_client__file_set_password(int argc, char *argv[], const char *file)
FILE *fptr;
char *fstr;
cJSON *j_tree, *j_clients, *j_client;
cJSON *j_password = NULL, *j_salt = NULL, *j_iterations = NULL;
struct dynsec__client client;
char *pw_buf = NULL, *salt_buf = NULL;
char *json_str;
int i;
int rc = MOSQ_ERR_UNKNOWN;
memset(&client, 0, sizeof(client));
@@ -179,7 +176,7 @@ int dynsec_client__file_set_password(int argc, char *argv[], const char *file)
fprintf(stderr, "Error: -i argument given, but no iterations provided.\n");
return MOSQ_ERR_INVAL;
}
client.pw.iterations = atoi(argv[i+1]);
client.pw.params.sha512_pbkdf2.iterations = atoi(argv[i+1]);
i++;
}else{
fprintf(stderr, "Error: Unknown argument: %s\n", argv[i]);
@@ -234,39 +231,51 @@ int dynsec_client__file_set_password(int argc, char *argv[], const char *file)
const char *username_json;
if(json_get_string(j_client, "username", &username_json, false) == MOSQ_ERR_SUCCESS){
if(!strcmp(username_json, username)){
if(dynsec_auth__pw_hash(&client, password, client.pw.password_hash, sizeof(client.pw.password_hash), true) != MOSQ_ERR_SUCCESS){
if(pw__create(&client.pw, password)){
fprintf(stderr, "Error: Problem generating password hash.\n");
goto error;
return MOSQ_ERR_NOMEM;
}
if(base64__encode(client.pw.password_hash, sizeof(client.pw.password_hash), &pw_buf) != MOSQ_ERR_SUCCESS){
fprintf(stderr, "Error: Problem generating password hash.\n");
rc = MOSQ_ERR_NOMEM;
goto error;
}
if(base64__encode(client.pw.salt, client.pw.salt_len, &salt_buf) != MOSQ_ERR_SUCCESS){
fprintf(stderr, "Error: Problem generating password hash.\n");
goto error;
}
if((j_password = cJSON_CreateString(pw_buf)) == NULL
|| (j_salt = cJSON_CreateString(salt_buf)) == NULL
|| (j_iterations = cJSON_CreateNumber(client.pw.iterations)) == NULL
){
if(client.pw.hashtype == pw_sha512_pbkdf2){
char *password_b64, *salt_b64;
cJSON *j_password = NULL, *j_salt = NULL, *j_iterations = NULL;
fprintf(stderr, "Error: Out of memory.\n");
rc = MOSQ_ERR_NOMEM;
goto error;
if(base64__encode(client.pw.params.sha512_pbkdf2.password_hash, sizeof(client.pw.params.sha512_pbkdf2.password_hash), &password_b64) != MOSQ_ERR_SUCCESS){
fprintf(stderr, "Error: Problem generating password hash.\n");
return MOSQ_ERR_NOMEM;
}
if(base64__encode(client.pw.params.sha512_pbkdf2.salt, client.pw.params.sha512_pbkdf2.salt_len, &salt_b64) != MOSQ_ERR_SUCCESS){
free(password_b64);
fprintf(stderr, "Error: Problem generating password hash.\n");
return MOSQ_ERR_NOMEM;
}
if((j_password = cJSON_CreateString(password_b64)) == NULL
|| (j_salt = cJSON_CreateString(salt_b64)) == NULL
|| (j_iterations = cJSON_CreateNumber(client.pw.params.sha512_pbkdf2.iterations)) == NULL
){
free(password_b64);
free(salt_b64);
fprintf(stderr, "Error: Out of memory.\n");
return MOSQ_ERR_NOMEM;
}
free(password_b64);
free(salt_b64);
cJSON_ReplaceItemInObject(j_client, "password", j_password);
cJSON_ReplaceItemInObject(j_client, "salt", j_salt);
cJSON_ReplaceItemInObject(j_client, "iterations", j_iterations);
j_password = NULL;
j_salt = NULL;
j_iterations = NULL;
}else{
if(pw__encode(&client.pw)){
fprintf(stderr, "Error: Out of memory.\n");
return MOSQ_ERR_NOMEM;
}
cJSON *j_encoded_password = cJSON_CreateString(client.pw.encoded_password);
cJSON_ReplaceItemInObject(j_client, "encoded_password", j_encoded_password);
}
cJSON_ReplaceItemInObject(j_client, "password", j_password);
cJSON_ReplaceItemInObject(j_client, "salt", j_salt);
cJSON_ReplaceItemInObject(j_client, "iterations", j_iterations);
j_password = NULL;
j_salt = NULL;
j_iterations = NULL;
free(pw_buf);
pw_buf = NULL;
free(salt_buf);
salt_buf = NULL;
json_str = cJSON_Print(j_tree);
cJSON_Delete(j_tree);
@@ -291,13 +300,6 @@ int dynsec_client__file_set_password(int argc, char *argv[], const char *file)
fprintf(stderr, "Error: Client %s not found.\n", username);
return MOSQ_ERR_SUCCESS;
error:
cJSON_Delete(j_password);
cJSON_Delete(j_salt);
cJSON_Delete(j_tree);
free(pw_buf);
free(salt_buf);
return rc;
}
int dynsec_client__set_password(int argc, char *argv[], cJSON *j_command)
+15 -20
View File
@@ -29,6 +29,7 @@ Contributors:
#include "get_password.h"
#include "base64_mosq.h"
#include "memory_mosq.h"
#include "password_mosq.h"
#ifdef WIN32
@@ -131,7 +132,6 @@ static void print_usage(void)
static int output_new_password(FILE *fptr, const char *username, const char *password, int iterations)
{
int rc;
char *salt64 = NULL, *hash64 = NULL;
struct mosquitto_pw pw;
if(password == NULL){
@@ -141,29 +141,24 @@ static int output_new_password(FILE *fptr, const char *username, const char *pas
memset(&pw, 0, sizeof(pw));
pw.hashtype = hashtype;
if(hashtype == pw_sha512_pbkdf2){
pw.params.sha512_pbkdf2.iterations = iterations;
}
rc = pw__hash(password, &pw, true, iterations);
rc = pw__create(&pw, password);
if(rc){
fprintf(stderr, "Error: Unable to hash password.\n");
}else{
rc = base64__encode(pw.salt, pw.salt_len, &salt64);
if(rc){
fprintf(stderr, "Error: Unable to encode salt.\n");
}else{
rc = base64__encode(pw.password_hash, sizeof(pw.password_hash), &hash64);
if(rc){
fprintf(stderr, "Error: Unable to encode hash.\n");
}else{
if(pw.hashtype == pw_sha512_pbkdf2){
fprintf(fptr, "%s:$%d$%d$%s$%s\n", username, hashtype, iterations, salt64, hash64);
}else{
fprintf(fptr, "%s:$%d$%s$%s\n", username, hashtype, salt64, hash64);
}
}
}
return rc;
}
free(salt64);
free(hash64);
rc = pw__encode(&pw);
if(rc){
fprintf(stderr, "Error: Unable to encode password.\n");
return rc;
}
fprintf(fptr, "%s:%s\n", username, pw.encoded_password);
mosquitto__free(pw.encoded_password);
return rc;
}
+395 -62
View File
@@ -18,20 +18,16 @@ Contributors:
#include "config.h"
#include <errno.h>
#ifdef WITH_TLS
# include <openssl/opensslv.h>
# include <openssl/evp.h>
# include <openssl/rand.h>
# include <openssl/buffer.h>
#endif
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "mosquitto.h"
#include "mosquitto_broker.h"
#include "base64_mosq.h"
#include "memory_mosq.h"
#include "password_mosq.h"
#ifdef WIN32
@@ -51,66 +47,25 @@ Contributors:
# include <windows.h>
#else
# include <stdbool.h>
# include <unistd.h>
# include <termios.h>
# include <sys/stat.h>
#endif
#ifdef WITH_TLS
int pw__hash(const char *password, struct mosquitto_pw *pw, bool new_password, int new_iterations)
#ifdef WITH_ARGON2
# include <argon2.h>
#endif
#ifdef WITH_ARGON2
static int pw__hash_argon2id(const char *password, struct mosquitto_pw *pw)
{
int rc;
unsigned int hash_len;
const EVP_MD *digest;
int iterations;
#if OPENSSL_VERSION_NUMBER < 0x10100000L
EVP_MD_CTX context;
#else
EVP_MD_CTX *context;
#endif
if(new_password){
pw->salt_len = HASH_LEN;
rc = RAND_bytes(pw->salt, (int)pw->salt_len);
if(!rc){
return MOSQ_ERR_UNKNOWN;
}
iterations = new_iterations;
}else{
iterations = pw->iterations;
}
if(iterations < 1){
return MOSQ_ERR_INVAL;
}
digest = EVP_get_digestbyname("sha512");
if(!digest){
return MOSQ_ERR_UNKNOWN;
}
if(pw->hashtype == pw_sha512){
#if OPENSSL_VERSION_NUMBER < 0x10100000L
EVP_MD_CTX_init(&context);
EVP_DigestInit_ex(&context, digest, NULL);
EVP_DigestUpdate(&context, password, strlen(password));
EVP_DigestUpdate(&context, pw->salt, pw->salt_len);
EVP_DigestFinal_ex(&context, pw->password_hash, &hash_len);
EVP_MD_CTX_cleanup(&context);
#else
context = EVP_MD_CTX_new();
EVP_DigestInit_ex(context, digest, NULL);
EVP_DigestUpdate(context, password, strlen(password));
EVP_DigestUpdate(context, pw->salt, pw->salt_len);
EVP_DigestFinal_ex(context, pw->password_hash, &hash_len);
EVP_MD_CTX_free(context);
#endif
}else{
pw->iterations = iterations;
hash_len = sizeof(pw->password_hash);
PKCS5_PBKDF2_HMAC(password, (int)strlen(password),
pw->salt, (int)pw->salt_len, iterations,
digest, (int)hash_len, pw->password_hash);
}
ARGON2_PUBLIC size_t argon2_encodedlen(uint32_t t_cost, uint32_t m_cost,
uint32_t parallelism, uint32_t saltlen,
uint32_t hashlen, argon2_type type);
char encoded[1024];
int rc = argon2id_hash_encoded(1, 47104, 1,
password, strlen(password),
pw->params.argon2id.salt, pw->params.argon2id.salt_len,
HASH_LEN,
encoded, sizeof(encoded));
return MOSQ_ERR_SUCCESS;
}
@@ -133,3 +88,381 @@ int pw__memcmp_const(const void *a, const void *b, size_t len)
return rc;
#endif
}
/* ==================================================
* SHA512 PBKDF2
* ================================================== */
#ifdef WITH_TLS
static int pw__hash_sha512_pbkdf2(const char *password, struct mosquitto_pw *pw, unsigned char *password_hash, unsigned int hash_len, int iterations)
{
const EVP_MD *digest;
digest = EVP_get_digestbyname("sha512");
if(!digest){
return MOSQ_ERR_UNKNOWN;
}
PKCS5_PBKDF2_HMAC(password, (int)strlen(password),
pw->params.sha512.salt, (int)pw->params.sha512.salt_len, iterations,
digest, (int)hash_len, password_hash);
return MOSQ_ERR_SUCCESS;
}
#endif
static int pw__create_sha512_pbkdf2(struct mosquitto_pw *pw, const char *password)
{
#ifdef WITH_TLS
pw->hashtype = pw_sha512_pbkdf2;
pw->params.sha512_pbkdf2.salt_len = HASH_LEN;
int rc = RAND_bytes(pw->params.sha512_pbkdf2.salt, (int)pw->params.sha512_pbkdf2.salt_len);
if(!rc){
return MOSQ_ERR_UNKNOWN;
}
if(pw->params.sha512_pbkdf2.iterations == 0){
pw->params.sha512_pbkdf2.iterations = PW_DEFAULT_ITERATIONS;
}
return pw__hash_sha512_pbkdf2(password, pw,
pw->params.sha512_pbkdf2.password_hash,
sizeof(pw->params.sha512_pbkdf2.password_hash),
pw->params.sha512_pbkdf2.iterations);
#else
return MOSQ_ERR_NOT_SUPPORTED
#endif
}
static int pw__verify_sha512_pbkdf2(struct mosquitto_pw *pw, const char *password)
{
#ifdef WITH_TLS
int rc;
unsigned char password_hash[HASH_LEN];
rc = pw__hash_sha512_pbkdf2(password, pw,
password_hash, sizeof(password_hash),
pw->params.sha512_pbkdf2.iterations);
if(rc != MOSQ_ERR_SUCCESS) return MOSQ_ERR_AUTH;
if(!pw__memcmp_const(pw->params.sha512_pbkdf2.password_hash, password_hash, HASH_LEN)){
return MOSQ_ERR_SUCCESS;
}else{
return MOSQ_ERR_AUTH;
}
#else
return MOSQ_ERR_NOT_SUPPORTED
#endif
}
static int pw__encode_sha512_pbkdf2(struct mosquitto_pw *pw)
{
#ifdef WITH_TLS
int rc;
char *salt64 = NULL, *hash64 = NULL;
rc = base64__encode(pw->params.sha512_pbkdf2.salt, pw->params.sha512_pbkdf2.salt_len, &salt64);
if(rc){
return MOSQ_ERR_UNKNOWN;
}
rc = base64__encode(pw->params.sha512_pbkdf2.password_hash, sizeof(pw->params.sha512_pbkdf2.password_hash), &hash64);
if(rc){
free(salt64);
return MOSQ_ERR_UNKNOWN;
}
free(pw->encoded_password);
size_t len = strlen("$6$$") + strlen("1,000,000,000,000") + strlen(salt64) + strlen(hash64) + 1;
pw->encoded_password = calloc(1, len);
if(!pw->encoded_password) return MOSQ_ERR_NOMEM;
snprintf(pw->encoded_password, len, "$%d$%d$%s$%s", pw->hashtype, pw->params.sha512_pbkdf2.iterations, salt64, hash64);
free(salt64);
free(hash64);
return MOSQ_ERR_SUCCESS;
#else
return MOSQ_ERR_NOT_SUPPORTED
#endif
}
static int pw__decode_sha512_pbkdf2(struct mosquitto_pw *pw, const char *salt_password)
{
#ifdef WITH_TLS
char *sp_heap, *saveptr = NULL;
char *iterations_s;
char *salt_b64, *password_b64;
unsigned char *salt, *password;
unsigned int salt_len, password_len;
int rc;
sp_heap = strdup(salt_password);
if(!sp_heap) return MOSQ_ERR_NOMEM;
iterations_s = strtok_r(sp_heap, "$", &saveptr);
if(iterations_s == NULL){
return MOSQ_ERR_INVAL;
}
pw->params.sha512_pbkdf2.iterations = atoi(iterations_s);
if(pw->params.sha512_pbkdf2.iterations < 1){
free(sp_heap);
return MOSQ_ERR_INVAL;
}
salt_b64 = strtok_r(NULL, "$", &saveptr);
if(salt_b64 == NULL){
free(sp_heap);
return MOSQ_ERR_INVAL;
}
rc = base64__decode(salt_b64, &salt, &salt_len);
if(rc != MOSQ_ERR_SUCCESS || (salt_len != 12 && salt_len != HASH_LEN)){
free(sp_heap);
return MOSQ_ERR_INVAL;
}
memcpy(pw->params.sha512_pbkdf2.salt, salt, salt_len);
free(salt);
pw->params.sha512_pbkdf2.salt_len = salt_len;
password_b64 = strtok_r(NULL, "$", &saveptr);
if(password_b64 == NULL){
free(sp_heap);
return MOSQ_ERR_INVAL;
}
rc = base64__decode(password_b64, &password, &password_len);
free(sp_heap);
if(rc != MOSQ_ERR_SUCCESS || password_len != HASH_LEN){
return MOSQ_ERR_INVAL;
}
memcpy(pw->params.sha512_pbkdf2.password_hash, password, password_len);
free(password);
return MOSQ_ERR_SUCCESS;
#else
return MOSQ_ERR_NOT_SUPPORTED;
#endif
}
/* ==================================================
* SHA512
* ================================================== */
#ifdef WITH_TLS
static int pw__hash_sha512(const char *password, struct mosquitto_pw *pw, unsigned char *password_hash, unsigned int hash_len)
{
const EVP_MD *digest;
#if OPENSSL_VERSION_NUMBER < 0x10100000L
EVP_MD_CTX context;
#else
EVP_MD_CTX *context;
#endif
digest = EVP_get_digestbyname("sha512");
if(!digest){
return MOSQ_ERR_UNKNOWN;
}
#if OPENSSL_VERSION_NUMBER < 0x10100000L
EVP_MD_CTX_init(&context);
EVP_DigestInit_ex(&context, digest, NULL);
EVP_DigestUpdate(&context, password, strlen(password));
EVP_DigestUpdate(&context, pw->params.sha512.salt, pw->params.sha512.salt_len);
EVP_DigestFinal_ex(&context, password_hash, &hash_len);
EVP_MD_CTX_cleanup(&context);
#else
context = EVP_MD_CTX_new();
EVP_DigestInit_ex(context, digest, NULL);
EVP_DigestUpdate(context, password, strlen(password));
EVP_DigestUpdate(context, pw->params.sha512.salt, pw->params.sha512.salt_len);
EVP_DigestFinal_ex(context, password_hash, &hash_len);
EVP_MD_CTX_free(context);
#endif
return MOSQ_ERR_SUCCESS;
}
#endif
static int pw__create_sha512(struct mosquitto_pw *pw, const char *password)
{
#ifdef WITH_TLS
pw->hashtype = pw_sha512;
pw->params.sha512.salt_len = HASH_LEN;
int rc = RAND_bytes(pw->params.sha512.salt, (int)pw->params.sha512.salt_len);
if(!rc){
return MOSQ_ERR_UNKNOWN;
}
return pw__hash_sha512(password, pw, pw->params.sha512.password_hash, sizeof(pw->params.sha512.password_hash));
#else
return MOSQ_ERR_NOT_SUPPORTED
#endif
}
static int pw__verify_sha512(struct mosquitto_pw *pw, const char *password)
{
#ifdef WITH_TLS
int rc;
unsigned char password_hash[HASH_LEN];
rc = pw__hash_sha512(password, pw, password_hash, sizeof(password_hash));
if(rc != MOSQ_ERR_SUCCESS) return MOSQ_ERR_AUTH;
if(!pw__memcmp_const(pw->params.sha512.password_hash, password_hash, HASH_LEN)){
return MOSQ_ERR_SUCCESS;
}else{
return MOSQ_ERR_AUTH;
}
#else
return MOSQ_ERR_NOT_SUPPORTED
#endif
}
static int pw__encode_sha512(struct mosquitto_pw *pw)
{
#ifdef WITH_TLS
int rc;
char *salt64 = NULL, *hash64 = NULL;
rc = base64__encode(pw->params.sha512.salt, pw->params.sha512.salt_len, &salt64);
if(rc){
return MOSQ_ERR_UNKNOWN;
}
rc = base64__encode(pw->params.sha512.password_hash, sizeof(pw->params.sha512.password_hash), &hash64);
if(rc){
return MOSQ_ERR_UNKNOWN;
}
free(pw->encoded_password);
size_t len = strlen("$6$$") + strlen(salt64) + strlen(hash64) + 1;
pw->encoded_password = calloc(1, len);
if(!pw->encoded_password) return MOSQ_ERR_NOMEM;
snprintf(pw->encoded_password, len, "$%d$%s$%s", pw->hashtype, salt64, hash64);
free(salt64);
free(hash64);
return MOSQ_ERR_SUCCESS;
#else
return MOSQ_ERR_NOT_SUPPORTED
#endif
}
static int pw__decode_sha512(struct mosquitto_pw *pw, const char *salt_password)
{
#ifdef WITH_TLS
char *sp_heap, *saveptr = NULL;
char *salt_b64, *password_b64;
unsigned char *salt, *password;
unsigned int salt_len, password_len;
int rc;
sp_heap = strdup(salt_password);
if(!sp_heap) return MOSQ_ERR_NOMEM;
salt_b64 = strtok_r(sp_heap, "$", &saveptr);
if(salt_b64 == NULL){
free(sp_heap);
return MOSQ_ERR_INVAL;
}
rc = base64__decode(salt_b64, &salt, &salt_len);
if(rc != MOSQ_ERR_SUCCESS || (salt_len != 12 && salt_len != HASH_LEN)){
free(sp_heap);
return MOSQ_ERR_INVAL;
}
memcpy(pw->params.sha512.salt, salt, salt_len);
free(salt);
pw->params.sha512.salt_len = salt_len;
password_b64 = strtok_r(NULL, "$", &saveptr);
if(password_b64 == NULL){
free(sp_heap);
return MOSQ_ERR_INVAL;
}
rc = base64__decode(password_b64, &password, &password_len);
free(sp_heap);
if(rc != MOSQ_ERR_SUCCESS || password_len != HASH_LEN){
return MOSQ_ERR_INVAL;
}
memcpy(pw->params.sha512.password_hash, password, password_len);
free(password);
return MOSQ_ERR_SUCCESS;
#else
return MOSQ_ERR_NOT_SUPPORTED;
#endif
}
/* ==================================================
* Public
* ================================================== */
int pw__create(struct mosquitto_pw *pw, const char *password)
{
switch(pw->hashtype){
case pw_sha512:
return pw__create_sha512(pw, password);
case pw_sha512_pbkdf2:
return pw__create_sha512_pbkdf2(pw, password);
case pw_argon2id:
return MOSQ_ERR_AUTH;
default:
return pw__create_sha512_pbkdf2(pw, password);
}
return MOSQ_ERR_INVAL;
}
int pw__verify(struct mosquitto_pw *pw, const char *password)
{
switch(pw->hashtype){
case pw_sha512:
return pw__verify_sha512(pw, password);
case pw_sha512_pbkdf2:
return pw__verify_sha512_pbkdf2(pw, password);
case pw_argon2id:
return MOSQ_ERR_AUTH;
}
return MOSQ_ERR_AUTH;
}
int pw__encode(struct mosquitto_pw *pw)
{
switch(pw->hashtype){
case pw_sha512:
return pw__encode_sha512(pw);
case pw_sha512_pbkdf2:
return pw__encode_sha512_pbkdf2(pw);
case pw_argon2id:
return MOSQ_ERR_AUTH;
}
return MOSQ_ERR_AUTH;
}
int pw__decode(struct mosquitto_pw *pw, const char *password)
{
if(password[0] != '$'){
return MOSQ_ERR_INVAL;
}
if(password[1] == '6' && password[2] == '$'){
pw->hashtype = pw_sha512;
return pw__decode_sha512(pw, &password[3]);
}else if(password[1] == '7' && password[2] == '$'){
pw->hashtype = pw_sha512_pbkdf2;
return pw__decode_sha512_pbkdf2(pw, &password[3]);
}else{
return MOSQ_ERR_INVAL;
}
}
+25 -6
View File
@@ -31,20 +31,39 @@ Contributors:
enum mosquitto_pwhash_type{
pw_sha512 = 6,
pw_sha512_pbkdf2 = 7,
pw_argon2id = 8,
};
#define PW_DEFAULT_ITERATIONS 101
struct mosquitto_pw{
unsigned char password_hash[HASH_LEN]; /* For SHA512 */
unsigned char salt[HASH_LEN];
size_t salt_len;
int iterations;
union {
struct {
unsigned char password_hash[HASH_LEN]; /* For SHA512 */
unsigned char salt[HASH_LEN];
size_t salt_len;
} sha512;
struct {
unsigned char password_hash[HASH_LEN]; /* For SHA512 */
unsigned char salt[HASH_LEN];
size_t salt_len;
int iterations;
} sha512_pbkdf2;
struct {
unsigned char password_hash[HASH_LEN];
unsigned char salt[HASH_LEN];
size_t salt_len;
int iterations;
} argon2id;
} params;
char *encoded_password;
enum mosquitto_pwhash_type hashtype;
bool valid;
};
int pw__hash(const char *password, struct mosquitto_pw *pw, bool new_password, int new_iterations);
int pw__memcmp_const(const void *ptr1, const void *b, size_t len);
int pw__create(struct mosquitto_pw *pw, const char *password);
int pw__encode(struct mosquitto_pw *pw);
int pw__decode(struct mosquitto_pw *pw, const char *password);
int pw__verify(struct mosquitto_pw *pw, const char *password);
#endif
-1
View File
@@ -25,7 +25,6 @@ if(CJSON_FOUND AND WITH_TLS)
dynamic_security.h
groups.c
grouplist.c
hash.c
../../common/json_help.c ../../common/json_help.h
kicklist.c
../../common/misc_mosq.c ../../common/misc_mosq.h
-1
View File
@@ -26,7 +26,6 @@ OBJS = \
default_acl.o \
groups.o \
grouplist.o \
hash.o \
kicklist.o \
plugin.o \
roles.o \
+3 -22
View File
@@ -34,26 +34,11 @@ Contributors:
* #
* ################################################################ */
static int memcmp_const(const void *a, const void *b, size_t len)
{
size_t i;
int rc = 0;
if(!a || !b) return 1;
for(i=0; i<len; i++){
rc |= ((char *)a)[i] ^ ((char *)b)[i];
}
return rc;
}
int dynsec_auth__basic_auth_callback(int event, void *event_data, void *userdata)
{
struct mosquitto_evt_basic_auth *ed = event_data;
struct dynsec__data *data = userdata;
struct dynsec__client *client;
unsigned char password_hash[64]; /* For SHA512 */
const char *clientid;
UNUSED(event);
@@ -72,14 +57,10 @@ int dynsec_auth__basic_auth_callback(int event, void *event_data, void *userdata
return MOSQ_ERR_AUTH;
}
}
if(client->pw.valid && dynsec_auth__pw_hash(client, ed->password, password_hash, sizeof(password_hash), false) == MOSQ_ERR_SUCCESS){
if(memcmp_const(client->pw.password_hash, password_hash, sizeof(password_hash)) == 0){
return MOSQ_ERR_SUCCESS;
}else{
return MOSQ_ERR_AUTH;
}
if(client->pw.valid && pw__verify(&client->pw, ed->password) == MOSQ_ERR_SUCCESS){
return MOSQ_ERR_SUCCESS;
}else{
return MOSQ_ERR_PLUGIN_DEFER;
return MOSQ_ERR_AUTH;
}
}else{
return MOSQ_ERR_PLUGIN_DEFER;
+68 -48
View File
@@ -148,37 +148,50 @@ int dynsec_clients__config_load(struct dynsec__data *data, cJSON *tree)
client->disabled = disabled;
}
int iterations;
const char *salt;
const char *password;
json_get_int(j_client, "iterations", &iterations, 0, true);
if(json_get_string(j_client, "salt", &salt, false) == MOSQ_ERR_SUCCESS
&& json_get_string(j_client, "password", &password, false) == MOSQ_ERR_SUCCESS
&& iterations > 0){
client->pw.iterations = iterations;
if(base64__decode(salt, &buf, &buf_len) != MOSQ_ERR_SUCCESS
|| buf_len > sizeof(client->pw.salt)){
mosquitto_free(client);
continue;
if(json_get_string(j_client, "encoded_password", &password, false) == MOSQ_ERR_SUCCESS){
/* FIXME */
if(pw__decode(&client->pw, password) == MOSQ_ERR_SUCCESS){
client->pw.valid = true;
}else{
client->pw.valid = false;
}
memcpy(client->pw.salt, buf, (size_t)buf_len);
client->pw.salt_len = (size_t)buf_len;
if(base64__decode(password, &buf, &buf_len) != MOSQ_ERR_SUCCESS
|| buf_len != sizeof(client->pw.password_hash)){
mosquitto_free(buf);
mosquitto_free(client);
continue;
}
memcpy(client->pw.password_hash, buf, (size_t)buf_len);
mosquitto_free(buf);
client->pw.valid = true;
}else{
client->pw.valid = false;
/* sha512-pbkdf2 only */
int iterations;
const char *salt;
json_get_int(j_client, "iterations", &iterations, 0, true);
if(json_get_string(j_client, "salt", &salt, false) == MOSQ_ERR_SUCCESS
&& json_get_string(j_client, "password", &password, false) == MOSQ_ERR_SUCCESS
&& iterations > 0){
client->pw.hashtype = pw_sha512_pbkdf2;
client->pw.params.sha512_pbkdf2.iterations = iterations;
if(base64__decode(salt, &buf, &buf_len) != MOSQ_ERR_SUCCESS
|| buf_len > sizeof(client->pw.params.sha512_pbkdf2.salt)){
mosquitto_free(buf);
mosquitto_free(client);
continue;
}
memcpy(client->pw.params.sha512_pbkdf2.salt, buf, (size_t)buf_len);
client->pw.params.sha512_pbkdf2.salt_len = (size_t)buf_len;
mosquitto_free(buf);
if(base64__decode(password, &buf, &buf_len) != MOSQ_ERR_SUCCESS
|| buf_len != sizeof(client->pw.params.sha512_pbkdf2.password_hash)){
mosquitto_free(buf);
mosquitto_free(client);
continue;
}
memcpy(client->pw.params.sha512_pbkdf2.password_hash, buf, (size_t)buf_len);
mosquitto_free(buf);
client->pw.valid = true;
}else{
client->pw.valid = false;
}
}
/* Client id */
@@ -241,8 +254,7 @@ int dynsec_clients__config_load(struct dynsec__data *data, cJSON *tree)
static int dynsec__config_add_clients(struct dynsec__data *data, cJSON *j_clients)
{
struct dynsec__client *client, *client_tmp;
cJSON *j_client, *j_roles, *jtmp;
char *buf;
cJSON *j_client, *j_roles;
HASH_ITER(hh, data->clients, client, client_tmp){
j_client = cJSON_CreateObject();
@@ -266,25 +278,33 @@ static int dynsec__config_add_clients(struct dynsec__data *data, cJSON *j_client
cJSON_AddItemToObject(j_client, "roles", j_roles);
if(client->pw.valid){
if(base64__encode(client->pw.password_hash, sizeof(client->pw.password_hash), &buf) != MOSQ_ERR_SUCCESS){
return 1;
}
jtmp = cJSON_CreateString(buf);
mosquitto_free(buf);
if(jtmp == NULL) return 1;
cJSON_AddItemToObject(j_client, "password", jtmp);
if(client->pw.hashtype == pw_sha512_pbkdf2){
char *buf;
if(base64__encode(client->pw.salt, client->pw.salt_len, &buf) != MOSQ_ERR_SUCCESS){
return 1;
}
if(base64__encode(client->pw.params.sha512_pbkdf2.password_hash, sizeof(client->pw.params.sha512_pbkdf2.password_hash), &buf) != MOSQ_ERR_SUCCESS){
return 1;
}
cJSON *jtmp = cJSON_CreateString(buf);
mosquitto_free(buf);
if(jtmp == NULL) return 1;
cJSON_AddItemToObject(j_client, "password", jtmp);
jtmp = cJSON_CreateString(buf);
mosquitto_free(buf);
if(jtmp == NULL) return 1;
cJSON_AddItemToObject(j_client, "salt", jtmp);
if(base64__encode(client->pw.params.sha512_pbkdf2.salt, client->pw.params.sha512_pbkdf2.salt_len, &buf) != MOSQ_ERR_SUCCESS){
return 1;
}
if(cJSON_AddIntToObject(j_client, "iterations", client->pw.iterations) == NULL){
return 1;
jtmp = cJSON_CreateString(buf);
mosquitto_free(buf);
if(jtmp == NULL) return 1;
cJSON_AddItemToObject(j_client, "salt", jtmp);
if(cJSON_AddIntToObject(j_client, "iterations", client->pw.params.sha512_pbkdf2.iterations) == NULL){
return 1;
}
}else{
if(cJSON_AddStringToObject(j_client, "encoded_password", client->pw.encoded_password) == NULL){
return 1;
}
}
}
}
@@ -389,7 +409,7 @@ int dynsec_clients__process_create(struct dynsec__data *data, struct mosquitto_c
}
if(password){
if(dynsec_auth__pw_hash(client, password, client->pw.password_hash, sizeof(client->pw.password_hash), true)){
if(pw__create(&client->pw, password)){
mosquitto_control_command_reply(cmd, "Internal error");
client__free_item(data, client);
return MOSQ_ERR_NOMEM;
@@ -630,7 +650,7 @@ int dynsec_clients__process_set_id(struct dynsec__data *data, struct mosquitto_c
static int client__set_password(struct dynsec__client *client, const char *password)
{
if(dynsec_auth__pw_hash(client, password, client->pw.password_hash, sizeof(client->pw.password_hash), true) == MOSQ_ERR_SUCCESS){
if(pw__create(&client->pw, password) == MOSQ_ERR_SUCCESS){
client->pw.valid = true;
return MOSQ_ERR_SUCCESS;
+48 -36
View File
@@ -113,7 +113,7 @@ static int get_password_from_init_file(struct dynsec__data *data, char **pw)
* * The contents of the MOSQUITTO_DYNSEC_PASSWORD environment variable
* * Randomly generated passwords for "admin", "user", stored in plain text at '<plugin_opt_config_file>.pw'
*/
static int generate_password(struct dynsec__data *data, int iterations, char **password, char **password_hash, char **salt)
static int generate_password(struct dynsec__data *data, cJSON *j_client, char **password)
{
struct mosquitto_pw pw;
int i;
@@ -158,21 +158,48 @@ static int generate_password(struct dynsec__data *data, int iterations, char **p
(*password)[20] = '\0';
}
if(pw__hash(*password, &pw, true, iterations) != MOSQ_ERR_SUCCESS){
pw.hashtype = pw_sha512_pbkdf2;
pw.params.sha512_pbkdf2.iterations = PW_DEFAULT_ITERATIONS + 1;
if(pw__create(&pw, *password) != MOSQ_ERR_SUCCESS){
free(*password);
*password = NULL;
return MOSQ_ERR_UNKNOWN;
}
if(base64__encode(pw.salt, (unsigned int)pw.salt_len, salt)
|| base64__encode(pw.password_hash, sizeof(pw.password_hash), password_hash)
){
if(pw.hashtype == pw_sha512_pbkdf2){
char *salt_b64 = NULL, *password_b64 = NULL;
free(*password);
free(*password_hash);
free(*salt);
return MOSQ_ERR_NOMEM;
if(base64__encode(pw.params.sha512_pbkdf2.salt, pw.params.sha512_pbkdf2.salt_len, &salt_b64)
|| base64__encode(pw.params.sha512_pbkdf2.password_hash, sizeof(pw.params.sha512_pbkdf2.password_hash), &password_b64)
|| cJSON_AddStringToObject(j_client, "salt", salt_b64) == NULL
|| cJSON_AddStringToObject(j_client, "password", password_b64) == NULL
|| cJSON_AddNumberToObject(j_client, "iterations", pw.params.sha512_pbkdf2.iterations) == NULL){
free(password_b64);
free(salt_b64);
free(*password);
*password = NULL;
return MOSQ_ERR_UNKNOWN;
}
free(password_b64);
free(salt_b64);
}else{
if(pw__encode(&pw) != MOSQ_ERR_SUCCESS){
free(*password);
*password = NULL;
return MOSQ_ERR_UNKNOWN;
}
if(cJSON_AddStringToObject(j_client, "encoded_password", pw.encoded_password)){
free(pw.encoded_password);
free(*password);
*password = NULL;
return MOSQ_ERR_UNKNOWN;
}
free(pw.encoded_password);
}
return MOSQ_ERR_SUCCESS;
}
@@ -198,34 +225,26 @@ static int client_add_admin(struct dynsec__data *data, FILE *pwfile, cJSON *j_cl
{
cJSON *j_client, *j_roles;
char *password = NULL;
char *password_hash = NULL;
char *salt = NULL;
if(generate_password(data, 10000, &password, &password_hash, &salt)){
return MOSQ_ERR_UNKNOWN;
}
j_client = cJSON_CreateObject();
if(j_client == NULL){
return MOSQ_ERR_NOMEM;
}
if(generate_password(data, j_client, &password)){
cJSON_Delete(j_client);
return MOSQ_ERR_UNKNOWN;
}
cJSON_AddItemToArray(j_clients, j_client);
if(cJSON_AddStringToObject(j_client, "username", "admin") == NULL
|| cJSON_AddStringToObject(j_client, "textname", "Admin user") == NULL
|| cJSON_AddStringToObject(j_client, "password", password_hash) == NULL
|| cJSON_AddStringToObject(j_client, "salt", salt) == NULL
|| cJSON_AddNumberToObject(j_client, "iterations", 10000) == NULL
|| (j_roles = cJSON_AddArrayToObject(j_client, "roles")) == NULL
){
cJSON_Delete(j_client);
free(password);
free(password_hash);
free(salt);
return MOSQ_ERR_NOMEM;
}
free(password_hash);
free(salt);
if(client_role_add(j_roles, "super-admin")
|| client_role_add(j_roles, "sys-observe")
@@ -247,37 +266,30 @@ static int client_add_user(struct dynsec__data *data, FILE *pwfile, cJSON *j_cli
{
cJSON *j_client, *j_roles;
char *password = NULL;
char *password_hash = NULL;
char *salt = NULL;
if(data->init_mode != dpwim_random){
return MOSQ_ERR_SUCCESS;
}
if(generate_password(data, 10000, &password, &password_hash, &salt)){
return MOSQ_ERR_UNKNOWN;
}
j_client = cJSON_CreateObject();
if(j_client == NULL){
return MOSQ_ERR_NOMEM;
}
cJSON_AddItemToArray(j_clients, j_client);
if(generate_password(data, j_client, &password)){
cJSON_Delete(j_client);
return MOSQ_ERR_UNKNOWN;
}
if(cJSON_AddStringToObject(j_client, "username", "democlient") == NULL
|| cJSON_AddStringToObject(j_client, "textname", "Demonstration client with full read/write access to the '#' topic hierarchy.") == NULL
|| cJSON_AddStringToObject(j_client, "password", password_hash) == NULL
|| cJSON_AddStringToObject(j_client, "salt", salt) == NULL
|| cJSON_AddNumberToObject(j_client, "iterations", 10000) == NULL
|| (j_roles = cJSON_AddArrayToObject(j_client, "roles")) == NULL
){
free(password);
free(password_hash);
free(salt);
cJSON_Delete(j_client);
return MOSQ_ERR_NOMEM;
}
free(password_hash);
free(salt);
cJSON_AddItemToArray(j_clients, j_client);
if(client_role_add(j_roles, "client")){
free(password);
-67
View File
@@ -1,67 +0,0 @@
/*
Copyright (c) 2020-2021 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License 2.0
and Eclipse Distribution License v1.0 which accompany this distribution.
The Eclipse Public License is available at
https://www.eclipse.org/legal/epl-2.0/
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
Contributors:
Roger Light - initial implementation and documentation.
*/
#include "config.h"
#include <openssl/bio.h>
#include <openssl/buffer.h>
#include <openssl/evp.h>
#include <openssl/rand.h>
#include "dynamic_security.h"
#include "mosquitto.h"
#include "mosquitto_broker.h"
/* ################################################################
* #
* # Password functions
* #
* ################################################################ */
int dynsec_auth__pw_hash(struct dynsec__client *client, const char *password, unsigned char *password_hash, int password_hash_len, bool new_password)
{
const EVP_MD *digest;
int iterations;
if(new_password){
client->pw.salt_len = HASH_LEN;
if(RAND_bytes(client->pw.salt, (int)client->pw.salt_len) != 1){
return MOSQ_ERR_UNKNOWN;
}
if(client->pw.iterations > 0){
iterations = client->pw.iterations;
}else{
iterations = PW_DEFAULT_ITERATIONS;
}
}else{
iterations = client->pw.iterations;
}
if(iterations < 1){
return MOSQ_ERR_INVAL;
}
client->pw.iterations = iterations;
digest = EVP_get_digestbyname("sha512");
if(!digest){
return MOSQ_ERR_UNKNOWN;
}
return !PKCS5_PBKDF2_HMAC(password, (int)strlen(password),
client->pw.salt, (int)client->pw.salt_len, iterations,
digest, password_hash_len, password_hash);
}
+2 -1
View File
@@ -1,3 +1,4 @@
rm test.json
export MOSQUITTO_DYNSEC_PASSWORD=passwordpass
../../src/mosquitto -c test.conf
export VG="valgrind --log-file=vglog"
${VG} ../../src/mosquitto -c test.conf
+1 -8
View File
@@ -428,15 +428,8 @@ struct mosquitto__client_msg{
struct mosquitto__unpwd{
UT_hash_handle hh;
char *username;
char *password;
char *clientid;
#ifdef WITH_TLS
unsigned char *salt;
unsigned int password_len;
unsigned int salt_len;
int iterations;
#endif
enum mosquitto_pwhash_type hashtype;
struct mosquitto_pw pw;
};
struct mosquitto__acl{
+13 -151
View File
@@ -35,9 +35,6 @@ static int unpwd__file_parse(struct mosquitto__unpwd **unpwd, const char *passwo
static int acl__cleanup(bool reload);
static int unpwd__cleanup(struct mosquitto__unpwd **unpwd, bool reload);
static int psk__file_parse(struct mosquitto__unpwd **psk_id, const char *psk_file);
#ifdef WITH_TLS
static int pw__digest(const char *password, const unsigned char *salt, unsigned int salt_len, unsigned char *hash, unsigned int *hash_len, enum mosquitto_pwhash_type hashtype, int iterations);
#endif
static int mosquitto_basic_auth_default(int event, void *event_data, void *userdata);
static int mosquitto_acl_check_default(int event, void *event_data, void *userdata);
@@ -782,8 +779,8 @@ static int pwfile__parse(const char *file, struct mosquitto__unpwd **root)
continue;
}
unpwd->password = mosquitto__strdup(password);
if(!unpwd->password){
unpwd->pw.encoded_password = mosquitto__strdup(password);
if(!unpwd->pw.encoded_password){
fclose(pwfile);
mosquitto__FREE(unpwd->username);
mosquitto__FREE(unpwd);
@@ -810,10 +807,7 @@ static int pwfile__parse(const char *file, struct mosquitto__unpwd **root)
void unpwd__free_item(struct mosquitto__unpwd **unpwd, struct mosquitto__unpwd *item)
{
mosquitto__FREE(item->username);
mosquitto__FREE(item->password);
#ifdef WITH_TLS
mosquitto__FREE(item->salt);
#endif
mosquitto__FREE(item->pw.encoded_password);
HASH_DEL(*unpwd, item);
mosquitto__FREE(item);
}
@@ -823,82 +817,19 @@ void unpwd__free_item(struct mosquitto__unpwd **unpwd, struct mosquitto__unpwd *
static int unpwd__decode_passwords(struct mosquitto__unpwd **unpwd)
{
struct mosquitto__unpwd *u, *tmp = NULL;
char *token;
unsigned char *salt;
unsigned int salt_len;
unsigned char *password;
unsigned int password_len;
int rc;
enum mosquitto_pwhash_type hashtype;
HASH_ITER(hh, *unpwd, u, tmp){
/* Need to decode password into hashed data + salt. */
if(u->password == NULL){
if(u->pw.encoded_password == NULL){
log__printf(NULL, MOSQ_LOG_ERR, "Error: Missing password hash for user %s, removing entry.", u->username);
unpwd__free_item(unpwd, u);
continue;
}
token = strtok(u->password, "$");
if(token == NULL){
log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid password hash for user %s, removing entry.", u->username);
unpwd__free_item(unpwd, u);
continue;
}
if(!strcmp(token, "6")){
hashtype = pw_sha512;
}else if(!strcmp(token, "7")){
hashtype = pw_sha512_pbkdf2;
}else{
log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid password hash type for user %s, removing entry.", u->username);
unpwd__free_item(unpwd, u);
continue;
}
if(hashtype == pw_sha512_pbkdf2){
token = strtok(NULL, "$");
if(token == NULL){
log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid password hash for user %s, removing entry.", u->username);
unpwd__free_item(unpwd, u);
continue;
}
u->iterations = atoi(token);
if(u->iterations < 1){
log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid hash iterations for user %s, removing entry.", u->username);
unpwd__free_item(unpwd, u);
continue;
}
}
token = strtok(NULL, "$");
if(token == NULL){
log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid password hash for user %s, removing entry.", u->username);
unpwd__free_item(unpwd, u);
continue;
}
rc = base64__decode(token, &salt, &salt_len);
if(rc == MOSQ_ERR_SUCCESS && (salt_len == 12 || salt_len == HASH_LEN)){
u->salt = salt;
u->salt_len = salt_len;
token = strtok(NULL, "$");
if(token){
rc = base64__decode(token, &password, &password_len);
if(rc == MOSQ_ERR_SUCCESS && password_len == HASH_LEN){
mosquitto__FREE(u->password);
u->password = (char *)password;
u->password_len = password_len;
u->hashtype = hashtype;
}else{
log__printf(NULL, MOSQ_LOG_ERR, "Error: Unable to decode password for user %s, removing entry.", u->username);
unpwd__free_item(unpwd, u);
}
}else{
log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid password hash for user %s, removing entry.", u->username);
unpwd__free_item(unpwd, u);
}
}else{
log__printf(NULL, MOSQ_LOG_ERR, "Error: Unable to decode password salt for user %s, removing entry.", u->username);
rc = pw__decode(&u->pw, u->pw.encoded_password);
if(rc){
log__printf(NULL, MOSQ_LOG_ERR, "Error: Unable to decode password/salt for user %s, removing entry.", u->username);
unpwd__free_item(unpwd, u);
}
}
@@ -940,11 +871,11 @@ static int psk__file_parse(struct mosquitto__unpwd **psk_id, const char *psk_fil
HASH_ITER(hh, (*psk_id), u, tmp){
/* Check for hex only digits */
if(!u->password){
if(!u->pw.encoded_password){
log__printf(NULL, MOSQ_LOG_ERR, "Error: Empty psk for identity \"%s\".", u->username);
return MOSQ_ERR_INVAL;
}
if(strspn(u->password, "0123456789abcdefABCDEF") < strlen(u->password)){
if(strspn(u->pw.encoded_password, "0123456789abcdefABCDEF") < strlen(u->pw.encoded_password)){
log__printf(NULL, MOSQ_LOG_ERR, "Error: psk for identity \"%s\" contains non-hexadecimal characters.", u->username);
return MOSQ_ERR_INVAL;
}
@@ -958,11 +889,6 @@ static int mosquitto_basic_auth_default(int event, void *event_data, void *userd
struct mosquitto_evt_basic_auth *ed = event_data;
struct mosquitto__unpwd *u;
struct mosquitto__unpwd *unpwd_ref;
#ifdef WITH_TLS
unsigned char hash[EVP_MAX_MD_SIZE];
unsigned int hash_len;
int rc;
#endif
UNUSED(event);
UNUSED(userdata);
@@ -981,24 +907,9 @@ static int mosquitto_basic_auth_default(int event, void *event_data, void *userd
HASH_FIND(hh, unpwd_ref, ed->client->username, strlen(ed->client->username), u);
if(u){
if(u->password){
if(u->pw.encoded_password){
if(ed->client->password){
#ifdef WITH_TLS
rc = pw__digest(ed->client->password, u->salt, u->salt_len, hash, &hash_len, u->hashtype, u->iterations);
if(rc == MOSQ_ERR_SUCCESS){
if(hash_len == u->password_len && !pw__memcmp_const(u->password, hash, hash_len)){
return MOSQ_ERR_SUCCESS;
}else{
return MOSQ_ERR_AUTH;
}
}else{
return rc;
}
#else
if(!strcmp(u->password, ed->client->password)){
return MOSQ_ERR_SUCCESS;
}
#endif
return pw__verify(&u->pw, ed->client->password);
}else{
return MOSQ_ERR_AUTH;
}
@@ -1020,11 +931,8 @@ static int unpwd__cleanup(struct mosquitto__unpwd **root, bool reload)
HASH_ITER(hh, *root, u, tmp){
HASH_DEL(*root, u);
mosquitto__FREE(u->password);
mosquitto__FREE(u->pw.encoded_password);
mosquitto__FREE(u->username);
#ifdef WITH_TLS
mosquitto__FREE(u->salt);
#endif
mosquitto__FREE(u);
}
@@ -1267,56 +1175,10 @@ int mosquitto_psk_key_get_default(struct mosquitto *context, const char *hint, c
HASH_ITER(hh, psk_id_ref, u, tmp){
if(!strcmp(u->username, identity)){
strncpy(key, u->password, (size_t)max_key_len);
strncpy(key, u->pw.encoded_password, (size_t)max_key_len);
return MOSQ_ERR_SUCCESS;
}
}
return MOSQ_ERR_AUTH;
}
#ifdef WITH_TLS
int pw__digest(const char *password, const unsigned char *salt, unsigned int salt_len, unsigned char *hash, unsigned int *hash_len, enum mosquitto_pwhash_type hashtype, int iterations)
{
const EVP_MD *digest;
#if OPENSSL_VERSION_NUMBER < 0x10100000L
EVP_MD_CTX context;
#else
EVP_MD_CTX *context;
#endif
digest = EVP_get_digestbyname("sha512");
if(!digest){
/* FIXME fprintf(stderr, "Error: Unable to create openssl digest.\n"); */
return 1;
}
if(hashtype == pw_sha512){
#if OPENSSL_VERSION_NUMBER < 0x10100000L
EVP_MD_CTX_init(&context);
EVP_DigestInit_ex(&context, digest, NULL);
EVP_DigestUpdate(&context, password, strlen(password));
EVP_DigestUpdate(&context, salt, salt_len);
/* hash is assumed to be EVP_MAX_MD_SIZE bytes long. */
EVP_DigestFinal_ex(&context, hash, hash_len);
EVP_MD_CTX_cleanup(&context);
#else
context = EVP_MD_CTX_new();
EVP_DigestInit_ex(context, digest, NULL);
EVP_DigestUpdate(context, password, strlen(password));
EVP_DigestUpdate(context, salt, salt_len);
/* hash is assumed to be EVP_MAX_MD_SIZE bytes long. */
EVP_DigestFinal_ex(context, hash, hash_len);
EVP_MD_CTX_free(context);
#endif
}else{
*hash_len = EVP_MAX_MD_SIZE;
PKCS5_PBKDF2_HMAC(password, (int)strlen(password),
salt, (int)salt_len, iterations,
digest, (int)(*hash_len), hash);
}
return MOSQ_ERR_SUCCESS;
}
#endif