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
4548typedef 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
85103static 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
0 commit comments