From cacda0d3194ac6063587fa2645c99ebbbd7c8b65 Mon Sep 17 00:00:00 2001 From: Qi Wang Date: Mon, 12 Aug 2019 14:35:20 -0400 Subject: [PATCH] add keyctl read&describe to remove auth from keyring Add method removeAllAuthFromKeyring. Get key describes from keyring using KEYCTL_READ and KEYCTL_DESCRIBE, and remove them from keyring if the decription has prefix 'container-registry-login:'. Signed-off-by: Qi Wang --- pkg/docker/config/config.go | 10 ++++- pkg/docker/config/config_linux.go | 49 ++++++++++++++++++++++++- pkg/docker/config/config_unsupported.go | 4 ++ pkg/keyctl/key.go | 8 ++-- pkg/keyctl/keyring.go | 4 +- 5 files changed, 67 insertions(+), 8 deletions(-) diff --git a/pkg/docker/config/config.go b/pkg/docker/config/config.go index eef629d5c6..5ab6ba649e 100644 --- a/pkg/docker/config/config.go +++ b/pkg/docker/config/config.go @@ -142,9 +142,17 @@ func RemoveAuthentication(sys *types.SystemContext, registry string) error { }) } -// RemoveAllAuthentication deletes all the credentials stored in auth.json +// RemoveAllAuthentication deletes all the credentials stored in auth.json and kernel keyring func RemoveAllAuthentication(sys *types.SystemContext) error { return modifyJSON(sys, func(auths *dockerConfigFile) (bool, error) { + if enableKeyring { + err := removeAllAuthFromKernelKeyring() + if err == nil { + logrus.Debugf("removing all credentials from kernel keyring") + return false, nil + } + logrus.Debugf("error removing credentials from kernel keyring") + } auths.CredHelpers = make(map[string]string) auths.AuthConfigs = make(map[string]dockerAuthConfig) return true, nil diff --git a/pkg/docker/config/config_linux.go b/pkg/docker/config/config_linux.go index 4d66a50df5..19b8d3e5bc 100644 --- a/pkg/docker/config/config_linux.go +++ b/pkg/docker/config/config_linux.go @@ -3,11 +3,16 @@ package config import ( "fmt" "strings" + "unsafe" "github.com/containers/image/pkg/keyctl" "github.com/pkg/errors" + "github.com/sirupsen/logrus" + "golang.org/x/sys/unix" ) +const keyDescribePrefix = "container-registry-login:" + func getAuthFromKernelKeyring(registry string) (string, string, error) { userkeyring, err := keyctl.UserKeyring() if err != nil { @@ -41,6 +46,38 @@ func deleteAuthFromKernelKeyring(registry string) error { return key.Unlink() } +func removeAllAuthFromKernelKeyring() error { + keyring := keyctl.Key{ + Id: unix.KEY_SPEC_USER_KEYRING, + } + + byteKIDs, err := keyring.Get() + if err != nil { + return err + } + keyIDs := getKeyIDsFromByte(byteKIDs) + for _, kID := range keyIDs { + keyAttr, err := unix.KeyctlString(unix.KEYCTL_DESCRIBE, int(kID)) + if err != nil { + return err + } + // split string "type;uid;gid;perm;description" + keyAttrs := strings.SplitN(keyAttr, ";", 5) + if len(keyAttrs) < 5 { + return errors.Errorf("Key attributes of %d are not avaliable", kID) + } + keyDescribe := keyAttrs[4] + if strings.HasPrefix(keyDescribe, keyDescribePrefix) { + _, err := unix.KeyctlInt(unix.KEYCTL_UNLINK, int(kID), int(unix.KEY_SPEC_USER_KEYRING), 0, 0) + if err != nil { + return errors.Wrapf(err, "error unlinking key %d", kID) + } + logrus.Debugf("unlink key %d:%s", kID, keyAttr) + } + } + return nil +} + func setAuthToKernelKeyring(registry, username, password string) error { keyring, err := keyctl.SessionKeyring() if err != nil { @@ -75,5 +112,15 @@ func setAuthToKernelKeyring(registry, username, password string) error { } func genDescription(registry string) string { - return fmt.Sprintf("container-registry-login:%s", registry) + return fmt.Sprintf("%s%s", keyDescribePrefix, registry) +} + +func getKeyIDsFromByte(byteKeyIDs []byte) []int32 { + idSize := 4 + var keyIDs []int32 + for idx := 0; idx+idSize <= len(byteKeyIDs); idx = idx + idSize { + tempID := *(*int32)(unsafe.Pointer(&byteKeyIDs[idx])) + keyIDs = append(keyIDs, tempID) + } + return keyIDs } diff --git a/pkg/docker/config/config_unsupported.go b/pkg/docker/config/config_unsupported.go index 1c1a02511c..9b0e8bee25 100644 --- a/pkg/docker/config/config_unsupported.go +++ b/pkg/docker/config/config_unsupported.go @@ -14,3 +14,7 @@ func deleteAuthFromKernelKeyring(registry string) error { func setAuthToKernelKeyring(registry, username, password string) error { return ErrNotSupported } + +func removeAllAuthFromKernelKeyring() error { + return ErrNotSupported +} diff --git a/pkg/keyctl/key.go b/pkg/keyctl/key.go index e4396a9df7..5b59b30c6a 100644 --- a/pkg/keyctl/key.go +++ b/pkg/keyctl/key.go @@ -14,13 +14,13 @@ import ( type Key struct { Name string - id, ring keyID + Id, ring keyID size int } // ID returns the 32-bit kernel identifier for a specific key func (k *Key) ID() int32 { - return int32(k.id) + return int32(k.Id) } // Get the key's value as a byte slice @@ -40,7 +40,7 @@ func (k *Key) Get() ([]byte, error) { b = make([]byte, int(size)) sizeRead = size + 1 for sizeRead > size { - r1, err := unix.KeyctlBuffer(unix.KEYCTL_READ, int(k.id), b, size) + r1, err := unix.KeyctlBuffer(unix.KEYCTL_READ, int(k.Id), b, size) if err != nil { return nil, err } @@ -59,6 +59,6 @@ func (k *Key) Get() ([]byte, error) { // Unlink a key from the keyring it was loaded from (or added to). If the key // is not linked to any other keyrings, it is destroyed. func (k *Key) Unlink() error { - _, err := unix.KeyctlInt(unix.KEYCTL_UNLINK, int(k.id), int(k.ring), 0, 0) + _, err := unix.KeyctlInt(unix.KEYCTL_UNLINK, int(k.Id), int(k.ring), 0, 0) return err } diff --git a/pkg/keyctl/keyring.go b/pkg/keyctl/keyring.go index 6e029c9235..fc1c19d0ed 100644 --- a/pkg/keyctl/keyring.go +++ b/pkg/keyctl/keyring.go @@ -34,7 +34,7 @@ type ID interface { func (kr *keyring) Add(name string, key []byte) (*Key, error) { r, err := unix.AddKey("user", name, key, int(kr.id)) if err == nil { - key := &Key{Name: name, id: keyID(r), ring: kr.id} + key := &Key{Name: name, Id: keyID(r), ring: kr.id} return key, nil } return nil, err @@ -46,7 +46,7 @@ func (kr *keyring) Add(name string, key []byte) (*Key, error) { func (kr *keyring) Search(name string) (*Key, error) { id, err := unix.KeyctlSearch(int(kr.id), "user", name, 0) if err == nil { - return &Key{Name: name, id: keyID(id), ring: kr.id}, nil + return &Key{Name: name, Id: keyID(id), ring: kr.id}, nil } return nil, err }