Files
mesh-catalog/modules/gnome-keyring/cmd/gnome-keyring-tools/keyring_test.go
T
jochen e810afb3eb gnome-keyring: the secret service as a module, claiming node-secret-service (hq ADR 0208, ADR 0102)
PAM lines written into login and passwd as blocks, so login unlocks the keyring
on both workstations; no daemon of its own; gcr's ssh agent named for the session
until the environment can say a runtime-directory path. Go tools unlocked, lock,
collections and ssh-keys, never reading a secret.
2026-10-04 13:15:39 +02:00

132 lines
4.9 KiB
Go

package main
import (
"errors"
"os"
"path/filepath"
"strconv"
"strings"
"testing"
)
const nobody = 4194400
// secretService fakes busctl answering as gnome-keyring did on 2026-10-04: a session, a login and a
// default keyring, the login one unlocked; Lock locks it.
func secretService(t *testing.T) string {
t.Helper()
fakeMachine(t)
runtime := filepath.Join(runUserDir, strconv.Itoa(os.Getuid()))
if err := os.MkdirAll(runtime, 0o700); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(filepath.Join(runtime, "bus"), nil, 0o600); err != nil {
t.Fatal(err)
}
bin := fakeBinaries(t, map[string]string{"busctl": `echo "$*" >> "$LOG"
locked=false; [ -f "$LOG.locked" ] && locked=true
case "$*" in
*"get-property org.freedesktop.secrets /org/freedesktop/secrets org.freedesktop.Secret.Service Collections")
echo '{"type":"ao","data":["/org/freedesktop/secrets/collection/session","/org/freedesktop/secrets/collection/login","/org/freedesktop/secrets/collection/Default_5fkeyring"]}' ;;
*"ReadAlias s default") echo '{"type":"o","data":["/org/freedesktop/secrets/collection/Default_5fkeyring"]}' ;;
*"/collection/login org.freedesktop.DBus.Properties GetAll"*)
echo '{"type":"a{sv}","data":[{"Items":{"type":"ao","data":["/x/1","/x/2"]},"Label":{"type":"s","data":"Login"},"Locked":{"type":"b","data":'$locked'},"Created":{"type":"t","data":1752488320},"Modified":{"type":"t","data":0}}]}' ;;
*"/collection/"*"GetAll"*)
echo '{"type":"a{sv}","data":[{"Items":{"type":"ao","data":[]},"Label":{"type":"s","data":"Other"},"Locked":{"type":"b","data":true},"Created":{"type":"t","data":0},"Modified":{"type":"t","data":0}}]}' ;;
*"Lock ao 1 /org/freedesktop/secrets/collection/login") touch "$LOG.locked"; echo '{"type":"aoo","data":[["/org/freedesktop/secrets/collection/login"],"/"]}' ;;
*) echo "no such call: $*" >&2; exit 1 ;;
esac`})
t.Setenv("LOG", filepath.Join(bin, "log"))
return bin
}
func TestCollectionsAreNamesCountsAndLocksNeverItems(t *testing.T) {
bin := secretService(t)
got, err := Collections()
if err != nil {
t.Fatal(err)
}
if len(got.Collections) != 3 {
t.Fatalf("%+v", got)
}
var login, def Collection
for _, c := range got.Collections {
switch c.ID {
case "login":
login = c
case "Default_5fkeyring":
def = c
}
}
if login.Label != "Login" || login.Locked || login.Items != 2 || login.Created == "" || login.Modified != "" || login.Default {
t.Fatalf("login: %+v", login)
}
if !def.Default || !def.Locked {
t.Fatalf("default: %+v", def)
}
asked, _ := os.ReadFile(filepath.Join(bin, "log"))
if strings.Contains(string(asked), "GetSecret") || strings.Contains(string(asked), "Item") && strings.Contains(string(asked), "Secret.Item") {
t.Fatalf("a secret was asked for:\n%s", asked)
}
}
func TestUnlockedAnswersTheLoginAndTheDefaultKeyring(t *testing.T) {
secretService(t)
fakeProcess(t, nobody, "gnome-keyring-d")
got, err := Unlocked()
if err != nil || !got.Daemon || got.Login == nil || got.Login.Locked || got.Default == nil || !got.Default.Default {
t.Fatalf("%+v, %v", got, err)
}
}
func TestLockLocksTheLoginKeyringAndRefusesAPathForAnId(t *testing.T) {
secretService(t)
got, err := Lock("")
if err != nil || got.Collection != "login" || !got.Locked {
t.Fatalf("%+v, %v", got, err)
}
for _, bad := range []string{"../service", "login /org/x", "a b"} {
if _, err := Lock(bad); err == nil {
t.Errorf("%q was accepted", bad)
}
}
}
func TestTheAgentsKeysAreFingerprints(t *testing.T) {
keys := parseKeys("256 SHA256:x+LmFabc op@laptop (ED25519)\n3072 SHA256:yyy a comment with spaces (RSA)\nThe agent has no identities.\n")
if len(keys) != 2 || keys[0] != (Key{Bits: 256, Fingerprint: "SHA256:x+LmFabc", Comment: "op@laptop", Type: "ED25519"}) ||
keys[1].Comment != "a comment with spaces" || keys[1].Type != "RSA" {
t.Fatalf("%+v", keys)
}
}
func TestSSHKeysAsksGcrsAgentAndSaysWhenItDoesNotAnswer(t *testing.T) {
secretService(t)
bin := fakeBinaries(t, map[string]string{"ssh-add": `echo "$SSH_AUTH_SOCK $*" > "$LOG.ssh"; [ -f "$NOAGENT" ] && { echo "Could not open a connection" >&2; exit 2; }; echo "256 SHA256:abc op (ED25519)"`})
t.Setenv("NOAGENT", filepath.Join(bin, "noagent"))
got, err := SSHKeys()
if err != nil || len(got.Keys) != 1 || !strings.HasSuffix(got.Agent, "/gcr/ssh") {
t.Fatalf("%+v, %v", got, err)
}
asked, _ := os.ReadFile(os.Getenv("LOG") + ".ssh")
if !strings.HasSuffix(strings.TrimSpace(string(asked)), "/gcr/ssh -l -E sha256") {
t.Fatalf("asked: %s", asked)
}
if err := os.WriteFile(filepath.Join(bin, "noagent"), nil, 0o644); err != nil {
t.Fatal(err)
}
if _, err := SSHKeys(); err == nil || !strings.Contains(err.Error(), "gcr-ssh-agent.socket") {
t.Fatalf("no agent: %v", err)
}
}
func TestWithoutABusTheToolsSaySo(t *testing.T) {
fakeMachine(t)
if _, err := Collections(); !errors.Is(err, ErrNoBus) {
t.Fatal(err)
}
if _, err := SSHKeys(); !errors.Is(err, ErrNoBus) {
t.Fatal(err)
}
}