Mercurial > hg > rlgwebd
comparison 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 |
comparison
equal
deleted
inserted
replaced
| 142:c4304f08e35b | 143:f1676e81c80a |
|---|---|
| 6 #include <unistd.h> | 6 #include <unistd.h> |
| 7 #include <crypt.h> | 7 #include <crypt.h> |
| 8 | 8 |
| 9 #define DATABASE "/dgldir/dgamelaunch.db" | 9 #define DATABASE "/dgldir/dgamelaunch.db" |
| 10 #define IBUFSIZE 200 | 10 #define IBUFSIZE 200 |
| 11 #define RANDOMSRC "/dev/urandom" | |
| 11 | 12 |
| 12 /* General idea for return status: | 13 /* General idea for return status: |
| 13 * 0: success | 14 * 0: success |
| 14 * 1: password check failed | 15 * 1: password check failed |
| 15 * 2: username not found | 16 * 2: username not found |
| 38 sqlite3_finalize(stmt); | 39 sqlite3_finalize(stmt); |
| 39 sqlite3_close(db); | 40 sqlite3_close(db); |
| 40 if (status) | 41 if (status) |
| 41 exit(status); | 42 exit(status); |
| 42 return; | 43 return; |
| 44 } | |
| 45 | |
| 46 char encode64(unsigned char c) { | |
| 47 if (c < 26) | |
| 48 return 'a' + c; | |
| 49 else if (c < 52) | |
| 50 return 'A' + c - 26; | |
| 51 else if (c < 62) | |
| 52 return '0' + c - 52; | |
| 53 else if (c == 62) | |
| 54 return '.'; | |
| 55 else | |
| 56 return '/'; | |
| 57 } | |
| 58 | |
| 59 /* Initializes a SHA-512 salt. salt must contain at least 20 bytes. */ | |
| 60 void setsalt(char *salt) { | |
| 61 unsigned char rnbytes[3], rnvals[4]; | |
| 62 FILE *urandom; | |
| 63 int loop; | |
| 64 salt[0] = salt[2] = salt[19] = '$'; /* Delimiters */ | |
| 65 salt[1] = '6'; /* SHA-512 */ | |
| 66 urandom = fopen(RANDOMSRC, "r"); | |
| 67 for (loop = 0; loop < 4; loop++) { | |
| 68 fread(rnbytes, 1, 3, urandom); | |
| 69 rnvals[0] = rnbytes[0] >> 2; | |
| 70 rnvals[1] = ((rnbytes[0] & 0x03) << 4) | ((rnbytes[1] & 0xf0) >> 4); | |
| 71 rnvals[2] = ((rnbytes[1] & 0x0f) << 2) | ((rnbytes[2] & 0xc0) >> 6); | |
| 72 rnvals[3] = rnbytes[2] & 0x3f; | |
| 73 salt[loop * 4 + 3] = encode64(rnvals[0]); | |
| 74 salt[loop * 4 + 4] = encode64(rnvals[1]); | |
| 75 salt[loop * 4 + 5] = encode64(rnvals[2]); | |
| 76 salt[loop * 4 + 6] = encode64(rnvals[3]); | |
| 77 } | |
| 78 fclose(urandom); | |
| 43 } | 79 } |
| 44 | 80 |
| 45 int check(char *uname, char *pw) { | 81 int check(char *uname, char *pw) { |
| 46 char *pwhash, *comphash; | 82 char *pwhash, *comphash; |
| 47 char *query = "SELECT password FROM dglusers WHERE username=?;"; | 83 char *query = "SELECT password FROM dglusers WHERE username=?;"; |
| 74 return status; | 110 return status; |
| 75 } | 111 } |
| 76 | 112 |
| 77 int insertuser(char *uname, char *pw, char *email) { | 113 int insertuser(char *uname, char *pw, char *email) { |
| 78 char *checkquery = "SELECT * FROM dglusers WHERE username = ?;"; | 114 char *checkquery = "SELECT * FROM dglusers WHERE username = ?;"; |
| 79 char *addquery = "INSERT INTO dglusers (username, password, email) VALUES (?, ?, ?);"; | 115 char *addquery = "INSERT INTO dglusers (username, password, email, flags, env) VALUES (?, ?, ?, ?, ?);"; |
| 80 int status; | 116 int status; |
| 81 sqlite3 *db; | 117 sqlite3 *db; |
| 82 sqlite3_stmt *qstmt; | 118 sqlite3_stmt *qstmt; |
| 119 char salt[20]; | |
| 83 | 120 |
| 84 opendb(&db, &qstmt, checkquery); | 121 opendb(&db, &qstmt, checkquery); |
| 85 /* Check for existing account in the same transaction with creating it. */ | 122 /* Check for existing account in the same transaction with creating it. */ |
| 86 status = sqlite3_exec(db, "BEGIN;", NULL, NULL, NULL); | 123 status = sqlite3_exec(db, "BEGIN;", NULL, NULL, NULL); |
| 87 if (status) | 124 if (status) |
| 94 if (status == SQLITE_ROW) | 131 if (status == SQLITE_ROW) |
| 95 return 1; /* User already exists */ | 132 return 1; /* User already exists */ |
| 96 return 3; | 133 return 3; |
| 97 } | 134 } |
| 98 /* The username doesn't exist yet, so create a new account. */ | 135 /* The username doesn't exist yet, so create a new account. */ |
| 136 setsalt(salt); | |
| 99 sqlite3_finalize(qstmt); | 137 sqlite3_finalize(qstmt); |
| 100 sqlite3_prepare_v2(db, addquery, -1, &qstmt, NULL); | 138 sqlite3_prepare_v2(db, addquery, -1, &qstmt, NULL); |
| 101 if (qstmt == NULL) | 139 if (qstmt == NULL) |
| 102 cleanup(db, NULL, 3); | 140 cleanup(db, NULL, 3); |
| 103 sqlite3_bind_text(qstmt, 1, uname, -1, SQLITE_TRANSIENT); | 141 sqlite3_bind_text(qstmt, 1, uname, -1, SQLITE_TRANSIENT); |
| 104 sqlite3_bind_text(qstmt, 2, strdup(crypt(pw, pw)), -1, free); | 142 sqlite3_bind_text(qstmt, 2, strdup(crypt(pw, salt)), -1, free); |
| 105 sqlite3_bind_text(qstmt, 3, email, -1, SQLITE_TRANSIENT); | 143 sqlite3_bind_text(qstmt, 3, email, -1, SQLITE_TRANSIENT); |
| 144 sqlite3_bind_int(qstmt, 4, 0); | |
| 145 sqlite3_bind_text(qstmt, 5, "", -1, SQLITE_STATIC); | |
| 106 status = sqlite3_step(qstmt); | 146 status = sqlite3_step(qstmt); |
| 107 if (status != SQLITE_DONE) | 147 if (status != SQLITE_DONE) |
| 108 cleanup(db, qstmt, 3); | 148 cleanup(db, qstmt, 3); |
| 109 status = sqlite3_exec(db, "COMMIT;", NULL, NULL, NULL); | 149 status = sqlite3_exec(db, "COMMIT;", NULL, NULL, NULL); |
| 110 cleanup(db, qstmt, 0); | 150 cleanup(db, qstmt, 0); |
| 158 char *setquery = "UPDATE dglusers SET password = ? WHERE username = ?;"; | 198 char *setquery = "UPDATE dglusers SET password = ? WHERE username = ?;"; |
| 159 sqlite3 *db; | 199 sqlite3 *db; |
| 160 sqlite3_stmt *qstmt; | 200 sqlite3_stmt *qstmt; |
| 161 int status; | 201 int status; |
| 162 char *hash; | 202 char *hash; |
| 163 | 203 char salt[20]; |
| 164 /* This is not a smart use of crypt, but it needs to be compatible with | 204 |
| 165 * dgamelaunch. */ | 205 setsalt(salt); |
| 166 hash = crypt(newpw, newpw); | 206 hash = crypt(newpw, salt); |
| 167 opendb(&db, &qstmt, setquery); | 207 opendb(&db, &qstmt, setquery); |
| 168 status = sqlite3_bind_text(qstmt, 2, uname, -1, SQLITE_TRANSIENT); | 208 status = sqlite3_bind_text(qstmt, 2, uname, -1, SQLITE_TRANSIENT); |
| 169 if (status) | 209 if (status) |
| 170 cleanup(db, qstmt, 3); | 210 cleanup(db, qstmt, 3); |
| 171 status = sqlite3_bind_text(qstmt, 1, hash, -1, SQLITE_TRANSIENT); | 211 status = sqlite3_bind_text(qstmt, 1, hash, -1, SQLITE_TRANSIENT); |
