Skip to content

Commit

Permalink
Merge pull request #1 from jiacfan/dev
Browse files Browse the repository at this point in the history
Create slim version of keyctl with basic keyring operations for session keyring.
  • Loading branch information
jiacfan authored Feb 13, 2019
2 parents 988d051 + 2b102d7 commit ff92b17
Show file tree
Hide file tree
Showing 17 changed files with 13 additions and 1,176 deletions.
12 changes: 1 addition & 11 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,17 +1,7 @@
branches:
only:
- master
language: go
go:
- 1.4
- 1.5
- 1.6
- tip
- 1.10
install:
- go get golang.org/x/crypto/openpgp
- go get golang.org/x/crypto/cast5
- go get golang.org/x/crypto/ssh/terminal
- go get golang.org/x/tools/cmd/cover
- go build -v ./...
script:
- go test -v -cover
69 changes: 1 addition & 68 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,70 +1,3 @@
[![GoDoc](https://godoc.org/github.com/jsipprell/keyctl?status.svg)](https://godoc.org/github.com/jsipprell/keyctl)
[![Build Status](https://travis-ci.org/jsipprell/keyctl.svg?branch=master)](https://travis-ci.org/jsipprell/keyctl)

# keyctl

A native Go API for the security key management system (aka "keyrings") found in Linux 2.6+

The keyctl interface is nominally provided by three or so Linux-specific syscalls, however it is almost always wrapped
in a library named `libkeyutils.so`.

This package interacts directly with the syscall interface and does not require CGO for linkage to the helper library
provided on most systems.

## Example Usages

To access the default session keyring (and create it if it doesn't exist)


```go
package main

import (
"log"
"github.com/jsipprell/keyctl"
)

func main() {
keyring, err := keyctl.SessionKeyring()
if err != nil {
log.Fatal(err)
}

// default timeout of 10 seconds for new or updated keys
keyring.SetDefaultTimeout(10)
secureData := []byte{1,2,3,4}
id, err := keyring.Add("some-data", secureData)
if err != nil {
log.Fatal(err)
}
log.Printf("created session key id %v", id)
}
```

To search for an existing key by name:

```go
package main

import (
"log"
"github.com/jsipprell/keyctl"
)

func main() {
keyring, err := keyctl.SessionKeyring()
if err != nil {
log.Fatal(err)
}
key, err := keyring.Search("some-data")
if err != nil {
log.Fatal(err)
}

data, err := key.Get()
if err != nil {
log.Fatal(err)
}
log.Printf("secure data: %v\n", data)
}
```
Slim version of `github.com/jsipprell/keyctl` with basic keyring operations.
24 changes: 3 additions & 21 deletions key.go
Original file line number Diff line number Diff line change
@@ -1,40 +1,25 @@
package keyctl

import (
"time"
"unsafe"
)

// Represents a single key linked to one or more kernel keyrings.
// Key represents a single key linked to one or more kernel keyrings.
type Key struct {
Name string

id, ring keyId
size int
ttl time.Duration
}

func (k *Key) private() {}

// Returns the 32-bit kernel identifier for a specific key
// Id returns the 32-bit kernel identifier for a specific key
func (k *Key) Id() int32 {
return int32(k.id)
}

// To expire a key automatically after some period of time call this method.
func (k *Key) ExpireAfter(nsecs uint) error {
k.ttl = time.Duration(nsecs) * time.Second

_, _, err := keyctl(keyctlSetTimeout, uintptr(k.id), uintptr(nsecs))
return err
}

// Return information about a key.
func (k *Key) Info() (Info, error) {
return getInfo(k.id)
}

// Get the key's value as a byte slice
// Get returns the key's value as a byte slice
func (k *Key) Get() ([]byte, error) {
var (
b []byte
Expand Down Expand Up @@ -70,9 +55,6 @@ func (k *Key) Get() ([]byte, error) {
// Set the key's value from a bytes slice. Expiration, if active, is reset by calling this method.
func (k *Key) Set(b []byte) error {
err := updateKey(k.id, b)
if err == nil && k.ttl > 0 {
err = k.ExpireAfter(uint(k.ttl.Seconds()))
}
return err
}

Expand Down
17 changes: 3 additions & 14 deletions key_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package keyctl
import (
"math/rand"
"testing"
"time"
)

func helperRandBlock(sz int) []byte {
Expand All @@ -28,7 +27,7 @@ func helperCompareBlock(t *testing.T, name string, blk2 []byte, ring Keyring) {
err error
)
if ring == nil {
ring, err = UserSessionKeyring()
ring, err = SessionKeyring()
if err != nil {
t.Fatal(err)
}
Expand All @@ -38,10 +37,6 @@ func helperCompareBlock(t *testing.T, name string, blk2 []byte, ring Keyring) {
t.Fatal(err)
}

if err = key.ExpireAfter(5); err != nil {
t.Fatal(err)
}

blk1, err := key.Get()
if err != nil {
t.Fatal(err)
Expand All @@ -63,7 +58,7 @@ func helperCmp(t *testing.T, blk1 []byte, blk2 []byte) {
}

func TestRandomKey256(t *testing.T) {
ring, err := UserSessionKeyring()
ring, err := SessionKeyring()
if err != nil {
t.Fatal(err)
}
Expand All @@ -78,7 +73,7 @@ func TestRandomKey256(t *testing.T) {
}

func TestRandomKey700(t *testing.T) {
ring, err := UserSessionKeyring()
ring, err := SessionKeyring()
if err != nil {
t.Fatal(err)
}
Expand All @@ -91,10 +86,4 @@ func TestRandomKey700(t *testing.T) {

t.Logf("added %d byte random value key as: %v (%v)\n", len(r700), id.Id(), r700)
helperCompareBlock(t, "rand700", r700, nil)
time.Sleep(time.Duration(5)*time.Second + time.Duration(250000))

if _, err = ring.Search("rand700"); err == nil {
t.Fatal("'rand700' key did not expire in five seconds")
}
t.Logf("key %v expired after five seconds", id.Id())
}
119 changes: 1 addition & 118 deletions keyring.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ package keyctl
// All Keys and Keyrings have unique 32-bit serial number identifiers.
type Id interface {
Id() int32
Info() (Info, error)

private()
}
Expand All @@ -20,7 +19,6 @@ type Keyring interface {
Id
Add(string, []byte) (*Key, error)
Search(string) (*Key, error)
SetDefaultTimeout(uint)
}

// Named keyrings are user-created keyrings linked to a parent keyring. The
Expand All @@ -33,15 +31,7 @@ type NamedKeyring interface {
}

type keyring struct {
id keyId
defaultTtl uint
}

type namedKeyring struct {
*keyring
parent keyId
name string // for non-anonymous keyrings
ttl uint
id keyId
}

func (kr *keyring) private() {}
Expand All @@ -51,31 +41,11 @@ func (kr *keyring) Id() int32 {
return int32(kr.id)
}

// Returns information about a keyring.
func (kr *keyring) Info() (Info, error) {
return getInfo(kr.id)
}

// Return the name of a NamedKeyring that was set when the keyring was created
// or opened.
func (kr *namedKeyring) Name() string {
return kr.name
}

// Set a default timeout, in seconds, after which newly added keys will be
// destroyed.
func (kr *keyring) SetDefaultTimeout(nsecs uint) {
kr.defaultTtl = nsecs
}

// Add a new key to a keyring. The key can be searched for later by name.
func (kr *keyring) Add(name string, key []byte) (*Key, error) {
r, err := add_key("user", name, key, int32(kr.id))
if err == nil {
key := &Key{Name: name, id: keyId(r), ring: kr.id}
if kr.defaultTtl != 0 {
err = key.ExpireAfter(kr.defaultTtl)
}
return key, err
}

Expand All @@ -98,95 +68,8 @@ func SessionKeyring() (Keyring, error) {
return newKeyring(keySpecSessionKeyring)
}

// Return the current user-session keyring (part of session, but private to
// current user)
func UserSessionKeyring() (Keyring, error) {
return newKeyring(keySpecUserSessionKeyring)
}

// Return the current group keyring.
func GroupKeyring() (Keyring, error) {
return newKeyring(keySpecGroupKeyring)
}

// Return the keyring specific to the current executing thread.
func ThreadKeyring() (Keyring, error) {
return newKeyring(keySpecThreadKeyring)
}

// Return the keyring specific to the current executing process.
func ProcessKeyring() (Keyring, error) {
return newKeyring(keySpecProcessKeyring)
}

// Creates a new named-keyring linked to a parent keyring. The parent may be
// one of those returned by SessionKeyring(), UserSessionKeyring() and friends
// or it may be an existing named-keyring. When searching is performed, all
// keyrings form a hierarchy and are searched top-down. If the keyring already
// exists it will be destroyed and a new one with the same name created. Named
// sub-keyrings inherit their initial ttl (if set) from the parent but can
// outlive the parent as the timer is restarted at creation.
func CreateKeyring(parent Keyring, name string) (NamedKeyring, error) {
var ttl uint

parentId := keyId(parent.Id())
kr, err := createKeyring(parentId, name)
if err != nil {
return nil, err
}

if pkr, ok := parent.(*namedKeyring); ok {
ttl = pkr.ttl
}
ring := &namedKeyring{
keyring: kr,
parent: parentId,
name: name,
ttl: ttl,
}

if ttl > 0 {
_, _, err = keyctl(keyctlSetTimeout, uintptr(ring.id), uintptr(ttl))
}

return ring, nil
}

// Search for and open an existing keyring with the given name linked to a
// parent keyring (at any depth).
func OpenKeyring(parent Keyring, name string) (NamedKeyring, error) {
parentId := keyId(parent.Id())
id, err := searchKeyring(parentId, name, "keyring")
if err != nil {
return nil, err
}

return &namedKeyring{
keyring: &keyring{id: id},
parent: parentId,
name: name,
}, nil
}

// Set the time to live in seconds for an entire keyring and all of its keys.
// Only named keyrings can have their time-to-live set, the in-built keyrings
// cannot (Session, UserSession, etc).
func SetKeyringTTL(kr NamedKeyring, nsecs uint) error {
_, _, err := keyctl(keyctlSetTimeout, uintptr(kr.Id()), uintptr(nsecs))
if err == nil {
kr.(*namedKeyring).ttl = nsecs
}
return err
}

// Unlink an object from a keyring
func Unlink(parent Keyring, child Id) error {
_, _, err := keyctl(keyctlUnlink, uintptr(parent.Id()), uintptr(child.Id()))
return err
}

// Unlink a named keyring from its parent.
func UnlinkKeyring(kr NamedKeyring) error {
_, _, err := keyctl(keyctlUnlink, uintptr(kr.Id()), uintptr(kr.(*namedKeyring).parent))
return err
}
Loading

0 comments on commit ff92b17

Please sign in to comment.