File: linuxudstpm.go

package info (click to toggle)
golang-github-google-go-tpm 0.9.5-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 1,932 kB
  • sloc: makefile: 13
file content (94 lines) | stat: -rw-r--r-- 2,737 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
//go:build !windows

// Package linuxudstpm provides access to a TPM device via a Unix domain socket.
package linuxudstpm

import (
	"errors"
	"fmt"
	"net"
	"os"

	"github.com/google/go-tpm/tpm2/transport"
)

var (
	// ErrFileIsNotSocket indicates that the TPM file is not a socket.
	ErrFileIsNotSocket = errors.New("TPM file is not a socket")
	// ErrMustCallWriteThenRead indicates that the file was not written-then-read in the expected pattern.
	ErrMustCallWriteThenRead = errors.New("must call Write then Read in an alternating sequence")
	// ErrNotOpen indicates that the TPM file is not currently open.
	ErrNotOpen = errors.New("no connection is open")
)

// Open opens the TPM socket at the given path.
func Open(path string) (transport.TPMCloser, error) {
	fi, err := os.Stat(path)
	if err != nil {
		return nil, err
	}

	if fi.Mode()&os.ModeSocket == 0 {
		return nil, fmt.Errorf("%w: %s (%s)", ErrFileIsNotSocket, fi.Mode().String(), path)
	}
	return transport.FromReadWriteCloser(newEmulatorReadWriteCloser(path)), nil
}

// dialer abstracts the net.Dial call so test code can provide its own net.Conn
// implementation.
type dialer func(network, path string) (net.Conn, error)

// emulatorReadWriteCloser manages connections with a TPM emulator over a Unix
// domain socket. These emulators often operate in a write/read/disconnect
// sequence, so the Write method always connects, and the Read method always
// closes. emulatorReadWriteCloser is not thread safe.
type emulatorReadWriteCloser struct {
	path   string
	conn   net.Conn
	dialer dialer
}

// newEmulatorReadWriteCloser stores information about a Unix domain socket to
// write to and read from.
func newEmulatorReadWriteCloser(path string) *emulatorReadWriteCloser {
	return &emulatorReadWriteCloser{
		path:   path,
		dialer: net.Dial,
	}
}

// Read implements the io.Reader interface.
func (erw *emulatorReadWriteCloser) Read(p []byte) (int, error) {
	// Read is always the second operation in a Write/Read sequence.
	if erw.conn == nil {
		return 0, ErrMustCallWriteThenRead
	}
	n, err := erw.conn.Read(p)
	erw.conn.Close()
	erw.conn = nil
	return n, err
}

// Write implements the io.Writer interface.
func (erw *emulatorReadWriteCloser) Write(p []byte) (int, error) {
	if erw.conn != nil {
		return 0, ErrMustCallWriteThenRead
	}
	var err error
	erw.conn, err = erw.dialer("unix", erw.path)
	if err != nil {
		return 0, err
	}
	return erw.conn.Write(p)
}

// Close implements the io.Closer interface.
func (erw *emulatorReadWriteCloser) Close() error {
	if erw.conn == nil {
		// This is an expected possible state, e.g., if someone sent the TPM a command and didn't read the response.
		return nil
	}
	err := erw.conn.Close()
	erw.conn = nil
	return err
}