Encrypt password for user mapping.

This commit is contained in:
TotaJ
2021-06-01 20:24:52 +08:00
parent 234e73ed59
commit 1a5464d5ef
9 changed files with 155 additions and 90 deletions

View File

@ -824,6 +824,11 @@ void decode_cipher_files(
securec_check_ss_c(ret, "\0", "\0");
ret = snprintf_s(randfile, MAXPGPATH, MAXPGPATH - 1, "%s/datasource%s", datadir, RAN_KEY_FILE);
securec_check_ss_c(ret, "\0", "\0");
} else if (mode == USER_MAPPING_MODE) {
ret = snprintf_s(cipherkeyfile, MAXPGPATH, MAXPGPATH - 1, "%s/usermapping%s", datadir, CIPHER_KEY_FILE);
securec_check_ss_c(ret, "\0", "\0");
ret = snprintf_s(randfile, MAXPGPATH, MAXPGPATH - 1, "%s/usermapping%s", datadir, RAN_KEY_FILE);
securec_check_ss_c(ret, "\0", "\0");
}
/*
* in client_mode,check with the user name is appointed.if so, read the files begin

View File

@ -344,6 +344,48 @@ bool isSpecifiedSrvTypeFromSrvName(const char* srvName, const char* SepcifiedTyp
return isSpecifiedSrvType;
}
static void DecryptOptions(List *options)
{
if (options == NULL) {
return;
}
ListCell *cell = NULL;
foreach(cell, options) {
DefElem* def = (DefElem*)lfirst(cell);
if (def->defname == NULL || def->arg == NULL || !IsA(def->arg, String)) {
continue;
}
char *str = strVal(def->arg);
if (str == NULL || strlen(str) == 0) {
continue;
}
for (int i = 0; i < sensitiveArrayLength; i++) {
if (pg_strcasecmp(def->defname, sensitiveOptionsArray[i]) == 0) {
char plainText[EC_CIPHER_TEXT_LENGTH] = {0};
/*
* If decryptECString return false, it means the stored values is not encrypted.
* This happened when user mapping was created in old gaussdb version.
*/
if (decryptECString(str, plainText, EC_CIPHER_TEXT_LENGTH, false)) {
pfree_ext(str);
pfree_ext(def->arg);
def->arg = (Node*)makeString(pstrdup(plainText));
/* Clear buffer */
errno_t errCode = memset_s(plainText, EC_CIPHER_TEXT_LENGTH, 0, EC_CIPHER_TEXT_LENGTH);
securec_check(errCode, "\0", "\0");
}
break;
}
}
}
}
/*
* GetUserMapping - look up the user mapping.
*
@ -381,6 +423,8 @@ UserMapping* GetUserMapping(Oid userid, Oid serverid)
um->options = untransformRelOptions(datum);
}
DecryptOptions(um->options);
ReleaseSysCache(tp);
return um;

View File

@ -42,10 +42,11 @@
#include "openssl/rand.h"
#include "openssl/evp.h"
#include "openssl/crypto.h"
#include "commands/defrem.h"
const char* pgname = "gs_encrypt";
void getOBSKeyString(GS_UCHAR** cipherKey);
static GS_UCHAR* getECKeyString(void);
static GS_UCHAR* getECKeyString(KeyMode mode);
char prefixCipherKey[2] = {'\0'};
char suffixCipherKey[100] = {'\0'};
bool gs_encrypt_aes_speed(GS_UCHAR* plaintext, GS_UCHAR* key, GS_UCHAR* ciphertext, GS_UINT32* cipherlen);
@ -804,12 +805,14 @@ bool gs_decrypt_aes_speed(
* @IN/OUT cipherKey: get key string and stored in cipherKey
* @RETURN: void
*/
static GS_UCHAR* getECKeyString(void)
static GS_UCHAR* getECKeyString(KeyMode mode)
{
Assert(mode == SOURCE_MODE || mode == USER_MAPPING_MODE);
GS_UCHAR* plainkey = NULL;
char* gshome = NULL;
char cipherdir[MAXPGPATH] = {0};
char cipherfile[MAXPGPATH] = {0};
const char *cipherPrefix = (mode == SOURCE_MODE ? "datasource" : "usermapping");
int ret = 0;
/*
@ -827,7 +830,7 @@ static GS_UCHAR* getECKeyString(void)
ret = snprintf_s(cipherdir, MAXPGPATH, MAXPGPATH - 1, "%s/bin", gshome);
securec_check_ss(ret, "\0", "\0");
ret = snprintf_s(cipherfile, MAXPGPATH, MAXPGPATH - 1, "%s/bin/datasource.key.cipher", gshome);
ret = snprintf_s(cipherfile, MAXPGPATH, MAXPGPATH - 1, "%s/bin/%s.key.cipher", gshome, cipherPrefix);
securec_check_ss_c(ret, "\0", "\0");
gshome = NULL;
@ -841,13 +844,13 @@ static GS_UCHAR* getECKeyString(void)
* Decode cipher file, if not given SOURCE cipher file, we use SERVER mode
*/
if (file_exists(cipherfile)) {
decode_cipher_files(SOURCE_MODE, NULL, cipherdir, plainkey);
decode_cipher_files(mode, NULL, cipherdir, plainkey);
} else {
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_FILE),
errmsg("No key file datasource.key.cipher"),
errhint("Please create datasource.key.cipher file with gs_guc and gs_ssh, such as : gs_ssh -c \"gs_guc generate -S XXX -D "
"$GAUSSHOME/bin -o datasource\"")));
errmsg("No key file %s.key.cipher", cipherPrefix),
errhint("Please create %s.key.cipher file with gs_guc and gs_ssh, such as : gs_ssh -c \"gs_guc generate -S XXX -D "
"$GAUSSHOME/bin -o %s\"", cipherPrefix, cipherPrefix)));
}
/*
* Note: plainkey is of length RANDOM_LEN + 1
@ -866,7 +869,7 @@ static GS_UCHAR* getECKeyString(void)
* @IN dest_cipher_length: dest buffer length which is given by the caller
* @RETURN: void
*/
void encryptECString(char* src_plain_text, char* dest_cipher_text, uint32 dest_cipher_length)
void encryptECString(char* src_plain_text, char* dest_cipher_text, uint32 dest_cipher_length, bool isDataSource)
{
GS_UINT32 ciphertextlen = 0;
GS_UCHAR ciphertext[1024];
@ -879,7 +882,7 @@ void encryptECString(char* src_plain_text, char* dest_cipher_text, uint32 dest_c
}
/* First, get encrypt key */
cipherkey = getECKeyString();
cipherkey = getECKeyString(isDataSource ? SOURCE_MODE : USER_MAPPING_MODE);
/* Clear cipher buffer which will be used later */
ret = memset_s(ciphertext, sizeof(ciphertext), 0, sizeof(ciphertext));
@ -962,9 +965,9 @@ void encryptECString(char* src_plain_text, char* dest_cipher_text, uint32 dest_c
* @OUT dest_plain_text: dest buffer to be filled with plain text, this buffer
* should be given by caller
* @IN dest_plain_length: dest buffer length which is given by the caller
* @RETURN: void
* @RETURN: bool, true if encrypt success, false if not
*/
void decryptECString(const char* src_cipher_text, char* dest_plain_text, uint32 dest_plain_length)
bool decryptECString(const char* src_cipher_text, char* dest_plain_text, uint32 dest_plain_length, bool isDataSource)
{
GS_UCHAR* ciphertext = NULL;
GS_UINT32 ciphertextlen = 0;
@ -974,11 +977,11 @@ void decryptECString(const char* src_cipher_text, char* dest_plain_text, uint32
errno_t ret = EOK;
if (NULL == src_cipher_text || !IsECEncryptedString(src_cipher_text)) {
return;
return false;
}
/* Get key string */
cipherkey = getECKeyString();
cipherkey = getECKeyString(isDataSource ? SOURCE_MODE : USER_MAPPING_MODE);
/* Step-1: Decode */
ciphertext = (GS_UCHAR*)(SEC_decodeBase64((char*)(src_cipher_text + strlen(EC_ENCRYPT_PREFIX)), &ciphertextlen));
@ -1036,6 +1039,7 @@ void decryptECString(const char* src_cipher_text, char* dest_plain_text, uint32
pfree_ext(cipherkey);
OPENSSL_free(ciphertext);
ciphertext = NULL;
return true;
}
/*
@ -1058,6 +1062,75 @@ bool IsECEncryptedString(const char* src_cipher_text)
}
}
/*
* EncryptGenericOptions:
* Encrypt data source or foreign data wrapper generic options, before transformation
*
* @IN src_options: source options to be encrypted
* @RETURN: void
*/
void EncryptGenericOptions(List* options, const char** sensitiveOptionsArray, int arrayLength, bool isDataSource)
{
int i;
char* srcString = NULL;
char encryptString[EC_CIPHER_TEXT_LENGTH];
errno_t ret;
ListCell* cell = NULL;
bool isPrint = false;
foreach (cell, options) {
DefElem* def = (DefElem*)lfirst(cell);
Node* arg = def->arg;
if (def->defaction == DEFELEM_DROP || def->arg == NULL || !IsA(def->arg, String))
continue;
/* Get src string to be encrypted */
srcString = strVal(def->arg);
/* For empty value, we do not encrypt */
if (srcString == NULL || strlen(srcString) == 0)
continue;
for (i = 0; i < arrayLength; i++) {
if (0 == pg_strcasecmp(def->defname, sensitiveOptionsArray[i])) {
/* For string with prefix='encryptOpt' (probably encrypted), we do not encrypt again */
if (IsECEncryptedString(srcString)) {
/* We report warning only once in a CREATE/ALTER stmt. */
if (!isPrint) {
ereport(NOTICE,
(errmodule(MOD_EC),
errcode(ERRCODE_INVALID_PASSWORD),
errmsg("Using probably encrypted option (prefix='encryptOpt') directly and it is not "
"recommended."),
errhint("The %s object can't be used if the option is not encrypted correctly.",
isDataSource ? "DATA SOURCE" : "USER MAPPING")));
isPrint = true;
}
break;
}
/* Encrypt the src string */
encryptECString(srcString, encryptString, EC_CIPHER_TEXT_LENGTH, isDataSource);
/* Substitute the src */
def->arg = (Node*)makeString(pstrdup(encryptString));
/* Clear the encrypted string */
ret = memset_s(encryptString, sizeof(encryptString), 0, sizeof(encryptString));
securec_check(ret, "\0", "\0");
/* Clear the src string */
ret = memset_s(srcString, strlen(srcString), 0, strlen(srcString));
securec_check(ret, "\0", "\0");
pfree_ext(srcString);
pfree_ext(arg);
break;
}
}
}
}
/*
* encryptBlockAndCU:
* encrypt block or CU exclude header and filling,user data must encrypt,if block or cu encrypt failed,

View File

@ -107,76 +107,6 @@ static void check_source_generic_options(const List* src_options)
}
}
/*
* encrypt_source_generic_options:
* Encrypt data source generic options, before transformation
*
* @IN src_options: source options to be encrypted
* @RETURN: void
*/
static void encrypt_source_generic_options(List* src_options)
{
int i;
int NumSensitiveOptions = sizeof(SensitiveOptionsArray) / sizeof(SensitiveOptionsArray[0]);
char* srcString = NULL;
char encryptString[EC_CIPHER_TEXT_LENGTH];
errno_t ret;
ListCell* cell = NULL;
bool isPrint = false;
foreach (cell, src_options) {
DefElem* def = (DefElem*)lfirst(cell);
Node* arg = def->arg;
if (def->defaction == DEFELEM_DROP || def->arg == NULL)
continue;
/* Get src string to be encrypted */
srcString = defGetString(def);
/* For empty value, we do not encrypt */
if (srcString == NULL || strlen(srcString) == 0)
continue;
for (i = 0; i < NumSensitiveOptions; i++) {
if (0 == pg_strcasecmp(def->defname, SensitiveOptionsArray[i])) {
/* For string with prefix='encryptOpt' (probably encrypted), we do not encrypt again */
if (IsECEncryptedString(srcString)) {
/* We report warning only once in a CREATE/ALTER stmt. */
if (!isPrint) {
ereport(NOTICE,
(errmodule(MOD_EC),
errcode(ERRCODE_INVALID_PASSWORD),
errmsg("Using probably encrypted option (prefix='encryptOpt') directly and it is not "
"recommended."),
errhint(
"The DATA SOURCE object can't be used if the option is not encrypted correctly.")));
isPrint = true;
}
break;
}
/* Encrypt the src string */
encryptECString(srcString, encryptString, EC_CIPHER_TEXT_LENGTH);
/* Substitute the src */
def->arg = (Node*)makeString(pstrdup(encryptString));
/* Clear the encrypted string */
ret = memset_s(encryptString, sizeof(encryptString), 0, sizeof(encryptString));
securec_check(ret, "\0", "\0");
/* Clear the src string */
ret = memset_s(srcString, strlen(srcString), 0, strlen(srcString));
securec_check(ret, "\0", "\0");
pfree_ext(srcString);
pfree_ext(arg);
break;
}
}
}
}
/*
* CreateDataSource:
* Create a Data Source, only allowed by superuser!
@ -249,7 +179,7 @@ void CreateDataSource(CreateDataSourceStmt* stmt)
check_source_generic_options(stmt->options);
/* Encrypt sensitive options before any operations */
encrypt_source_generic_options(stmt->options);
EncryptGenericOptions(stmt->options, SensitiveOptionsArray, lengthof(SensitiveOptionsArray), true);
/* Add source options */
srcoptions = transformGenericOptions(DataSourceRelationId, PointerGetDatum(NULL), stmt->options, InvalidOid);
@ -361,7 +291,7 @@ void AlterDataSource(AlterDataSourceStmt* stmt)
check_source_generic_options((const List*)stmt->options);
/* Encrypt sensitive options before any operations */
encrypt_source_generic_options(stmt->options);
EncryptGenericOptions(stmt->options, SensitiveOptionsArray, lengthof(SensitiveOptionsArray), true);
/* Prepare the options array */
datum = transformGenericOptions(DataSourceRelationId, datum, stmt->options, InvalidOid);

View File

@ -53,6 +53,10 @@
#include "catalog/toasting.h"
#include "bulkload/dist_fdw.h"
/* Sensitive options for user mapping, will be encrypted when saved to catalog. */
const char* sensitiveOptionsArray[] = {"password"};
const int sensitiveArrayLength = lengthof(sensitiveOptionsArray);
/*
* Convert a DefElem list to the text array format that is used in
* pg_foreign_data_wrapper, pg_foreign_server, and pg_user_mapping.
@ -1153,6 +1157,8 @@ void CreateUserMapping(CreateUserMappingStmt* stmt)
values[Anum_pg_user_mapping_umuser - 1] = ObjectIdGetDatum(useId);
values[Anum_pg_user_mapping_umserver - 1] = ObjectIdGetDatum(srv->serverid);
EncryptGenericOptions(stmt->options, sensitiveOptionsArray, sensitiveArrayLength, false);
/* Add user options */
useoptions =
transformGenericOptions(UserMappingRelationId, PointerGetDatum(NULL), stmt->options, fdw->fdwvalidator);
@ -1248,6 +1254,8 @@ void AlterUserMapping(AlterUserMappingStmt* stmt)
if (isnull)
datum = PointerGetDatum(NULL);
EncryptGenericOptions(stmt->options, sensitiveOptionsArray, sensitiveArrayLength, false);
/* Prepare the options array */
datum = transformGenericOptions(UserMappingRelationId, datum, stmt->options, fdw->fdwvalidator);

View File

@ -54,5 +54,8 @@ typedef FormData_pg_user_mapping *Form_pg_user_mapping;
#define Anum_pg_user_mapping_umserver 2
#define Anum_pg_user_mapping_umoptions 3
extern const char* sensitiveOptionsArray[];
extern const int sensitiveArrayLength;
#endif /* PG_USER_MAPPING_H */

View File

@ -82,7 +82,8 @@ typedef enum {
CLIENT_MODE,
OBS_MODE,
SOURCE_MODE,
GDS_MODE
GDS_MODE,
USER_MAPPING_MODE
} KeyMode;
typedef struct {

View File

@ -40,6 +40,4 @@ extern Oid get_data_source_oid(const char* sourcename, bool missing_ok);
extern DataSource* GetDataSource(Oid sourceid);
extern DataSource* GetDataSourceByName(const char* sourcename, bool missing_ok);
#define EC_CIPHER_TEXT_LENGTH 1024
#endif /* DATASOURCE_H */

View File

@ -1513,9 +1513,12 @@ extern Datum text_timestamp(PG_FUNCTION_ARGS);
extern void encryptOBS(char* srcplaintext, char destciphertext[], uint32 destcipherlength);
extern void decryptOBS(
const char* srcciphertext, char destplaintext[], uint32 destplainlength, const char* obskey = NULL);
extern void encryptECString(char* src_plain_text, char* dest_cipher_text, uint32 dest_cipher_length);
extern void decryptECString(const char* src_cipher_text, char* dest_plain_text, uint32 dest_plain_length);
extern void encryptECString(char* src_plain_text, char* dest_cipher_text, uint32 dest_cipher_length, bool isDataSource);
extern bool decryptECString(const char* src_cipher_text, char* dest_plain_text, uint32 dest_plain_length, bool isDataSource = true);
extern bool IsECEncryptedString(const char* src_cipher_text);
extern void EncryptGenericOptions(List* options, const char** sensitiveOptionsArray, int arrayLength, bool isDataSource);
#define EC_CIPHER_TEXT_LENGTH 1024
/* fencedudf.cpp */
extern Datum fenced_udf_process(PG_FUNCTION_ARGS);