-
Notifications
You must be signed in to change notification settings - Fork 997
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #4479 from sysown/v2.x-sqlite3_pass_exts-2
Add new SQLite3 functions for password hash generation
- Loading branch information
Showing
12 changed files
with
976 additions
and
76 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,196 @@ | ||
--- sqlite3.c 2024-03-22 19:22:47.046093173 +0100 | ||
+++ sqlite3-pass-exts.c 2024-03-22 19:24:09.557303716 +0100 | ||
@@ -25168,6 +25168,183 @@ | ||
sqlite3ResultStrAccum(context, &sRes); | ||
} | ||
|
||
+#define DEF_SALT_SIZE 20 | ||
+#define SHA_DIGEST_LENGTH 20 | ||
+ | ||
+/// Forward declarations | ||
+//////////////////////////////////////////////////////////////////////////////// | ||
+ | ||
+// ctype.h | ||
+extern int toupper (int __c) __THROW; | ||
+ | ||
+// SHA256_crypt | ||
+char * sha256_crypt_r (const char *key, const char *salt, char *buffer, int buflen); | ||
+ | ||
+// OpenSSL | ||
+unsigned char *SHA1(const unsigned char *d, size_t n, unsigned char *md); | ||
+int RAND_bytes(unsigned char *buf, int num); | ||
+unsigned long ERR_get_error(void); | ||
+char *ERR_error_string(unsigned long e, char *buf); | ||
+ | ||
+//////////////////////////////////////////////////////////////////////////////// | ||
+ | ||
+int check_args_types(int argc, sqlite3_value** argv) { | ||
+ int inv_type = sqlite3_value_type(argv[0]) != SQLITE_TEXT; | ||
+ | ||
+ if (inv_type == 0 && argc == 2) { | ||
+ return | ||
+ sqlite3_value_type(argv[1]) != SQLITE_TEXT && | ||
+ sqlite3_value_type(argv[1]) != SQLITE_BLOB; | ||
+ } else { | ||
+ return inv_type; | ||
+ } | ||
+} | ||
+ | ||
+int check_args_lengths(int argc, sqlite3_value** argv) { | ||
+ int inv_size = 1; | ||
+ | ||
+ int pass_size = sqlite3_value_bytes(argv[0]); | ||
+ if (pass_size > 0) { | ||
+ inv_size = 0; | ||
+ } | ||
+ | ||
+ if (inv_size == 0 && argc == 2) { | ||
+ int salt_size = sqlite3_value_bytes(argv[1]); | ||
+ | ||
+ return salt_size <= 0 || salt_size > DEF_SALT_SIZE; | ||
+ } else { | ||
+ return inv_size; | ||
+ } | ||
+} | ||
+ | ||
+/** | ||
+ * @brief SQLite3 extension function for hash generation. | ||
+ * @details Computes a hash equivalent to the one generated by MySQL for 'mysql_native_password'. | ||
+ * @param context SQLite3 context used for returning computation result. | ||
+ * @param argc Number of arguments; expected to be 1. | ||
+ * @param argv Argument list; expected to hold one argument with len > 0 of type 'SQLITE_TEXT'. | ||
+ */ | ||
+static void mysql_native_passwordFunc(sqlite3_context* context, int argc, sqlite3_value** argv) { | ||
+ if (argc != 1) { | ||
+ sqlite3_result_text(context, "Invalid number of arguments", -1, SQLITE_TRANSIENT); | ||
+ return; | ||
+ } else { | ||
+ if (check_args_types(argc, argv)) { | ||
+ sqlite3_result_text(context, "Invalid argument type", -1, SQLITE_TRANSIENT); | ||
+ return; | ||
+ } | ||
+ if (check_args_lengths(argc, argv)) { | ||
+ sqlite3_result_text(context, "Invalid argument size", -1, SQLITE_TRANSIENT); | ||
+ return; | ||
+ } | ||
+ } | ||
+ | ||
+ const unsigned char* input = sqlite3_value_text(argv[0]); | ||
+ int input_len = strlen((const char*)input); | ||
+ | ||
+ unsigned char hash1[SHA_DIGEST_LENGTH] = { 0 }; | ||
+ unsigned char hash2[SHA_DIGEST_LENGTH] = { 0 }; | ||
+ | ||
+ SHA1(input, input_len, hash1); | ||
+ SHA1(hash1, SHA_DIGEST_LENGTH, hash2); | ||
+ | ||
+ char hex_hash[2 * SHA_DIGEST_LENGTH + 2]; | ||
+ unsigned int i = 0; | ||
+ | ||
+ for (i = 0; i < SHA_DIGEST_LENGTH; i++) { | ||
+ sprintf(hex_hash + 2 * i + 1, "%02x", hash2[i]); | ||
+ | ||
+ hex_hash[2 * i + 1] = toupper(hex_hash[2 * i + 1]); | ||
+ hex_hash[2 * i + 1 + 1] = toupper(hex_hash[2 * i + 1 + 1]); | ||
+ } | ||
+ | ||
+ hex_hash[0] = '*'; | ||
+ hex_hash[2 * SHA_DIGEST_LENGTH + 1] = '\0'; | ||
+ | ||
+ sqlite3_result_text(context, hex_hash, -1, SQLITE_TRANSIENT); | ||
+} | ||
+ | ||
+/** | ||
+ * @brief SQLite3 extension function for hash generation. | ||
+ * @details Computes a hash equivalent to the one generated by MySQL for 'caching_sha2_password'. | ||
+ * @param context SQLite3 context used for returning computation result. | ||
+ * @param argc Number of arguments; either 1 or 2. One for random salt, two providing salt. | ||
+ * @param argv Argument list; expected to hold either 1 or 2 arguments: | ||
+ * 1. Password to be hashed; with len > 0 and of type 'SQLITE_TEXT'. | ||
+ * 1. Optional salt; with (len > 0 && len <= 20) and of type ('SQLITE_TEXT' || 'SQLITE_BLOB'). If no salt is | ||
+ * provided a randomly generated salt with length 20 will be used. | ||
+ */ | ||
+static void caching_sha2_passwordFunc(sqlite3_context* context, int argc, sqlite3_value** argv) { | ||
+ if (argc < 1 || argc > 2) { | ||
+ sqlite3_result_text(context, "Invalid number of arguments", -1, SQLITE_TRANSIENT); | ||
+ return; | ||
+ } else { | ||
+ if (check_args_types(argc, argv)) { | ||
+ sqlite3_result_text(context, "Invalid argument type", -1, SQLITE_TRANSIENT); | ||
+ return; | ||
+ } | ||
+ if (check_args_lengths(argc, argv)) { | ||
+ sqlite3_result_text(context, "Invalid argument size", -1, SQLITE_TRANSIENT); | ||
+ return; | ||
+ } | ||
+ } | ||
+ | ||
+ unsigned int salt_size = DEF_SALT_SIZE; | ||
+ const char* cpass = (const char*)sqlite3_value_text(argv[0]); | ||
+ unsigned char salt[DEF_SALT_SIZE + 1] = { 0 }; | ||
+ | ||
+ if (argc == 2) { | ||
+ salt_size = sqlite3_value_bytes(argv[1]); | ||
+ const void* b_salt = sqlite3_value_blob(argv[1]); | ||
+ | ||
+ memcpy(salt, b_salt, salt_size); | ||
+ } else { | ||
+ unsigned char salt_buf[DEF_SALT_SIZE + 1] = { 0 }; | ||
+ | ||
+ if (RAND_bytes(salt_buf, DEF_SALT_SIZE) != 1) { | ||
+ const char t_msg[] = { "SALT creation failed (%lu:'%s')" }; | ||
+ char err_buf[256] = { 0 }; | ||
+ char err_msg[sizeof(err_buf)/sizeof(char) + sizeof(t_msg)/sizeof(char) + 20] = { 0 }; | ||
+ | ||
+ const unsigned long err = ERR_get_error(); | ||
+ ERR_error_string(err, err_buf); | ||
+ | ||
+ sprintf(err_msg, t_msg, err, err_buf); | ||
+ sqlite3_result_text(context, err_msg, -1, SQLITE_TRANSIENT); | ||
+ return; | ||
+ } else { | ||
+ unsigned int i = 0; | ||
+ | ||
+ for (i = 0; i < sizeof(salt_buf)/sizeof(unsigned char); i++) { | ||
+ salt_buf[i] = salt_buf[i] & 0x7f; | ||
+ | ||
+ if (salt_buf[i] == '\0' || salt_buf[i] == '$') { | ||
+ salt_buf[i] = salt_buf[i] + 1; | ||
+ } | ||
+ } | ||
+ | ||
+ memcpy(salt, salt_buf, salt_size); | ||
+ } | ||
+ } | ||
+ | ||
+ #define BASE_SHA2_SALT "$5$rounds=5000$" | ||
+ #define BASE_SHA2_HASH "$A$005$" | ||
+ | ||
+ char sha2_buf[100] = { 0 }; | ||
+ char sha2_salt[100] = { BASE_SHA2_SALT }; | ||
+ | ||
+ strcat(sha2_salt, (const char*)salt); | ||
+ sha256_crypt_r(cpass, sha2_salt, sha2_buf, sizeof(sha2_buf)); | ||
+ | ||
+ char sha2_hash[100] = { BASE_SHA2_HASH }; | ||
+ const char* sha256 = sha2_buf + salt_size + strlen(BASE_SHA2_SALT) + 1; | ||
+ | ||
+ strcat(sha2_hash, (const char*)salt); | ||
+ strcat(sha2_hash, sha256); | ||
+ | ||
+ sqlite3_result_text(context, sha2_hash, -1, SQLITE_TRANSIENT); | ||
+} | ||
+ | ||
/* | ||
** current_time() | ||
** | ||
@@ -129263,6 +129440,9 @@ | ||
FUNCTION(substr, 3, 0, 0, substrFunc ), | ||
FUNCTION(substring, 2, 0, 0, substrFunc ), | ||
FUNCTION(substring, 3, 0, 0, substrFunc ), | ||
+ FUNCTION(mysql_native_password, 1, 0, 0, mysql_native_passwordFunc ), | ||
+ FUNCTION(caching_sha2_password, 1, 0, 0, caching_sha2_passwordFunc ), | ||
+ FUNCTION(caching_sha2_password, 2, 0, 0, caching_sha2_passwordFunc ), | ||
WAGGREGATE(sum, 1,0,0, sumStep, sumFinalize, sumFinalize, sumInverse, 0), | ||
WAGGREGATE(total, 1,0,0, sumStep,totalFinalize,totalFinalize,sumInverse, 0), | ||
WAGGREGATE(avg, 1,0,0, sumStep, avgFinalize, avgFinalize, sumInverse, 0), |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.