mirror of
https://github.com/eclipse/mosquitto.git
synced 2025-05-09 01:01:11 +08:00
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 *username;
|
||||
const char *password;
|
||||
int iterations;
|
||||
bool found;
|
||||
};
|
||||
|
||||
@ -117,7 +118,7 @@ void print_usage(void)
|
||||
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;
|
||||
char *salt64 = NULL, *hash64 = NULL;
|
||||
@ -127,7 +128,7 @@ int output_new_password(FILE *fptr, const char *username, const char *password)
|
||||
|
||||
pw.hashtype = hashtype;
|
||||
|
||||
if(pw__hash(password, &pw, true)){
|
||||
if(pw__hash(password, &pw, true, iterations)){
|
||||
fprintf(stderr, "Error: Unable to hash password.\n");
|
||||
return 1;
|
||||
}
|
||||
@ -147,7 +148,11 @@ int output_new_password(FILE *fptr, const char *username, const char *password)
|
||||
return 1;
|
||||
}
|
||||
|
||||
fprintf(fptr, "%s:$%d$%s$%s\n", username, hashtype, salt64, hash64);
|
||||
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);
|
||||
}
|
||||
free(salt64);
|
||||
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)
|
||||
{
|
||||
return output_new_password(ftmp, username, password);
|
||||
return output_new_password(ftmp, username, password, helper->iterations);
|
||||
}
|
||||
|
||||
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{
|
||||
/* Write out a new line for our matching username */
|
||||
helper->found = true;
|
||||
rc = output_new_password(ftmp, username, helper->password);
|
||||
rc = output_new_password(ftmp, username, helper->password, helper->iterations);
|
||||
}
|
||||
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;
|
||||
int rc;
|
||||
@ -296,12 +301,13 @@ int update_pwuser(FILE *fptr, FILE *ftmp, const char *username, const char *pass
|
||||
memset(&helper, 0, sizeof(helper));
|
||||
helper.username = username;
|
||||
helper.password = password;
|
||||
helper.iterations = iterations;
|
||||
rc = pwfile_iterate(fptr, ftmp, update_pwuser_cb, &helper);
|
||||
|
||||
if(helper.found){
|
||||
return rc;
|
||||
}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;
|
||||
char *backup_file;
|
||||
int idx;
|
||||
int iterations = PW_DEFAULT_ITERATIONS;
|
||||
|
||||
signal(SIGINT, handle_sigint);
|
||||
signal(SIGTERM, handle_sigint);
|
||||
@ -505,6 +512,17 @@ int main(int argc, char *argv[])
|
||||
create_new = true;
|
||||
}else if(!strcmp(argv[idx], "-D")){
|
||||
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")){
|
||||
do_update_file = true;
|
||||
}else{
|
||||
@ -622,7 +640,7 @@ int main(int argc, char *argv[])
|
||||
return 1;
|
||||
}
|
||||
free(password_file);
|
||||
rc = output_new_password(fptr, username, password_cmd);
|
||||
rc = output_new_password(fptr, username, password_cmd, iterations);
|
||||
fclose(fptr);
|
||||
return rc;
|
||||
}else{
|
||||
@ -663,7 +681,7 @@ int main(int argc, char *argv[])
|
||||
}else{
|
||||
if(batch_mode){
|
||||
/* Update password for individual user */
|
||||
rc = update_pwuser(fptr, ftmp, username, password_cmd);
|
||||
rc = update_pwuser(fptr, ftmp, username, password_cmd, iterations);
|
||||
}else{
|
||||
rc = get_password(password, MAX_BUFFER_LEN);
|
||||
if(rc){
|
||||
@ -674,7 +692,7 @@ int main(int argc, char *argv[])
|
||||
return rc;
|
||||
}
|
||||
/* Update password for individual user */
|
||||
rc = update_pwuser(fptr, ftmp, username, password);
|
||||
rc = update_pwuser(fptr, ftmp, username, password, iterations);
|
||||
}
|
||||
}
|
||||
if(rc){
|
||||
|
@ -454,6 +454,7 @@ struct mosquitto__unpwd{
|
||||
unsigned char *salt;
|
||||
unsigned int password_len;
|
||||
unsigned int salt_len;
|
||||
int iterations;
|
||||
#endif
|
||||
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;
|
||||
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_salt){
|
||||
if(new_password){
|
||||
rc = RAND_bytes(pw->salt, sizeof(pw->salt));
|
||||
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");
|
||||
@ -173,9 +180,10 @@ int pw__hash(const char *password, struct mosquitto_pw *pw, bool new_salt)
|
||||
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, sizeof(pw->salt), 20000,
|
||||
pw->salt, sizeof(pw->salt), iterations,
|
||||
digest, (int)hash_len, pw->password_hash);
|
||||
}
|
||||
|
||||
|
@ -24,14 +24,16 @@ enum mosquitto_pwhash_type{
|
||||
};
|
||||
|
||||
#define SALT_LEN 12
|
||||
#define PW_DEFAULT_ITERATIONS 101
|
||||
|
||||
struct mosquitto_pw{
|
||||
unsigned char password_hash[64]; /* For SHA512 */
|
||||
unsigned char salt[SALT_LEN];
|
||||
int iterations;
|
||||
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 base64_encode(unsigned char *in, unsigned int in_len, char **encoded);
|
||||
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 psk__file_parse(struct mosquitto_db *db, 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);
|
||||
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_unpwd_check_default(int event, void *event_data, void *userdata);
|
||||
static int mosquitto_acl_check_default(int event, void *event_data, void *userdata);
|
||||
@ -841,46 +841,64 @@ static int unpwd__decode_passwords(struct mosquitto__unpwd **unpwd)
|
||||
|
||||
HASH_ITER(hh, *unpwd, u, tmp){
|
||||
/* Need to decode password into hashed data + salt. */
|
||||
if(u->password){
|
||||
token = strtok(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, "$");
|
||||
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){
|
||||
u->salt = salt;
|
||||
u->salt_len = salt_len;
|
||||
token = strtok(NULL, "$");
|
||||
if(token){
|
||||
if(!strcmp(token, "6")){
|
||||
hashtype = pw_sha512;
|
||||
}else if(!strcmp(token, "7")){
|
||||
hashtype = pw_sha512_pbkdf2;
|
||||
rc = base64__decode(token, &password, &password_len);
|
||||
if(rc == MOSQ_ERR_SUCCESS && password_len == 64){
|
||||
mosquitto__free(u->password);
|
||||
u->password = (char *)password;
|
||||
u->password_len = password_len;
|
||||
u->hashtype = hashtype;
|
||||
}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;
|
||||
}
|
||||
token = strtok(NULL, "$");
|
||||
if(token){
|
||||
rc = base64__decode(token, &salt, &salt_len);
|
||||
if(rc == MOSQ_ERR_SUCCESS && salt_len == 12){
|
||||
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 == 64){
|
||||
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);
|
||||
unpwd__free_item(unpwd, u);
|
||||
}
|
||||
}else{
|
||||
log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid password hash for user %s, removing entry.", u->username);
|
||||
log__printf(NULL, MOSQ_LOG_ERR, "Error: Unable to decode password for user %s, removing entry.", u->username);
|
||||
unpwd__free_item(unpwd, u);
|
||||
}
|
||||
}else{
|
||||
@ -888,7 +906,7 @@ static int unpwd__decode_passwords(struct mosquitto__unpwd **unpwd)
|
||||
unpwd__free_item(unpwd, u);
|
||||
}
|
||||
}else{
|
||||
log__printf(NULL, MOSQ_LOG_ERR, "Error: Missing password hash 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);
|
||||
}
|
||||
}
|
||||
@ -992,7 +1010,7 @@ static int mosquitto_unpwd_check_default(int event, void *event_data, void *user
|
||||
if(u->password){
|
||||
if(ed->client->password){
|
||||
#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(hash_len == u->password_len && !mosquitto__memcmp_const(u->password, hash, hash_len)){
|
||||
return MOSQ_ERR_SUCCESS;
|
||||
@ -1293,7 +1311,7 @@ int mosquitto_psk_key_get_default(struct mosquitto_db *db, struct mosquitto *con
|
||||
}
|
||||
|
||||
#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;
|
||||
#if OPENSSL_VERSION_NUMBER < 0x10100000L
|
||||
@ -1329,7 +1347,7 @@ int pw__digest(const char *password, const unsigned char *salt, unsigned int sal
|
||||
}else{
|
||||
*hash_len = EVP_MAX_MD_SIZE;
|
||||
PKCS5_PBKDF2_HMAC(password, (int)strlen(password),
|
||||
salt, (int)salt_len, 20000,
|
||||
salt, (int)salt_len, iterations,
|
||||
digest, (int)(*hash_len), hash);
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user