# HG changeset patch # User John "Elwin" Edwards # Date 1382329153 25200 # Node ID f1676e81c80a772907afe2caa99c4a01589eafce # Parent c4304f08e35b1a9989d91e2d6fbe0734ffcb1fe7 sqlickrypt: add support for salted SHA-512 passwords, and fix NULL bug. Passwords will now be securely encrypted with random salt. Also avoid storing NULL in the database, because that makes dgamelaunch segfault. diff -r c4304f08e35b -r f1676e81c80a sqlickrypt.c --- a/sqlickrypt.c Sun Oct 20 19:26:39 2013 -0700 +++ b/sqlickrypt.c Sun Oct 20 21:19:13 2013 -0700 @@ -8,6 +8,7 @@ #define DATABASE "/dgldir/dgamelaunch.db" #define IBUFSIZE 200 +#define RANDOMSRC "/dev/urandom" /* General idea for return status: * 0: success @@ -42,6 +43,41 @@ return; } +char encode64(unsigned char c) { + if (c < 26) + return 'a' + c; + else if (c < 52) + return 'A' + c - 26; + else if (c < 62) + return '0' + c - 52; + else if (c == 62) + return '.'; + else + return '/'; +} + +/* Initializes a SHA-512 salt. salt must contain at least 20 bytes. */ +void setsalt(char *salt) { + unsigned char rnbytes[3], rnvals[4]; + FILE *urandom; + int loop; + salt[0] = salt[2] = salt[19] = '$'; /* Delimiters */ + salt[1] = '6'; /* SHA-512 */ + urandom = fopen(RANDOMSRC, "r"); + for (loop = 0; loop < 4; loop++) { + fread(rnbytes, 1, 3, urandom); + rnvals[0] = rnbytes[0] >> 2; + rnvals[1] = ((rnbytes[0] & 0x03) << 4) | ((rnbytes[1] & 0xf0) >> 4); + rnvals[2] = ((rnbytes[1] & 0x0f) << 2) | ((rnbytes[2] & 0xc0) >> 6); + rnvals[3] = rnbytes[2] & 0x3f; + salt[loop * 4 + 3] = encode64(rnvals[0]); + salt[loop * 4 + 4] = encode64(rnvals[1]); + salt[loop * 4 + 5] = encode64(rnvals[2]); + salt[loop * 4 + 6] = encode64(rnvals[3]); + } + fclose(urandom); +} + int check(char *uname, char *pw) { char *pwhash, *comphash; char *query = "SELECT password FROM dglusers WHERE username=?;"; @@ -76,10 +112,11 @@ int insertuser(char *uname, char *pw, char *email) { char *checkquery = "SELECT * FROM dglusers WHERE username = ?;"; - char *addquery = "INSERT INTO dglusers (username, password, email) VALUES (?, ?, ?);"; + char *addquery = "INSERT INTO dglusers (username, password, email, flags, env) VALUES (?, ?, ?, ?, ?);"; int status; sqlite3 *db; sqlite3_stmt *qstmt; + char salt[20]; opendb(&db, &qstmt, checkquery); /* Check for existing account in the same transaction with creating it. */ @@ -96,13 +133,16 @@ return 3; } /* The username doesn't exist yet, so create a new account. */ + setsalt(salt); sqlite3_finalize(qstmt); sqlite3_prepare_v2(db, addquery, -1, &qstmt, NULL); if (qstmt == NULL) cleanup(db, NULL, 3); sqlite3_bind_text(qstmt, 1, uname, -1, SQLITE_TRANSIENT); - sqlite3_bind_text(qstmt, 2, strdup(crypt(pw, pw)), -1, free); + sqlite3_bind_text(qstmt, 2, strdup(crypt(pw, salt)), -1, free); sqlite3_bind_text(qstmt, 3, email, -1, SQLITE_TRANSIENT); + sqlite3_bind_int(qstmt, 4, 0); + sqlite3_bind_text(qstmt, 5, "", -1, SQLITE_STATIC); status = sqlite3_step(qstmt); if (status != SQLITE_DONE) cleanup(db, qstmt, 3); @@ -160,10 +200,10 @@ sqlite3_stmt *qstmt; int status; char *hash; + char salt[20]; - /* This is not a smart use of crypt, but it needs to be compatible with - * dgamelaunch. */ - hash = crypt(newpw, newpw); + setsalt(salt); + hash = crypt(newpw, salt); opendb(&db, &qstmt, setquery); status = sqlite3_bind_text(qstmt, 2, uname, -1, SQLITE_TRANSIENT); if (status)