From e3d1254f1e7e60baa086142c46174bf6d8d0fe50 Mon Sep 17 00:00:00 2001 From: Nicola Date: Sun, 1 Feb 2026 14:55:12 +0100 Subject: [PATCH] ssh/agent: don't accept keys with unsupported constraints The in-memory keyring cannot enforce constraint extensions, so silently accepting a key that carries them gave callers a false sense of restriction. Refuse keys with constraint extensions instead: a key whose constraints cannot be enforced must not be loaded. This behavior is consistent with OpenSSH. This is a deliberate behavior change: keyring.Add previously accepted and ignored ConstraintExtensions and now returns an error. This issue was found during a security audit by NCC Group Cryptography Services, sponsored by Teleport. Fixes CVE-2026-39832 Fixes golang/go#79435 Change-Id: I6ca4f1c29f8edfabb287fe07299641f70896d5fe Reviewed-on: https://go-review.googlesource.com/c/crypto/+/778641 Auto-Submit: Neal Patel LUCI-TryBot-Result: golang-scoped@luci-project-accounts.iam.gserviceaccount.com Reviewed-by: Neal Patel Reviewed-by: Dmitri Shuralyov Reviewed-by: Neal Patel --- ssh/agent/keyring.go | 12 +++++++++--- ssh/agent/keyring_test.go | 24 ++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/ssh/agent/keyring.go b/ssh/agent/keyring.go index d129875..75328bb 100644 --- a/ssh/agent/keyring.go +++ b/ssh/agent/keyring.go @@ -143,15 +143,21 @@ func (r *keyring) List() ([]*Key, error) { return ids, nil } -// Insert adds a private key to the keyring. If a certificate -// is given, that certificate is added as public key. Note that -// any constraints given are ignored. +// Add adds a private key to the keyring. If a certificate is given, that +// certificate is added as public key. +// +// Add returns an error if key contains ConstraintExtensions. func (r *keyring) Add(key AddedKey) error { r.mu.Lock() defer r.mu.Unlock() if r.locked { return errLocked } + + if len(key.ConstraintExtensions) > 0 { + return errors.New("agent: constraint extensions are present but not supported") + } + signer, err := ssh.NewSignerFromKey(key.PrivateKey) if err != nil { diff --git a/ssh/agent/keyring_test.go b/ssh/agent/keyring_test.go index 5e495d5..6c08252 100644 --- a/ssh/agent/keyring_test.go +++ b/ssh/agent/keyring_test.go @@ -120,3 +120,27 @@ func TestAddDuplicateKey(t *testing.T) { t.Fatal("key with the updated comment not found") } } + +func TestAddKeyWithConstraints(t *testing.T) { + // Verifies the keyring refuses keys carrying constraint extensions it + // cannot enforce. + agent, cleanup := startKeyringAgent(t) + defer cleanup() + + constraints := []ConstraintExtension{ + { + ExtensionName: "extension1", + ExtensionDetails: []byte("details1"), + }, + } + + key := testPrivateKeys["rsa"] + + err := agent.Add(AddedKey{ + PrivateKey: key, + ConstraintExtensions: constraints, + }) + if err == nil { + t.Fatal("adding a key with unsupported constraints succeeded") + } +}