Browse Source

ckdb/sql - revise password code, use a salt, db update to 0.9.2

master
kanoi 10 years ago
parent
commit
85b2dfae3e
  1. 2
      sql/ckdb.sql
  2. 28
      sql/v0.9.1-v0.9.2.sql
  3. 179
      src/ckdb.c

2
sql/ckdb.sql

@ -15,6 +15,7 @@ SET search_path = public, pg_catalog;
CREATE TABLE users ( CREATE TABLE users (
userid bigint NOT NULL, userid bigint NOT NULL,
username character varying(256) NOT NULL, username character varying(256) NOT NULL,
status character varying(256) DEFAULT ''::character varying NOT NULL;
emailaddress character varying(256) NOT NULL, emailaddress character varying(256) NOT NULL,
joineddate timestamp with time zone NOT NULL, joineddate timestamp with time zone NOT NULL,
passwordhash character varying(256) NOT NULL, passwordhash character varying(256) NOT NULL,
@ -33,6 +34,7 @@ CREATE UNIQUE INDEX usersusername ON users USING btree (username, expirydate);
CREATE TABLE useratts ( CREATE TABLE useratts (
userid bigint NOT NULL, userid bigint NOT NULL,
attname character varying(64) NOT NULL, attname character varying(64) NOT NULL,
status character varying(256) DEFAULT ''::character varying NOT NULL;
attstr character varying(256) DEFAULT ''::character varying NOT NULL, attstr character varying(256) DEFAULT ''::character varying NOT NULL,
attstr2 character varying(256) DEFAULT ''::character varying NOT NULL, attstr2 character varying(256) DEFAULT ''::character varying NOT NULL,
attnum bigint DEFAULT 0 NOT NULL, attnum bigint DEFAULT 0 NOT NULL,

28
sql/v0.9.1-v0.9.2.sql

@ -0,0 +1,28 @@
SET SESSION AUTHORIZATION 'postgres';
BEGIN transaction;
DO $$
DECLARE ver TEXT;
BEGIN
UPDATE version set version='0.9.2' where vlock=1 and version='0.9.1';
IF found THEN
RETURN;
END IF;
SELECT version into ver from version
WHERE vlock=1;
RAISE EXCEPTION 'Wrong DB version - expect "0.9.1" - found "%"', ver;
END $$;
ALTER TABLE ONLY usersatts
ADD COLUMN status character varying(256) DEFAULT ''::character varying NOT NULL;
ALTER TABLE ONLY users
ADD COLUMN status character varying(256) DEFAULT ''::character varying NOT NULL;
END transaction;

179
src/ckdb.c

@ -25,7 +25,9 @@
#include <string.h> #include <string.h>
#include <ctype.h> #include <ctype.h>
#include <unistd.h> #include <unistd.h>
#include <stdint.h>
#include <regex.h> #include <regex.h>
#include <sha2.h>
#ifdef HAVE_LIBPQ_FE_H #ifdef HAVE_LIBPQ_FE_H
#include <libpq-fe.h> #include <libpq-fe.h>
#elif defined (HAVE_POSTGRESQL_LIBPQ_FE_H) #elif defined (HAVE_POSTGRESQL_LIBPQ_FE_H)
@ -46,8 +48,8 @@
*/ */
#define DB_VLOCK "1" #define DB_VLOCK "1"
#define DB_VERSION "0.9.1" #define DB_VERSION "0.9.2"
#define CKDB_VERSION DB_VERSION"-0.280" #define CKDB_VERSION DB_VERSION"-0.300"
#define WHERE_FFL " - from %s %s() line %d" #define WHERE_FFL " - from %s %s() line %d"
#define WHERE_FFL_HERE __FILE__, __func__, __LINE__ #define WHERE_FFL_HERE __FILE__, __func__, __LINE__
@ -950,6 +952,8 @@ static tv_t missing_secuser_max = { 1408860000L, 0L };
typedef struct users { typedef struct users {
int64_t userid; int64_t userid;
char username[TXT_BIG+1]; char username[TXT_BIG+1];
// Anything in 'status' disables the account
char status[TXT_BIG+1];
char emailaddress[TXT_BIG+1]; char emailaddress[TXT_BIG+1];
tv_t joineddate; tv_t joineddate;
char passwordhash[TXT_BIG+1]; char passwordhash[TXT_BIG+1];
@ -964,6 +968,12 @@ typedef struct users {
#define DATA_USERS(_var, _item) DATA_GENERIC(_var, _item, users, true) #define DATA_USERS(_var, _item) DATA_GENERIC(_var, _item, users, true)
#define DATA_USERS_NULL(_var, _item) DATA_GENERIC(_var, _item, users, false) #define DATA_USERS_NULL(_var, _item) DATA_GENERIC(_var, _item, users, false)
#define SHA256SIZHEX 64
#define SHA256SIZBIN 32
#define SALTSIZHEX 32
#define SALTSIZBIN 16
static K_TREE *users_root; static K_TREE *users_root;
static K_TREE *userid_root; static K_TREE *userid_root;
static K_LIST *users_free; static K_LIST *users_free;
@ -973,6 +983,8 @@ static K_STORE *users_store;
// USERATTS // USERATTS
typedef struct useratts { typedef struct useratts {
int64_t userid; int64_t userid;
char attname[TXT_BIG+1];
char status[TXT_BIG+1];
char attstr[TXT_BIG+1]; char attstr[TXT_BIG+1];
char attstr2[TXT_BIG+1]; char attstr2[TXT_BIG+1];
int64_t attnum; int64_t attnum;
@ -2652,6 +2664,97 @@ static K_ITEM *find_userid(int64_t userid)
return find_in_ktree(userid_root, &look, cmp_userid, ctx); return find_in_ktree(userid_root, &look, cmp_userid, ctx);
} }
static void make_salt(USERS *users)
{
long int r1, r2, r3, r4;
r1 = random();
r2 = random();
r3 = random();
r4 = random();
__bin2hex(users->salt, (void *)(&r1), 4);
__bin2hex(users->salt+8, (void *)(&r2), 4);
__bin2hex(users->salt+16, (void *)(&r3), 4);
__bin2hex(users->salt+24, (void *)(&r4), 4);
}
static void password_hash(char *username, char *passwordhash, char *salt, char *result, size_t siz)
{
char tohash[TXT_BIG+1];
char buf[TXT_BIG+1];
size_t len, tot;
char why[1024];
if (siz < SHA256SIZHEX+1) {
snprintf(why, sizeof(why),
"target result too small (%d/%d)",
(int)siz, SHA256SIZHEX+1);
goto hashfail;
}
if (sizeof(buf) < SHA256SIZBIN) {
snprintf(why, sizeof(why),
"temporary target buf too small (%d/%d)",
(int)sizeof(buf), SHA256SIZBIN);
goto hashfail;
}
tot = len = strlen(passwordhash) / 2;
if (len != SHA256SIZBIN) {
snprintf(why, sizeof(why),
"passwordhash wrong size (%d/%d)",
(int)len, SHA256SIZBIN);
goto hashfail;
}
if (len > sizeof(tohash)) {
snprintf(why, sizeof(why),
"temporary tohash too small (%d/%d)",
(int)sizeof(tohash), (int)len);
goto hashfail;
}
hex2bin(tohash, passwordhash, len);
len = strlen(salt) / 2;
if (len != SALTSIZBIN) {
snprintf(why, sizeof(why),
"salt wrong size (%d/%d)",
(int)len, SALTSIZBIN);
goto hashfail;
}
if ((tot + len) > sizeof(tohash)) {
snprintf(why, sizeof(why),
"passwordhash+salt too big (%d/%d)",
(int)(tot + len), (int)sizeof(tohash));
goto hashfail;
}
hex2bin(tohash+tot, salt, len);
tot += len;
sha256((const unsigned char *)tohash, (unsigned int)tot, (unsigned char *)buf);
__bin2hex(result, (void *)buf, SHA256SIZBIN);
return;
hashfail:
LOGERR("%s() Failed to hash '%s' password: %s",
__func__, username, why);
result[0] = '\0';
}
static bool check_hash(USERS *users, char *passwordhash)
{
char hex[SHA256SIZHEX+1];
if (*(users->salt)) {
password_hash(users->username, passwordhash, users->salt, hex, sizeof(hex));
return (strcasecmp(hex, users->passwordhash) == 0);
} else
return (strcasecmp(passwordhash, users->passwordhash) == 0);
}
static bool users_pass_email(PGconn *conn, K_ITEM *u_item, char *oldhash, static bool users_pass_email(PGconn *conn, K_ITEM *u_item, char *oldhash,
char *newhash, char *email, char *by, char *code, char *newhash, char *email, char *by, char *code,
char *inet, tv_t *cd, K_TREE *trf_root) char *inet, tv_t *cd, K_TREE *trf_root)
@ -2665,7 +2768,7 @@ static bool users_pass_email(PGconn *conn, K_ITEM *u_item, char *oldhash,
USERS *row, *users; USERS *row, *users;
char *upd, *ins; char *upd, *ins;
bool ok = false; bool ok = false;
char *params[4 + HISTORYDATECOUNT]; char *params[5 + HISTORYDATECOUNT];
bool hash; bool hash;
int par; int par;
@ -2677,7 +2780,8 @@ static bool users_pass_email(PGconn *conn, K_ITEM *u_item, char *oldhash,
hash = false; hash = false;
DATA_USERS(users, u_item); DATA_USERS(users, u_item);
if (hash && strcasecmp(oldhash, users->passwordhash)) // i.e. if oldhash == EMPTY, just overwrite with new
if (hash && oldhash != EMPTY && !check_hash(users, oldhash))
return false; return false;
K_WLOCK(users_free); K_WLOCK(users_free);
@ -2687,10 +2791,14 @@ static bool users_pass_email(PGconn *conn, K_ITEM *u_item, char *oldhash,
DATA_USERS(row, item); DATA_USERS(row, item);
memcpy(row, users, sizeof(*row)); memcpy(row, users, sizeof(*row));
// Update one, leave the other // Update one, leave the other
if (hash) if (hash) {
STRNCPY(row->passwordhash, newhash); // New salt each password change
else make_salt(row);
password_hash(row->username, newhash, row->salt,
row->passwordhash, sizeof(row->passwordhash));
} else
STRNCPY(row->emailaddress, email); STRNCPY(row->emailaddress, email);
HISTORYDATEINIT(row, cd, by, code, inet); HISTORYDATEINIT(row, cd, by, code, inet);
HISTORYDATETRANSFER(trf_root, row); HISTORYDATETRANSFER(trf_root, row);
@ -2731,15 +2839,18 @@ static bool users_pass_email(PGconn *conn, K_ITEM *u_item, char *oldhash,
// Copy them both in - one will be new and one will be old // Copy them both in - one will be new and one will be old
params[par++] = str_to_buf(row->emailaddress, NULL, 0); params[par++] = str_to_buf(row->emailaddress, NULL, 0);
params[par++] = str_to_buf(row->passwordhash, NULL, 0); params[par++] = str_to_buf(row->passwordhash, NULL, 0);
// New salt for each password change (or recopy old)
params[par++] = str_to_buf(row->salt, NULL, 0);
HISTORYDATEPARAMS(params, par, row); HISTORYDATEPARAMS(params, par, row);
PARCHKVAL(par, 4 + HISTORYDATECOUNT, params); // 9 as per ins PARCHKVAL(par, 5 + HISTORYDATECOUNT, params); // 10 as per ins
ins = "insert into users " ins = "insert into users "
"(userid,username,emailaddress,joineddate,passwordhash," "(userid,username,status,emailaddress,joineddate,"
"secondaryuserid,salt" "passwordhash,secondaryuserid,salt"
HISTORYDATECONTROL ") select " HISTORYDATECONTROL ") select "
"userid,username,$3,joineddate,$4,secondaryuserid,salt," "userid,username,status,$3,joineddate,"
"$5,$6,$7,$8,$9 from users where " "$4,secondaryuserid,$5,"
"$6,$7,$8,$9,$10 from users where "
"userid=$1 and expirydate=$2"; "userid=$1 and expirydate=$2";
res = PQexecParams(conn, ins, par, NULL, (const char **)params, NULL, NULL, 0, CKPQ_WRITE); res = PQexecParams(conn, ins, par, NULL, (const char **)params, NULL, NULL, 0, CKPQ_WRITE);
@ -2794,7 +2905,7 @@ static K_ITEM *users_add(PGconn *conn, char *username, char *emailaddress,
uint64_t hash; uint64_t hash;
__maybe_unused uint64_t tmp; __maybe_unused uint64_t tmp;
bool ok = false; bool ok = false;
char *params[7 + HISTORYDATECOUNT]; char *params[8 + HISTORYDATECOUNT];
int par; int par;
LOGDEBUG("%s(): add", __func__); LOGDEBUG("%s(): add", __func__);
@ -2805,7 +2916,7 @@ static K_ITEM *users_add(PGconn *conn, char *username, char *emailaddress,
DATA_USERS(row, item); DATA_USERS(row, item);
row->userid = nextid(conn, "userid", (int64_t)(666 + (rand() % 334)), row->userid = nextid(conn, "userid", (int64_t)(666 + (random() % 334)),
cd, by, code, inet); cd, by, code, inet);
if (row->userid == 0) if (row->userid == 0)
goto unitem; goto unitem;
@ -2813,6 +2924,7 @@ static K_ITEM *users_add(PGconn *conn, char *username, char *emailaddress,
// TODO: pre-check the username exists? (to save finding out via a DB error) // TODO: pre-check the username exists? (to save finding out via a DB error)
STRNCPY(row->username, username); STRNCPY(row->username, username);
row->status[0] = '\0';
STRNCPY(row->emailaddress, emailaddress); STRNCPY(row->emailaddress, emailaddress);
STRNCPY(row->passwordhash, passwordhash); STRNCPY(row->passwordhash, passwordhash);
@ -2820,7 +2932,7 @@ static K_ITEM *users_add(PGconn *conn, char *username, char *emailaddress,
HASH_BER(tohash, strlen(tohash), 1, hash, tmp); HASH_BER(tohash, strlen(tohash), 1, hash, tmp);
__bin2hex(row->secondaryuserid, (void *)(&hash), sizeof(hash)); __bin2hex(row->secondaryuserid, (void *)(&hash), sizeof(hash));
row->salt[0] = '\0'; make_salt(row);
HISTORYDATEINIT(row, cd, by, code, inet); HISTORYDATEINIT(row, cd, by, code, inet);
HISTORYDATETRANSFER(trf_root, row); HISTORYDATETRANSFER(trf_root, row);
@ -2832,6 +2944,7 @@ static K_ITEM *users_add(PGconn *conn, char *username, char *emailaddress,
par = 0; par = 0;
params[par++] = bigint_to_buf(row->userid, NULL, 0); params[par++] = bigint_to_buf(row->userid, NULL, 0);
params[par++] = str_to_buf(row->username, NULL, 0); params[par++] = str_to_buf(row->username, NULL, 0);
params[par++] = str_to_buf(row->status, NULL, 0);
params[par++] = str_to_buf(row->emailaddress, NULL, 0); params[par++] = str_to_buf(row->emailaddress, NULL, 0);
params[par++] = tv_to_buf(&(row->joineddate), NULL, 0); params[par++] = tv_to_buf(&(row->joineddate), NULL, 0);
params[par++] = str_to_buf(row->passwordhash, NULL, 0); params[par++] = str_to_buf(row->passwordhash, NULL, 0);
@ -2841,9 +2954,9 @@ static K_ITEM *users_add(PGconn *conn, char *username, char *emailaddress,
PARCHK(par, params); PARCHK(par, params);
ins = "insert into users " ins = "insert into users "
"(userid,username,emailaddress,joineddate,passwordhash," "(userid,username,status,emailaddress,joineddate,passwordhash,"
"secondaryuserid,salt" "secondaryuserid,salt"
HISTORYDATECONTROL ") values (" PQPARAM12 ")"; HISTORYDATECONTROL ") values (" PQPARAM13 ")";
if (!conn) { if (!conn) {
conn = dbconnect(); conn = dbconnect();
@ -2890,14 +3003,14 @@ static bool users_fill(PGconn *conn)
USERS *row; USERS *row;
char *field; char *field;
char *sel; char *sel;
int fields = 7; int fields = 8;
bool ok; bool ok;
LOGDEBUG("%s(): select", __func__); LOGDEBUG("%s(): select", __func__);
sel = "select " sel = "select "
"userid,username,emailaddress,joineddate,passwordhash," "userid,username,status,emailaddress,joineddate,"
"secondaryuserid,salt" "passwordhash,secondaryuserid,salt"
HISTORYDATECONTROL HISTORYDATECONTROL
" from users"; " from users";
res = PQexec(conn, sel, CKPQ_READ); res = PQexec(conn, sel, CKPQ_READ);
@ -2939,6 +3052,11 @@ static bool users_fill(PGconn *conn)
break; break;
TXT_TO_STR("username", field, row->username); TXT_TO_STR("username", field, row->username);
PQ_GET_FLD(res, i, "status", field, ok);
if (!ok)
break;
TXT_TO_STR("status", field, row->status);
PQ_GET_FLD(res, i, "emailaddress", field, ok); PQ_GET_FLD(res, i, "emailaddress", field, ok);
if (!ok) if (!ok)
break; break;
@ -7930,6 +8048,7 @@ static char *cmd_newpass(__maybe_unused PGconn *conn, char *cmd, char *id,
char reply[1024] = ""; char reply[1024] = "";
size_t siz = sizeof(reply); size_t siz = sizeof(reply);
bool ok = false; bool ok = false;
char *oldhash;
LOGDEBUG("%s(): cmd '%s'", __func__, cmd); LOGDEBUG("%s(): cmd '%s'", __func__, cmd);
@ -7937,9 +8056,11 @@ static char *cmd_newpass(__maybe_unused PGconn *conn, char *cmd, char *id,
if (!i_username) if (!i_username)
return strdup(reply); return strdup(reply);
i_oldhash = require_name(trf_root, "oldhash", 64, (char *)hashpatt, reply, siz); i_oldhash = optional_name(trf_root, "oldhash", 64, (char *)hashpatt);
if (!i_oldhash) if (i_oldhash)
return strdup(reply); oldhash = transfer_data(i_oldhash);
else
oldhash = EMPTY;
i_newhash = require_name(trf_root, "newhash", 64, (char *)hashpatt, reply, siz); i_newhash = require_name(trf_root, "newhash", 64, (char *)hashpatt, reply, siz);
if (!i_newhash) if (!i_newhash)
@ -7951,7 +8072,7 @@ static char *cmd_newpass(__maybe_unused PGconn *conn, char *cmd, char *id,
if (u_item) { if (u_item) {
ok = users_pass_email(NULL, u_item, ok = users_pass_email(NULL, u_item,
transfer_data(i_oldhash), oldhash,
transfer_data(i_newhash), transfer_data(i_newhash),
NULL, NULL,
by, code, inet, now, trf_root); by, code, inet, now, trf_root);
@ -7994,10 +8115,7 @@ static char *cmd_chkpass(__maybe_unused PGconn *conn, char *cmd, char *id,
ok = false; ok = false;
else { else {
DATA_USERS(users, u_item); DATA_USERS(users, u_item);
if (strcasecmp(transfer_data(i_passwordhash), users->passwordhash) == 0) ok = check_hash(users, transfer_data(i_passwordhash));
ok = true;
else
ok = false;
} }
if (!ok) { if (!ok) {
@ -8065,8 +8183,7 @@ static char *cmd_userset(PGconn *conn, char *cmd, char *id,
APPEND_REALLOC(answer, off, len, tmp); APPEND_REALLOC(answer, off, len, tmp);
} }
} else { } else {
if (strcasecmp(transfer_data(i_passwordhash), if (!check_hash(users, transfer_data(i_passwordhash))) {
users->passwordhash)) {
reason = "Incorrect password"; reason = "Incorrect password";
goto struckout; goto struckout;
} }
@ -12472,7 +12589,7 @@ int main(int argc, char **argv)
ckp.logdir, ckp.name, dbcode); ckp.logdir, ckp.name, dbcode);
setnow(&now); setnow(&now);
srand((unsigned int)(now.tv_usec * 4096 + now.tv_sec % 4096)); srandom((unsigned int)(now.tv_usec * 4096 + now.tv_sec % 4096));
ckp.main.ckp = &ckp; ckp.main.ckp = &ckp;
ckp.main.processname = strdup("main"); ckp.main.processname = strdup("main");

Loading…
Cancel
Save