From 1a5464d5ef1aacea42ec4b8c996051c72d1ffea9 Mon Sep 17 00:00:00 2001 From: TotaJ Date: Tue, 1 Jun 2021 20:24:52 +0800 Subject: [PATCH] Encrypt password for user mapping. --- src/common/port/cipher.cpp | 5 + .../cbb/extension/foreign/foreign.cpp | 44 +++++++++ src/gausskernel/cbb/utils/aes/cipherfn.cpp | 99 ++++++++++++++++--- .../optimizer/commands/datasourcecmds.cpp | 74 +------------- .../optimizer/commands/foreigncmds.cpp | 8 ++ src/include/catalog/pg_user_mapping.h | 3 + src/include/cipher.h | 3 +- src/include/datasource/datasource.h | 2 - src/include/utils/builtins.h | 7 +- 9 files changed, 155 insertions(+), 90 deletions(-) diff --git a/src/common/port/cipher.cpp b/src/common/port/cipher.cpp index 3f97c60f0..5042d424e 100644 --- a/src/common/port/cipher.cpp +++ b/src/common/port/cipher.cpp @@ -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 diff --git a/src/gausskernel/cbb/extension/foreign/foreign.cpp b/src/gausskernel/cbb/extension/foreign/foreign.cpp index 33a4b3a25..eb0b4c187 100644 --- a/src/gausskernel/cbb/extension/foreign/foreign.cpp +++ b/src/gausskernel/cbb/extension/foreign/foreign.cpp @@ -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; diff --git a/src/gausskernel/cbb/utils/aes/cipherfn.cpp b/src/gausskernel/cbb/utils/aes/cipherfn.cpp index f0a0301d5..b802b7515 100644 --- a/src/gausskernel/cbb/utils/aes/cipherfn.cpp +++ b/src/gausskernel/cbb/utils/aes/cipherfn.cpp @@ -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, diff --git a/src/gausskernel/optimizer/commands/datasourcecmds.cpp b/src/gausskernel/optimizer/commands/datasourcecmds.cpp index b182c218d..9eee28a6d 100644 --- a/src/gausskernel/optimizer/commands/datasourcecmds.cpp +++ b/src/gausskernel/optimizer/commands/datasourcecmds.cpp @@ -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); diff --git a/src/gausskernel/optimizer/commands/foreigncmds.cpp b/src/gausskernel/optimizer/commands/foreigncmds.cpp index 71140742b..cef4e223d 100644 --- a/src/gausskernel/optimizer/commands/foreigncmds.cpp +++ b/src/gausskernel/optimizer/commands/foreigncmds.cpp @@ -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); diff --git a/src/include/catalog/pg_user_mapping.h b/src/include/catalog/pg_user_mapping.h index f6f77fd54..43de7274e 100644 --- a/src/include/catalog/pg_user_mapping.h +++ b/src/include/catalog/pg_user_mapping.h @@ -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 */ diff --git a/src/include/cipher.h b/src/include/cipher.h index 858c11400..b2564ee2d 100644 --- a/src/include/cipher.h +++ b/src/include/cipher.h @@ -82,7 +82,8 @@ typedef enum { CLIENT_MODE, OBS_MODE, SOURCE_MODE, - GDS_MODE + GDS_MODE, + USER_MAPPING_MODE } KeyMode; typedef struct { diff --git a/src/include/datasource/datasource.h b/src/include/datasource/datasource.h index 7f02ec732..fc0ed2ef3 100644 --- a/src/include/datasource/datasource.h +++ b/src/include/datasource/datasource.h @@ -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 */ diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 9a271de9e..011f5b740 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -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);