forked from canonical/secboot
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
tpm2: Use tpm2.TPMDevice abstraction
Secboot currently opens a transport to the Linux character device at a hardcoded path (/dev/tpm0) and passes the opened transport to the deprecated tpm2.NewTPMContext API. The replacement API (tpm2.OpenTPMDevice) makes use of a new abstraction (tpm2.TPMDevice). The linux sub-package provides a way of enumerating TPM devices or providing access to the default one. It also provides access to the resource managed device, which we might want to use in secboot in the future, and it provides access to the physical presence interface for performing actions that are implemented in platform firmware. Implementing this now is required to write test cases for canonical#353, as the current test harness will permit the code under test to open as many TPM connections as it likes, when in reality, it can only have a single open connection if it is using the direct character device (/dev/tpm0).
- Loading branch information
1 parent
082c84b
commit 0687f8d
Showing
17 changed files
with
979 additions
and
253 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
// -*- Mode: Go; indent-tabs-mode: t -*- | ||
|
||
/* | ||
* Copyright (C) 2024 Canonical Ltd | ||
* | ||
* This program is free software: you can redistribute it and/or modify | ||
* it under the terms of the GNU General Public License version 3 as | ||
* published by the Free Software Foundation. | ||
* | ||
* This program is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
* GNU General Public License for more details. | ||
* | ||
* You should have received a copy of the GNU General Public License | ||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
* | ||
*/ | ||
|
||
package tpm2_device | ||
|
||
import ( | ||
"errors" | ||
|
||
"github.com/canonical/go-tpm2" | ||
"github.com/canonical/go-tpm2/ppi" | ||
) | ||
|
||
// DeviceMode describes the mode to select the default device. | ||
type DeviceMode int | ||
|
||
const ( | ||
// DeviceModeDirect requests the most direct TPM2 device, without | ||
// the use of a resource manager. These devices cannot be opened more | ||
// than once and don't permit the TPM to be shared. | ||
DeviceModeDirect DeviceMode = iota | ||
|
||
// DeviceModeResourceManaged requests a resource managed TPM2 device. | ||
// These devices can be opened more than once and sharedi, relying on | ||
// the resource manager to handle context switching between users | ||
// (although they can't be shared with a direct device). | ||
DeviceModeResourceManaged | ||
|
||
// DeviceModeTryResourceManaged is like DeviceModeResourceManaged except | ||
// it will return a direct device if a resource managed device is not | ||
// available. Some older linux kernels do not support an in-kernel resource | ||
// manager. | ||
DeviceModeTryResourceManaged | ||
) | ||
|
||
var ( | ||
// ErrNoTPM2Device indicates that no TPM2 device is available. | ||
ErrNoTPM2Device = errors.New("no TPM2 device is available") | ||
|
||
// ErrNoResourceManagedTPM2Device indicates that there is no resource | ||
// managed TPM2 device option available. | ||
ErrNoResourceManagedTPM2Device = errors.New("no resource managed TPM2 device available") | ||
|
||
// ErrNoPPI indicates that no physical presence interface is available. | ||
ErrNoPPI = errors.New("no physical presence interface available") | ||
) | ||
|
||
type tpmDevice struct { | ||
tpm2.TPMDevice | ||
|
||
mode DeviceMode | ||
|
||
ppi ppi.PPI | ||
ppiErr error | ||
} | ||
|
||
func (d *tpmDevice) Mode() DeviceMode { | ||
return d.mode | ||
} | ||
|
||
func (d *tpmDevice) PPI() (ppi.PPI, error) { | ||
if d.ppiErr != nil { | ||
return nil, d.ppiErr | ||
} | ||
if d.ppi == nil { | ||
return nil, ErrNoPPI | ||
} | ||
return d.ppi, nil | ||
} | ||
|
||
// TPMDevice corresponds to a [tpm2.TPMDevice] with some extra features. | ||
type TPMDevice interface { | ||
tpm2.TPMDevice | ||
Mode() DeviceMode // either DeviceModeDirect or DeviceModeResourceManaged | ||
PPI() (ppi.PPI, error) // provide access to the physical presence interface | ||
} | ||
|
||
// DefaultDevice returns the default TPM device. The specified mode controls what kind | ||
// of device to return, if available. | ||
var DefaultDevice = func(DeviceMode) (TPMDevice, error) { | ||
return nil, ErrNoTPM2Device | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
// -*- Mode: Go; indent-tabs-mode: t -*- | ||
|
||
/* | ||
* Copyright (C) 2024 Canonical Ltd | ||
* | ||
* This program is free software: you can redistribute it and/or modify | ||
* it under the terms of the GNU General Public License version 3 as | ||
* published by the Free Software Foundation. | ||
* | ||
* This program is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
* GNU General Public License for more details. | ||
* | ||
* You should have received a copy of the GNU General Public License | ||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
* | ||
*/ | ||
|
||
package tpm2_device | ||
|
||
import ( | ||
"errors" | ||
|
||
"github.com/canonical/go-tpm2/linux" | ||
) | ||
|
||
var ( | ||
linuxDefaultTPM2Device = linux.DefaultTPM2Device | ||
linuxRawDeviceResourceManagedDevice = (*linux.RawDevice).ResourceManagedDevice | ||
linuxRawDevicePhysicalPresenceInterface = (*linux.RawDevice).PhysicalPresenceInterface | ||
linuxRMDeviceRawDevice = (*linux.RMDevice).RawDevice | ||
) | ||
|
||
func newTpmDeviceDirect(dev *linux.RawDevice) TPMDevice { | ||
ppi, err := linuxRawDevicePhysicalPresenceInterface(dev) | ||
if errors.Is(err, linux.ErrNoPhysicalPresenceInterface) { | ||
err = nil | ||
} | ||
return &tpmDevice{ | ||
TPMDevice: dev, | ||
mode: DeviceModeDirect, | ||
ppi: ppi, | ||
ppiErr: err, | ||
} | ||
} | ||
|
||
func newTpmDeviceRM(dev *linux.RMDevice) TPMDevice { | ||
ppi, err := linuxRawDevicePhysicalPresenceInterface(linuxRMDeviceRawDevice(dev)) | ||
if errors.Is(err, linux.ErrNoPhysicalPresenceInterface) { | ||
err = nil | ||
} | ||
return &tpmDevice{ | ||
TPMDevice: dev, | ||
mode: DeviceModeResourceManaged, | ||
ppi: ppi, | ||
ppiErr: err, | ||
} | ||
} | ||
|
||
func init() { | ||
DefaultDevice = func(mode DeviceMode) (TPMDevice, error) { | ||
rawDev, err := linuxDefaultTPM2Device() | ||
switch { | ||
case errors.Is(err, linux.ErrDefaultNotTPM2Device) || errors.Is(err, linux.ErrNoTPMDevices): | ||
// Either there are no TPM devices or the default device is a TPM1.2 device | ||
return nil, ErrNoTPM2Device | ||
case err != nil: | ||
return nil, err | ||
} | ||
|
||
if mode == DeviceModeDirect { | ||
// Return the direct device | ||
return newTpmDeviceDirect(rawDev), nil | ||
} | ||
|
||
rmDev, err := linuxRawDeviceResourceManagedDevice(rawDev) | ||
switch { | ||
case errors.Is(err, linux.ErrNoResourceManagedDevice) && mode == DeviceModeTryResourceManaged: | ||
// No in-kernel resource manager, but the mode allows us to return the direct device | ||
return newTpmDeviceDirect(rawDev), nil | ||
case errors.Is(err, linux.ErrNoResourceManagedDevice): | ||
// No in-kernel resource manager, return an error | ||
return nil, ErrNoResourceManagedTPM2Device | ||
case err != nil: | ||
return nil, err | ||
} | ||
|
||
// Return the resource managed device | ||
return newTpmDeviceRM(rmDev), nil | ||
} | ||
} |
Oops, something went wrong.