mirror of https://github.com/postgres/postgres
commit
a21bfac9b6
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,248 @@ |
||||
/*-------------------------------------------------------------------------
|
||||
* |
||||
* tde_global_catalog.c |
||||
* Global catalog key management |
||||
* |
||||
* |
||||
* IDENTIFICATION |
||||
* src/catalog/tde_global_catalog.c |
||||
* |
||||
*------------------------------------------------------------------------- |
||||
*/ |
||||
|
||||
#include "postgres.h" |
||||
|
||||
#ifdef PERCONA_FORK |
||||
|
||||
#include "storage/shmem.h" |
||||
#include "utils/guc.h" |
||||
|
||||
#include "access/pg_tde_tdemap.h" |
||||
#include "catalog/tde_global_catalog.h" |
||||
#include "catalog/tde_keyring.h" |
||||
#include "catalog/tde_master_key.h" |
||||
|
||||
#include <openssl/rand.h> |
||||
#include <openssl/err.h> |
||||
#include <sys/time.h> |
||||
|
||||
#define MASTER_KEY_DEFAULT_NAME "tde-global-catalog-key" |
||||
|
||||
/* TODO: not sure if we need an option of multiple master keys for the global catalog */ |
||||
typedef enum |
||||
{ |
||||
TDE_GCAT_XLOG_KEY, |
||||
|
||||
/* must be last */ |
||||
TDE_GCAT_KEYS_COUNT |
||||
} GlobalCatalogKeyTypes; |
||||
|
||||
typedef struct EncryptionStateData |
||||
{ |
||||
GenericKeyring *keyring; |
||||
TDEMasterKey master_keys[TDE_GCAT_KEYS_COUNT]; |
||||
} EncryptionStateData; |
||||
|
||||
static EncryptionStateData * EncryptionState = NULL; |
||||
|
||||
/* GUC */ |
||||
static char *KRingProviderType = NULL; |
||||
static char *KRingProviderFilePath = NULL; |
||||
|
||||
static void init_gl_catalog_keys(void); |
||||
static void init_keyring(void); |
||||
static TDEMasterKey * create_master_key(const char *key_name, |
||||
GenericKeyring * keyring, Oid dbOid, Oid spcOid, |
||||
bool ensure_new_key); |
||||
|
||||
void |
||||
TDEGlCatInitGUC(void) |
||||
{ |
||||
DefineCustomStringVariable("pg_tde.global_keyring_type", |
||||
"Keyring type for global catalog", |
||||
NULL, |
||||
&KRingProviderType, |
||||
NULL, |
||||
PGC_POSTMASTER, |
||||
0, /* no flags required */ |
||||
NULL, |
||||
NULL, |
||||
NULL |
||||
); |
||||
DefineCustomStringVariable("pg_tde.global_keyring_file_path", |
||||
"Keyring file options for global catalog", |
||||
NULL, |
||||
&KRingProviderFilePath, |
||||
NULL, |
||||
PGC_POSTMASTER, |
||||
0, /* no flags required */ |
||||
NULL, |
||||
NULL, |
||||
NULL |
||||
); |
||||
} |
||||
|
||||
|
||||
Size |
||||
TDEGlCatEncStateSize(void) |
||||
{ |
||||
Size size; |
||||
|
||||
size = sizeof(EncryptionStateData); |
||||
size = add_size(size, sizeof(KeyringProviders)); |
||||
|
||||
return MAXALIGN(size); |
||||
} |
||||
|
||||
void |
||||
TDEGlCatShmemInit(void) |
||||
{ |
||||
bool foundBuf; |
||||
char *allocptr; |
||||
|
||||
EncryptionState = (EncryptionStateData *) |
||||
ShmemInitStruct("TDE XLog Encryption State", |
||||
TDEGlCatEncStateSize(), &foundBuf); |
||||
|
||||
allocptr = ((char *) EncryptionState) + MAXALIGN(sizeof(EncryptionStateData)); |
||||
EncryptionState->keyring = (GenericKeyring *) allocptr; |
||||
memset(EncryptionState->keyring, 0, sizeof(KeyringProviders)); |
||||
memset(EncryptionState->master_keys, 0, sizeof(TDEMasterKey) * TDE_GCAT_KEYS_COUNT); |
||||
} |
||||
|
||||
void |
||||
TDEGlCatKeyInit(void) |
||||
{ |
||||
char db_map_path[MAXPGPATH] = {0}; |
||||
|
||||
init_keyring(); |
||||
|
||||
pg_tde_set_db_file_paths(&GLOBAL_SPACE_RLOCATOR(XLOG_TDE_OID), |
||||
db_map_path, NULL); |
||||
if (access(db_map_path, F_OK) == -1) |
||||
{ |
||||
init_gl_catalog_keys(); |
||||
} |
||||
else |
||||
{ |
||||
/* put an internal key into the cache */ |
||||
GetGlCatInternalKey(XLOG_TDE_OID); |
||||
} |
||||
} |
||||
|
||||
TDEMasterKey * |
||||
TDEGetGlCatKeyFromCache(void) |
||||
{ |
||||
TDEMasterKey *mkey; |
||||
|
||||
mkey = &EncryptionState->master_keys[TDE_GCAT_XLOG_KEY]; |
||||
if (mkey->keyLength == 0) |
||||
return NULL; |
||||
|
||||
return mkey; |
||||
} |
||||
|
||||
void |
||||
TDEPutGlCatKeyInCache(TDEMasterKey * mkey) |
||||
{ |
||||
memcpy(EncryptionState->master_keys + TDE_GCAT_XLOG_KEY, mkey, sizeof(TDEMasterKey)); |
||||
} |
||||
|
||||
RelKeyData * |
||||
GetGlCatInternalKey(Oid obj_id) |
||||
{ |
||||
return GetRelationKeyWithKeyring(GLOBAL_SPACE_RLOCATOR(obj_id), EncryptionState->keyring); |
||||
} |
||||
|
||||
/*
|
||||
* TODO: should be aligned with the rest of the keyring_provider code after its |
||||
* refactoring |
||||
* |
||||
* TODO: add Vault
|
||||
*/ |
||||
static void |
||||
init_keyring(void) |
||||
{ |
||||
EncryptionState->keyring->type = get_keyring_provider_from_typename(KRingProviderType); |
||||
switch (EncryptionState->keyring->type) |
||||
{ |
||||
case FILE_KEY_PROVIDER: |
||||
FileKeyring * kring = (FileKeyring *) EncryptionState->keyring; |
||||
strncpy(kring->file_name, KRingProviderFilePath, sizeof(kring->file_name)); |
||||
break; |
||||
} |
||||
} |
||||
|
||||
/*
|
||||
* Keys are created during the cluster start only, so no locks needed here. |
||||
*/ |
||||
static void |
||||
init_gl_catalog_keys(void) |
||||
{ |
||||
InternalKey int_key; |
||||
RelKeyData *rel_key_data; |
||||
RelKeyData *enc_rel_key_data; |
||||
RelFileLocator *rlocator; |
||||
TDEMasterKey *mkey; |
||||
|
||||
mkey = create_master_key(MASTER_KEY_DEFAULT_NAME, |
||||
EncryptionState->keyring, |
||||
GLOBAL_DATA_TDE_OID, GLOBALTABLESPACE_OID, false); |
||||
|
||||
memset(&int_key, 0, sizeof(InternalKey)); |
||||
|
||||
/* Create and store an internal key for XLog */ |
||||
if (!RAND_bytes(int_key.key, INTERNAL_KEY_LEN)) |
||||
{ |
||||
ereport(FATAL, |
||||
(errcode(ERRCODE_INTERNAL_ERROR), |
||||
errmsg("could not generate internal key for \"WAL\": %s", |
||||
ERR_error_string(ERR_get_error(), NULL)))); |
||||
} |
||||
|
||||
rlocator = &GLOBAL_SPACE_RLOCATOR(XLOG_TDE_OID); |
||||
rel_key_data = tde_create_rel_key(rlocator->relNumber, &int_key, &mkey->keyInfo); |
||||
enc_rel_key_data = tde_encrypt_rel_key(mkey, rel_key_data, rlocator); |
||||
pg_tde_write_key_map_entry(rlocator, enc_rel_key_data, &mkey->keyInfo); |
||||
|
||||
/*
|
||||
* TODO: move global catalog internal keys into own cache. This cache should |
||||
* be in the TopMemmoryContext because of SSL contexts |
||||
* (see https://github.com/Percona-Lab/pg_tde/pull/214#discussion_r1648998317)
|
||||
*/ |
||||
pg_tde_put_key_into_map(rlocator->relNumber, rel_key_data); |
||||
TDEPutGlCatKeyInCache(mkey); |
||||
} |
||||
|
||||
static TDEMasterKey * |
||||
create_master_key(const char *key_name, GenericKeyring * keyring, |
||||
Oid dbOid, Oid spcOid, bool ensure_new_key) |
||||
{ |
||||
TDEMasterKey *masterKey; |
||||
keyInfo *keyInfo = NULL; |
||||
|
||||
masterKey = palloc(sizeof(TDEMasterKey)); |
||||
masterKey->keyInfo.databaseId = dbOid; |
||||
masterKey->keyInfo.tablespaceId = spcOid; |
||||
masterKey->keyInfo.keyId.version = DEFAULT_MASTER_KEY_VERSION; |
||||
masterKey->keyInfo.keyringId = keyring->key_id; |
||||
strncpy(masterKey->keyInfo.keyId.name, key_name, TDE_KEY_NAME_LEN); |
||||
gettimeofday(&masterKey->keyInfo.creationTime, NULL); |
||||
|
||||
keyInfo = load_latest_versioned_key_name(&masterKey->keyInfo, keyring, ensure_new_key); |
||||
|
||||
if (keyInfo == NULL) |
||||
keyInfo = KeyringGenerateNewKeyAndStore(keyring, masterKey->keyInfo.keyId.versioned_name, INTERNAL_KEY_LEN, false); |
||||
|
||||
if (keyInfo == NULL) |
||||
{ |
||||
ereport(ERROR, |
||||
(errmsg("failed to retrieve master key"))); |
||||
} |
||||
|
||||
masterKey->keyLength = keyInfo->data.len; |
||||
memcpy(masterKey->keyData, keyInfo->data.data, keyInfo->data.len); |
||||
|
||||
return masterKey; |
||||
} |
||||
#endif /* PERCONA_FORK */ |
@ -0,0 +1,41 @@ |
||||
/*-------------------------------------------------------------------------
|
||||
* |
||||
* tde_global_catalog.h |
||||
* Global catalog key management |
||||
* |
||||
* src/include/catalog/tde_global_catalog.h |
||||
* |
||||
*------------------------------------------------------------------------- |
||||
*/ |
||||
|
||||
#ifndef TDE_GLOBAL_CATALOG_H |
||||
#define TDE_GLOBAL_CATALOG_H |
||||
|
||||
#include "postgres.h" |
||||
|
||||
#include "catalog/tde_master_key.h" |
||||
|
||||
/*
|
||||
* Needed for glogbal data (WAL etc) keys identification in caches and storage. |
||||
* We take IDs the oid type operators, so there is no overlap with the "real" |
||||
* catalog object possible. |
||||
*/ |
||||
#define GLOBAL_DATA_TDE_OID 607 /* Global objects fake "db" */ |
||||
#define XLOG_TDE_OID 608 |
||||
|
||||
#define GLOBAL_SPACE_RLOCATOR(_obj_oid) (RelFileLocator) { \ |
||||
GLOBALTABLESPACE_OID, \
|
||||
GLOBAL_DATA_TDE_OID, \
|
||||
_obj_oid \
|
||||
} |
||||
|
||||
extern void TDEGlCatInitGUC(void); |
||||
extern Size TDEGlCatEncStateSize(void); |
||||
extern void TDEGlCatShmemInit(void); |
||||
extern void TDEGlCatKeyInit(void); |
||||
|
||||
extern TDEMasterKey *TDEGetGlCatKeyFromCache(void); |
||||
extern void TDEPutGlCatKeyInCache(TDEMasterKey *mkey); |
||||
extern RelKeyData *GetGlCatInternalKey(Oid obj_id); |
||||
|
||||
#endif /*TDE_GLOBAL_CATALOG_H*/ |
@ -0,0 +1,33 @@ |
||||
/*-------------------------------------------------------------------------
|
||||
* |
||||
* pg_tde_event_capture.h |
||||
* |
||||
*------------------------------------------------------------------------- |
||||
*/ |
||||
#ifndef PG_TDE_EVENT_CAPTURE_H |
||||
#define PG_TDE_EVENT_CAPTURE_H |
||||
|
||||
#include "postgres.h" |
||||
#include "nodes/parsenodes.h" |
||||
|
||||
typedef enum TdeCreateEventType |
||||
{ |
||||
TDE_UNKNOWN_CREATE_EVENT, |
||||
TDE_TABLE_CREATE_EVENT, |
||||
TDE_INDEX_CREATE_EVENT |
||||
} TdeCreateEventType; |
||||
|
||||
typedef struct TdeCreateEvent |
||||
{ |
||||
TdeCreateEventType eventType; /* DDL statement type */ |
||||
bool encryptMode; /* true when the table uses encryption */ |
||||
Oid baseTableOid; /* Oid of table on which index is being
|
||||
* created on. For create table statement this |
||||
* contains InvalidOid */ |
||||
RangeVar *relation; /* Reference to the parsed relation from
|
||||
* create statement */ |
||||
} TdeCreateEvent; |
||||
|
||||
extern TdeCreateEvent * GetCurrentTdeCreateEvent(void); |
||||
|
||||
#endif |
@ -0,0 +1,4 @@ |
||||
|
||||
#pragma once |
||||
|
||||
extern void RegisterStorageMgr(); |
Binary file not shown.
@ -0,0 +1,147 @@ |
||||
/*-------------------------------------------------------------------------
|
||||
* |
||||
* pg_tde_event_capture.c |
||||
* event trigger logic to identify if we are creating the encrypted table or not. |
||||
* |
||||
* IDENTIFICATION |
||||
* contrib/pg_tde/src/pg_tde_event_trigger.c |
||||
* |
||||
*------------------------------------------------------------------------- |
||||
*/ |
||||
|
||||
#include "postgres.h" |
||||
#include "funcapi.h" |
||||
#include "fmgr.h" |
||||
#include "utils/rel.h" |
||||
#include "utils/builtins.h" |
||||
#include "catalog/pg_class.h" |
||||
#include "access/table.h" |
||||
#include "catalog/pg_event_trigger.h" |
||||
#include "catalog/namespace.h" |
||||
#include "commands/event_trigger.h" |
||||
#include "common/pg_tde_utils.h" |
||||
#include "pg_tde_event_capture.h" |
||||
|
||||
/* Global variable that gets set at ddl start and cleard out at ddl end*/ |
||||
TdeCreateEvent tdeCurrentCreateEvent = {.relation = NULL}; |
||||
|
||||
|
||||
static void reset_current_tde_create_event(void); |
||||
|
||||
PG_FUNCTION_INFO_V1(pg_tde_ddl_command_start_capture); |
||||
PG_FUNCTION_INFO_V1(pg_tde_ddl_command_end_capture); |
||||
|
||||
TdeCreateEvent * |
||||
GetCurrentTdeCreateEvent(void) |
||||
{ |
||||
return &tdeCurrentCreateEvent; |
||||
} |
||||
|
||||
/*
|
||||
* pg_tde_ddl_command_start_capture is an event trigger function triggered |
||||
* at the start of any DDL command execution. |
||||
* |
||||
* The function specifically focuses on CREATE INDEX and CREATE TABLE statements, |
||||
* aiming to determine if the create table or the table on which an index is being created |
||||
* utilizes the pg_tde access method for encryption. |
||||
* Once it confirms the table's encryption requirement or usage, |
||||
* it updates the table information in the tdeCurrentCreateEvent global variable. |
||||
* This information can be accessed by SMGR or any other component |
||||
* during the execution of this DDL statement. |
||||
*/ |
||||
Datum |
||||
pg_tde_ddl_command_start_capture(PG_FUNCTION_ARGS) |
||||
{ |
||||
/* TODO: verify update_compare_indexes failure related to this */ |
||||
#ifdef PERCONA_FORK |
||||
EventTriggerData *trigdata; |
||||
Node *parsetree; |
||||
|
||||
/* Ensure this function is being called as an event trigger */ |
||||
if (!CALLED_AS_EVENT_TRIGGER(fcinfo)) /* internal error */ |
||||
ereport(ERROR, |
||||
(errmsg("Function can only be fired by event trigger manager"))); |
||||
|
||||
trigdata = (EventTriggerData *) fcinfo->context; |
||||
parsetree = trigdata->parsetree; |
||||
|
||||
elog(DEBUG2, "EVENT TRIGGER (%s) %s", trigdata->event, nodeToString(parsetree)); |
||||
reset_current_tde_create_event(); |
||||
|
||||
if (IsA(parsetree, IndexStmt)) |
||||
{ |
||||
IndexStmt *stmt = (IndexStmt *) parsetree; |
||||
Oid relationId = RangeVarGetRelid(stmt->relation, NoLock, true); |
||||
|
||||
tdeCurrentCreateEvent.eventType = TDE_INDEX_CREATE_EVENT; |
||||
tdeCurrentCreateEvent.baseTableOid = relationId; |
||||
tdeCurrentCreateEvent.relation = stmt->relation; |
||||
|
||||
if (relationId != InvalidOid) |
||||
{ |
||||
LOCKMODE lockmode = AccessShareLock; /* TODO. Verify lock mode? */ |
||||
Relation rel = table_open(relationId, lockmode); |
||||
|
||||
if (rel->rd_rel->relam == get_tde_table_am_oid()) |
||||
{ |
||||
/* We are creating the index on encrypted table */ |
||||
/* set the global state */ |
||||
tdeCurrentCreateEvent.encryptMode = true; |
||||
} |
||||
else |
||||
table_close(rel, lockmode); |
||||
} |
||||
else |
||||
ereport(DEBUG1, (errmsg("Failed to get relation Oid for relation:%s", stmt->relation->relname))); |
||||
|
||||
} |
||||
else if (IsA(parsetree, CreateStmt)) |
||||
{ |
||||
CreateStmt *stmt = (CreateStmt *) parsetree; |
||||
|
||||
tdeCurrentCreateEvent.eventType = TDE_TABLE_CREATE_EVENT; |
||||
tdeCurrentCreateEvent.relation = stmt->relation; |
||||
|
||||
if (stmt->accessMethod && !strcmp(stmt->accessMethod, "pg_tde2")) |
||||
{ |
||||
tdeCurrentCreateEvent.encryptMode = true; |
||||
} |
||||
} |
||||
#endif |
||||
PG_RETURN_NULL(); |
||||
} |
||||
|
||||
/*
|
||||
* trigger function called at the end of DDL statement execution. |
||||
* It just clears the tdeCurrentCreateEvent global variable. |
||||
*/ |
||||
Datum |
||||
pg_tde_ddl_command_end_capture(PG_FUNCTION_ARGS) |
||||
{ |
||||
#ifdef PERCONA_FORK |
||||
/* Ensure this function is being called as an event trigger */ |
||||
if (!CALLED_AS_EVENT_TRIGGER(fcinfo)) /* internal error */ |
||||
ereport(ERROR, |
||||
(errmsg("Function can only be fired by event trigger manager"))); |
||||
|
||||
elog(DEBUG1, "Type:%s EncryptMode:%s, Oid:%d, Relation:%s ", |
||||
(tdeCurrentCreateEvent.eventType == TDE_INDEX_CREATE_EVENT) ? "CREATE INDEX" : |
||||
(tdeCurrentCreateEvent.eventType == TDE_TABLE_CREATE_EVENT) ? "CREATE TABLE" : "UNKNOWN", |
||||
tdeCurrentCreateEvent.encryptMode ? "true" : "false", |
||||
tdeCurrentCreateEvent.baseTableOid, |
||||
tdeCurrentCreateEvent.relation ? tdeCurrentCreateEvent.relation->relname : "UNKNOWN"); |
||||
|
||||
/* All we need to do is to clear the event state */ |
||||
reset_current_tde_create_event(); |
||||
#endif |
||||
PG_RETURN_NULL(); |
||||
} |
||||
|
||||
static void |
||||
reset_current_tde_create_event(void) |
||||
{ |
||||
tdeCurrentCreateEvent.encryptMode = false; |
||||
tdeCurrentCreateEvent.eventType = TDE_UNKNOWN_CREATE_EVENT; |
||||
tdeCurrentCreateEvent.baseTableOid = InvalidOid; |
||||
tdeCurrentCreateEvent.relation = NULL; |
||||
} |
@ -0,0 +1,213 @@ |
||||
|
||||
#include "smgr/pg_tde_smgr.h" |
||||
#include "postgres.h" |
||||
#include "storage/smgr.h" |
||||
#include "storage/md.h" |
||||
#include "catalog/catalog.h" |
||||
#include "encryption/enc_aes.h" |
||||
#include "access/pg_tde_tdemap.h" |
||||
#include "pg_tde_event_capture.h" |
||||
|
||||
#ifdef PERCONA_FORK |
||||
|
||||
// TODO: implement proper IV
|
||||
// iv should be based on blocknum + relfile, available in the API
|
||||
static char iv[16] = {0,}; |
||||
|
||||
static RelKeyData* |
||||
tde_smgr_get_key(SMgrRelation reln) |
||||
{ |
||||
// TODO: This recursion counter is a dirty hack until the metadata is in the catalog
|
||||
// As otherwise we would call GetMasterKey recursively and deadlock
|
||||
static int recursion = 0; |
||||
|
||||
if(IsCatalogRelationOid(reln->smgr_rlocator.locator.relNumber)) |
||||
{ |
||||
// do not try to encrypt/decrypt catalog tables
|
||||
return NULL; |
||||
} |
||||
|
||||
if(recursion != 0)
|
||||
{ |
||||
return NULL; |
||||
} |
||||
|
||||
recursion++; |
||||
|
||||
|
||||
if(GetMasterKey(reln->smgr_rlocator.locator.relNumber, reln->smgr_rlocator.locator.spcOid, NULL)==NULL) |
||||
{ |
||||
recursion--; |
||||
return NULL; |
||||
} |
||||
|
||||
TdeCreateEvent* event = GetCurrentTdeCreateEvent(); |
||||
|
||||
// if this is a CREATE TABLE, we have to generate the key
|
||||
if(event->encryptMode == true && event->eventType == TDE_TABLE_CREATE_EVENT) |
||||
{ |
||||
recursion--; |
||||
return pg_tde_create_key_map_entry(&reln->smgr_rlocator.locator); |
||||
} |
||||
|
||||
// if this is a CREATE INDEX, we have to load the key based on the table
|
||||
if(event->encryptMode == true && event->eventType == TDE_INDEX_CREATE_EVENT) |
||||
{ |
||||
// For now keep it simple and create separate key for indexes
|
||||
// Later we might modify the map infrastructure to support the same keys
|
||||
recursion--; |
||||
return pg_tde_create_key_map_entry(&reln->smgr_rlocator.locator); |
||||
} |
||||
|
||||
// otherwise, see if we have a key for the relation, and return if yes
|
||||
RelKeyData* rkd = GetRelationKey(reln->smgr_rlocator.locator); |
||||
|
||||
recursion--; |
||||
|
||||
return rkd; |
||||
} |
||||
|
||||
void |
||||
tde_mdwritev(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum, |
||||
const void **buffers, BlockNumber nblocks, bool skipFsync) |
||||
{ |
||||
AesInit(); |
||||
|
||||
char* local_blocks = malloc( BLCKSZ * (nblocks+1) ); |
||||
char* local_blocks_aligned = (char*)TYPEALIGN(PG_IO_ALIGN_SIZE, local_blocks); |
||||
const void** local_buffers = malloc ( sizeof(void*) * nblocks ); |
||||
|
||||
RelKeyData* rkd = tde_smgr_get_key(reln); |
||||
|
||||
if(rkd == NULL) |
||||
{ |
||||
mdwritev(reln, forknum, blocknum, buffers, nblocks, skipFsync); |
||||
|
||||
return; |
||||
} |
||||
|
||||
for(int i = 0; i < nblocks; ++i ) |
||||
{ |
||||
local_buffers[i] = &local_blocks_aligned[i*BLCKSZ];
|
||||
int out_len = BLCKSZ; |
||||
AesEncrypt(rkd->internal_key.key, iv, ((char**)buffers)[i], BLCKSZ, local_buffers[i], &out_len); |
||||
} |
||||
|
||||
mdwritev(reln, forknum, blocknum, |
||||
local_buffers, nblocks, skipFsync); |
||||
|
||||
free(local_blocks); |
||||
free(local_buffers); |
||||
} |
||||
|
||||
void |
||||
tde_mdextend(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum, |
||||
const void *buffer, bool skipFsync) |
||||
{ |
||||
AesInit(); |
||||
|
||||
char* local_blocks = malloc( BLCKSZ * (1+1) ); |
||||
char* local_blocks_aligned = (char*)TYPEALIGN(PG_IO_ALIGN_SIZE, local_blocks); |
||||
|
||||
RelKeyData* rkd = tde_smgr_get_key(reln); |
||||
|
||||
if(rkd == NULL) |
||||
{ |
||||
mdextend(reln, forknum, blocknum, buffer, skipFsync); |
||||
|
||||
return; |
||||
} |
||||
|
||||
int out_len = BLCKSZ; |
||||
AesEncrypt(rkd->internal_key.key, iv, ((char*)buffer), BLCKSZ, local_blocks_aligned, &out_len); |
||||
|
||||
mdextend(reln, forknum, blocknum, local_blocks_aligned, skipFsync); |
||||
|
||||
|
||||
free(local_blocks); |
||||
} |
||||
|
||||
void |
||||
tde_mdreadv(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum, |
||||
void **buffers, BlockNumber nblocks) |
||||
{ |
||||
AesInit(); |
||||
|
||||
mdreadv(reln, forknum, blocknum, buffers, nblocks); |
||||
|
||||
RelKeyData* rkd = tde_smgr_get_key(reln); |
||||
|
||||
if(rkd == NULL) |
||||
{ |
||||
return; |
||||
} |
||||
|
||||
for(int i = 0; i < nblocks; ++i) |
||||
{ |
||||
bool allZero = true; |
||||
for(int j = 0; j < 32; ++j) |
||||
{ |
||||
if(((char**)buffers)[i][j] != 0) |
||||
{ |
||||
// Postgres creates all zero blocks in an optimized route, which we do not try
|
||||
// to encrypt.
|
||||
// Instead we detect if a block is all zero at decryption time, and
|
||||
// leave it as is.
|
||||
// This could be a security issue later, but it is a good first prototype
|
||||
allZero = false; |
||||
break; |
||||
} |
||||
} |
||||
if(allZero) continue; |
||||
|
||||
int out_len = BLCKSZ; |
||||
AesDecrypt(rkd->internal_key.key, iv, ((char**)buffers)[i], BLCKSZ, ((char**)buffers)[i], &out_len); |
||||
} |
||||
} |
||||
|
||||
void |
||||
tde_mdcreate(SMgrRelation reln, ForkNumber forknum, bool isRedo) |
||||
{ |
||||
// This is the only function that gets called during actual CREATE TABLE/INDEX (EVENT TRIGGER)
|
||||
// so we create the key here by loading it
|
||||
// Later calls then decide to encrypt or not based on the existence of the key
|
||||
tde_smgr_get_key(reln); |
||||
|
||||
return mdcreate(reln, forknum, isRedo); |
||||
} |
||||
|
||||
|
||||
static SMgrId tde_smgr_id; |
||||
static const struct f_smgr tde_smgr = { |
||||
.name = "tde", |
||||
.smgr_init = mdinit, |
||||
.smgr_shutdown = NULL, |
||||
.smgr_open = mdopen, |
||||
.smgr_close = mdclose, |
||||
.smgr_create = tde_mdcreate, |
||||
.smgr_exists = mdexists, |
||||
.smgr_unlink = mdunlink, |
||||
.smgr_extend = tde_mdextend, |
||||
.smgr_zeroextend = mdzeroextend, |
||||
.smgr_prefetch = mdprefetch, |
||||
.smgr_readv = tde_mdreadv, |
||||
.smgr_writev = tde_mdwritev, |
||||
.smgr_writeback = mdwriteback, |
||||
.smgr_nblocks = mdnblocks, |
||||
.smgr_truncate = mdtruncate, |
||||
.smgr_immedsync = mdimmedsync, |
||||
}; |
||||
|
||||
void RegisterStorageMgr() |
||||
{ |
||||
tde_smgr_id = smgr_register(&tde_smgr, 0); |
||||
|
||||
// TODO: figure out how this part should work in a real extension
|
||||
storage_manager_id = tde_smgr_id;
|
||||
} |
||||
|
||||
#else |
||||
void RegisterStorageMgr() |
||||
{ |
||||
} |
||||
#endif /* PERCONA_FORK */ |
@ -1,13 +0,0 @@ |
||||
CREATE EXTENSION pg_tde; |
||||
-- server restart |
||||
CREATE TABLE test_enc(id SERIAL,k INTEGER,PRIMARY KEY (id)) USING pg_tde; |
||||
INSERT INTO test_enc (k) VALUES (5),(6); |
||||
SELECT * FROM test_enc ORDER BY id ASC; |
||||
1|5 |
||||
2|6 |
||||
-- server restart |
||||
SELECT * FROM test_enc ORDER BY id ASC; |
||||
1|5 |
||||
2|6 |
||||
DROP TABLE test_enc; |
||||
DROP EXTENSION pg_tde; |
Loading…
Reference in new issue