Add support for pbkdf2 hash iterations.
This commit is contained in:
parent
899695261a
commit
1851a0e1b1
@ -57,6 +57,7 @@ struct cb_helper {
|
|||||||
const char *line;
|
const char *line;
|
||||||
const char *username;
|
const char *username;
|
||||||
const char *password;
|
const char *password;
|
||||||
|
int iterations;
|
||||||
bool found;
|
bool found;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -117,7 +118,7 @@ void print_usage(void)
|
|||||||
printf("\nSee https://mosquitto.org/ for more information.\n\n");
|
printf("\nSee https://mosquitto.org/ for more information.\n\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
int output_new_password(FILE *fptr, const char *username, const char *password)
|
int output_new_password(FILE *fptr, const char *username, const char *password, int iterations)
|
||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
char *salt64 = NULL, *hash64 = NULL;
|
char *salt64 = NULL, *hash64 = NULL;
|
||||||
@ -127,7 +128,7 @@ int output_new_password(FILE *fptr, const char *username, const char *password)
|
|||||||
|
|
||||||
pw.hashtype = hashtype;
|
pw.hashtype = hashtype;
|
||||||
|
|
||||||
if(pw__hash(password, &pw, true)){
|
if(pw__hash(password, &pw, true, iterations)){
|
||||||
fprintf(stderr, "Error: Unable to hash password.\n");
|
fprintf(stderr, "Error: Unable to hash password.\n");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@ -147,7 +148,11 @@ int output_new_password(FILE *fptr, const char *username, const char *password)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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);
|
fprintf(fptr, "%s:$%d$%s$%s\n", username, hashtype, salt64, hash64);
|
||||||
|
}
|
||||||
free(salt64);
|
free(salt64);
|
||||||
free(hash64);
|
free(hash64);
|
||||||
|
|
||||||
@ -261,7 +266,7 @@ int delete_pwuser(FILE *fptr, FILE *ftmp, const char *username)
|
|||||||
* ====================================================================== */
|
* ====================================================================== */
|
||||||
static int update_file_cb(FILE *fptr, FILE *ftmp, const char *username, const char *password, const char *line, struct cb_helper *helper)
|
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);
|
return output_new_password(ftmp, username, password, helper->iterations);
|
||||||
}
|
}
|
||||||
|
|
||||||
int update_file(FILE *fptr, FILE *ftmp)
|
int update_file(FILE *fptr, FILE *ftmp)
|
||||||
@ -283,12 +288,12 @@ static int update_pwuser_cb(FILE *fptr, FILE *ftmp, const char *username, const
|
|||||||
}else{
|
}else{
|
||||||
/* Write out a new line for our matching username */
|
/* Write out a new line for our matching username */
|
||||||
helper->found = true;
|
helper->found = true;
|
||||||
rc = output_new_password(ftmp, username, helper->password);
|
rc = output_new_password(ftmp, username, helper->password, helper->iterations);
|
||||||
}
|
}
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
int update_pwuser(FILE *fptr, FILE *ftmp, const char *username, const char *password)
|
int update_pwuser(FILE *fptr, FILE *ftmp, const char *username, const char *password, int iterations)
|
||||||
{
|
{
|
||||||
struct cb_helper helper;
|
struct cb_helper helper;
|
||||||
int rc;
|
int rc;
|
||||||
@ -296,12 +301,13 @@ int update_pwuser(FILE *fptr, FILE *ftmp, const char *username, const char *pass
|
|||||||
memset(&helper, 0, sizeof(helper));
|
memset(&helper, 0, sizeof(helper));
|
||||||
helper.username = username;
|
helper.username = username;
|
||||||
helper.password = password;
|
helper.password = password;
|
||||||
|
helper.iterations = iterations;
|
||||||
rc = pwfile_iterate(fptr, ftmp, update_pwuser_cb, &helper);
|
rc = pwfile_iterate(fptr, ftmp, update_pwuser_cb, &helper);
|
||||||
|
|
||||||
if(helper.found){
|
if(helper.found){
|
||||||
return rc;
|
return rc;
|
||||||
}else{
|
}else{
|
||||||
return output_new_password(ftmp, username, password);
|
return output_new_password(ftmp, username, password, iterations);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -466,6 +472,7 @@ int main(int argc, char *argv[])
|
|||||||
bool do_update_file = false;
|
bool do_update_file = false;
|
||||||
char *backup_file;
|
char *backup_file;
|
||||||
int idx;
|
int idx;
|
||||||
|
int iterations = PW_DEFAULT_ITERATIONS;
|
||||||
|
|
||||||
signal(SIGINT, handle_sigint);
|
signal(SIGINT, handle_sigint);
|
||||||
signal(SIGTERM, handle_sigint);
|
signal(SIGTERM, handle_sigint);
|
||||||
@ -505,6 +512,17 @@ int main(int argc, char *argv[])
|
|||||||
create_new = true;
|
create_new = true;
|
||||||
}else if(!strcmp(argv[idx], "-D")){
|
}else if(!strcmp(argv[idx], "-D")){
|
||||||
delete_user = true;
|
delete_user = true;
|
||||||
|
}else if(!strcmp(argv[idx], "-I")){
|
||||||
|
if(idx+1 == argc){
|
||||||
|
fprintf(stderr, "Error: -I argument given but not enough other arguments.\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
iterations = atoi(argv[idx+1]);
|
||||||
|
idx++;
|
||||||
|
if(iterations < 1){
|
||||||
|
fprintf(stderr, "Error: Number of iterations must be > 0.\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
}else if(!strcmp(argv[idx], "-U")){
|
}else if(!strcmp(argv[idx], "-U")){
|
||||||
do_update_file = true;
|
do_update_file = true;
|
||||||
}else{
|
}else{
|
||||||
@ -622,7 +640,7 @@ int main(int argc, char *argv[])
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
free(password_file);
|
free(password_file);
|
||||||
rc = output_new_password(fptr, username, password_cmd);
|
rc = output_new_password(fptr, username, password_cmd, iterations);
|
||||||
fclose(fptr);
|
fclose(fptr);
|
||||||
return rc;
|
return rc;
|
||||||
}else{
|
}else{
|
||||||
@ -663,7 +681,7 @@ int main(int argc, char *argv[])
|
|||||||
}else{
|
}else{
|
||||||
if(batch_mode){
|
if(batch_mode){
|
||||||
/* Update password for individual user */
|
/* Update password for individual user */
|
||||||
rc = update_pwuser(fptr, ftmp, username, password_cmd);
|
rc = update_pwuser(fptr, ftmp, username, password_cmd, iterations);
|
||||||
}else{
|
}else{
|
||||||
rc = get_password(password, MAX_BUFFER_LEN);
|
rc = get_password(password, MAX_BUFFER_LEN);
|
||||||
if(rc){
|
if(rc){
|
||||||
@ -674,7 +692,7 @@ int main(int argc, char *argv[])
|
|||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
/* Update password for individual user */
|
/* Update password for individual user */
|
||||||
rc = update_pwuser(fptr, ftmp, username, password);
|
rc = update_pwuser(fptr, ftmp, username, password, iterations);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(rc){
|
if(rc){
|
||||||
|
@ -454,6 +454,7 @@ struct mosquitto__unpwd{
|
|||||||
unsigned char *salt;
|
unsigned char *salt;
|
||||||
unsigned int password_len;
|
unsigned int password_len;
|
||||||
unsigned int salt_len;
|
unsigned int salt_len;
|
||||||
|
int iterations;
|
||||||
#endif
|
#endif
|
||||||
enum mosquitto_pwhash_type hashtype;
|
enum mosquitto_pwhash_type hashtype;
|
||||||
};
|
};
|
||||||
|
@ -133,22 +133,29 @@ int base64__decode(char *in, unsigned char **decoded, unsigned int *decoded_len)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
int pw__hash(const char *password, struct mosquitto_pw *pw, bool new_salt)
|
int pw__hash(const char *password, struct mosquitto_pw *pw, bool new_password, int new_iterations)
|
||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
unsigned int hash_len;
|
unsigned int hash_len;
|
||||||
const EVP_MD *digest;
|
const EVP_MD *digest;
|
||||||
|
int iterations;
|
||||||
#if OPENSSL_VERSION_NUMBER < 0x10100000L
|
#if OPENSSL_VERSION_NUMBER < 0x10100000L
|
||||||
EVP_MD_CTX context;
|
EVP_MD_CTX context;
|
||||||
#else
|
#else
|
||||||
EVP_MD_CTX *context;
|
EVP_MD_CTX *context;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if(new_salt){
|
if(new_password){
|
||||||
rc = RAND_bytes(pw->salt, sizeof(pw->salt));
|
rc = RAND_bytes(pw->salt, sizeof(pw->salt));
|
||||||
if(!rc){
|
if(!rc){
|
||||||
return MOSQ_ERR_UNKNOWN;
|
return MOSQ_ERR_UNKNOWN;
|
||||||
}
|
}
|
||||||
|
iterations = new_iterations;
|
||||||
|
}else{
|
||||||
|
iterations = pw->iterations;
|
||||||
|
}
|
||||||
|
if(iterations < 1){
|
||||||
|
return MOSQ_ERR_INVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
digest = EVP_get_digestbyname("sha512");
|
digest = EVP_get_digestbyname("sha512");
|
||||||
@ -173,9 +180,10 @@ int pw__hash(const char *password, struct mosquitto_pw *pw, bool new_salt)
|
|||||||
EVP_MD_CTX_free(context);
|
EVP_MD_CTX_free(context);
|
||||||
#endif
|
#endif
|
||||||
}else{
|
}else{
|
||||||
|
pw->iterations = iterations;
|
||||||
hash_len = sizeof(pw->password_hash);
|
hash_len = sizeof(pw->password_hash);
|
||||||
PKCS5_PBKDF2_HMAC(password, (int)strlen(password),
|
PKCS5_PBKDF2_HMAC(password, (int)strlen(password),
|
||||||
pw->salt, sizeof(pw->salt), 20000,
|
pw->salt, sizeof(pw->salt), iterations,
|
||||||
digest, (int)hash_len, pw->password_hash);
|
digest, (int)hash_len, pw->password_hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,14 +24,16 @@ enum mosquitto_pwhash_type{
|
|||||||
};
|
};
|
||||||
|
|
||||||
#define SALT_LEN 12
|
#define SALT_LEN 12
|
||||||
|
#define PW_DEFAULT_ITERATIONS 101
|
||||||
|
|
||||||
struct mosquitto_pw{
|
struct mosquitto_pw{
|
||||||
unsigned char password_hash[64]; /* For SHA512 */
|
unsigned char password_hash[64]; /* For SHA512 */
|
||||||
unsigned char salt[SALT_LEN];
|
unsigned char salt[SALT_LEN];
|
||||||
|
int iterations;
|
||||||
enum mosquitto_pwhash_type hashtype;
|
enum mosquitto_pwhash_type hashtype;
|
||||||
};
|
};
|
||||||
|
|
||||||
int pw__hash(const char *password, struct mosquitto_pw *pw, bool new_salt);
|
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__memcmp_const(const void *ptr1, const void *b, size_t len);
|
||||||
int base64_encode(unsigned char *in, unsigned int in_len, char **encoded);
|
int base64_encode(unsigned char *in, unsigned int in_len, char **encoded);
|
||||||
int base64__decode(char *in, unsigned char **decoded, unsigned int *decoded_len);
|
int base64__decode(char *in, unsigned char **decoded, unsigned int *decoded_len);
|
||||||
|
@ -33,7 +33,7 @@ static int acl__cleanup(struct mosquitto_db *db, bool reload);
|
|||||||
static int unpwd__cleanup(struct mosquitto__unpwd **unpwd, bool reload);
|
static int unpwd__cleanup(struct mosquitto__unpwd **unpwd, bool reload);
|
||||||
static int psk__file_parse(struct mosquitto_db *db, struct mosquitto__unpwd **psk_id, const char *psk_file);
|
static int psk__file_parse(struct mosquitto_db *db, struct mosquitto__unpwd **psk_id, const char *psk_file);
|
||||||
#ifdef WITH_TLS
|
#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);
|
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
|
#endif
|
||||||
static int mosquitto_unpwd_check_default(int event, void *event_data, void *userdata);
|
static int mosquitto_unpwd_check_default(int event, void *event_data, void *userdata);
|
||||||
static int mosquitto_acl_check_default(int event, void *event_data, void *userdata);
|
static int mosquitto_acl_check_default(int event, void *event_data, void *userdata);
|
||||||
@ -841,9 +841,19 @@ static int unpwd__decode_passwords(struct mosquitto__unpwd **unpwd)
|
|||||||
|
|
||||||
HASH_ITER(hh, *unpwd, u, tmp){
|
HASH_ITER(hh, *unpwd, u, tmp){
|
||||||
/* Need to decode password into hashed data + salt. */
|
/* Need to decode password into hashed data + salt. */
|
||||||
if(u->password){
|
if(u->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, "$");
|
token = strtok(u->password, "$");
|
||||||
if(token){
|
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")){
|
if(!strcmp(token, "6")){
|
||||||
hashtype = pw_sha512;
|
hashtype = pw_sha512;
|
||||||
}else if(!strcmp(token, "7")){
|
}else if(!strcmp(token, "7")){
|
||||||
@ -853,8 +863,28 @@ static int unpwd__decode_passwords(struct mosquitto__unpwd **unpwd)
|
|||||||
unpwd__free_item(unpwd, u);
|
unpwd__free_item(unpwd, u);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(hashtype == pw_sha512_pbkdf2){
|
||||||
token = strtok(NULL, "$");
|
token = strtok(NULL, "$");
|
||||||
if(token){
|
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);
|
rc = base64__decode(token, &salt, &salt_len);
|
||||||
if(rc == MOSQ_ERR_SUCCESS && salt_len == 12){
|
if(rc == MOSQ_ERR_SUCCESS && salt_len == 12){
|
||||||
u->salt = salt;
|
u->salt = salt;
|
||||||
@ -879,18 +909,6 @@ static int unpwd__decode_passwords(struct mosquitto__unpwd **unpwd)
|
|||||||
log__printf(NULL, MOSQ_LOG_ERR, "Error: Unable to decode password salt for user %s, removing entry.", u->username);
|
log__printf(NULL, MOSQ_LOG_ERR, "Error: Unable to decode password salt for user %s, removing entry.", u->username);
|
||||||
unpwd__free_item(unpwd, u);
|
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: Invalid password hash for user %s, removing entry.", u->username);
|
|
||||||
unpwd__free_item(unpwd, u);
|
|
||||||
}
|
|
||||||
}else{
|
|
||||||
log__printf(NULL, MOSQ_LOG_ERR, "Error: Missing password hash for user %s, removing entry.", u->username);
|
|
||||||
unpwd__free_item(unpwd, u);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return MOSQ_ERR_SUCCESS;
|
return MOSQ_ERR_SUCCESS;
|
||||||
@ -992,7 +1010,7 @@ static int mosquitto_unpwd_check_default(int event, void *event_data, void *user
|
|||||||
if(u->password){
|
if(u->password){
|
||||||
if(ed->client->password){
|
if(ed->client->password){
|
||||||
#ifdef WITH_TLS
|
#ifdef WITH_TLS
|
||||||
rc = pw__digest(ed->client->password, u->salt, u->salt_len, hash, &hash_len, u->hashtype);
|
rc = pw__digest(ed->client->password, u->salt, u->salt_len, hash, &hash_len, u->hashtype, u->iterations);
|
||||||
if(rc == MOSQ_ERR_SUCCESS){
|
if(rc == MOSQ_ERR_SUCCESS){
|
||||||
if(hash_len == u->password_len && !mosquitto__memcmp_const(u->password, hash, hash_len)){
|
if(hash_len == u->password_len && !mosquitto__memcmp_const(u->password, hash, hash_len)){
|
||||||
return MOSQ_ERR_SUCCESS;
|
return MOSQ_ERR_SUCCESS;
|
||||||
@ -1293,7 +1311,7 @@ int mosquitto_psk_key_get_default(struct mosquitto_db *db, struct mosquitto *con
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef WITH_TLS
|
#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 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;
|
const EVP_MD *digest;
|
||||||
#if OPENSSL_VERSION_NUMBER < 0x10100000L
|
#if OPENSSL_VERSION_NUMBER < 0x10100000L
|
||||||
@ -1329,7 +1347,7 @@ int pw__digest(const char *password, const unsigned char *salt, unsigned int sal
|
|||||||
}else{
|
}else{
|
||||||
*hash_len = EVP_MAX_MD_SIZE;
|
*hash_len = EVP_MAX_MD_SIZE;
|
||||||
PKCS5_PBKDF2_HMAC(password, (int)strlen(password),
|
PKCS5_PBKDF2_HMAC(password, (int)strlen(password),
|
||||||
salt, (int)salt_len, 20000,
|
salt, (int)salt_len, iterations,
|
||||||
digest, (int)(*hash_len), hash);
|
digest, (int)(*hash_len), hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user