aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Smeding <tom.smeding@gmail.com>2020-06-20 21:14:26 +0200
committerTom Smeding <tom.smeding@gmail.com>2020-06-20 21:14:41 +0200
commit2675fac7fe07783d3266b59f6e1c0e0a337a84db (patch)
tree4e73285bc4972f0aeccb772ae512b2901f10bee1
parentf45959b53b8322a852554aa8dd7f8c9f4f99b5b4 (diff)
server: User password hashing (libsodium Argon2id(3,64MB))
-rw-r--r--command.c4
-rw-r--r--db.c84
-rw-r--r--db.h2
-rw-r--r--schema.sql2
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 <string.h>
#include <assert.h>
#include <sqlite3.h>
+#include <sodium.h>
#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);