Merge pull request #2049 from eeff/2.6-encrypt_pass

v2.6: encrypt user password
This commit is contained in:
fengzeroz
2024-05-31 15:29:36 +08:00
committed by GitHub
9 changed files with 259 additions and 23 deletions
+2 -2
View File
@@ -35,8 +35,8 @@
#define NEU_DRIVER_TAG_CACHE_EXPIRE_TIME 60
#define NEU_APP_SUBSCRIBE_MSG_SIZE 4
#define NEU_TAG_FLOAG_PRECISION_MAX 17
#define NEU_USER_PASSWORD_MIN_LEN 4
#define NEU_USER_PASSWORD_MAX_LEN 16
#define NEU_USER_PASSWORD_MIN_LEN 8
#define NEU_USER_PASSWORD_MAX_LEN 32
#define NEU_LOG_LEVEL_LEN 12
#define NEU_FILE_PATH_LEN 128
#define NEU_MSG_MAX_SIZE 2048
+3
View File
@@ -47,6 +47,9 @@ typedef enum {
NEU_ERR_COMMAND_EXECUTION_FAILED = 1014,
NEU_ERR_IP_ADDRESS_INVALID = 1015,
NEU_ERR_IP_ADDRESS_IN_USE = 1016,
NEU_ERR_WEAK_PASSWORD_ALPHABET = 1017,
NEU_ERR_WEAK_PASSWORD_PATTERN = 1018,
NEU_ERR_DECRYPT_PASSWORD_FAILED = 1019,
NEU_ERR_NODE_EXIST = 2002,
NEU_ERR_NODE_NOT_EXIST = 2003,
+29
View File
@@ -0,0 +1,29 @@
/**
* NEURON IIoT System for Industry 4.0
* Copyright (C) 2020-2024 EMQ Technologies Co., Ltd All rights reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
**/
BEGIN TRANSACTION;
-- use stronger password
UPDATE users
SET
password = "$5$PwFeXpBBIBZuZdZl$MUEmeqpMfQA9fzQ3s9bojsBhZOHJwKHiukeWyutsSR6"
WHERE
name = "admin"
AND password = "$5$PwFeXpBBIBZuZdZl$fP8fFPWCLoaWcnVXVSR.3Xi8TEqCvX92gjhowNNn6S4";
COMMIT;
+25 -18
View File
@@ -53,23 +53,20 @@ void handle_login(nng_aio *aio)
NEU_PROCESS_HTTP_REQUEST(
aio, neu_json_login_req_t, neu_json_decode_login_req, {
neu_json_login_resp_t login_resp = { 0 };
neu_user_t * user = neu_load_user(req->name);
int pass_len = strlen(req->pass);
neu_user_t * user = NULL;
char *password = neu_decrypt_user_password(req->pass);
if (NULL == user) {
if (NULL == password) {
nlog_error("could not decrypt user `%s` password", req->name);
NEU_JSON_RESPONSE_ERROR(NEU_ERR_DECRYPT_PASSWORD_FAILED, {
neu_http_response(aio, error_code.error, result_error);
});
} else if (NULL == (user = neu_load_user(req->name))) {
nlog_error("could not find user `%s`", req->name);
NEU_JSON_RESPONSE_ERROR(NEU_ERR_INVALID_USER_OR_PASSWORD, {
neu_http_response(aio, error_code.error, result_error);
});
} else if (pass_len < NEU_USER_PASSWORD_MIN_LEN ||
pass_len > NEU_USER_PASSWORD_MAX_LEN) {
nlog_error("user `%s` password too short or too long",
req->name);
NEU_JSON_RESPONSE_ERROR(NEU_ERR_INVALID_PASSWORD_LEN, {
neu_http_response(aio, error_code.error, result_error);
});
} else if (neu_user_check_password(user, req->pass)) {
} else if (neu_user_check_password(user, password)) {
char *token = NULL;
char *result = NULL;
@@ -95,6 +92,7 @@ void handle_login(nng_aio *aio)
});
}
free(password);
neu_user_free(user);
})
}
@@ -105,21 +103,28 @@ void handle_password(nng_aio *aio)
aio, neu_json_password_req_t, neu_json_decode_password_req, {
neu_user_t *user = NULL;
int rv = 0;
int pass_len = strlen(req->new_pass);
char * new_pass = neu_decrypt_user_password(req->new_pass);
char * old_pass = neu_decrypt_user_password(req->old_pass);
int pass_len = new_pass ? strlen(new_pass) : 0;
if (pass_len < NEU_USER_PASSWORD_MIN_LEN ||
pass_len > NEU_USER_PASSWORD_MAX_LEN) {
if (NULL == new_pass) {
nlog_error("could not decrypt user `%s` new pass", req->name);
rv = NEU_ERR_DECRYPT_PASSWORD_FAILED;
} else if (NULL == old_pass) {
nlog_error("could not decrypt user `%s` old pass", req->name);
rv = NEU_ERR_DECRYPT_PASSWORD_FAILED;
} else if (pass_len < NEU_USER_PASSWORD_MIN_LEN ||
pass_len > NEU_USER_PASSWORD_MAX_LEN) {
nlog_error("user `%s` new password too short or too long",
req->name);
rv = NEU_ERR_INVALID_PASSWORD_LEN;
} else if (NULL == (user = neu_load_user(req->name))) {
nlog_error("could not find user `%s`", req->name);
rv = NEU_ERR_INVALID_USER_OR_PASSWORD;
} else if (!neu_user_check_password(user, req->old_pass)) {
} else if (!neu_user_check_password(user, old_pass)) {
nlog_error("user `%s` password check fail", req->name);
rv = NEU_ERR_INVALID_USER_OR_PASSWORD;
} else if (0 !=
(rv = neu_user_update_password(user, req->new_pass))) {
} else if (0 != (rv = neu_user_update_password(user, new_pass))) {
nlog_error("user `%s` update password fail", req->name);
} else if (0 != (rv = neu_save_user(user))) {
nlog_error("user `%s` persist fail", req->name);
@@ -129,6 +134,8 @@ void handle_password(nng_aio *aio)
neu_http_response(aio, error_code.error, result_error);
});
free(new_pass);
free(old_pass);
neu_user_free(user);
})
}
+188
View File
@@ -1,4 +1,5 @@
#include <assert.h>
#include <ctype.h>
#include <string.h>
#include <openssl/evp.h>
@@ -10,6 +11,7 @@
#include "errcodes.h"
#include "user.h"
#include "utils/base64.h"
#include "utils/log.h"
static const unsigned char cov_2char[64] = {
@@ -343,6 +345,187 @@ end:
return hash;
}
static bool check_password_alphabet(const char *password)
{
int kind = 0;
// check special characters
const char *special = "_`~!@#$%^&*()+=|{}':;',\\[\\].<>/"
"?~!@#¥%……&*()——+|{}【】‘;:”“’。,、?\n\r\t";
if (NULL != strpbrk(password, special)) {
++kind;
}
// check digits
if (NULL != strpbrk(password, "0123456789")) {
++kind;
}
// check uppercase letters
if (NULL != strpbrk(password, "ABCDEFGHIJKLMNOPQRSTUVWXYZ")) {
++kind;
}
// check lowercase letters
if (NULL != strpbrk(password, "abcdefghijklmnopqrstuvwxyz")) {
++kind;
}
return kind > 2;
}
static bool check_password_pattern(const char *password)
{
const int n = strlen(password) - 2;
// password must not contain character of length 3
for (int i = 0; i < n; ++i) {
if (password[i] == password[i + 1] && password[i] == password[i + 2]) {
nlog_error("password has three `%c` in a row", password[i]);
return false;
}
}
const char *patterns[] = {
"qaz", "wsx", "edc", "rfv", "tgb", "yhn", "ujm",
"ik,", "ol.", "p;/", "esz", "rdx", "tfc", "ygv",
"uhb", "ijn", "okm", "pl,", "[;.", "]'/", "1qa",
"2ws", "3ed", "4rf", "5tg", "6yh", "7uj", "8ik",
"9ol", "0p;", "-['", "=[;", "-pl", "0ok", "9ij",
"8uh", "7yg", "6tf", "5rd", "4es", "3wa", "root",
"admin", "mysql", "oracle", "system", "windows", "linux", "java",
"python", "unix", "test",
};
// password must not contain any pattern
for (unsigned i = 0; i < sizeof(patterns) / sizeof(patterns[0]); ++i) {
if (NULL != strstr(password, patterns[i])) {
nlog_error("password has weak pattern `%s`", patterns[i]);
return false;
}
}
const char *sequences[] = {
"qwertyuiop[]\\", "asdfghjkl;'", "zxcvbnm,./",
"901234567890-=", "9876543210", "abcdefghijklmnopqrstuvwxyz"
};
// password must not have common sub string of length 3 with any sequence
for (unsigned i = 0; i < sizeof(sequences) / sizeof(sequences[0]); ++i) {
const char *seq = sequences[i];
const int m = strlen(seq) - 2;
for (int j = 0; j < m; ++j) {
for (int k = 0; k < n; ++k) {
if (seq[j] == password[k] && seq[j + 1] == password[k + 1] &&
seq[j + 2] == password[k + 2]) {
nlog_error("password has weak pattern `%.3s`", &seq[j]);
return false;
}
}
}
}
return true;
}
static inline int checkout_password_strength(const char *password)
{
if (false == check_password_alphabet(password)) {
return NEU_ERR_WEAK_PASSWORD_ALPHABET;
}
if (false == check_password_pattern(password)) {
return NEU_ERR_WEAK_PASSWORD_PATTERN;
}
return 0;
}
static inline EVP_CIPHER_CTX *aes_ctx_new()
{
int ret = 0;
unsigned char key[32] = { 90, 45, 54, 10, 182, 19, 52, 130,
109, 151, 202, 252, 248, 177, 136, 35,
234, 204, 188, 2, 72, 215, 207, 52,
22, 5, 193, 52, 228, 224, 157, 28 };
unsigned char iv[16] = { 239, 227, 132, 238, 4, 225, 127, 67,
202, 77, 31, 79, 203, 70, 149, 132 };
EVP_CIPHER_CTX *e = EVP_CIPHER_CTX_new();
if (NULL == e) {
return NULL;
}
EVP_CIPHER_CTX_init(e);
ret = EVP_DecryptInit_ex(e, EVP_aes_256_cbc(), NULL, key, iv);
if (0 == ret) {
EVP_CIPHER_CTX_free(e);
return NULL;
}
return e;
}
static char *aes_decrypt(unsigned char *cipher_text, int *len)
{
char *plain_text = NULL;
int plain_text_len = *len + EVP_CIPHER_block_size(EVP_aes_256_cbc());
int f_len = 0;
int ret = 0;
EVP_CIPHER_CTX *ctx = aes_ctx_new();
if (NULL == ctx) {
nlog_warn("alloc ctx fail");
return NULL;
}
plain_text = calloc(1, plain_text_len + 1);
if (NULL == plain_text) {
nlog_warn("alloc plain_text fail");
return NULL;
}
ret = EVP_DecryptUpdate(ctx, (unsigned char *) plain_text, &plain_text_len,
(unsigned char *) cipher_text, *len);
if (ret <= 0) {
free(plain_text);
EVP_CIPHER_CTX_free(ctx);
nlog_warn("decrypt update error: %d", ret);
return NULL;
}
ret = EVP_DecryptFinal_ex(
ctx, (unsigned char *) plain_text + plain_text_len, &f_len);
if (ret <= 0) {
free(plain_text);
EVP_CIPHER_CTX_free(ctx);
nlog_warn("decrypt final error: %d", ret);
return NULL;
}
*len = plain_text_len + f_len;
EVP_CIPHER_CTX_free(ctx);
return plain_text;
}
char *neu_decrypt_user_password(const char *crypted_password)
{
int len = 0;
char * password = NULL;
unsigned char *cipher_text = neu_decode64(&len, crypted_password);
if (cipher_text && (password = aes_decrypt(cipher_text, &len))) {
password[len] = '\0';
for (int i = 0; i < len; ++i) {
if ('\0' == password[i]) {
free(password);
password = NULL;
}
}
}
free(cipher_text);
return password;
}
neu_user_t *neu_user_new(const char *name, const char *password)
{
neu_user_t *user = malloc(sizeof(*user));
@@ -415,6 +598,11 @@ int neu_save_user(neu_user_t *user)
int neu_user_update_password(neu_user_t *user, const char *new_password)
{
int rv = checkout_password_strength(new_password);
if (0 != rv) {
return rv;
}
char *salt = user->hash + 3;
char *hash = hash_password(new_password, salt);
if (NULL == hash) {
+2
View File
@@ -41,6 +41,8 @@ int neu_user_update_password(neu_user_t *user, const char *new_password);
neu_user_t *neu_load_user(const char *name);
int neu_save_user(neu_user_t *user);
char *neu_decrypt_user_password(const char *crypted_password);
#ifdef __cplusplus
}
#endif
+1 -1
View File
@@ -99,7 +99,7 @@ static inline int reset_password()
neu_persist_user_info_t info = {
.name = "admin",
.hash =
"$5$PwFeXpBBIBZuZdZl$fP8fFPWCLoaWcnVXVSR.3Xi8TEqCvX92gjhowNNn6S4",
"$5$PwFeXpBBIBZuZdZl$MUEmeqpMfQA9fzQ3s9bojsBhZOHJwKHiukeWyutsSR6",
};
int rv = neu_persister_create(g_config_dir);
+6 -2
View File
@@ -68,10 +68,14 @@ int Base64Decode(const char *b64message, unsigned char **buffer, int *length)
BIO_set_flags(
bio, BIO_FLAGS_BASE64_NO_NL); // Do not use newlines to flush buffer
*length = BIO_read(bio, *buffer, strlen(b64message));
assert(*length == decodeLen); // length should equal decodeLen, else
// something went horribly wrong
BIO_free_all(bio);
if (*length != decodeLen) {
free(*buffer);
*buffer = NULL;
return -1;
}
return (0); // success
}
+3
View File
@@ -338,6 +338,9 @@ int neu_http_response(nng_aio *aio, neu_err_code_e code, char *content)
case NEU_ERR_GROUP_NAME_TOO_LONG:
case NEU_ERR_INVALID_PASSWORD_LEN:
case NEU_ERR_DUPLICATE_PASSWORD:
case NEU_ERR_WEAK_PASSWORD_ALPHABET:
case NEU_ERR_WEAK_PASSWORD_PATTERN:
case NEU_ERR_DECRYPT_PASSWORD_FAILED:
case NEU_ERR_TEMPLATE_NAME_TOO_LONG:
case NEU_ERR_PLUGIN_NAME_TOO_LONG:
case NEU_ERR_PLUGIN_NOT_SUPPORT_TEMPLATE: