Skip to content

Commit

Permalink
add keyctl read&describe to remove auth from keyring
Browse files Browse the repository at this point in the history
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 <[email protected]>
  • Loading branch information
QiWang19 committed Aug 12, 2019
1 parent 7136ea7 commit 0ec0452
Show file tree
Hide file tree
Showing 4 changed files with 185 additions and 1 deletion.
34 changes: 34 additions & 0 deletions pkg/docker/config/config_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,11 @@ import (

"github.com/containers/image/pkg/keyctl"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)

const keyDescribePrefix = "container-registry-login:"

func getAuthFromKernelKeyring(registry string) (string, string, error) {
userkeyring, err := keyctl.UserKeyring()
if err != nil {
Expand Down Expand Up @@ -41,6 +44,37 @@ func deleteAuthFromKernelKeyring(registry string) error {
return key.Unlink()
}

func removeAllAuthFromKernelKeyring() error {
userkeyring, err := keyctl.UserKeyring()
if err != nil {
return err
}
keyIDs, err := userkeyring.ReadUserKeyring()
if err != nil {
return err
}
for _, kID := range keyIDs {
keyAttr, err := keyctl.Describe(kID)
if err != nil {
return nil
}
// split string "type;uid;gid;perm;description"
keyAttrs := strings.SplitN(keyAttr, ";", 5)
if len(keyAttrs) == 0 {
return errors.Errorf("Key attributes of %d are not avaliable", kID.ID())
}
keyDescribe := keyAttrs[len(keyAttrs)-1]
if strings.HasPrefix(keyDescribe, keyDescribePrefix) {
err := keyctl.Unlink(userkeyring, kID)
if err != nil {
return errors.Wrapf(err, "error unlinking key %d", kID.ID())
}
logrus.Debugf("unlink key %d:%s", kID.ID(), keyAttr)
}
}
return nil
}

func setAuthToKernelKeyring(registry, username, password string) error {
keyring, err := keyctl.SessionKeyring()
if err != nil {
Expand Down
9 changes: 9 additions & 0 deletions pkg/keyctl/key.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,12 @@ func (k *Key) Unlink() error {
_, err := unix.KeyctlInt(unix.KEYCTL_UNLINK, int(k.id), int(k.ring), 0, 0)
return err
}

// Describe returns a string describing the attributes of a specified key
func Describe(kID ID) (string, error) {
keyDescribe, err := unix.KeyctlString(unix.KEYCTL_DESCRIBE, int(kID.ID()))
if err != nil {
return "", err
}
return keyDescribe, nil
}
72 changes: 71 additions & 1 deletion pkg/keyctl/keyring.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@
package keyctl

import (
"encoding/binary"
"unsafe"

"golang.org/x/sys/unix"
)

Expand All @@ -19,10 +22,12 @@ type Keyring interface {
ID
Add(string, []byte) (*Key, error)
Search(string) (*Key, error)
ReadUserKeyring() ([]*Key, error)
}

type keyring struct {
id keyID
id keyID
size int
}

// ID is unique 32-bit serial number identifiers for all Keys and Keyrings have.
Expand Down Expand Up @@ -77,3 +82,68 @@ func Link(parent Keyring, child ID) error {
_, err := unix.KeyctlInt(unix.KEYCTL_LINK, int(child.ID()), int(parent.ID()), 0, 0)
return err
}

// ReadUserKeyring reads user keyring and returns slice of Key with id field(key_serial_t) representing the IDs of all the keys that are linked to it
func (kr *keyring) ReadUserKeyring() ([]*Key, error) {
var (
b []byte
err error
sizeRead int
keyArr []*Key
)

if kr.size == 0 {
kr.size = 4
}

size := kr.size

b = make([]byte, size)
sizeRead = size + 1
for sizeRead > size {
r1, err := unix.KeyctlBuffer(unix.KEYCTL_READ, unix.KEY_SPEC_USER_KEYRING, b, size)

if err != nil {
return nil, err
}

if sizeRead = int(r1); sizeRead > size {
b = make([]byte, sizeRead)
size = sizeRead
sizeRead = size + 1
} else {
kr.size = sizeRead
}
}

keyIDs := getKeyIDsFromByte(b[:kr.size])
for _, kid := range keyIDs {
keyArr = append(keyArr, &Key{id: kid})
}
return keyArr, err
}

func getKeyIDsFromByte(byteKeyIDs []byte) []keyID {
idSize := 4

// check the endianness
var nativeEndian binary.ByteOrder
buf := [2]byte{}
*(*uint16)(unsafe.Pointer(&buf[0])) = uint16(0xABCD)

switch buf {
case [2]byte{0xCD, 0xAB}:
nativeEndian = binary.LittleEndian
case [2]byte{0xAB, 0xCD}:
nativeEndian = binary.BigEndian
default:
panic("Could not determine native endianness.")
}

var keyIDs []keyID
for idx := 0; idx+idSize <= len(byteKeyIDs); idx = idx + idSize {
tempID := byteKeyIDs[idx : idx+idSize]
keyIDs = append(keyIDs, keyID(nativeEndian.Uint32(tempID)))
}
return keyIDs
}
71 changes: 71 additions & 0 deletions pkg/keyctl/keyring_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ package keyctl

import (
"crypto/rand"
"strings"
"testing"
)

Expand Down Expand Up @@ -124,3 +125,73 @@ func TestUnlink(t *testing.T) {
t.Fatal(err)
}
}

func TestReadUserKeyring(t *testing.T) {
token := make([]byte, 20)
rand.Read(token)

testname := "testread"

userKeyring, err := UserKeyring()
if err != nil {
t.Fatal(err)
}

sessionKeyring, err := SessionKeyring()
if err != nil {
t.Fatal(err)
}

key, err := sessionKeyring.Add(testname, token)
if err != nil {
t.Fatal(err)
}

expected := true
err = Link(userKeyring, key)
if err != nil {
t.Fatal(err)
}
keys, err := userKeyring.ReadUserKeyring()
if err != nil {
t.Fatal(err)
}
keyExists := false
for _, k := range keys {
if key.ID() == k.ID() {
keyExists = true
break
}
}
if keyExists != expected {
t.Errorf("ReadUserKeyring error: expected ID %d does not exist", key.ID())
}
}

func TestDescribe(t *testing.T) {
token := make([]byte, 20)
rand.Read(token)

testname := "testdescribe"

sessionKeyring, err := SessionKeyring()
if err != nil {
t.Fatal(err)
}

key, err := sessionKeyring.Add(testname, token)
if err != nil {
t.Fatal(err)
}

describe, err := Describe(key)
if err != nil {
t.Fatal(err)
}

expected := true
getDescribe := strings.Contains(describe, testname)
if getDescribe != expected {
t.Errorf("Describe error: expect to get %s, but get %s", testname, describe)
}
}

0 comments on commit 0ec0452

Please sign in to comment.