From 2675fac7fe07783d3266b59f6e1c0e0a337a84db Mon Sep 17 00:00:00 2001 From: Tom Smeding Date: Sat, 20 Jun 2020 21:14:26 +0200 Subject: server: User password hashing (libsodium Argon2id(3,64MB)) --- command.c | 4 +-- db.c | 84 +++++++++++++++++++++++++++++++++++++++++++++++++++++--------- db.h | 2 +- schema.sql | 2 +- 4 files changed, 75 insertions(+), 17 deletions(-) diff --git a/command.c b/command.c index 04e9fe0..51acfb6 100644 --- a/command.c +++ b/command.c @@ -50,9 +50,7 @@ static struct cmd_retval cmd_login(struct conn_data *data,const char *tag,const data->userid=-1; } } else { - char *pass=db_get_pass(userid); - bool success=strcmp(args[1],pass)==0; - free(pass); + bool success = db_check_pass(userid, args[1]); if(data->userid!=-1){ userdata_unregister(data->userid,data->fd); broadcast_online_change(data->userid); diff --git a/db.c b/db.c index 4572ea2..413953b 100644 --- a/db.c +++ b/db.c @@ -3,6 +3,7 @@ #include #include #include +#include #include "db.h" #include "schema.sql.h" @@ -10,6 +11,8 @@ #define SQLITE(func,...) do{if(sqlite3_##func(__VA_ARGS__)!=SQLITE_OK){die_sqlite("sqlite3_" #func);}}while(0) #define DATABASE_VERSION 1 +#define PASSHASH_OPSLIMIT 3 +#define PASSHASH_MEMLIMIT crypto_pwhash_MEMLIMIT_INTERACTIVE sqlite3 *database=NULL; @@ -19,6 +22,24 @@ static void die_sqlite(const char *func){ die("%s: %s",func,sqlite3_errmsg(database)); } +struct hashed_password { + char str[crypto_pwhash_STRBYTES + 1]; + int length; // -1 if hashing failed +}; + +static struct hashed_password hash_password(const char *pass) { + struct hashed_password result; + int ret = crypto_pwhash_str( + result.str, pass, strlen(pass), + PASSHASH_OPSLIMIT, PASSHASH_MEMLIMIT); + if (ret != 0) { + result.length = -1; + } else { + result.length = crypto_pwhash_STRBYTES; + } + return result; +} + void db_init(void){ SQLITE(config,SQLITE_CONFIG_SERIALIZED); @@ -210,10 +231,13 @@ struct db_room_list db_list_rooms(i64 userid){ i64 db_create_user(const char *name,const char *pass){ + struct hashed_password passhash = hash_password(pass); + if (passhash.length == -1) return -1; + sqlite3_stmt *stmt; - SQLITE(prepare_v2,database,"insert into Users (name, pass) values (?, ?)",-1,&stmt,NULL); + SQLITE(prepare_v2,database,"insert into Users (name, passhash) values (?, ?)",-1,&stmt,NULL); SQLITE(bind_text,stmt,1,name,-1,SQLITE_STATIC); - SQLITE(bind_text,stmt,2,pass,-1,SQLITE_STATIC); + SQLITE(bind_text,stmt,2,passhash.str,passhash.length,SQLITE_STATIC); bool success=sqlite3_step(stmt)==SQLITE_DONE; SQLITE(finalize,stmt); if(success){ @@ -223,6 +247,29 @@ i64 db_create_user(const char *name,const char *pass){ } } +bool db_set_username(i64 userid, const char *name) { + sqlite3_stmt *stmt; + SQLITE(prepare_v2, database, "update USERS set name = ? where id = ?", -1, &stmt, NULL); + SQLITE(bind_text, stmt, 1, name, -1, SQLITE_STATIC); + SQLITE(bind_int64, stmt, 2, userid); + bool success = sqlite3_step(stmt) == SQLITE_DONE; + SQLITE(finalize, stmt); + return success; +} + +bool db_set_pass(i64 userid, const char *pass) { + struct hashed_password passhash = hash_password(pass); + if (passhash.length == -1) return false; + + sqlite3_stmt *stmt; + SQLITE(prepare_v2, database, "update USERS set passhash = ? where id = ?", -1, &stmt, NULL); + SQLITE(bind_text, stmt, 1, passhash.str, passhash.length, SQLITE_STATIC); + SQLITE(bind_int64, stmt, 2, userid); + bool success = sqlite3_step(stmt) == SQLITE_DONE; + SQLITE(finalize, stmt); + return success; +} + char* db_get_username(i64 userid){ sqlite3_stmt *stmt; SQLITE(prepare_v2,database,"select name from Users where id = ?",-1,&stmt,NULL); @@ -237,18 +284,31 @@ char* db_get_username(i64 userid){ return name; } -char* db_get_pass(i64 userid){ +bool db_check_pass(i64 userid, const char *pass) { sqlite3_stmt *stmt; - SQLITE(prepare_v2,database,"select pass from Users where id = ?",-1,&stmt,NULL); - SQLITE(bind_int64,stmt,1,userid); - const unsigned char *pass_sq=NULL; - if(sqlite3_step(stmt)==SQLITE_ROW){ - pass_sq=sqlite3_column_text(stmt,0); + SQLITE(prepare_v2, database, "select passhash from Users where id = ?", -1, &stmt, NULL); + SQLITE(bind_int64, stmt, 1, userid); + + const unsigned char *passhash_sq = NULL; + if (sqlite3_step(stmt) == SQLITE_ROW) { + passhash_sq = sqlite3_column_text(stmt, 0); } - char *pass=NULL; - if(pass_sq)pass=strdup((const char*)pass_sq); - SQLITE(finalize,stmt); - return pass; + + int ret = crypto_pwhash_str_verify((const char*)passhash_sq, pass, strlen(pass)); + bool success = ret == 0; + bool rehash = success && + crypto_pwhash_str_needs_rehash( + (const char*)passhash_sq, PASSHASH_OPSLIMIT, PASSHASH_MEMLIMIT); + + SQLITE(finalize, stmt); + + if (rehash) { + fprintf(stderr, "Rehashing password for userid=%" PRIi64 "\n", userid); + // Ignore whether this succeeds; if it doesn't, we continue using the old hash + db_set_pass(userid, pass); + } + + return success; } i64 db_find_user(const char *name){ diff --git a/db.h b/db.h index a56bc70..c27b243 100644 --- a/db.h +++ b/db.h @@ -52,7 +52,7 @@ i64 db_create_user(const char *name,const char *pass); bool db_set_username(i64 userid,const char *name); bool db_set_pass(i64 userid,const char *pass); char* db_get_username(i64 userid); -char* db_get_pass(i64 userid); +bool db_check_pass(i64 userid,const char *pass); bool db_delete_user(i64 userid); i64 db_find_user(const char *name); // -1 if not found struct db_strings_list db_user_tokens(i64 userid); diff --git a/schema.sql b/schema.sql index 3efe0f1..06baafd 100644 --- a/schema.sql +++ b/schema.sql @@ -21,7 +21,7 @@ create table Members ( create table Users ( id integer primary key not null, name text not null, - pass text not null + passhash text not null ); create unique index users_name_index on Users(name); -- cgit v1.2.3-70-g09d2