mirror of
https://github.com/tailscale/tailscale.git
synced 2025-08-14 06:57:31 +00:00
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>
This commit is contained in:
8
feature/condregister/maybe_tpm.go
Normal file
8
feature/condregister/maybe_tpm.go
Normal file
@@ -0,0 +1,8 @@
|
||||
// Copyright (c) Tailscale Inc & AUTHORS
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
//go:build !ios && !ts_omit_tpm
|
||||
|
||||
package condregister
|
||||
|
||||
import _ "tailscale.com/feature/tpm"
|
83
feature/tpm/tpm.go
Normal file
83
feature/tpm/tpm.go
Normal file
@@ -0,0 +1,83 @@
|
||||
// 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 > '~' }))
|
||||
}
|
18
feature/tpm/tpm_linux.go
Normal file
18
feature/tpm/tpm_linux.go
Normal file
@@ -0,0 +1,18 @@
|
||||
// Copyright (c) Tailscale Inc & AUTHORS
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package tpm
|
||||
|
||||
import (
|
||||
"github.com/google/go-tpm/tpm2/transport/linuxtpm"
|
||||
"tailscale.com/tailcfg"
|
||||
)
|
||||
|
||||
func info() *tailcfg.TPMInfo {
|
||||
t, err := linuxtpm.Open("/dev/tpm0")
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
defer t.Close()
|
||||
return infoFromCapabilities(t)
|
||||
}
|
12
feature/tpm/tpm_other.go
Normal file
12
feature/tpm/tpm_other.go
Normal file
@@ -0,0 +1,12 @@
|
||||
// Copyright (c) Tailscale Inc & AUTHORS
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
//go:build !linux && !windows
|
||||
|
||||
package tpm
|
||||
|
||||
import "tailscale.com/tailcfg"
|
||||
|
||||
func info() *tailcfg.TPMInfo {
|
||||
return nil
|
||||
}
|
19
feature/tpm/tpm_test.go
Normal file
19
feature/tpm/tpm_test.go
Normal file
@@ -0,0 +1,19 @@
|
||||
// Copyright (c) Tailscale Inc & AUTHORS
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package tpm
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestPropToString(t *testing.T) {
|
||||
for prop, want := range map[uint32]string{
|
||||
0: "",
|
||||
0x4D534654: "MSFT",
|
||||
0x414D4400: "AMD",
|
||||
0x414D440D: "AMD",
|
||||
} {
|
||||
if got := propToString(prop); got != want {
|
||||
t.Errorf("propToString(0x%x): got %q, want %q", prop, got, want)
|
||||
}
|
||||
}
|
||||
}
|
18
feature/tpm/tpm_windows.go
Normal file
18
feature/tpm/tpm_windows.go
Normal file
@@ -0,0 +1,18 @@
|
||||
// Copyright (c) Tailscale Inc & AUTHORS
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package tpm
|
||||
|
||||
import (
|
||||
"github.com/google/go-tpm/tpm2/transport/windowstpm"
|
||||
"tailscale.com/tailcfg"
|
||||
)
|
||||
|
||||
func info() *tailcfg.TPMInfo {
|
||||
t, err := windowstpm.Open()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
defer t.Close()
|
||||
return infoFromCapabilities(t)
|
||||
}
|
Reference in New Issue
Block a user