@@ -16,6 +16,7 @@ import (
16
16
"io/ioutil"
17
17
"math/big"
18
18
"os"
19
+ "path/filepath"
19
20
"sort"
20
21
"unsafe"
21
22
@@ -77,6 +78,29 @@ const (
77
78
// TpmSavedDiskSealingPcrs is the file that holds a copy of PCR values
78
79
// at the time of generating and sealing the disk key into the TPM.
79
80
TpmSavedDiskSealingPcrs = types .PersistStatusDir + "/sealingpcrs"
81
+
82
+ // MeasurementLogSealSuccess is files that holds a copy of event log at the time
83
+ // of generating/sealing the disk key into the TPM.
84
+ MeasurementLogSealSuccess = types .PersistStatusDir + "/tpm_measurement_seal_success"
85
+
86
+ // MeasurementLogUnsealFail is files that holds a copy of event log at the time EVE
87
+ // fails to unseal the vault key from TPM.
88
+ MeasurementLogUnsealFail = types .PersistStatusDir + "/tpm_measurement_unseal_fail"
89
+
90
+ // TpmEvtLogSavePattern contains the pattern used to save event log files,
91
+ // considering the case when multiple tpm devices are present.
92
+ TpmEvtLogSavePattern = "%s-tpm%d"
93
+
94
+ // TpmEvtLogCollectPattern contains the pattern used to collect event log files.
95
+ TpmEvtLogCollectPattern = "%s-backup"
96
+
97
+ // measurementLogFile is a kernel exposed variable that contains the
98
+ // TPM measurements and events log.
99
+ measurementLogFile = "binary_bios_measurements"
100
+
101
+ // syfsTpmDir is directory that TPMs get mapped on sysfs, and it contains
102
+ // measurement logs.
103
+ syfsTpmDir = "/hostfs/sys/kernel/security/tpm*"
80
104
)
81
105
82
106
// PCRBank256Status stores info about support for
@@ -628,11 +652,31 @@ func SealDiskKey(key []byte, pcrSel tpm2.PCRSelection) error {
628
652
return fmt .Errorf ("NVWrite %v failed: %v" , TpmSealedDiskPubHdl , err )
629
653
}
630
654
631
- // save a snapshot of PCR values
655
+ // save a snapshot of current PCR values
632
656
if err := saveDiskKeySealingPCRs (TpmSavedDiskSealingPcrs ); err != nil {
633
657
return fmt .Errorf ("saving snapshot of sealing PCRs failed: %w" , err )
634
658
}
635
659
660
+ // Backup the previous pair of logs if any, so at most we have two pairs of
661
+ // measurement logs (per available tpm devices). This is needed because if the
662
+ // failing devices get connected to the controller and collects the backup key,
663
+ // we end up here again and will override the MeasurementLogSealSuccess with
664
+ // current measurement log (which is same as the content of MeasurementLogSealFail)
665
+ // and lose the ability to diff and diagnose the issue.
666
+ if err := backupCopiedMeasurementLogs (); err != nil {
667
+ return fmt .Errorf ("collecting previous snapshot of TPM event log failed: %w" , err )
668
+ }
669
+
670
+ // fresh start, remove old copies of measurement logs.
671
+ if err := removeCopiedMeasurementLogs (); err != nil {
672
+ return fmt .Errorf ("removing old copies of TPM measurement log failed: %w" , err )
673
+ }
674
+
675
+ // save a copy of the current measurement log
676
+ if err := copyMeasurementLog (MeasurementLogSealSuccess ); err != nil {
677
+ return fmt .Errorf ("copying current TPM measurement log failed: %w" , err )
678
+ }
679
+
636
680
return nil
637
681
}
638
682
@@ -688,14 +732,22 @@ func UnsealDiskKey(pcrSel tpm2.PCRSelection) ([]byte, error) {
688
732
689
733
key , err := tpm2 .UnsealWithSession (rw , session , sealedObjHandle , EmptyPassword )
690
734
if err != nil {
691
- // We get here mostly because of RCPolicyFail error, so try to get more
692
- // information about the failure by finding the mismatching PCR index.
693
- mismatch , extraErr := findMismatchingPCRs (TpmSavedDiskSealingPcrs )
694
- if extraErr != nil {
695
- return nil , fmt .Errorf ("UnsealWithSession failed: %w, getting more info failed: %v" , err , extraErr )
735
+ // We get here mostly because of RCPolicyFail error, so first try to save
736
+ // a copy of TPM measurement log, it comes handy for diagnosing the issue.
737
+ evtLogStat := "copied (failed unseal) TPM measurement log"
738
+ if errEvtLog := copyMeasurementLog (MeasurementLogUnsealFail ); errEvtLog != nil {
739
+ // just report the failure, still give findMismatchingPCRs a chance so
740
+ // we can at least have some partial information about why unseal failed.
741
+ evtLogStat = fmt .Sprintf ("copying (failed unseal) TPM measurement log failed: %v" , errEvtLog )
742
+ }
743
+
744
+ // try to find out the mismatching PCR index
745
+ mismatch , errPcrMiss := findMismatchingPCRs (TpmSavedDiskSealingPcrs )
746
+ if errPcrMiss != nil {
747
+ return nil , fmt .Errorf ("UnsealWithSession failed: %w, %s, finding mismatching PCR failed: %v" , err , evtLogStat , errPcrMiss )
696
748
}
697
749
698
- return nil , fmt .Errorf ("UnsealWithSession failed: %w, possibly mismatching PCR indexes %v" , err , mismatch )
750
+ return nil , fmt .Errorf ("UnsealWithSession failed: %w, %s, possibly mismatching PCR indexes: %v" , err , evtLogStat , mismatch )
699
751
}
700
752
return key , nil
701
753
}
@@ -805,6 +857,124 @@ func pcrBankSHA256EnabledHelper() bool {
805
857
return err == nil
806
858
}
807
859
860
+ func getMappedTpmsPath () ([]string , error ) {
861
+ paths , err := filepath .Glob (syfsTpmDir )
862
+ if err != nil {
863
+ return nil , fmt .Errorf ("failed to enumerate TPM(s) in sysfs: %w" , err )
864
+ } else if len (paths ) == 0 {
865
+ return nil , fmt .Errorf ("found no TPM in sysfs" )
866
+ }
867
+
868
+ return paths , nil
869
+ }
870
+
871
+ func countMappedTpms () (int , error ) {
872
+ paths , err := getMappedTpmsPath ()
873
+ if err != nil {
874
+ return 0 , fmt .Errorf ("getMappedTpmsPath failed: %w" , err )
875
+ }
876
+
877
+ return len (paths ), nil
878
+ }
879
+
880
+ func getMeasurementLogFiles () ([]string , error ) {
881
+ paths , err := getMappedTpmsPath ()
882
+ if err != nil {
883
+ return nil , fmt .Errorf ("getMappedTpmsPath failed: %w" , err )
884
+ }
885
+
886
+ enumerated := make ([]string , 0 )
887
+ for _ , path := range paths {
888
+ fullPath := filepath .Join (path , measurementLogFile )
889
+ if fileutils .FileExists (nil , fullPath ) {
890
+ enumerated = append (enumerated , fullPath )
891
+ }
892
+ }
893
+
894
+ return enumerated , nil
895
+ }
896
+
897
+ func backupCopiedMeasurementLogs () error {
898
+ counted , err := countMappedTpms ()
899
+ if err != nil {
900
+ return fmt .Errorf ("countMappedTPMs failed: %w" , err )
901
+ }
902
+
903
+ leftToBackup := counted
904
+ for i := 0 ; i < counted ; i ++ {
905
+ sealSuccessPath := fmt .Sprintf (TpmEvtLogSavePattern , MeasurementLogSealSuccess , i )
906
+ unsealFailPath := fmt .Sprintf (TpmEvtLogSavePattern , MeasurementLogUnsealFail , i )
907
+ if fileutils .FileExists (nil , sealSuccessPath ) && fileutils .FileExists (nil , unsealFailPath ) {
908
+ sealSuccessBackupPath := fmt .Sprintf (TpmEvtLogCollectPattern , sealSuccessPath )
909
+ unsealFailBackupPath := fmt .Sprintf (TpmEvtLogCollectPattern , unsealFailPath )
910
+ if err := os .Rename (sealSuccessPath , sealSuccessBackupPath ); err != nil {
911
+ fmt .Fprintf (os .Stderr , "failed to backup tpm%d \" seal success\" previously copied measurement log: %v" , i , err )
912
+ continue
913
+ }
914
+ if err := os .Rename (unsealFailPath , unsealFailBackupPath ); err != nil {
915
+ fmt .Fprintf (os .Stderr , "failed to backup tpm%d \" unseal fail\" previously copied measurement log: %v" , i , err )
916
+ _ = os .Rename (sealSuccessBackupPath , sealSuccessPath )
917
+ continue
918
+ }
919
+ }
920
+
921
+ leftToBackup --
922
+ }
923
+
924
+ if leftToBackup != 0 {
925
+ return fmt .Errorf ("failed to backup %d number of previously copied TPM measurement logs" , leftToBackup )
926
+ }
927
+
928
+ return nil
929
+ }
930
+
931
+ func removeCopiedMeasurementLogs () error {
932
+ counted , err := countMappedTpms ()
933
+ if err != nil {
934
+ return fmt .Errorf ("countMappedTPMs failed: %w" , err )
935
+ }
936
+
937
+ for i := 0 ; i < counted ; i ++ {
938
+ sealSuccessPath := fmt .Sprintf (TpmEvtLogSavePattern , MeasurementLogSealSuccess , i )
939
+ unsealFailPath := fmt .Sprintf (TpmEvtLogSavePattern , MeasurementLogUnsealFail , i )
940
+ _ = os .Remove (sealSuccessPath )
941
+ _ = os .Remove (unsealFailPath )
942
+ }
943
+
944
+ return nil
945
+ }
946
+
947
+ func copyMeasurementLog (dstPath string ) error {
948
+ paths , err := getMeasurementLogFiles ()
949
+ if err != nil {
950
+ return fmt .Errorf ("enumSourceEventLogFiles failed: %w" , err )
951
+ }
952
+
953
+ leftToCopy := len (paths )
954
+ for i , path := range paths {
955
+ measurementLogContent , err := os .ReadFile (path )
956
+ if err != nil {
957
+ fmt .Fprintf (os .Stderr , "failed to read stored measurement log file: %v" , err )
958
+ continue
959
+ }
960
+
961
+ copyPath := fmt .Sprintf (TpmEvtLogSavePattern , dstPath , i )
962
+ err = fileutils .WriteRename (copyPath , measurementLogContent )
963
+ if err != nil {
964
+ fmt .Fprintf (os .Stderr , "failed to copy stored measurement log file: %v" , err )
965
+ continue
966
+ }
967
+
968
+ leftToCopy --
969
+ }
970
+
971
+ if leftToCopy != 0 {
972
+ return fmt .Errorf ("failed to copy %d number of stored measurement log files" , leftToCopy )
973
+ }
974
+
975
+ return nil
976
+ }
977
+
808
978
func saveDiskKeySealingPCRs (pcrsFile string ) error {
809
979
trw , err := tpm2 .OpenTPM (TpmDevicePath )
810
980
if err != nil {
0 commit comments