From 4c46d9d0ef9ea87040f596236208366793f41f53 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Sat, 6 Feb 2016 14:45:38 +0100 Subject: [PATCH 1/7] add the apache md5 implementation to valid password hash algorithms Conflicts: src/util/password.c src/util/password.h --- crypto | 2 +- src/util/password.c | 99 +++++++++++++++++++++++++++++++++++++++++++++ src/util/password.h | 16 ++++++++ 3 files changed, 116 insertions(+), 1 deletion(-) create mode 100644 src/util/password.c create mode 100644 src/util/password.h diff --git a/crypto b/crypto index f9ecf5a10..10f60eafb 160000 --- a/crypto +++ b/crypto @@ -1 +1 @@ -Subproject commit f9ecf5a10983adfc7bd1bee8ac1f9a3abf1d41d9 +Subproject commit 10f60eafb8cbb4f6af2a4df65371e12464c2fd45 diff --git a/src/util/password.c b/src/util/password.c new file mode 100644 index 000000000..c3ba54546 --- /dev/null +++ b/src/util/password.c @@ -0,0 +1,99 @@ +#include +#include "password.h" + +#include +#include + +#include +#include +#include +#include + +#define MAXSALTLEN 32 // maximum length in characters of any salt +#define SALTLEN 8 // length of salts we generate + +#define b64_from_24bit(B2, B1, B0, N) \ + do { \ + unsigned int w = ((B2) << 16) | ((B1) << 8) | (B0); \ + int n = (N); \ + while (n-- > 0 && buflen > 0) \ + { \ + *cp++ = itoa64[w & 0x3f]; \ + --buflen; \ + w >>= 6; \ + } \ + } while (0) + + +char *password_gensalt(void) { + static char salt[SALTLEN + 1]; + char *cp = salt; + int buflen = SALTLEN; + while (buflen) { + unsigned long ul = genrand_int32() & (unsigned long)time(0); + b64_from_24bit((char)(ul & 0xFF), (char)((ul>>8)&0xff), (char)((ul>>16)&0xFF), 4); + } + salt[SALTLEN] = 0; + return salt; +} + +static const char * password_hash_i(const char * passwd, const char *salt, int algo, char *result, size_t len) { + assert(passwd); + if (!salt) { + salt = password_gensalt(); + } + if (algo==PASSWORD_PLAIN) { + _snprintf(result, len, "$0$%s$%s", salt, passwd); + } + else if (algo == PASSWORD_MD5) { + return md5_crypt_r(passwd, salt, result, len); + } + else if (algo == PASSWORD_APACHE_MD5) { + apr_md5_encode(passwd, salt, result, len); + return result; + } + else { + return NULL; + } + return result; +} + +const char * password_hash(const char * passwd, const char * salt, int algo) { + static char result[64]; // TODO: static result buffers are bad mojo! + if (algo < 0) algo = PASSWORD_DEFAULT; + return password_hash_i(passwd, salt, algo, result, sizeof(result)); +} + +static bool password_is_implemented(int algo) { + return algo==PASSWORD_PLAIN || algo==PASSWORD_MD5 || algo==PASSWORD_APACHE_MD5; +} + +int password_verify(const char * pwhash, const char * passwd) { + char salt[MAXSALTLEN+1]; + char hash[64]; + size_t len; + int algo; + char *pos; + const char *dol, *result; + assert(passwd); + assert(pwhash); + assert(pwhash[0] == '$'); + algo = pwhash[1]; + pos = strchr(pwhash+2, '$'); + assert(pos && pos[0] == '$'); + ++pos; + dol = strchr(pos, '$'); + assert(dol>pos && dol[0] == '$'); + len = dol - pos; + assert(len <= MAXSALTLEN); + strncpy(salt, pos, len); + salt[len] = 0; + result = password_hash_i(passwd, salt, algo, hash, sizeof(hash)); + if (!password_is_implemented(algo)) { + return VERIFY_UNKNOWN; + } + if (strcmp(pwhash, result) == 0) { + return VERIFY_OK; + } + return VERIFY_FAIL; +} diff --git a/src/util/password.h b/src/util/password.h new file mode 100644 index 000000000..d9c58a672 --- /dev/null +++ b/src/util/password.h @@ -0,0 +1,16 @@ +#pragma once + +#define PASSWORD_PLAIN '0' +#define PASSWORD_MD5 '1' +#define PASSWORD_BCRYPT '2' // not implemented +#define PASSWORD_APACHE_MD5 'a' +#define PASSWORD_SHA256 '5' // not implemented +#define PASSWORD_SHA512 '6' // not implemented +#define PASSWORD_DEFAULT PASSWORD_MD5 + + +#define VERIFY_OK 0 // password matches hash +#define VERIFY_FAIL 1 // password is wrong +#define VERIFY_UNKNOWN 2 // hashing algorithm not supported +int password_verify(const char *hash, const char *passwd); +const char * password_hash(const char *passwd, const char *salt, int algo); From d2d50cb23f561871766938d4fc96467a703e21c6 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Sat, 6 Feb 2016 14:52:32 +0100 Subject: [PATCH 2/7] fix build, add test for new algorithm Conflicts: src/util/password.test.c --- src/util/password.test.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 src/util/password.test.c diff --git a/src/util/password.test.c b/src/util/password.test.c new file mode 100644 index 000000000..b4f4005da --- /dev/null +++ b/src/util/password.test.c @@ -0,0 +1,30 @@ +#include +#include +#include "password.h" + +static void test_passwords(CuTest *tc) { + const char *hash; + + hash = password_hash("Hodor", "FqQLkl8g", PASSWORD_APACHE_MD5); + CuAssertPtrNotNull(tc, hash); + CuAssertStrEquals(tc, "$apr1$FqQLkl8g$.icQqaDJpim4BVy.Ho5660", hash); + CuAssertIntEquals(tc, VERIFY_OK, password_verify(hash, "Hodor")); + + hash = password_hash("jollygood", "ZouUn04i", PASSWORD_MD5); + CuAssertPtrNotNull(tc, hash); + CuAssertStrEquals(tc, "$1$ZouUn04i$yNnT1Oy8azJ5V.UM9ppP5/", hash); + CuAssertIntEquals(tc, VERIFY_OK, password_verify(hash, "jollygood")); + + hash = password_hash("password", "hodor", PASSWORD_PLAIN); + CuAssertPtrNotNull(tc, hash); + CuAssertStrEquals(tc, "$0$hodor$password", hash); + CuAssertIntEquals(tc, VERIFY_OK, password_verify(hash, "password")); + CuAssertIntEquals(tc, VERIFY_FAIL, password_verify(hash, "arseword")); + CuAssertIntEquals(tc, VERIFY_UNKNOWN, password_verify("$9$saltyfish$password", "password")); +} + +CuSuite *get_password_suite(void) { + CuSuite *suite = CuSuiteNew(); + SUITE_ADD_TEST(suite, test_passwords); + return suite; +} From bee97c002c103c92b81fd3b64dc632cec56bc992 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Sat, 6 Feb 2016 15:09:36 +0100 Subject: [PATCH 4/7] use apache implementation by default --- src/util/password.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/password.h b/src/util/password.h index d9c58a672..8dfd730a2 100644 --- a/src/util/password.h +++ b/src/util/password.h @@ -6,7 +6,7 @@ #define PASSWORD_APACHE_MD5 'a' #define PASSWORD_SHA256 '5' // not implemented #define PASSWORD_SHA512 '6' // not implemented -#define PASSWORD_DEFAULT PASSWORD_MD5 +#define PASSWORD_DEFAULT PASSWORD_APACHE_MD5 #define VERIFY_OK 0 // password matches hash From 4f0e8d29bfe6cb8e2b89f0d15a4795f0d56c12ed Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Sat, 6 Feb 2016 15:17:16 +0100 Subject: [PATCH 5/7] latest module --- crypto | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crypto b/crypto index 10f60eafb..e0f9891a9 160000 --- a/crypto +++ b/crypto @@ -1 +1 @@ -Subproject commit 10f60eafb8cbb4f6af2a4df65371e12464c2fd45 +Subproject commit e0f9891a91d69c042f82c1e13e48ab4c7160381d From b89e4242537b998ee1020bf1c599e86efba9ea22 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Sat, 6 Feb 2016 15:25:40 +0100 Subject: [PATCH 6/7] submodules. more like suckmodules, amirite? --- crypto | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crypto b/crypto index f9ecf5a10..e0f9891a9 160000 --- a/crypto +++ b/crypto @@ -1 +1 @@ -Subproject commit f9ecf5a10983adfc7bd1bee8ac1f9a3abf1d41d9 +Subproject commit e0f9891a91d69c042f82c1e13e48ab4c7160381d From 8279c3b348a73d9a0d1c12c4214f7bc1c03a1ddb Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Sat, 6 Feb 2016 15:28:54 +0100 Subject: [PATCH 7/7] release version 3.8.2 --- src/buildno.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/buildno.h b/src/buildno.h index ba8ddfa0a..48976f45b 100644 --- a/src/buildno.h +++ b/src/buildno.h @@ -1,3 +1,3 @@ #define VERSION_MAJOR 3 #define VERSION_MINOR 8 -#define VERSION_BUILD 1 +#define VERSION_BUILD 2