diff sqlickrypt.c @ 143:f1676e81c80a

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.
author John "Elwin" Edwards
date Sun, 20 Oct 2013 21:19:13 -0700
parents 0a3ff1267c24
children bc69717ff386
line wrap: on
line diff
--- 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)