Andrew Lytvynov 3105ecd958
hostinfo,tailcfg: report TPM availability on windows/linux (#15831)
Start collecting fleet data on TPM availability via hostinfo.

Updates #15830

Signed-off-by: Andrew Lytvynov <awly@tailscale.com>
2025-05-02 11:01:13 -07:00

84 lines
2.5 KiB
Go

// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
// Package tpm implements support for TPM 2.0 devices.
package tpm
import (
"slices"
"sync"
"github.com/google/go-tpm/tpm2"
"github.com/google/go-tpm/tpm2/transport"
"tailscale.com/feature"
"tailscale.com/hostinfo"
"tailscale.com/tailcfg"
)
var infoOnce = sync.OnceValue(info)
func init() {
feature.Register("tpm")
hostinfo.RegisterHostinfoNewHook(func(hi *tailcfg.Hostinfo) {
hi.TPM = infoOnce()
})
}
//lint:ignore U1000 used in Linux and Windows builds only
func infoFromCapabilities(tpm transport.TPM) *tailcfg.TPMInfo {
info := new(tailcfg.TPMInfo)
toStr := func(s *string) func(*tailcfg.TPMInfo, uint32) {
return func(info *tailcfg.TPMInfo, value uint32) {
*s += propToString(value)
}
}
for _, cap := range []struct {
prop tpm2.TPMPT
apply func(info *tailcfg.TPMInfo, value uint32)
}{
{tpm2.TPMPTManufacturer, toStr(&info.Manufacturer)},
{tpm2.TPMPTVendorString1, toStr(&info.Vendor)},
{tpm2.TPMPTVendorString2, toStr(&info.Vendor)},
{tpm2.TPMPTVendorString3, toStr(&info.Vendor)},
{tpm2.TPMPTVendorString4, toStr(&info.Vendor)},
{tpm2.TPMPTRevision, func(info *tailcfg.TPMInfo, value uint32) { info.SpecRevision = int(value) }},
{tpm2.TPMPTVendorTPMType, func(info *tailcfg.TPMInfo, value uint32) { info.Model = int(value) }},
{tpm2.TPMPTFirmwareVersion1, func(info *tailcfg.TPMInfo, value uint32) { info.FirmwareVersion += uint64(value) << 32 }},
{tpm2.TPMPTFirmwareVersion2, func(info *tailcfg.TPMInfo, value uint32) { info.FirmwareVersion += uint64(value) }},
} {
resp, err := tpm2.GetCapability{
Capability: tpm2.TPMCapTPMProperties,
Property: uint32(cap.prop),
PropertyCount: 1,
}.Execute(tpm)
if err != nil {
continue
}
props, err := resp.CapabilityData.Data.TPMProperties()
if err != nil {
continue
}
if len(props.TPMProperty) == 0 {
continue
}
cap.apply(info, props.TPMProperty[0].Value)
}
return info
}
// propToString converts TPM_PT property value, which is a uint32, into a
// string of up to 4 ASCII characters. This encoding applies only to some
// properties, see
// https://trustedcomputinggroup.org/resource/tpm-library-specification/ Part
// 2, section 6.13.
func propToString(v uint32) string {
chars := []byte{
byte(v >> 24),
byte(v >> 16),
byte(v >> 8),
byte(v),
}
// Delete any non-printable ASCII characters.
return string(slices.DeleteFunc(chars, func(b byte) bool { return b < ' ' || b > '~' }))
}