2016-01-12 23:52:30 +01:00
|
|
|
#include <platform.h>
|
|
|
|
#include "password.h"
|
|
|
|
|
2016-01-13 14:41:09 +01:00
|
|
|
#include <md5.h>
|
2016-02-16 07:30:26 +01:00
|
|
|
#include <crypt_blowfish.h>
|
2016-01-14 16:50:42 +01:00
|
|
|
#include <mtrand.h>
|
2016-02-16 07:30:26 +01:00
|
|
|
#include <drepper.h>
|
2016-01-13 14:41:09 +01:00
|
|
|
|
2016-01-12 23:52:30 +01:00
|
|
|
#include <assert.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <stdio.h>
|
2016-01-15 08:01:12 +01:00
|
|
|
#include <time.h>
|
2016-01-12 23:52:30 +01:00
|
|
|
|
2016-01-13 14:41:09 +01:00
|
|
|
#define MAXSALTLEN 32 // maximum length in characters of any salt
|
2016-01-14 16:50:42 +01:00
|
|
|
#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) \
|
|
|
|
{ \
|
2016-02-06 15:23:42 +01:00
|
|
|
*cp++ = itoa64[w & 0x3f]; \
|
2016-01-14 16:50:42 +01:00
|
|
|
--buflen; \
|
|
|
|
w >>= 6; \
|
|
|
|
} \
|
|
|
|
} while (0)
|
|
|
|
|
|
|
|
|
2016-02-16 07:30:26 +01:00
|
|
|
char *password_gensalt(char *salt, size_t salt_len) {
|
|
|
|
size_t buflen = salt_len-1;
|
2016-01-14 16:50:42 +01:00
|
|
|
char *cp = salt;
|
|
|
|
while (buflen) {
|
2016-01-15 08:54:03 +01:00
|
|
|
unsigned long ul = genrand_int32() & (unsigned long)time(0);
|
2016-02-16 07:30:26 +01:00
|
|
|
b64_from_24bit((char)(ul & 0xFF), (char)((ul >> 8) & 0xff), (char)((ul >> 16) & 0xFF), 4);
|
2016-01-14 16:50:42 +01:00
|
|
|
}
|
2016-02-16 07:30:26 +01:00
|
|
|
salt[salt_len-1] = 0;
|
2016-01-14 16:50:42 +01:00
|
|
|
return salt;
|
|
|
|
}
|
2016-01-13 14:41:09 +01:00
|
|
|
|
2016-02-25 10:46:46 +01:00
|
|
|
bool password_is_implemented(int algo) {
|
|
|
|
return algo == PASSWORD_PLAINTEXT;// || algo == PASSWORD_BCRYPT || algo == PASSWORD_NOCRYPT || algo == PASSWORD_MD5 || algo == PASSWORD_APACHE_MD5;
|
2016-02-16 07:30:26 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static const char * password_hash_i(const char * passwd, const char *input, int algo, char *result, size_t len) {
|
|
|
|
if (algo == PASSWORD_BCRYPT) {
|
|
|
|
char salt[MAXSALTLEN];
|
|
|
|
char setting[40];
|
|
|
|
if (!input) {
|
|
|
|
input = password_gensalt(salt, MAXSALTLEN);
|
|
|
|
}
|
|
|
|
if (_crypt_gensalt_blowfish_rn("$2y$", 5, input, strlen(input), setting, sizeof(setting)) == NULL) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
if (_crypt_blowfish_rn(passwd, setting, result, len) == NULL) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
return result;
|
2016-01-12 23:52:30 +01:00
|
|
|
}
|
2016-02-16 07:30:26 +01:00
|
|
|
else if (algo == PASSWORD_PLAINTEXT) {
|
|
|
|
_snprintf(result, len, "%s", passwd);
|
|
|
|
return result;
|
2016-02-06 14:45:38 +01:00
|
|
|
}
|
2016-02-16 07:30:26 +01:00
|
|
|
else if (algo == PASSWORD_NOCRYPT) {
|
|
|
|
_snprintf(result, len, "$0$%s", passwd);
|
2016-01-14 15:49:09 +01:00
|
|
|
return result;
|
2016-02-06 14:45:38 +01:00
|
|
|
}
|
2016-02-16 07:30:26 +01:00
|
|
|
else if (password_is_implemented(algo)) {
|
|
|
|
char salt[MAXSALTLEN];
|
|
|
|
assert(passwd);
|
|
|
|
if (input) {
|
|
|
|
const char * dol = strchr(input, '$');
|
|
|
|
size_t salt_len;
|
|
|
|
if (dol) {
|
|
|
|
assert(dol > input && dol[0] == '$');
|
|
|
|
salt_len = dol - input;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
salt_len = strlen(input);
|
|
|
|
}
|
|
|
|
assert(salt_len < MAXSALTLEN);
|
|
|
|
memcpy(salt, input, salt_len);
|
|
|
|
salt[salt_len] = 0;
|
|
|
|
} else {
|
2016-02-13 13:42:02 +01:00
|
|
|
input = password_gensalt(salt, SALTLEN);
|
2016-02-16 07:30:26 +01:00
|
|
|
}
|
|
|
|
if (algo == PASSWORD_MD5) {
|
|
|
|
return md5_crypt_r(passwd, input, result, len);
|
|
|
|
}
|
|
|
|
else if (algo == PASSWORD_APACHE_MD5) {
|
|
|
|
apr_md5_encode(passwd, input, result, len);
|
|
|
|
return result;
|
|
|
|
}
|
2016-01-12 23:52:30 +01:00
|
|
|
}
|
2016-02-16 07:30:26 +01:00
|
|
|
return NULL;
|
2016-01-12 23:52:30 +01:00
|
|
|
}
|
|
|
|
|
2016-02-16 07:30:26 +01:00
|
|
|
const char * password_encode(const char * passwd, int algo) {
|
2016-01-13 16:16:02 +01:00
|
|
|
static char result[64]; // TODO: static result buffers are bad mojo!
|
|
|
|
if (algo < 0) algo = PASSWORD_DEFAULT;
|
2016-02-16 07:30:26 +01:00
|
|
|
return password_hash_i(passwd, 0, algo, result, sizeof(result));
|
2016-01-13 14:41:09 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
int password_verify(const char * pwhash, const char * passwd) {
|
2016-01-13 16:16:02 +01:00
|
|
|
char hash[64];
|
2016-02-16 07:30:26 +01:00
|
|
|
int algo = PASSWORD_PLAINTEXT;
|
2016-01-13 14:41:09 +01:00
|
|
|
char *pos;
|
2016-02-16 07:30:26 +01:00
|
|
|
const char *result;
|
2016-01-12 23:52:30 +01:00
|
|
|
assert(passwd);
|
2016-01-14 15:49:09 +01:00
|
|
|
assert(pwhash);
|
2016-02-16 07:30:26 +01:00
|
|
|
if (pwhash[0] == '$') {
|
|
|
|
algo = pwhash[1];
|
|
|
|
}
|
2016-01-13 14:41:09 +01:00
|
|
|
if (!password_is_implemented(algo)) {
|
|
|
|
return VERIFY_UNKNOWN;
|
|
|
|
}
|
2016-02-16 07:30:26 +01:00
|
|
|
if (algo == PASSWORD_PLAINTEXT) {
|
|
|
|
return (strcmp(passwd, pwhash) == 0) ? VERIFY_OK : VERIFY_FAIL;
|
|
|
|
} else if (algo == PASSWORD_BCRYPT) {
|
|
|
|
char sample[200];
|
|
|
|
_crypt_blowfish_rn(passwd, pwhash, sample, sizeof(sample));
|
|
|
|
return (strcmp(sample, pwhash) == 0) ? VERIFY_OK : VERIFY_FAIL;
|
|
|
|
}
|
|
|
|
pos = strchr(pwhash+2, '$');
|
|
|
|
assert(pos && pos[0] == '$');
|
|
|
|
pos = strchr(pos, '$')+1;
|
|
|
|
result = password_hash_i(passwd, pos, algo, hash, sizeof(hash));
|
2016-01-14 15:49:09 +01:00
|
|
|
if (strcmp(pwhash, result) == 0) {
|
2016-01-13 14:41:09 +01:00
|
|
|
return VERIFY_OK;
|
|
|
|
}
|
|
|
|
return VERIFY_FAIL;
|
2016-01-12 23:52:30 +01:00
|
|
|
}
|