Skip to content

Commit 12f4c62

Browse files
handle terminal tpm errors (#2110)
1 parent 4f23f65 commit 12f4c62

File tree

5 files changed

+112
-9
lines changed

5 files changed

+112
-9
lines changed

ee/tpmrunner/tpmrunner.go

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import (
1010
"fmt"
1111
"io"
1212
"log/slog"
13+
"os"
14+
"runtime"
1315
"sync"
1416
"sync/atomic"
1517
"time"
@@ -67,7 +69,23 @@ func New(ctx context.Context, slogger *slog.Logger, store types.GetterSetterDele
6769
}
6870

6971
// assume we have a tpm until we know otherwise
70-
tpmRunner.machineHasTpm.Store(true)
72+
hasTPM := true
73+
74+
// on linux the TPM is at /dev/tpm0
75+
// if it doesn't exist, we don't have a TPM
76+
if runtime.GOOS == "linux" {
77+
_, err := os.Stat("/dev/tpm0")
78+
hasTPM = err == nil
79+
80+
if !hasTPM {
81+
slogger.Log(ctx, slog.LevelInfo,
82+
"no tpm found",
83+
"err", err,
84+
)
85+
}
86+
}
87+
88+
tpmRunner.machineHasTpm.Store(hasTPM)
7189

7290
for _, opt := range opts {
7391
opt(tpmRunner)
@@ -228,11 +246,11 @@ func (tr *tpmRunner) loadOrCreateKeys(ctx context.Context) error {
228246
priData, pubData, err = tr.signerCreator.CreateKey()
229247
if err != nil {
230248

231-
if isTPMNotFoundErr(err) {
249+
if isTerminalTPMError(err) {
232250
tr.machineHasTpm.Store(false)
233251

234252
tr.slogger.Log(ctx, slog.LevelInfo,
235-
"tpm not found",
253+
"terminal tpm error, not retrying",
236254
"err", err,
237255
)
238256

ee/tpmrunner/tpmrunner_linux.go

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,26 @@
33

44
package tpmrunner
55

6-
// isTPMNotFoundErr always return false on linux because we don't yet how to
7-
// detect if a TPM is not found on linux.
8-
func isTPMNotFoundErr(err error) bool {
6+
import (
7+
"errors"
8+
"os"
9+
)
10+
11+
var terminalErrors = []error{
12+
// on linux, if the tpm device is not present, we get this error
13+
// stat /dev/tpm0: no such file or directory
14+
// we should check this when we create a new tpmrunner so this
15+
// is maybe belt and suspenders
16+
os.ErrNotExist,
17+
}
18+
19+
// isTerminalTPMError returns true if we should stop trying to use the TPM.
20+
func isTerminalTPMError(err error) bool {
21+
for _, e := range terminalErrors {
22+
if errors.Is(err, e) {
23+
return true
24+
}
25+
}
26+
927
return false
1028
}

ee/tpmrunner/tpmrunner_test.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,11 @@ func Test_tpmRunner(t *testing.T) {
3737
tpmRunner, err := New(context.TODO(), multislogger.NewNopLogger(), inmemory.NewStore(), withTpmSignerCreator(tpmSignerCreatorMock))
3838
require.NoError(t, err)
3939

40+
// force the runner to think the machine has a TPM
41+
// not using usual detection methods since were
42+
// mocking it
43+
tpmRunner.machineHasTpm.Store(true)
44+
4045
tpmSignerCreatorMock.On("CreateKey").Return(nil, nil, errors.New("not available yet")).Once()
4146
require.Nil(t, tpmRunner.Public())
4247

@@ -66,6 +71,11 @@ func Test_tpmRunner(t *testing.T) {
6671
tpmRunner, err := New(context.TODO(), multislogger.NewNopLogger(), store, withTpmSignerCreator(tpmSignerCreatorMock))
6772
require.NoError(t, err)
6873

74+
// force the runner to think the machine has a TPM
75+
// not using usual detection methods since were
76+
// mocking it
77+
tpmRunner.machineHasTpm.Store(true)
78+
6979
tpmSignerCreatorMock.On("New", fakePrivData, fakePubData).Return(privKey, nil).Once()
7080

7181
// the call to public should load the key from the store and signer creator should not be called any more after
@@ -88,6 +98,11 @@ func Test_tpmRunner(t *testing.T) {
8898
tpmRunner, err := New(context.TODO(), multislogger.NewNopLogger(), inmemory.NewStore(), withTpmSignerCreator(tpmSignerCreatorMock))
8999
require.NoError(t, err)
90100

101+
// force the runner to think the machine has a TPM
102+
// not using usual detection methods since were
103+
// mocking it
104+
tpmRunner.machineHasTpm.Store(true)
105+
91106
tpmSignerCreatorMock.On("CreateKey").Return(nil, nil, errors.New("not available yet")).Once()
92107
require.Nil(t, tpmRunner.Public())
93108

ee/tpmrunner/tpmrunner_windows.go

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,27 @@ package tpmrunner
66
import (
77
"errors"
88

9+
"github.com/google/go-tpm/tpm2"
910
"github.com/google/go-tpm/tpmutil/tbs"
1011
)
1112

12-
func isTPMNotFoundErr(err error) bool {
13-
return errors.Is(err, tbs.ErrTPMNotFound)
13+
var terminalErrors = []error{
14+
tbs.ErrTPMNotFound,
15+
16+
// this covers the error "integrity check failed" we dont
17+
// believe a machine will recover from this
18+
tpm2.Error{
19+
Code: tpm2.RCIntegrity,
20+
},
21+
}
22+
23+
// isTerminalTPMError returns true if we should stop trying to use the TPM.
24+
func isTerminalTPMError(err error) bool {
25+
for _, e := range terminalErrors {
26+
if errors.Is(err, e) {
27+
return true
28+
}
29+
}
30+
31+
return false
1432
}

ee/tpmrunner/tpmrunner_windows_test.go

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"testing"
1010
"time"
1111

12+
"github.com/google/go-tpm/tpm2"
1213
"github.com/google/go-tpm/tpmutil/tbs"
1314
"github.com/kolide/launcher/ee/agent/storage/inmemory"
1415
"github.com/kolide/launcher/ee/tpmrunner/mocks"
@@ -41,7 +42,7 @@ func Test_tpmRunner_windows(t *testing.T) {
4142
require.Nil(t, tpmRunner.Public())
4243
})
4344

44-
t.Run("handles no tpm in Public() call", func(t *testing.T) {
45+
t.Run("handles terminal errors Public() call", func(t *testing.T) {
4546
t.Parallel()
4647

4748
tpmSignerCreatorMock := mocks.NewTpmSignerCreator(t)
@@ -63,5 +64,38 @@ func Test_tpmRunner_windows(t *testing.T) {
6364
require.NoError(t, tpmRunner.Execute())
6465
require.Nil(t, tpmRunner.Public())
6566
})
67+
}
68+
69+
func Test_isTerminalError(t *testing.T) {
70+
t.Parallel()
6671

72+
tests := []struct {
73+
name string
74+
err error
75+
expected bool
76+
}{
77+
{
78+
name: "tpm not found err",
79+
err: tbs.ErrTPMNotFound,
80+
expected: true,
81+
},
82+
{
83+
name: "integrity check failed",
84+
err: tpm2.Error{Code: tpm2.RCIntegrity},
85+
expected: true,
86+
},
87+
{
88+
name: "is not terminal error",
89+
err: errors.New("not terminal"),
90+
expected: false,
91+
},
92+
}
93+
94+
for _, tt := range tests {
95+
tt := tt
96+
t.Run(tt.name, func(t *testing.T) {
97+
t.Parallel()
98+
require.Equal(t, tt.expected, isTerminalTPMError(tt.err))
99+
})
100+
}
67101
}

0 commit comments

Comments
 (0)