├── .gitignore ├── LICENSE ├── README.md ├── example ├── add_and_read_key.go └── create_keyring.go ├── keyutils.go └── keyutils_test.go /.gitignore: -------------------------------------------------------------------------------- 1 | .gopath 2 | *.sw* 3 | 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Jen Andre 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # What is this? 2 | 3 | [![GoDoc](https://godoc.org/github.com/jandre/keyutils?status.svg)](http://godoc.org/github.com/jandre/keyutils) 4 | 5 | Go bindings for Linux's libkeyutils. libkeyutils provides an interface to the Linux kernel's 6 | keyring APIs, useful for storing secrets. 7 | 8 | It requires headers and libs for libkeyutils to be installed, e.g. `apt-get install libkeyutils-dev`. 9 | 10 | # How to build and install 11 | 12 | ```bash 13 | sudo apt-get install -y libkeyutils-dev # on ubuntu 14 | go get github.com/jandre/keyutils 15 | ```` 16 | 17 | # How to use 18 | 19 | ## Adding and reading a key from a keyring. 20 | 21 | See `example/add_and_read_key.go` to see an example of adding and reading a 22 | key from the user keyring. 23 | 24 | For other examples, please see `example/`. 25 | 26 | ```go 27 | package main 28 | 29 | import ( 30 | "log" 31 | 32 | "github.com/jandre/keyutils" 33 | ) 34 | 35 | func main() { 36 | id, err := keyutils.AddKey(keyutils.USER, "test123", "hello", keyutils.KEY_SPEC_USER_KEYRING) 37 | 38 | if err != nil { 39 | log.Fatal("Error adding key:", err) 40 | } 41 | log.Println("Added key test123 with serial:", id) 42 | val, err := keyutils.ReadKey(id) 43 | 44 | if err != nil { 45 | log.Fatal("Error reading key:", err) 46 | } 47 | 48 | log.Println("Read:", val) 49 | } 50 | ``` 51 | 52 | ```bash 53 | $ go run example/main.go 54 | 2015/03/29 17:20:36 Added key test123 with serial: 222717072 55 | 2015/03/29 17:20:36 Read: hello 56 | ``` 57 | # Documentation 58 | 59 | See [godoc](http://godoc.org/github.com/jandre/keyutils) 60 | 61 | # TODO 62 | Many of the `keyctl_*` apis are not yet supported. Please read keyutils.go to see what APIs have been wrapped. 63 | 64 | 65 | -------------------------------------------------------------------------------- /example/add_and_read_key.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | 6 | "github.com/jandre/keyutils" 7 | ) 8 | 9 | func main() { 10 | id, err := keyutils.AddKey(keyutils.USER, "test123", "hello", keyutils.KEY_SPEC_USER_KEYRING) 11 | 12 | if err != nil { 13 | log.Fatal("Error adding key:", err) 14 | } 15 | log.Println("Added key test123 with serial:", id) 16 | val, err := keyutils.ReadKey(id) 17 | 18 | if err != nil { 19 | log.Fatal("Error reading key:", err) 20 | } 21 | 22 | log.Println("Read:", val) 23 | } 24 | -------------------------------------------------------------------------------- /example/create_keyring.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | 6 | "github.com/jandre/keyutils" 7 | ) 8 | 9 | // 10 | // create a keyring, then add a key to it, and list the result. 11 | // 12 | func main() { 13 | 14 | keyRingName := "jen's keyring" 15 | 16 | keyring, err := keyutils.NewKeyRing(keyRingName, keyutils.KEY_SPEC_USER_KEYRING) 17 | 18 | if err != nil { 19 | log.Fatal("Error adding keyring:", err) 20 | } 21 | 22 | id, err := keyutils.AddKey(keyutils.USER, "ssh key", "ssh key secret data", keyring) 23 | 24 | if err != nil || id == 0 { 25 | log.Fatal("Error adding key:", err) 26 | } 27 | 28 | id, err = keyutils.AddKey(keyutils.USER, "password for Github", "my github password", keyring) 29 | 30 | if err != nil || id == 0 { 31 | log.Fatal("Error adding key:", err) 32 | } 33 | 34 | if keys, err := keyutils.ListKeysInKeyRing(keyring); err != nil { 35 | log.Fatal(err) 36 | } else { 37 | log.Printf("%s (%d keys):", keyRingName, len(keys)) 38 | for id, key := range keys { 39 | log.Printf("-- #%d: %s (uid=%d, gid=%d)", id, key.Description, key.Uid, key.Gid) 40 | } 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /keyutils.go: -------------------------------------------------------------------------------- 1 | // 2 | // keyutils provides libkeyutils bindings for Go. 3 | // 4 | // To build, it requires libkeyutils binaries and headers, e.g.. 5 | // apt-get install libkeyutils-dev 6 | // 7 | package keyutils 8 | 9 | /* 10 | #cgo linux LDFLAGS: -lkeyutils 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | */ 18 | import "C" 19 | import ( 20 | "bytes" 21 | "encoding/binary" 22 | "errors" 23 | "strconv" 24 | "strings" 25 | "syscall" 26 | "unsafe" 27 | ) 28 | 29 | type KeySerial int32 30 | type KeyType string 31 | type KeyPerm int 32 | 33 | type KeyDesc struct { 34 | Serial KeySerial 35 | Type KeyType 36 | Uid uint 37 | Gid uint 38 | Permissions uint 39 | Description string 40 | } 41 | 42 | const ( 43 | USER KeyType = "user" 44 | KEYRING KeyType = "keyring" 45 | ) 46 | 47 | const ( 48 | KEY_SPEC_THREAD_KEYRING KeySerial = KeySerial(C.KEY_SPEC_THREAD_KEYRING) 49 | KEY_SPEC_USER_KEYRING = KeySerial(C.KEY_SPEC_USER_KEYRING) 50 | KEY_SPEC_PROCESS_KEYRING = KeySerial(C.KEY_SPEC_PROCESS_KEYRING) 51 | KEY_SPEC_SESSION_KEYRING = KeySerial(C.KEY_SPEC_SESSION_KEYRING) 52 | KEY_SPEC_USER_SESSION_KEYRING = KeySerial(C.KEY_SPEC_USER_SESSION_KEYRING) 53 | ) 54 | 55 | const ( 56 | KEY_POS_VIEW = KeyPerm(C.KEY_POS_VIEW) 57 | KEY_POS_READ = KeyPerm(C.KEY_POS_READ) 58 | KEY_POS_WRITE = KeyPerm(C.KEY_POS_WRITE) 59 | KEY_POS_SEARCH = KeyPerm(C.KEY_POS_SEARCH) 60 | KEY_POS_LINK = KeyPerm(C.KEY_POS_LINK) 61 | KEY_POS_SETATTR = KeyPerm(C.KEY_POS_SETATTR) 62 | KEY_POS_ALL = KeyPerm(C.KEY_POS_ALL) 63 | 64 | KEY_USR_VIEW = KeyPerm(C.KEY_USR_VIEW) 65 | KEY_USR_READ = KeyPerm(C.KEY_USR_READ) 66 | KEY_USR_WRITE = KeyPerm(C.KEY_USR_WRITE) 67 | KEY_USR_SEARCH = KeyPerm(C.KEY_USR_SEARCH) 68 | KEY_USR_LINK = KeyPerm(C.KEY_USR_LINK) 69 | KEY_USR_SETATTR = KeyPerm(C.KEY_USR_SETATTR) 70 | KEY_USR_ALL = KeyPerm(C.KEY_USR_ALL) 71 | 72 | KEY_GRP_VIEW = KeyPerm(C.KEY_GRP_VIEW) 73 | KEY_GRP_READ = KeyPerm(C.KEY_GRP_READ) 74 | KEY_GRP_WRITE = KeyPerm(C.KEY_GRP_WRITE) 75 | KEY_GRP_SEARCH = KeyPerm(C.KEY_GRP_SEARCH) 76 | KEY_GRP_LINK = KeyPerm(C.KEY_GRP_LINK) 77 | KEY_GRP_SETATTR = KeyPerm(C.KEY_GRP_SETATTR) 78 | KEY_GRP_ALL = KeyPerm(C.KEY_GRP_ALL) 79 | 80 | KEY_OTH_VIEW = KeyPerm(C.KEY_OTH_VIEW) 81 | KEY_OTH_READ = KeyPerm(C.KEY_OTH_READ) 82 | KEY_OTH_WRITE = KeyPerm(C.KEY_OTH_WRITE) 83 | KEY_OTH_SEARCH = KeyPerm(C.KEY_OTH_SEARCH) 84 | KEY_OTH_LINK = KeyPerm(C.KEY_OTH_LINK) 85 | KEY_OTH_SETATTR = KeyPerm(C.KEY_OTH_SETATTR) 86 | KEY_OTH_ALL = KeyPerm(C.KEY_OTH_ALL) 87 | ) 88 | 89 | // 90 | // RequestKey() wraps request_key(2). 91 | // 92 | // It returns the serial number of the key found with type = `keyType` 93 | // and description = `desc` in the keyring `keyring`. 94 | // 95 | func RequestKey(keyType KeyType, desc string, keyring KeySerial) (KeySerial, error) { 96 | cKeyType := C.CString(string(keyType)) 97 | cDesc := C.CString(desc) 98 | result, err := C.request_key( 99 | cKeyType, 100 | cDesc, 101 | nil, 102 | C.key_serial_t(int(keyring))) 103 | C.free(unsafe.Pointer(cKeyType)) 104 | C.free(unsafe.Pointer(cDesc)) 105 | if err != nil { 106 | return 0, err.(syscall.Errno) 107 | } else { 108 | return KeySerial(int(result)), nil 109 | } 110 | } 111 | 112 | // 113 | // AddKeyBytes wraps add_key(2). 114 | // 115 | // It returns the serial number of the added key. 116 | // 117 | func AddKeyBytes(keyType KeyType, desc string, data []byte, keyring KeySerial) (KeySerial, error) { 118 | cKeyType := C.CString(string(keyType)) 119 | cDesc := C.CString(desc) 120 | payloadLen := C.size_t(len(data)) 121 | 122 | var secret unsafe.Pointer 123 | 124 | if data != nil { 125 | secret = unsafe.Pointer(&data[0]) 126 | } 127 | 128 | result, err := C.add_key(cKeyType, cDesc, secret, payloadLen, C.key_serial_t(int(keyring))) 129 | 130 | C.free(unsafe.Pointer(cKeyType)) 131 | C.free(unsafe.Pointer(cDesc)) 132 | 133 | if err != nil { 134 | return 0, err.(syscall.Errno) 135 | } else { 136 | return KeySerial(int(result)), nil 137 | } 138 | } 139 | 140 | // 141 | // AddKey is a helper for AddKeyBytes() that accepts a data string instead of 142 | // a byte array. 143 | // 144 | func AddKey(keyType KeyType, desc string, data string, keyring KeySerial) (KeySerial, error) { 145 | bytes := []byte(data) 146 | 147 | return AddKeyBytes(keyType, desc, bytes, keyring) 148 | } 149 | 150 | // 151 | // ReadKeyBytes() reads a key with the given serial # using keyctl_read_alloc(3), and returns the bytes read. 152 | // 153 | func ReadKeyBytes(key KeySerial) ([]byte, error) { 154 | 155 | var ptr unsafe.Pointer = nil 156 | bytes, err := C.keyctl_read_alloc(C.key_serial_t(int(key)), (*unsafe.Pointer)(&ptr)) 157 | 158 | if err != nil { 159 | return nil, err.(syscall.Errno) 160 | } 161 | if bytes > 0 && ptr != nil { 162 | result := C.GoBytes(ptr, bytes) 163 | C.free(ptr) 164 | return result, nil 165 | } 166 | return nil, nil 167 | 168 | } 169 | 170 | func parseKeyDesc(keySerial KeySerial, keyDesc string) (*KeyDesc, error) { 171 | // description is type;uid;gid;permMask;desc 172 | tokens := strings.SplitN(keyDesc, ";", 5) 173 | 174 | if len(tokens) < 5 { 175 | return nil, errors.New("malformed key desc string, not enough tokens: " + keyDesc) 176 | } 177 | 178 | uid, _ := strconv.Atoi(tokens[1]) 179 | gid, _ := strconv.Atoi(tokens[2]) 180 | permissions, _ := strconv.ParseUint(tokens[3], 16, 32) 181 | desc := tokens[4] 182 | 183 | return &KeyDesc{ 184 | Serial: keySerial, 185 | Type: KeyType(tokens[0]), 186 | Uid: uint(uid), 187 | Gid: uint(gid), 188 | Permissions: uint(permissions), 189 | Description: desc, 190 | }, nil 191 | } 192 | 193 | // 194 | // DescribeKey() wraps keyctl_describe_alloc() to describe a key 195 | // 196 | func DescribeKey(key KeySerial) (*KeyDesc, error) { 197 | var ptr *C.char = nil 198 | bytes, err := C.keyctl_describe_alloc(C.key_serial_t(int(key)), (&ptr)) 199 | 200 | if err == nil && bytes > 0 && ptr != nil { 201 | descString := C.GoString(ptr) 202 | C.free(unsafe.Pointer(ptr)) 203 | return parseKeyDesc(key, descString) 204 | } 205 | 206 | return nil, err.(syscall.Errno) 207 | } 208 | 209 | // 210 | // ReadKey() is a wrapper for ReadKeyBytes() that reads a key with the given serial #, and converts 211 | // whatever is in the output buffer to a string value. 212 | // 213 | func ReadKey(key KeySerial) (string, error) { 214 | bytes, err := ReadKeyBytes(key) 215 | 216 | if err != nil { 217 | return "", err 218 | } else { 219 | return string(bytes), nil 220 | } 221 | } 222 | 223 | // 224 | // Clear() will call keyctl_clear(3) to clear a keyring. 225 | // 226 | func Clear(keyring KeySerial) error { 227 | _, err := C.keyctl_clear(C.key_serial_t(keyring)) 228 | 229 | if err != nil { 230 | return err.(syscall.Errno) 231 | } 232 | return nil 233 | } 234 | 235 | // 236 | // Chown wraps keyctl_chown(3) to change ownership of the key. 237 | // 238 | // See: http://man7.org/linux/man-pages/man3/keyctl_chown.3.html 239 | // 240 | func Chown(key KeySerial, uid uint, gid uint) error { 241 | 242 | _, err := C.keyctl_chown(C.key_serial_t(key), C.uid_t(uid), C.gid_t(gid)) 243 | 244 | if err != nil { 245 | return err.(syscall.Errno) 246 | } 247 | return nil 248 | } 249 | 250 | // 251 | // Revoke() will call keyctl_revoke(3) to revoke a key. 252 | // 253 | // See: http://man7.org/linux/man-pages/man3/keyctl_revoke.3.html 254 | // 255 | func Revoke(key KeySerial) error { 256 | _, err := C.keyctl_revoke(C.key_serial_t(key)) 257 | 258 | if err != nil { 259 | return err.(syscall.Errno) 260 | } 261 | return nil 262 | } 263 | 264 | // 265 | // SetTimeout() will call keyctl_set_timeout(3) to set a `seconds` 266 | // timeout on a key. 267 | // 268 | // See: http://man7.org/linux/man-pages/man3/keyctl_set_timeout.3.html 269 | // 270 | func SetTimeout(key KeySerial, seconds uint) error { 271 | _, err := C.keyctl_set_timeout(C.key_serial_t(key), C.uint(seconds)) 272 | 273 | if err != nil { 274 | return err.(syscall.Errno) 275 | } 276 | return nil 277 | } 278 | 279 | // 280 | // SetPerm() will call keyctl_setperm(3) to set permissions on a key. 281 | // mask is a bitwise `or` value of KeyPerm values, e.g. 282 | // KEY_USR_VIEW | KEY_USR_READ 283 | // 284 | // See: http://man7.org/linux/man-pages/man3/keyctl_setperm.3.html 285 | // 286 | func SetPerm(key KeySerial, mask KeyPerm) error { 287 | _, err := C.keyctl_setperm(C.key_serial_t(key), C.key_perm_t(mask)) 288 | 289 | if err != nil { 290 | return err.(syscall.Errno) 291 | } 292 | return nil 293 | } 294 | 295 | // 296 | // NewKeyRing() creates a keyring with description `desc` 297 | // under the parent keyring `parentRing` 298 | // 299 | func NewKeyRing(desc string, parentRing KeySerial) (KeySerial, error) { 300 | return AddKeyBytes(KEYRING, desc, nil, parentRing) 301 | } 302 | 303 | // 304 | // ListKeysInKeyRing() will list all keys in keyring, returning a 305 | // `KeyDesc` for each. 306 | // 307 | func ListKeysInKeyRing(keyring KeySerial) ([]*KeyDesc, error) { 308 | 309 | desc, err := DescribeKey(keyring) 310 | 311 | if err != nil { 312 | return nil, err 313 | } 314 | 315 | if desc == nil || desc.Type != KEYRING { 316 | // it's not a keyring, so makes no sense to list the keys inside of it.. 317 | return nil, errors.New("not a valid keyring: " + string(keyring)) 318 | } 319 | 320 | keyBytes, err := ReadKeyBytes(keyring) 321 | 322 | if err != nil { 323 | return nil, err 324 | } 325 | 326 | if len(keyBytes)%4 != 0 { 327 | return nil, errors.New("Expected a string of key_serial_t") 328 | } 329 | 330 | keys := make([]*KeyDesc, 0) 331 | 332 | for c := 0; c < len(keyBytes); c += 4 { 333 | var key int32 334 | buf := bytes.NewBuffer(keyBytes[c:(c + 4)]) 335 | err = binary.Read(buf, binary.LittleEndian, &key) 336 | if err != nil { 337 | return nil, err 338 | } 339 | 340 | desc, err := DescribeKey(KeySerial(key)) 341 | 342 | if err != nil { 343 | return nil, err 344 | } 345 | 346 | keys = append(keys, desc) 347 | } 348 | 349 | return keys, nil 350 | 351 | } 352 | 353 | // 354 | // Link wraps keyctl_link(3). 355 | // 356 | func Link(key KeySerial, keyRing KeySerial) error { 357 | 358 | _, err := C.keyctl_link(C.key_serial_t(key), C.key_serial_t(keyRing)) 359 | 360 | if err != nil { 361 | return err.(syscall.Errno) 362 | } 363 | return nil 364 | } 365 | 366 | // 367 | // Unlink wraps keyctl_unlink(3). 368 | // 369 | func Unlink(key KeySerial, keyRing KeySerial) error { 370 | 371 | _, err := C.keyctl_unlink(C.key_serial_t(key), C.key_serial_t(keyRing)) 372 | 373 | if err != nil { 374 | return err.(syscall.Errno) 375 | } 376 | return nil 377 | } 378 | -------------------------------------------------------------------------------- /keyutils_test.go: -------------------------------------------------------------------------------- 1 | package keyutils 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestReadAndAddKey(t *testing.T) { 8 | k, err := AddKey(USER, "testkey", "hello this is new data", KEY_SPEC_USER_KEYRING) 9 | t.Log("key is", k) 10 | if err != nil { 11 | t.Fatal("error adding key", err) 12 | } else { 13 | 14 | result, err := ReadKey(k) 15 | if err != nil { 16 | t.Fatal(err) 17 | } 18 | t.Log("decrypted is", result) 19 | if result != "hello this is new data" { 20 | t.Fatal("mismatched result and data", result) 21 | } 22 | } 23 | } 24 | 25 | func TestRequestKey(t *testing.T) { 26 | k, err := AddKey(USER, "testkey", "hello this is new data", KEY_SPEC_USER_KEYRING) 27 | t.Log("key is", k) 28 | if err != nil { 29 | t.Fatal("error adding key", err) 30 | } else { 31 | 32 | result, err := RequestKey(USER, "testkey", KEY_SPEC_USER_KEYRING) 33 | if err != nil { 34 | t.Fatal(err) 35 | } 36 | t.Log("read key is", result) 37 | if result != k { 38 | t.Fatal("mismatched key", result) 39 | } 40 | } 41 | } 42 | 43 | func TestClearKey(t *testing.T) { 44 | 45 | TestRequestKey(t) 46 | err := Clear(KEY_SPEC_USER_KEYRING) 47 | 48 | if err != nil { 49 | t.Fatal(err) 50 | } 51 | result, err := RequestKey(USER, "testkey", KEY_SPEC_USER_KEYRING) 52 | t.Log("Clear() read key is", result) 53 | if result != 0 { 54 | t.Fatal("found a key that should have been cleared", result) 55 | } 56 | } 57 | 58 | func TestDescribeKey(t *testing.T) { 59 | 60 | k, err := AddKey(USER, "testkey", "hello this is new data", KEY_SPEC_USER_KEYRING) 61 | t.Log("key is", k) 62 | 63 | if err != nil { 64 | t.Fatal("error adding key", err) 65 | } 66 | result, err := DescribeKey(k) 67 | 68 | if err != nil { 69 | t.Fatal("error describing key", err) 70 | } 71 | 72 | if result == nil { 73 | t.Fatal("expected a result from DescribeKey, and got nothing") 74 | } 75 | 76 | t.Log("result is", result) 77 | 78 | if result.Description != "testkey" { 79 | t.Fatal("bad result", result) 80 | } 81 | } 82 | 83 | func TestCreateKeyring(t *testing.T) { 84 | 85 | keyring, err := NewKeyRing("myring", KEY_SPEC_USER_KEYRING) 86 | if err != nil { 87 | t.Fatal("error adding keyring", err) 88 | } 89 | 90 | k, err := AddKey(USER, "hello key", "hello this is data", keyring) 91 | t.Log("key is", k) 92 | 93 | if err != nil { 94 | t.Fatal("error adding key", err) 95 | } 96 | 97 | keys, err := ListKeysInKeyRing(keyring) 98 | 99 | if err != nil { 100 | t.Fatal("error listing keys", err) 101 | } 102 | 103 | if len(keys) != 1 { 104 | t.Fatal("expected 1 key") 105 | } 106 | if keys[0].Description != "hello key" { 107 | t.Fatal("expected description to be 'hello key':", keys[0].Description) 108 | } 109 | for _, stuff := range keys { 110 | t.Log("key:", stuff) 111 | } 112 | 113 | } 114 | --------------------------------------------------------------------------------