This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

superconfig / pkgs / ssh-tpm-agent / client-pid.patch
13 kB 320 lines
1diff --git a/agent/agent.go b/agent/agent.go 2index ac39d24..b1e399b 100644 3--- a/agent/agent.go 4+++ b/agent/agent.go 5@@ -26,6 +26,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@@ -39,17 +40,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 any 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 any 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@@ -120,8 +122,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@@ -260,7 +262,7 @@ func (a *Agent) confirmKeyUse(wantKey []byte, peer string) error { 55 if peer != "" { 56 prompt += "\nRequested by " + peer 57 } 58- ok, err := askpass.AskPermission(prompt) 59+ ok, err := askpass.AskPermission(prompt, a.clientPID) 60 if err != nil { 61 slog.Info("askpass confirmation failed", slog.String("error", err.Error())) 62 return ErrUserDeniedConfirm 63@@ -289,6 +291,18 @@ func (c *connAgent) SignWithFlags(key ssh.PublicKey, data []byte, flags agent.Si 64 } 65 66 func (a *Agent) serveConn(c *net.UnixConn) { 67+ // Extract peer credentials to get the client PID 68+ if rc, err := c.SyscallConn(); err == nil { 69+ rc.Control(func(fd uintptr) { 70+ if cred, err := unix.GetsockoptUcred(int(fd), unix.SOL_SOCKET, unix.SO_PEERCRED); err == nil { 71+ a.mu.Lock() 72+ a.clientPID = int(cred.Pid) 73+ a.mu.Unlock() 74+ slog.Debug("client connected", slog.Int("pid", int(cred.Pid))) 75+ } 76+ }) 77+ } 78+ 79 ca := &connAgent{Agent: a, peer: peerChain(c)} 80 if err := agent.ServeAgent(ca, c); err != io.EOF { 81 slog.Info("Agent client connection ended unsuccessfully", slog.String("error", err.Error())) 82@@ -524,7 +538,7 @@ func LoadKeys(keyDir string) ([]key.SSHTPMKeys, error) { 83 return keys, err 84 } 85 86-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 { 87+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 { 88 a := &Agent{ 89 agents: agents, 90 tpm: tpmFetch, 91diff --git a/agent/agent_test.go b/agent/agent_test.go 92index acaec3d..189b607 100644 93--- a/agent/agent_test.go 94+++ b/agent/agent_test.go 95@@ -45,7 +45,7 @@ func TestAddKey(t *testing.T) { 96 // Owner password 97 func() ([]byte, error) { return []byte(""), nil }, 98 // PIN Callback 99- func(_ key.SSHTPMKeys) ([]byte, error) { return []byte(""), nil }, 100+ func(_ key.SSHTPMKeys, _ int) ([]byte, error) { return []byte(""), nil }, 101 ) 102 defer ag.Stop() 103 104diff --git a/askpass/askpass.go b/askpass/askpass.go 105index 9830586..aefe469 100644 106--- a/askpass/askpass.go 107+++ b/askpass/askpass.go 108@@ -8,6 +8,7 @@ import ( 109 "log/slog" 110 "os" 111 "os/exec" 112+ "strconv" 113 "strings" 114 "syscall" 115 116@@ -66,7 +67,7 @@ func isTerminal(fd uintptr) bool { 117 return err == nil 118 } 119 120-func ReadPassphrase(prompt string, flags ReadPassFlags) ([]byte, error) { 121+func ReadPassphrase(prompt string, flags ReadPassFlags, clientPID int) ([]byte, error) { 122 var allow_askpass bool 123 var use_askpass bool 124 var askpass_hint string 125@@ -104,7 +105,7 @@ func ReadPassphrase(prompt string, flags ReadPassFlags) ([]byte, error) { 126 if (flags & RP_ASK_PERMISSION) != 0 { 127 askpass_hint = "confirm" 128 } 129- return SshAskPass(prompt, askpass_hint) 130+ return SshAskPass(prompt, askpass_hint, clientPID) 131 } 132 133 // If we want to echo stuff, we read directly from stdin 134@@ -130,7 +131,7 @@ func ReadPassphrase(prompt string, flags ReadPassFlags) ([]byte, error) { 135 return pin, nil 136 } 137 138-func SshAskPass(prompt, hint string) ([]byte, error) { 139+func SshAskPass(prompt, hint string, clientPID int) ([]byte, error) { 140 var askpass string 141 var err error 142 if s, ok := os.LookupEnv("SSH_ASKPASS"); ok { 143@@ -144,7 +145,15 @@ func SshAskPass(prompt, hint string) ([]byte, error) { 144 } 145 } 146 147+ if clientPID > 0 && os.Getenv("SSH_CLIENT_PID") == "" { 148+ // Pass the requesting client's PID to pinentry so it can cache per-client. 149+ os.Setenv("SSH_CLIENT_PID", strconv.Itoa(clientPID)) 150+ } 151+ 152 cmd := exec.Command(askpass, prompt) 153+ if clientPID > 0 { 154+ cmd.Env = append(os.Environ(), "SSH_CLIENT_PID="+strconv.Itoa(clientPID)) 155+ } 156 if hint != "" { 157 // Set SSH_ASKPASS_PROMPT only on the subprocess. os.Setenv() would 158 // leak into subsequent passphrase prompts from the same agent 159@@ -173,8 +182,8 @@ func SshAskPass(prompt, hint string) ([]byte, error) { 160 161 // AskPermission runs SSH_ASKPASS with SSH_ASKPASS_PROMPT=confirm set as env. 162 // It will expect exit code 0 or !0 and return true and false respectively. 163-func AskPermission(prompt string) (bool, error) { 164- a, err := ReadPassphrase(prompt, RP_USE_ASKPASS|RP_ASK_PERMISSION) 165+func AskPermission(prompt string, clientPID int) (bool, error) { 166+ a, err := ReadPassphrase(prompt, RP_USE_ASKPASS|RP_ASK_PERMISSION, clientPID) 167 if err != nil { 168 return false, err 169 } 170diff --git a/cmd/ssh-tpm-agent/main.go b/cmd/ssh-tpm-agent/main.go 171index 7c6f397..8e19473 100644 172--- a/cmd/ssh-tpm-agent/main.go 173+++ b/cmd/ssh-tpm-agent/main.go 174@@ -243,7 +243,7 @@ func main() { 175 // Owner password 176 func() ([]byte, error) { 177 if askOwnerPassword { 178- return askpass.ReadPassphrase("Enter owner password for TPM", askpass.RP_USE_ASKPASS) 179+ return askpass.ReadPassphrase("Enter owner password for TPM", askpass.RP_USE_ASKPASS, 0) 180 } else { 181 ownerPassword := os.Getenv("SSH_TPM_AGENT_OWNER_PASSWORD") 182 return []byte(ownerPassword), nil 183@@ -253,7 +253,7 @@ func main() { 184 // PIN Callback with caching 185 // SSHKeySigner in signer/signer.go resets this value if 186 // we get a TPMRCAuthFail 187- func(key key.SSHTPMKeys) ([]byte, error) { 188+ func(key key.SSHTPMKeys, clientPID int) ([]byte, error) { 189 auth, err := agentkeyring.ReadKey(key.Fingerprint()) 190 switch { 191 case errors.Is(err, syscall.ENOENT): 192@@ -262,7 +262,7 @@ func main() { 193 case errors.Is(err, syscall.ENOKEY) || errors.Is(err, syscall.EACCES): 194 keyInfo := fmt.Sprintf("Enter passphrase for (%s): ", key.GetDescription()) 195 // TODO: askpass should box the byte slice 196- userauth, err := askpass.ReadPassphrase(keyInfo, askpass.RP_USE_ASKPASS) 197+ userauth, err := askpass.ReadPassphrase(keyInfo, askpass.RP_USE_ASKPASS, clientPID) 198 fmt.Println(err) 199 if !noCache && err == nil { 200 slog.Debug("caching userauth for key in keyring", slog.String("fp", key.Fingerprint())) 201diff --git a/cmd/ssh-tpm-agent/main_test.go b/cmd/ssh-tpm-agent/main_test.go 202index 8335058..89697f2 100644 203--- a/cmd/ssh-tpm-agent/main_test.go 204+++ b/cmd/ssh-tpm-agent/main_test.go 205@@ -144,7 +144,7 @@ func runSSHAuth(t *testing.T, keytype tpm2.TPMAlgID, bits int, pin []byte, keyfn 206 // Owner password 207 func() ([]byte, error) { return []byte(""), nil }, 208 // PIN Callback 209- func(_ key.SSHTPMKeys) ([]byte, error) { 210+ func(_ key.SSHTPMKeys, _ int) ([]byte, error) { 211 return pin, nil 212 }, 213 ) 214diff --git a/cmd/ssh-tpm-keygen/main.go b/cmd/ssh-tpm-keygen/main.go 215index 4002026..eae8d39 100644 216--- a/cmd/ssh-tpm-keygen/main.go 217+++ b/cmd/ssh-tpm-keygen/main.go 218@@ -89,11 +89,11 @@ Example: 219 220 func getPin() ([]byte, error) { 221 for { 222- pin1, err := askpass.ReadPassphrase("Enter passphrase (empty for no passphrase): ", askpass.RP_ALLOW_STDIN|askpass.RP_NEWLINE) 223+ pin1, err := askpass.ReadPassphrase("Enter passphrase (empty for no passphrase): ", askpass.RP_ALLOW_STDIN|askpass.RP_NEWLINE, 0) 224 if err != nil { 225 return nil, err 226 } 227- pin2, err := askpass.ReadPassphrase("Enter same passphrase again: ", askpass.RP_ALLOW_STDIN|askpass.RP_NEWLINE) 228+ pin2, err := askpass.ReadPassphrase("Enter same passphrase again: ", askpass.RP_ALLOW_STDIN|askpass.RP_NEWLINE, 0) 229 if err != nil { 230 return nil, err 231 } 232@@ -106,7 +106,7 @@ func getPin() ([]byte, error) { 233 } 234 235 func getOwnerPassword() ([]byte, error) { 236- return askpass.ReadPassphrase("Enter owner password: ", askpass.RP_ALLOW_STDIN) 237+ return askpass.ReadPassphrase("Enter owner password: ", askpass.RP_ALLOW_STDIN, 0) 238 } 239 240 func doHostKeys(tpm transport.TPMCloser, outputFile string, ownerPassword []byte, hierarchy string) { 241@@ -191,7 +191,7 @@ func doChangePin(tpm transport.TPMCloser, passphrase, keyPin, ownerPassword []by 242 fmt.Printf("Key has comment '%s'\n", k.Description) 243 } 244 if outputFile == "" { 245- f, err := askpass.ReadPassphrase(fmt.Sprintf("Enter file in which the key is (%s): ", filename), askpass.RP_ALLOW_STDIN|askpass.RPP_ECHO_ON) 246+ f, err := askpass.ReadPassphrase(fmt.Sprintf("Enter file in which the key is (%s): ", filename), askpass.RP_ALLOW_STDIN|askpass.RPP_ECHO_ON, 0) 247 if err != nil { 248 return err 249 } 250@@ -202,18 +202,18 @@ func doChangePin(tpm transport.TPMCloser, passphrase, keyPin, ownerPassword []by 251 } 252 253 if len(passphrase) == 0 { 254- passphrase, err = askpass.ReadPassphrase("Enter old passphrase: ", askpass.RP_ALLOW_STDIN|askpass.RP_NEWLINE) 255+ passphrase, err = askpass.ReadPassphrase("Enter old passphrase: ", askpass.RP_ALLOW_STDIN|askpass.RP_NEWLINE, 0) 256 if err != nil { 257 return err 258 } 259 } 260 261 if len(keyPin) == 0 { 262- keyPin, err = askpass.ReadPassphrase("Enter new passphrase (empty for no passphrase): ", askpass.RP_ALLOW_STDIN|askpass.RP_NEWLINE) 263+ keyPin, err = askpass.ReadPassphrase("Enter new passphrase (empty for no passphrase): ", askpass.RP_ALLOW_STDIN|askpass.RP_NEWLINE, 0) 264 if err != nil { 265 return err 266 } 267- newPin, err := askpass.ReadPassphrase("Enter same passphrase: ", askpass.RP_ALLOW_STDIN) 268+ newPin, err := askpass.ReadPassphrase("Enter same passphrase: ", askpass.RP_ALLOW_STDIN, 0) 269 if err != nil { 270 return err 271 } 272@@ -260,7 +260,7 @@ func doWrapWith(supportedECCBitsizes []int, wrap, wrapWith string, keyParentHand 273 rawKey, err = ssh.ParseRawPrivateKey(pem) 274 if errors.As(err, &kerr) { 275 for { 276- pin, err := askpass.ReadPassphrase("Enter existing passphrase (empty for no passphrase): ", askpass.RP_ALLOW_STDIN|askpass.RP_NEWLINE) 277+ pin, err := askpass.ReadPassphrase("Enter existing passphrase (empty for no passphrase): ", askpass.RP_ALLOW_STDIN|askpass.RP_NEWLINE, 0) 278 if err != nil { 279 log.Fatal(err) 280 } 281@@ -358,7 +358,7 @@ func supportedECCBitsizes(tpm transport.TPMCloser) []int { 282 func checkFile(f string) error { 283 if utils.FileExists(f) { 284 fmt.Printf("%s already exists.\n", f) 285- s, err := askpass.ReadPassphrase("Overwrite (y/n)? ", askpass.RP_ALLOW_STDIN|askpass.RPP_ECHO_ON) 286+ s, err := askpass.ReadPassphrase("Overwrite (y/n)? ", askpass.RP_ALLOW_STDIN|askpass.RPP_ECHO_ON, 0) 287 if err != nil { 288 return err 289 } 290@@ -378,7 +378,7 @@ func doImportKey(tpm transport.TPMCloser, keyParentHandle tpm2.TPMHandle, ownerP 291 rawKey, err = ssh.ParseRawPrivateKey(pem) 292 if errors.As(err, &kerr) { 293 for { 294- pin, err := askpass.ReadPassphrase("Enter existing passphrase (empty for no passphrase): ", askpass.RP_ALLOW_STDIN|askpass.RP_NEWLINE) 295+ pin, err := askpass.ReadPassphrase("Enter existing passphrase (empty for no passphrase): ", askpass.RP_ALLOW_STDIN|askpass.RP_NEWLINE, 0) 296 if err != nil { 297 return nil, err 298 } 299@@ -670,7 +670,7 @@ func main() { 300 // Else create a normal key 301 fmt.Printf("Generating a sealed public/private %s key pair.\n", keyType) 302 if outputFile == "" { 303- f, err := askpass.ReadPassphrase(fmt.Sprintf("Enter file in which to save the key (%s): ", filename), askpass.RP_ALLOW_STDIN|askpass.RPP_ECHO_ON) 304+ 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) 305 if err != nil { 306 log.Fatal(err) 307 } 308diff --git a/internal/keytest/keytest.go b/internal/keytest/keytest.go 309index ade4ad8..90fcf77 100644 310--- a/internal/keytest/keytest.go 311+++ b/internal/keytest/keytest.go 312@@ -121,7 +121,7 @@ func NewTestAgent(t *testing.T, tpm transport.TPMCloser) *agent.Agent { 313 func() *keyring.ThreadKeyring { return &keyring.ThreadKeyring{} }, 314 func() transport.TPMCloser { return tpm }, 315 func() ([]byte, error) { return []byte(""), nil }, 316- func(_ key.SSHTPMKeys) ([]byte, error) { 317+ func(_ key.SSHTPMKeys, _ int) ([]byte, error) { 318 return []byte(""), nil 319 }, 320 )