Skip to content

Commit b851c0f

Browse files
committed
Add support for 32 byte keys in smgr
Including migration
1 parent 0b8794c commit b851c0f

File tree

3 files changed

+246
-17
lines changed

3 files changed

+246
-17
lines changed

contrib/pg_tde/src/access/pg_tde_tdemap.c

Lines changed: 243 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,11 @@
3939
}
4040
#endif
4141

42-
#define PG_TDE_FILEMAGIC 0x03454454 /* version ID value = TDE 03 */
43-
#define PG_TDE_MAP_FILENAME "%d_keys"
42+
#define PG_TDE_FILEMAGIC_OLD 0x03454454 /* old version ID value = TDE 03 */
43+
#define PG_TDE_MAP_OLD_FNAME_SUFFIX "_keys"
44+
45+
#define PG_TDE_FILEMAGIC 0x04454454 /* version ID value = TDE 04 */
46+
#define PG_TDE_MAP_FILENAME "%d_keys_v2" /* TODO: should be _v2 of _v4 ? */
4447

4548
typedef enum
4649
{
@@ -63,14 +66,14 @@ typedef struct TDEFileHeader
6366
* encrypting/decrypting existing keys from the key files, so any changes here
6467
* might break existing clusters.
6568
*/
66-
typedef struct TDEMapEntry
69+
typedef struct TDEMapEntryOld
6770
{
6871
Oid spcOid; /* Part of AAD */
6972
RelFileNumber relNumber; /* Part of AAD */
7073
uint32 type; /* Part of AAD */
7174
uint32 _unused3; /* Part of AAD */
7275

73-
uint8 encrypted_key_data[INTERNAL_KEY_MAX_LEN];
76+
uint8 encrypted_key_data[INTERNAL_KEY_OLD_LEN];
7477
uint8 key_base_iv[INTERNAL_KEY_IV_LEN];
7578

7679
uint32 _unused1; /* Will be 1 in existing files entries. */
@@ -80,6 +83,21 @@ typedef struct TDEMapEntry
8083
/* IV and tag used when encrypting the key itself */
8184
unsigned char entry_iv[MAP_ENTRY_IV_SIZE];
8285
unsigned char aead_tag[MAP_ENTRY_AEAD_TAG_SIZE];
86+
} TDEMapEntryOld;
87+
88+
typedef struct TDEMapEntry
89+
{
90+
uint32 key_len; /* Part of AAD */
91+
Oid spcOid; /* Part of AAD */
92+
RelFileNumber relNumber; /* Part of AAD */
93+
uint32 type; /* Part of AAD */
94+
95+
/* IV and tag used when encrypting the key itself */
96+
unsigned char entry_iv[MAP_ENTRY_IV_SIZE];
97+
unsigned char aead_tag[MAP_ENTRY_AEAD_TAG_SIZE];
98+
99+
uint8 key_base_iv[INTERNAL_KEY_IV_LEN];
100+
uint8 encrypted_key_data[INTERNAL_KEY_MAX_LEN];
83101
} TDEMapEntry;
84102

85103
static void pg_tde_set_db_file_path(Oid dbOid, char *path);
@@ -397,12 +415,7 @@ pg_tde_initialize_map_entry(TDEMapEntry *map_entry, const TDEPrincipalKey *princ
397415
map_entry->type = MAP_ENTRY_TYPE_KEY;
398416
memcpy(map_entry->key_base_iv, rel_key_data->base_iv, INTERNAL_KEY_IV_LEN);
399417

400-
/*
401-
* We set these fields here so that existing file entries will be
402-
* consistent and future use of these fields easier.
403-
*/
404-
map_entry->_unused1 = 1;
405-
map_entry->_unused2 = 0;
418+
map_entry->key_len = rel_key_data->key_len;
406419

407420
if (!RAND_bytes(map_entry->entry_iv, MAP_ENTRY_IV_SIZE))
408421
ereport(ERROR,
@@ -411,8 +424,8 @@ pg_tde_initialize_map_entry(TDEMapEntry *map_entry, const TDEPrincipalKey *princ
411424

412425
AesGcmEncrypt(principal_key->keyData, principal_key->keyLength,
413426
map_entry->entry_iv, MAP_ENTRY_IV_SIZE,
414-
(unsigned char *) map_entry, offsetof(TDEMapEntry, encrypted_key_data),
415-
rel_key_data->key, INTERNAL_KEY_MAX_LEN,
427+
(unsigned char *) map_entry, offsetof(TDEMapEntry, entry_iv),
428+
rel_key_data->key, rel_key_data->key_len,
416429
map_entry->encrypted_key_data,
417430
map_entry->aead_tag, MAP_ENTRY_AEAD_TAG_SIZE);
418431
}
@@ -590,14 +603,15 @@ tde_decrypt_rel_key(const TDEPrincipalKey *principal_key, TDEMapEntry *map_entry
590603

591604
if (!AesGcmDecrypt(principal_key->keyData, principal_key->keyLength,
592605
map_entry->entry_iv, MAP_ENTRY_IV_SIZE,
593-
(unsigned char *) map_entry, offsetof(TDEMapEntry, encrypted_key_data),
594-
map_entry->encrypted_key_data, INTERNAL_KEY_MAX_LEN,
606+
(unsigned char *) map_entry, offsetof(TDEMapEntry, entry_iv),
607+
map_entry->encrypted_key_data, map_entry->key_len,
595608
key->key,
596609
map_entry->aead_tag, MAP_ENTRY_AEAD_TAG_SIZE))
597610
ereport(ERROR,
598611
errmsg("Failed to decrypt key, incorrect principal key or corrupted key file"));
599612

600613
memcpy(key->base_iv, map_entry->key_base_iv, INTERNAL_KEY_IV_LEN);
614+
key->key_len = map_entry->key_len;
601615

602616
return key;
603617
}
@@ -785,10 +799,36 @@ pg_tde_get_principal_key_info(Oid dbOid)
785799
fd = pg_tde_open_file_basic(db_map_path, O_RDONLY, true);
786800

787801
/* The file does not exist. */
788-
if (fd < 0)
789-
return NULL;
802+
if (fd >= 0)
803+
pg_tde_file_header_read(db_map_path, fd, &fheader, &bytes_read);
804+
else
805+
{
806+
/*
807+
* TODO: An ugly hack for now, we need to get a key info when rewriting
808+
* an old file...
809+
*/
810+
char old_key_file_path[MAXPGPATH] = {0};
811+
char *fname = psprintf("%d"PG_TDE_MAP_OLD_FNAME_SUFFIX, dbOid);
812+
813+
join_path_components(old_key_file_path, pg_tde_get_data_dir(), fname);
814+
pfree(fname);
790815

791-
pg_tde_file_header_read(db_map_path, fd, &fheader, &bytes_read);
816+
fd = pg_tde_open_file_basic(old_key_file_path, O_RDONLY, true);
817+
818+
/* The file does not exist */
819+
if (fd < 0)
820+
return NULL;
821+
822+
bytes_read = pg_pread(fd, &fheader, sizeof(TDEFileHeader), 0);
823+
824+
if (bytes_read > 0 && (bytes_read != sizeof(TDEFileHeader)
825+
|| fheader.file_version != PG_TDE_FILEMAGIC_OLD))
826+
{
827+
ereport(FATAL,
828+
errcode_for_file_access(),
829+
errmsg("old smgr key file \"%s\" is corrupted: %m", old_key_file_path));
830+
}
831+
}
792832

793833
CloseTransientFile(fd);
794834

@@ -881,3 +921,189 @@ pg_tde_get_smgr_key(RelFileLocator rel)
881921

882922
return rel_key;
883923
}
924+
925+
#ifndef FRONTEND
926+
927+
/*
928+
* Functions for rewriting old smgr _key into a new format file.
929+
*
930+
* TODO: The old format should be deprecated. And this code should be removed
931+
* eventually.
932+
*/
933+
934+
static int
935+
pg_tde_open_old_file_read(const char *tde_filename, bool ignore_missing, off_t *curr_pos)
936+
{
937+
int fd;
938+
TDEFileHeader fheader;
939+
off_t bytes_read = 0;
940+
941+
Assert(LWLockHeldByMeInMode(tde_lwlock_enc_keys(), LW_SHARED) ||
942+
LWLockHeldByMeInMode(tde_lwlock_enc_keys(), LW_EXCLUSIVE));
943+
944+
fd = pg_tde_open_file_basic(tde_filename, O_RDONLY | PG_BINARY, ignore_missing);
945+
if (ignore_missing && fd < 0)
946+
return fd;
947+
948+
bytes_read = pg_pread(fd, &fheader, sizeof(TDEFileHeader), 0);
949+
950+
/* File is empty */
951+
if (bytes_read == 0)
952+
return fd;
953+
954+
if (bytes_read != sizeof(TDEFileHeader)
955+
|| fheader.file_version != PG_TDE_FILEMAGIC_OLD)
956+
{
957+
ereport(FATAL,
958+
errcode_for_file_access(),
959+
errmsg("old TDE map file \"%s\" is corrupted: %m", tde_filename));
960+
}
961+
962+
*curr_pos = bytes_read;
963+
964+
return fd;
965+
}
966+
967+
static bool
968+
pg_tde_read_one_old_map_entry(int map_file, TDEMapEntryOld *map_entry, off_t *offset)
969+
{
970+
off_t bytes_read = 0;
971+
972+
Assert(map_entry);
973+
Assert(offset);
974+
975+
bytes_read = pg_pread(map_file, map_entry, sizeof(TDEMapEntryOld), *offset);
976+
977+
/* We've reached the end of the file. */
978+
if (bytes_read != sizeof(TDEMapEntryOld))
979+
return false;
980+
981+
*offset += bytes_read;
982+
983+
return true;
984+
}
985+
986+
static InternalKey *
987+
tde_decrypt_old_rel_key(const TDEPrincipalKey *principal_key, TDEMapEntryOld *map_entry)
988+
{
989+
InternalKey *key = palloc_object(InternalKey);
990+
991+
Assert(principal_key);
992+
993+
if (!AesGcmDecrypt(principal_key->keyData, principal_key->keyLength,
994+
map_entry->entry_iv, MAP_ENTRY_IV_SIZE,
995+
(unsigned char *) map_entry, offsetof(TDEMapEntryOld, encrypted_key_data),
996+
map_entry->encrypted_key_data, INTERNAL_KEY_OLD_LEN,
997+
key->key,
998+
map_entry->aead_tag, MAP_ENTRY_AEAD_TAG_SIZE))
999+
ereport(ERROR,
1000+
errmsg("Failed to decrypt key, incorrect principal key or corrupted key file"));
1001+
1002+
memcpy(key->base_iv, map_entry->key_base_iv, INTERNAL_KEY_IV_LEN);
1003+
key->key_len = INTERNAL_KEY_OLD_LEN;
1004+
1005+
return key;
1006+
}
1007+
1008+
void
1009+
pg_tde_migrate_smgr_keys_file(void)
1010+
{
1011+
DIR *dir;
1012+
LWLock *lock_pk = tde_lwlock_enc_keys();
1013+
struct dirent *file;
1014+
TDEPrincipalKey *principal_key = NULL;
1015+
TDESignedPrincipalKeyInfo signed_key_info;
1016+
int old_suffix_len = strlen(PG_TDE_MAP_OLD_FNAME_SUFFIX);
1017+
1018+
/*
1019+
* No real need in lock here as the func should be called only on the server
1020+
* start, but GetPrincipalKey() expects lock.
1021+
*/
1022+
LWLockAcquire(lock_pk, LW_EXCLUSIVE);
1023+
1024+
dir = opendir(pg_tde_get_data_dir());
1025+
if (dir == NULL && errno != ENOENT)
1026+
elog(ERROR, "could not open directory \"%s\": %m",
1027+
pg_tde_get_data_dir());
1028+
1029+
/*
1030+
* TODO: create a "current_version_v4" file after the successfull migration
1031+
* or during pg_tde dir creatation, so no need to scan the dir all the time,
1032+
* but just test the file?
1033+
*/
1034+
while (errno = 0, (file = readdir(dir)) != NULL)
1035+
{
1036+
if (strlen(file->d_name) > old_suffix_len &&
1037+
strncmp(file->d_name + strlen(file->d_name) - old_suffix_len, PG_TDE_MAP_OLD_FNAME_SUFFIX, old_suffix_len) == 0)
1038+
{
1039+
char old_db_map_path[MAXPGPATH] = {0};
1040+
char db_map_path[MAXPGPATH] = {0};
1041+
off_t read_pos,
1042+
write_pos;
1043+
int old_fd,
1044+
new_fd;
1045+
Oid dbOid;
1046+
char *suffix;
1047+
char *old_fname;
1048+
1049+
1050+
dbOid = strtoul(file->d_name, &suffix, 10);
1051+
if (strcmp(suffix, PG_TDE_MAP_OLD_FNAME_SUFFIX) != 0)
1052+
continue;
1053+
1054+
old_fname = psprintf("%d"PG_TDE_MAP_OLD_FNAME_SUFFIX, dbOid);
1055+
join_path_components(old_db_map_path, pg_tde_get_data_dir(), old_fname);
1056+
pfree(old_fname);
1057+
1058+
old_fd = pg_tde_open_old_file_read(old_db_map_path, false, &read_pos);
1059+
1060+
pg_tde_set_db_file_path(dbOid, db_map_path);
1061+
/*
1062+
* The old file exists and it's not empty, hece a principal key
1063+
* should exist as well.
1064+
*/
1065+
if (principal_key == NULL)
1066+
{
1067+
principal_key = GetPrincipalKey(dbOid, LW_EXCLUSIVE);
1068+
if (principal_key == NULL)
1069+
{
1070+
ereport(ERROR,
1071+
errmsg("could not get server principal key"),
1072+
errdetail("Failed to migrate the keys file of %u database.", dbOid));
1073+
}
1074+
pg_tde_sign_principal_key_info(&signed_key_info, principal_key);
1075+
}
1076+
new_fd = pg_tde_open_file_write(db_map_path, &signed_key_info, true, &write_pos);
1077+
1078+
while (1)
1079+
{
1080+
TDEMapEntryOld old_entry;
1081+
TDEMapEntry new_entry;
1082+
InternalKey *rel_key_data;
1083+
RelFileLocator rloc;
1084+
1085+
if (!pg_tde_read_one_old_map_entry(old_fd, &old_entry, &read_pos))
1086+
break;
1087+
1088+
rloc.spcOid = old_entry.spcOid;
1089+
rloc.dbOid = dbOid;
1090+
rloc.relNumber = old_entry.relNumber;
1091+
1092+
rel_key_data = tde_decrypt_old_rel_key(principal_key, &old_entry);
1093+
pg_tde_initialize_map_entry(&new_entry, principal_key, &rloc, rel_key_data);
1094+
pg_tde_write_one_map_entry(new_fd, &new_entry, &write_pos, db_map_path);
1095+
1096+
pfree(rel_key_data);
1097+
}
1098+
1099+
CloseTransientFile(old_fd);
1100+
CloseTransientFile(new_fd);
1101+
durable_unlink(old_db_map_path, ERROR);
1102+
}
1103+
}
1104+
1105+
closedir(dir);
1106+
LWLockRelease(lock_pk);
1107+
}
1108+
1109+
#endif

contrib/pg_tde/src/include/access/pg_tde_tdemap.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,6 @@ extern void pg_tde_sign_principal_key_info(TDESignedPrincipalKeyInfo *signed_key
2727

2828
const char *tde_sprint_key(InternalKey *k);
2929

30+
extern void pg_tde_migrate_smgr_keys_file(void);
31+
3032
#endif /* PG_TDE_MAP_H */

contrib/pg_tde/src/pg_tde.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ tde_shmem_startup(void)
7171
TDEXLogShmemInit();
7272
TDEXLogSmgrInit();
7373
pg_tde_update_wal_keys_file();
74+
pg_tde_migrate_smgr_keys_file();
7475
TDEXLogSmgrInitWrite(EncryptXLog, TdeKeyLength);
7576

7677
LWLockRelease(AddinShmemInitLock);

0 commit comments

Comments
 (0)