Files
bee/audit/internal/platform/tpm.go
T

88 lines
3.1 KiB
Go

package platform
import (
"context"
"fmt"
"os"
"path/filepath"
"strings"
"time"
)
// tpmDeviceGlob is a seam for tests. It reports the sysfs TPM device nodes
// the kernel has registered; an empty result means the platform exposes no
// TPM at all (no discrete chip, or firmware/BIOS has it disabled).
var tpmDeviceGlob = func() []string {
matches, _ := filepath.Glob("/sys/class/tpm/tpm*")
return matches
}
var tpmReadFile = os.ReadFile
// TPMPresent reports whether sysfs identifies a registered device as TPM 2.x.
// The validation pack uses tpm2-tools, so a TPM 1.2 device is not sufficient.
func (s *System) TPMPresent() bool {
for _, device := range tpmDeviceGlob() {
version, err := tpmReadFile(filepath.Join(device, "tpm_version_major"))
if err == nil && strings.TrimSpace(string(version)) == "2" {
return true
}
}
return false
}
// RunTPMValidationPack verifies TPM 2.0 communication using read-only
// commands. It deliberately excludes SelfTest, provisioning, NV writes, PCR
// changes, key creation, and ownership operations.
//
// When the platform exposes no TPM device at all, the pack does not run the
// tpm2_* tools: without a TCTI device they only ever emit a wall of
// "Failed to open ... /dev/tpmrm0" errors that read as a hard failure when
// the real situation is "this machine has no TPM". Instead it writes an
// UNSUPPORTED summary and returns, the same way the storage pack handles a
// host with no drives.
func (s *System) RunTPMValidationPack(ctx context.Context, baseDir string, logFunc func(string)) (string, error) {
if !s.TPMPresent() {
return writeTPMUnsupportedRun(baseDir, logFunc)
}
return runAcceptancePackCtx(ctx, baseDir, "tpm", tpmValidationJobs(), logFunc)
}
func writeTPMUnsupportedRun(baseDir string, logFunc func(string)) (string, error) {
if strings.TrimSpace(baseDir) == "" {
baseDir = "/var/log/bee-sat"
}
now := time.Now().UTC()
runDir := filepath.Join(baseDir, "tpm-"+now.Format("20060102-150405"))
if err := os.MkdirAll(runDir, 0755); err != nil {
return "", err
}
if logFunc != nil {
logFunc("no TPM 2.x device reported by sysfs; skipping read-only TPM checks")
}
var summary strings.Builder
fmt.Fprintf(&summary, "run_at_utc=%s\n", now.Format(time.RFC3339))
summary.WriteString("tpm_present=false\n")
summary.WriteString("skip_reason=no TPM 2.x device reported by sysfs; tpm2-tools are not applicable\n")
summary.WriteString("tpm_check_status=UNSUPPORTED\n")
summary.WriteString("overall_status=UNSUPPORTED\n")
summary.WriteString("job_ok=0\n")
summary.WriteString("job_failed=0\n")
summary.WriteString("job_unsupported=1\n")
summary.WriteString("job_informational_failed=0\n")
if err := os.WriteFile(filepath.Join(runDir, "summary.txt"), []byte(summary.String()), 0644); err != nil {
return "", err
}
return runDir, nil
}
func tpmValidationJobs() []satJob {
return []satJob{
{name: "01-properties-fixed.log", cmd: []string{"tpm2_getcap", "properties-fixed"}},
{name: "02-pcr-banks.log", cmd: []string{"tpm2_getcap", "pcrs"}},
{name: "03-pcr-values.log", cmd: []string{"tpm2_pcrread"}},
{name: "04-test-result.log", cmd: []string{"tpm2_gettestresult"}},
}
}