This repository has no description
13 kB
312 lines
1diff --git a/agent/agent.go b/agent/agent.go
2index 76a2610..e643575 100644
3--- a/agent/agent.go
4+++ b/agent/agent.go
5@@ -25,6 +25,7 @@ import (
6 "github.com/google/go-tpm/tpm2/transport"
7 "golang.org/x/crypto/ssh"
8 "golang.org/x/crypto/ssh/agent"
9+ "golang.org/x/sys/unix"
10 "golang.org/x/text/cases"
11 "golang.org/x/text/language"
12 )
13@@ -37,17 +38,18 @@ var (
14 var SSH_TPM_AGENT_ADD = "tpm-add-key"
15
16 type Agent struct {
17- mu sync.Mutex
18- tpm func() transport.TPMCloser
19- op func() ([]byte, error)
20- pin func(key.SSHTPMKeys) ([]byte, error)
21- listener *net.UnixListener
22- quit chan interface{}
23- wg sync.WaitGroup
24- keyring func() *keyring.ThreadKeyring
25- keys []key.SSHTPMKeys
26- hierkeys []*key.HierSSHTPMKey
27- agents []agent.ExtendedAgent
28+ mu sync.Mutex
29+ tpm func() transport.TPMCloser
30+ op func() ([]byte, error)
31+ pin func(key.SSHTPMKeys, int) ([]byte, error)
32+ listener *net.UnixListener
33+ quit chan interface{}
34+ wg sync.WaitGroup
35+ keyring func() *keyring.ThreadKeyring
36+ keys []key.SSHTPMKeys
37+ hierkeys []*key.HierSSHTPMKey
38+ agents []agent.ExtendedAgent
39+ clientPID int
40 }
41
42 var _ agent.ExtendedAgent = &Agent{}
43@@ -118,8 +120,8 @@ func (a *Agent) signers() ([]ssh.Signer, error) {
44 s, err := ssh.NewSignerFromSigner(k.Signer(
45 a.keyring(), a.op, a.tpm,
46 func(_ *keyfile.TPMKey) ([]byte, error) {
47- // Shimming the function to get the correct type
48- return a.pin(k)
49+ // Shimming the function to get the correct type, pass client PID
50+ return a.pin(k, a.clientPID)
51 }),
52 )
53 if err != nil {
54@@ -225,6 +227,21 @@ func (a *Agent) Sign(key ssh.PublicKey, data []byte) (*ssh.Signature, error) {
55 }
56
57 func (a *Agent) serveConn(c net.Conn) {
58+ // Extract peer credentials to get the client PID
59+ if uc, ok := c.(*net.UnixConn); ok {
60+ rawConn, err := uc.SyscallConn()
61+ if err == nil {
62+ rawConn.Control(func(fd uintptr) {
63+ if cred, err := unix.GetsockoptUcred(int(fd), unix.SOL_SOCKET, unix.SO_PEERCRED); err == nil {
64+ a.mu.Lock()
65+ a.clientPID = int(cred.Pid)
66+ a.mu.Unlock()
67+ slog.Debug("client connected", slog.Int("pid", int(cred.Pid)))
68+ }
69+ })
70+ }
71+ }
72+
73 if err := agent.ServeAgent(a, c); err != io.EOF {
74 slog.Info("Agent client connection ended unsuccessfully", slog.String("error", err.Error()))
75 }
76@@ -461,7 +478,7 @@ func LoadKeys(keyDir string) ([]key.SSHTPMKeys, error) {
77 return keys, err
78 }
79
80-func NewAgent(listener *net.UnixListener, agents []agent.ExtendedAgent, keyring func() *keyring.ThreadKeyring, tpmFetch func() transport.TPMCloser, ownerPassword func() ([]byte, error), pin func(key.SSHTPMKeys) ([]byte, error)) *Agent {
81+func NewAgent(listener *net.UnixListener, agents []agent.ExtendedAgent, keyring func() *keyring.ThreadKeyring, tpmFetch func() transport.TPMCloser, ownerPassword func() ([]byte, error), pin func(key.SSHTPMKeys, int) ([]byte, error)) *Agent {
82 a := &Agent{
83 agents: agents,
84 tpm: tpmFetch,
85diff --git a/agent/agent_test.go b/agent/agent_test.go
86index acaec3d..189b607 100644
87--- a/agent/agent_test.go
88+++ b/agent/agent_test.go
89@@ -45,7 +45,7 @@ func TestAddKey(t *testing.T) {
90 // Owner password
91 func() ([]byte, error) { return []byte(""), nil },
92 // PIN Callback
93- func(_ key.SSHTPMKeys) ([]byte, error) { return []byte(""), nil },
94+ func(_ key.SSHTPMKeys, _ int) ([]byte, error) { return []byte(""), nil },
95 )
96 defer ag.Stop()
97
98diff --git a/askpass/askpass.go b/askpass/askpass.go
99index 8ef6370..0653165 100644
100--- a/askpass/askpass.go
101+++ b/askpass/askpass.go
102@@ -8,6 +8,7 @@ import (
103 "log/slog"
104 "os"
105 "os/exec"
106+ "strconv"
107 "strings"
108 "syscall"
109
110@@ -66,7 +67,7 @@ func isTerminal(fd uintptr) bool {
111 return err == nil
112 }
113
114-func ReadPassphrase(prompt string, flags ReadPassFlags) ([]byte, error) {
115+func ReadPassphrase(prompt string, flags ReadPassFlags, clientPID int) ([]byte, error) {
116 var allow_askpass bool
117 var use_askpass bool
118 var askpass_hint string
119@@ -104,7 +105,7 @@ func ReadPassphrase(prompt string, flags ReadPassFlags) ([]byte, error) {
120 if (flags & RP_ASK_PERMISSION) != 0 {
121 askpass_hint = "confirm"
122 }
123- return SshAskPass(prompt, askpass_hint)
124+ return SshAskPass(prompt, askpass_hint, clientPID)
125 }
126
127 // If we want to echo stuff, we read directly from stdin
128@@ -130,7 +131,7 @@ func ReadPassphrase(prompt string, flags ReadPassFlags) ([]byte, error) {
129 return pin, nil
130 }
131
132-func SshAskPass(prompt, hint string) ([]byte, error) {
133+func SshAskPass(prompt, hint string, clientPID int) ([]byte, error) {
134 var askpass string
135 var err error
136 if s, ok := os.LookupEnv("SSH_ASKPASS"); ok {
137@@ -147,6 +148,11 @@ func SshAskPass(prompt, hint string) ([]byte, error) {
138 if hint != "" {
139 os.Setenv("SSH_ASKPASS_PROMPT", hint)
140 }
141+
142+ if clientPID > 0 {
143+ os.Setenv("SSH_CLIENT_PID", strconv.Itoa(clientPID))
144+ }
145+
146 out, err := exec.Command(askpass, prompt).Output()
147 switch hint {
148 case "confirm":
149@@ -169,8 +175,8 @@ func SshAskPass(prompt, hint string) ([]byte, error) {
150
151 // AskPremission runs SSH_ASKPASS in with SSH_ASKPASS_PROMPT=confirm set as env
152 // it will expect exit code 0 or !0 and return 'yes' and 'no' respectively.
153-func AskPermission() (bool, error) {
154- a, err := ReadPassphrase("Confirm touch", RP_USE_ASKPASS|RP_ASK_PERMISSION)
155+func AskPermission(clientPID int) (bool, error) {
156+ a, err := ReadPassphrase("Confirm touch", RP_USE_ASKPASS|RP_ASK_PERMISSION, clientPID)
157 if err != nil {
158 return false, err
159 }
160diff --git a/cmd/ssh-tpm-agent/main.go b/cmd/ssh-tpm-agent/main.go
161index 1b76351..797428c 100644
162--- a/cmd/ssh-tpm-agent/main.go
163+++ b/cmd/ssh-tpm-agent/main.go
164@@ -243,7 +243,7 @@ func main() {
165 // Owner password
166 func() ([]byte, error) {
167 if askOwnerPassword {
168- return askpass.ReadPassphrase("Enter owner password for TPM", askpass.RP_USE_ASKPASS)
169+ return askpass.ReadPassphrase("Enter owner password for TPM", askpass.RP_USE_ASKPASS, 0)
170 } else {
171 ownerPassword := os.Getenv("SSH_TPM_AGENT_OWNER_PASSWORD")
172 return []byte(ownerPassword), nil
173@@ -253,7 +253,7 @@ func main() {
174 // PIN Callback with caching
175 // SSHKeySigner in signer/signer.go resets this value if
176 // we get a TPMRCAuthFail
177- func(key key.SSHTPMKeys) ([]byte, error) {
178+ func(key key.SSHTPMKeys, clientPID int) ([]byte, error) {
179 auth, err := agentkeyring.ReadKey(key.Fingerprint())
180 if err == nil {
181 slog.Debug("providing cached userauth for key", slog.String("fp", key.Fingerprint()))
182@@ -261,8 +261,8 @@ func main() {
183 return auth.Read(), nil
184 } else if errors.Is(err, syscall.ENOKEY) || errors.Is(err, syscall.EACCES) {
185 keyInfo := fmt.Sprintf("Enter passphrase for (%s): ", key.GetDescription())
186- // TODOt kjk: askpass should box the byte slice
187- userauth, err := askpass.ReadPassphrase(keyInfo, askpass.RP_USE_ASKPASS)
188+ // TODO: askpass should box the byte slice
189+ userauth, err := askpass.ReadPassphrase(keyInfo, askpass.RP_USE_ASKPASS, clientPID)
190 fmt.Println(err)
191 if !noCache && err == nil {
192 slog.Debug("caching userauth for key in keyring", slog.String("fp", key.Fingerprint()))
193diff --git a/cmd/ssh-tpm-agent/main_test.go b/cmd/ssh-tpm-agent/main_test.go
194index 051febf..d2ff3c4 100644
195--- a/cmd/ssh-tpm-agent/main_test.go
196+++ b/cmd/ssh-tpm-agent/main_test.go
197@@ -144,7 +144,7 @@ func runSSHAuth(t *testing.T, keytype tpm2.TPMAlgID, bits int, pin []byte, keyfn
198 // Owner password
199 func() ([]byte, error) { return []byte(""), nil },
200 // PIN Callback
201- func(_ key.SSHTPMKeys) ([]byte, error) {
202+ func(_ key.SSHTPMKeys, _ int) ([]byte, error) {
203 return pin, nil
204 },
205 )
206diff --git a/cmd/ssh-tpm-keygen/main.go b/cmd/ssh-tpm-keygen/main.go
207index f5d9421..12073b0 100644
208--- a/cmd/ssh-tpm-keygen/main.go
209+++ b/cmd/ssh-tpm-keygen/main.go
210@@ -89,11 +89,11 @@ Example:
211
212 func getPin() ([]byte, error) {
213 for {
214- pin1, err := askpass.ReadPassphrase("Enter passphrase (empty for no passphrase): ", askpass.RP_ALLOW_STDIN|askpass.RP_NEWLINE)
215+ pin1, err := askpass.ReadPassphrase("Enter passphrase (empty for no passphrase): ", askpass.RP_ALLOW_STDIN|askpass.RP_NEWLINE, 0)
216 if err != nil {
217 return nil, err
218 }
219- pin2, err := askpass.ReadPassphrase("Enter same passphrase again: ", askpass.RP_ALLOW_STDIN|askpass.RP_NEWLINE)
220+ pin2, err := askpass.ReadPassphrase("Enter same passphrase again: ", askpass.RP_ALLOW_STDIN|askpass.RP_NEWLINE, 0)
221 if err != nil {
222 return nil, err
223 }
224@@ -106,7 +106,7 @@ func getPin() ([]byte, error) {
225 }
226
227 func getOwnerPassword() ([]byte, error) {
228- return askpass.ReadPassphrase("Enter owner password: ", askpass.RP_ALLOW_STDIN)
229+ return askpass.ReadPassphrase("Enter owner password: ", askpass.RP_ALLOW_STDIN, 0)
230 }
231
232 func doHostKeys(tpm transport.TPMCloser, outputFile string, ownerPassword []byte, hierarchy string) {
233@@ -191,7 +191,7 @@ func doChangePin(tpm transport.TPMCloser, passphrase, keyPin, ownerPassword []by
234 fmt.Printf("Key has comment '%s'\n", k.Description)
235 }
236 if outputFile == "" {
237- f, err := askpass.ReadPassphrase(fmt.Sprintf("Enter file in which the key is (%s): ", filename), askpass.RP_ALLOW_STDIN|askpass.RPP_ECHO_ON)
238+ f, err := askpass.ReadPassphrase(fmt.Sprintf("Enter file in which the key is (%s): ", filename), askpass.RP_ALLOW_STDIN|askpass.RPP_ECHO_ON, 0)
239 if err != nil {
240 return err
241 }
242@@ -199,18 +199,18 @@ func doChangePin(tpm transport.TPMCloser, passphrase, keyPin, ownerPassword []by
243 }
244
245 if len(passphrase) == 0 {
246- passphrase, err = askpass.ReadPassphrase("Enter old passphrase: ", askpass.RP_ALLOW_STDIN|askpass.RP_NEWLINE)
247+ passphrase, err = askpass.ReadPassphrase("Enter old passphrase: ", askpass.RP_ALLOW_STDIN|askpass.RP_NEWLINE, 0)
248 if err != nil {
249 return err
250 }
251 }
252
253 if len(keyPin) == 0 {
254- keyPin, err = askpass.ReadPassphrase("Enter new passphrase (empty for no passphrase): ", askpass.RP_ALLOW_STDIN|askpass.RP_NEWLINE)
255+ keyPin, err = askpass.ReadPassphrase("Enter new passphrase (empty for no passphrase): ", askpass.RP_ALLOW_STDIN|askpass.RP_NEWLINE, 0)
256 if err != nil {
257 return err
258 }
259- newPin, err := askpass.ReadPassphrase("Enter same passphrase: ", askpass.RP_ALLOW_STDIN)
260+ newPin, err := askpass.ReadPassphrase("Enter same passphrase: ", askpass.RP_ALLOW_STDIN, 0)
261 if err != nil {
262 return err
263 }
264@@ -256,7 +256,7 @@ func doWrapWith(supportedECCBitsizes []int, wrap, wrapWith string, keyParentHand
265 rawKey, err = ssh.ParseRawPrivateKey(pem)
266 if errors.As(err, &kerr) {
267 for {
268- pin, err := askpass.ReadPassphrase("Enter existing passphrase (empty for no passphrase): ", askpass.RP_ALLOW_STDIN|askpass.RP_NEWLINE)
269+ pin, err := askpass.ReadPassphrase("Enter existing passphrase (empty for no passphrase): ", askpass.RP_ALLOW_STDIN|askpass.RP_NEWLINE, 0)
270 if err != nil {
271 log.Fatal(err)
272 }
273@@ -353,7 +353,7 @@ func supportedECCBitsizes(tpm transport.TPMCloser) []int {
274 func checkFile(f string) error {
275 if utils.FileExists(f) {
276 fmt.Printf("%s already exists.\n", f)
277- s, err := askpass.ReadPassphrase("Overwrite (y/n)? ", askpass.RP_ALLOW_STDIN|askpass.RPP_ECHO_ON)
278+ s, err := askpass.ReadPassphrase("Overwrite (y/n)? ", askpass.RP_ALLOW_STDIN|askpass.RPP_ECHO_ON, 0)
279 if err != nil {
280 return err
281 }
282@@ -373,7 +373,7 @@ func doImportKey(tpm transport.TPMCloser, keyParentHandle tpm2.TPMHandle, ownerP
283 rawKey, err = ssh.ParseRawPrivateKey(pem)
284 if errors.As(err, &kerr) {
285 for {
286- pin, err := askpass.ReadPassphrase("Enter existing passphrase (empty for no passphrase): ", askpass.RP_ALLOW_STDIN|askpass.RP_NEWLINE)
287+ pin, err := askpass.ReadPassphrase("Enter existing passphrase (empty for no passphrase): ", askpass.RP_ALLOW_STDIN|askpass.RP_NEWLINE, 0)
288 if err != nil {
289 return nil, err
290 }
291@@ -665,7 +665,7 @@ func main() {
292 // Else create a normal key
293 fmt.Printf("Generating a sealed public/private %s key pair.\n", keyType)
294 if outputFile == "" {
295- f, err := askpass.ReadPassphrase(fmt.Sprintf("Enter file in which to save the key (%s): ", filename), askpass.RP_ALLOW_STDIN|askpass.RPP_ECHO_ON)
296+ f, err := askpass.ReadPassphrase(fmt.Sprintf("Enter file in which to save the key (%s): ", filename), askpass.RP_ALLOW_STDIN|askpass.RPP_ECHO_ON, 0)
297 if err != nil {
298 log.Fatal(err)
299 }
300diff --git a/internal/keytest/keytest.go b/internal/keytest/keytest.go
301index ade4ad8..90fcf77 100644
302--- a/internal/keytest/keytest.go
303+++ b/internal/keytest/keytest.go
304@@ -121,7 +121,7 @@ func NewTestAgent(t *testing.T, tpm transport.TPMCloser) *agent.Agent {
305 func() *keyring.ThreadKeyring { return &keyring.ThreadKeyring{} },
306 func() transport.TPMCloser { return tpm },
307 func() ([]byte, error) { return []byte(""), nil },
308- func(_ key.SSHTPMKeys) ([]byte, error) {
309+ func(_ key.SSHTPMKeys, _ int) ([]byte, error) {
310 return []byte(""), nil
311 },
312 )