rewrite checkpoint

This commit is contained in:
Cory Bennett
2017-08-13 18:23:38 -07:00
parent b00021ccbd
commit 36632a52f0
883 changed files with 222856 additions and 2972 deletions
+14
View File
@@ -0,0 +1,14 @@
Copyright (c) 2013, Travis Cline <travis.cline@gmail.com>
Permission to use, copy, modify, and/or distribute this software for any purpose
with or without fee is hereby granted, provided that the above copyright notice
and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE.
+45
View File
@@ -0,0 +1,45 @@
# keyring provides cross-platform keychain access
http://godoc.org/github.com/tmc/keyring
Keyring provides a common interface to keyring/keychain tools.
License: ISC
Currently implemented:
- OSX
- SecretService
- gnome-keychain (via "gnome_keyring" build flag)
Contributions welcome!
Usage example:
```go
err := keyring.Set("libraryFoo", "jack", "sacrifice")
password, err := keyring.Get("libraryFoo", "jack")
fmt.Println(password) //Output: sacrifice
```
## Linux
Linux requirements:
### SecretService provider
- dbus
### gnome-keychain provider
- gnome-keychain headers
- Ubuntu/Debian: `libsecret-dev`
- Fedora: `libsecret-devel`
- Archlinux: `libsecret`
Tests on Linux:
```sh
$ go test github.com/tmc/keyring
$ # for gnome-keyring provider
$ go test -tags gnome_keyring github.com/tmc/keyring
```
+27
View File
@@ -0,0 +1,27 @@
/*
Package keyring provides a cross-platform interface to keychains for
password management
Currently implemented:
* OSX
* SecretService
* gnome-keychain (via "gnome_keyring" build flag)
Usage
Example usage:
err := keyring.Set("libraryFoo", "jack", "sacrifice")
password, err := keyring.Get("libraryFoo", "jack")
fmt.Println(password)
Output: sacrifice
TODO
* Write Windows provider
*/
package keyring
+96
View File
@@ -0,0 +1,96 @@
// +build gnome_keyring
package keyring
/*
#cgo pkg-config: libsecret-1 glib-2.0
#include <stdlib.h>
#include "libsecret/secret.h"
SecretSchema keyring_schema =
{
"org.github.tmc.keyring.Password",
SECRET_SCHEMA_NONE,
{
{ "username", SECRET_SCHEMA_ATTRIBUTE_STRING },
{ "service", SECRET_SCHEMA_ATTRIBUTE_STRING },
{ NULL, 0 },
}
};
// wrap the gnome calls because cgo can't deal with vararg functions
gboolean gkr_set_password(gchar *description, gchar *service, gchar *username, gchar *password, GError **err) {
return secret_password_store_sync(
&keyring_schema,
NULL,
description,
password,
NULL,
err,
"service", service,
"username", username,
NULL);
}
gchar * gkr_get_password(gchar *service, gchar *username, GError **err) {
return secret_password_lookup_sync(
&keyring_schema,
NULL,
err,
"service", service,
"username", username,
NULL);
}
*/
import "C"
import (
"fmt"
"unsafe"
)
type gnomeKeyring struct{}
func (p gnomeKeyring) Set(Service, Username, Password string) error {
desc := (*C.gchar)(C.CString("Username and password for " + Service))
username := (*C.gchar)(C.CString(Username))
service := (*C.gchar)(C.CString(Service))
password := (*C.gchar)(C.CString(Password))
defer C.free(unsafe.Pointer(desc))
defer C.free(unsafe.Pointer(username))
defer C.free(unsafe.Pointer(service))
defer C.free(unsafe.Pointer(password))
var gerr *C.GError
result := C.gkr_set_password(desc, service, username, password, &gerr)
defer C.free(unsafe.Pointer(gerr))
if result == 0 {
return fmt.Errorf("Gnome-keyring error: %+v", gerr)
}
return nil
}
func (p gnomeKeyring) Get(Service string, Username string) (string, error) {
var gerr *C.GError
var pw *C.gchar
username := (*C.gchar)(C.CString(Username))
service := (*C.gchar)(C.CString(Service))
defer C.free(unsafe.Pointer(username))
defer C.free(unsafe.Pointer(service))
pw = C.gkr_get_password(service, username, &gerr)
defer C.free(unsafe.Pointer(gerr))
defer C.secret_password_free((*C.gchar)(pw))
if pw == nil {
return "", fmt.Errorf("Gnome-keyring error: %+v", gerr)
}
return C.GoString((*C.char)(pw)), nil
}
func initializeProvider() (provider, error) {
return gnomeKeyring{}, nil
}
+45
View File
@@ -0,0 +1,45 @@
// Shows example use of the keyring package
//
// May need to be built with a platform-specific build flag to specify a
// provider. See keyring documentation for details.
//
package main
import (
"fmt"
"os"
"syscall"
"github.com/tmc/keyring"
"golang.org/x/crypto/ssh/terminal"
)
func main() {
if pw, err := keyring.Get("keyring_example", "jack"); err == nil {
fmt.Println("current stored password:", pw)
} else if err == keyring.ErrNotFound {
fmt.Println("no password stored yet")
} else {
fmt.Println("got unexpected error:", err)
os.Exit(1)
}
fmt.Printf("enter new password: ")
pw, err := terminal.ReadPassword(int(syscall.Stdin))
if err != nil {
fmt.Println(err)
os.Exit(1)
}
fmt.Println("setting keyring_example/jack to..", pw)
err = keyring.Set("keyring_example", "jack", string(pw))
if err != nil {
fmt.Println(err)
os.Exit(1)
}
fmt.Println("fetching keyring_example/jack..")
if pw, err := keyring.Get("keyring_example", "jack"); err == nil {
fmt.Println("got", pw)
} else {
fmt.Println("error:", err)
}
}
+58
View File
@@ -0,0 +1,58 @@
package keyring
import (
"errors"
"sync"
)
var (
// ErrNotFound means the requested password was not found
ErrNotFound = errors.New("keyring: Password not found")
// ErrNoDefault means that no default keyring provider has been found
ErrNoDefault = errors.New("keyring: No suitable keyring provider found (check your build flags)")
providerInitOnce sync.Once
defaultProvider provider
providerInitError error
)
// provider provides a simple interface to keychain sevice
type provider interface {
Get(service, username string) (string, error)
Set(service, username, password string) error
}
func setupProvider() (provider, error) {
providerInitOnce.Do(func() {
defaultProvider, providerInitError = initializeProvider()
})
if providerInitError != nil {
return nil, providerInitError
} else if defaultProvider == nil {
return nil, ErrNoDefault
}
return defaultProvider, nil
}
// Get gets the password for a paricular Service and Username using the
// default keyring provider.
func Get(service, username string) (string, error) {
p, err := setupProvider()
if err != nil {
return "", err
}
return p.Get(service, username)
}
// Set sets the password for a particular Service and Username using the
// default keyring provider.
func Set(service, username, password string) error {
p, err := setupProvider()
if err != nil {
return err
}
return p.Set(service, username, password)
}
+54
View File
@@ -0,0 +1,54 @@
package keyring
import (
"fmt"
"os/exec"
"regexp"
"syscall"
)
type osxProvider struct {
}
var pwRe = regexp.MustCompile("password: \"(.+)\"")
func (p osxProvider) Get(Service, Username string) (string, error) {
args := []string{"find-generic-password",
"-s", Service,
"-a", Username,
"-g"}
c := exec.Command("/usr/bin/security", args...)
o, err := c.CombinedOutput()
if err != nil {
exitCode := c.ProcessState.Sys().(syscall.WaitStatus).ExitStatus()
// check particular exit code
if exitCode == 44 {
return "", ErrNotFound
}
return "", fmt.Errorf("/usr/bin/security: %s", err)
}
matches := pwRe.FindStringSubmatch(string(o))
if len(matches) != 2 {
return "", ErrNotFound
}
return matches[1], nil
}
func (p osxProvider) Set(Service, Username, Password string) error {
args := []string{"add-generic-password",
"-s", Service,
"-a", Username,
"-w", Password,
"-U"}
c := exec.Command("/usr/bin/security", args...)
err := c.Run()
if err != nil {
o, _ := c.CombinedOutput()
return fmt.Errorf(string(o))
}
return nil
}
func initializeProvider() (provider, error) {
return osxProvider{}, nil
}
+152
View File
@@ -0,0 +1,152 @@
// +build !gnome_keyring
package keyring
import (
"fmt"
dbus "github.com/guelfey/go.dbus"
)
const (
ssServiceName = "org.freedesktop.secrets"
ssServicePath = "/org/freedesktop/secrets"
ssCollectionPath = "/org/freedesktop/secrets/collection/Default"
ssServiceIface = "org.freedesktop.Secret.Service."
ssSessionIface = "org.freedesktop.Secret.Session."
ssCollectionIface = "org.freedesktop.Secret.Collection."
ssItemIface = "org.freedesktop.Secret.Item."
ssPromptIface = "org.freedesktop.Secret.Prompt."
)
// ssSecret corresponds to org.freedesktop.Secret.Item
// Note: Order is important
type ssSecret struct {
Session dbus.ObjectPath
Parameters []byte
Value []byte
ContentType string `dbus:"content_type"`
}
// newSSSecret prepares an ssSecret for use
// Uses text/plain as the Content-type which may need to change in the future
func newSSSecret(session dbus.ObjectPath, secret string) (s ssSecret) {
s = ssSecret{
ContentType: "text/plain; charset=utf8",
Parameters: []byte{},
Session: session,
Value: []byte(secret),
}
return
}
// ssProvider implements the provider interface freedesktop SecretService
type ssProvider struct {
*dbus.Conn
srv *dbus.Object
}
// This is used to open a seassion for every get/set. Alternative might be to
// defer() the call to close when constructing the ssProvider
func (s *ssProvider) openSession() (*dbus.Object, error) {
var disregard dbus.Variant
var sessionPath dbus.ObjectPath
method := fmt.Sprint(ssServiceIface, "OpenSession")
err := s.srv.Call(method, 0, "plain", dbus.MakeVariant("")).Store(&disregard, &sessionPath)
if err != nil {
return nil, err
}
return s.Object(ssServiceName, sessionPath), nil
}
// Unsure how the .Prompt call surfaces, it hasn't come up.
func (s *ssProvider) unlock(p dbus.ObjectPath) error {
var unlocked []dbus.ObjectPath
var prompt dbus.ObjectPath
method := fmt.Sprint(ssServiceIface, "Unlock")
err := s.srv.Call(method, 0, []dbus.ObjectPath{p}).Store(&unlocked, &prompt)
if err != nil {
return fmt.Errorf("keyring/dbus: Unlock error: %s", err)
}
if prompt != dbus.ObjectPath("/") {
method = fmt.Sprint(ssPromptIface, "Prompt")
call := s.Object(ssServiceName, prompt).Call(method, 0, "unlock")
return call.Err
}
return nil
}
func (s *ssProvider) Get(c, u string) (string, error) {
results := []dbus.ObjectPath{}
var secret ssSecret
search := map[string]string{
"username": u,
"service": c,
}
session, err := s.openSession()
if err != nil {
return "", err
}
defer session.Call(fmt.Sprint(ssSessionIface, "Close"), 0)
s.unlock(ssCollectionPath)
collection := s.Object(ssServiceName, ssCollectionPath)
method := fmt.Sprint(ssCollectionIface, "SearchItems")
call := collection.Call(method, 0, search)
err = call.Store(&results)
if call.Err != nil {
return "", call.Err
}
// results is a slice. Just grab the first one.
if len(results) == 0 {
return "", ErrNotFound
}
method = fmt.Sprint(ssItemIface, "GetSecret")
err = s.Object(ssServiceName, results[0]).Call(method, 0, session.Path()).Store(&secret)
if err != nil {
return "", err
}
return string(secret.Value), nil
}
func (s *ssProvider) Set(c, u, p string) error {
var item, prompt dbus.ObjectPath
properties := map[string]dbus.Variant{
"org.freedesktop.Secret.Item.Label": dbus.MakeVariant(fmt.Sprintf("%s - %s", u, c)),
"org.freedesktop.Secret.Item.Attributes": dbus.MakeVariant(map[string]string{
"username": u,
"service": c,
}),
}
session, err := s.openSession()
if err != nil {
return err
}
defer session.Call(fmt.Sprint(ssSessionIface, "Close"), 0)
s.unlock(ssCollectionPath)
collection := s.Object(ssServiceName, ssCollectionPath)
secret := newSSSecret(session.Path(), p)
// the bool is "replace"
err = collection.Call(fmt.Sprint(ssCollectionIface, "CreateItem"), 0, properties, secret, true).Store(&item, &prompt)
if err != nil {
return fmt.Errorf("keyring/dbus: CreateItem error: %s", err)
}
if prompt != "/" {
s.Object(ssServiceName, prompt).Call(fmt.Sprint(ssPromptIface, "Prompt"), 0, "unlock")
}
return nil
}
func initializeProvider() (provider, error) {
conn, err := dbus.SessionBus()
if err != nil {
return nil, err
}
srv := conn.Object(ssServiceName, ssServicePath)
p := &ssProvider{conn, srv}
return p, nil
}
+30
View File
@@ -0,0 +1,30 @@
package keyring
import (
"fmt"
"testing"
)
func TestBasicSetGet(t *testing.T) {
var (
pw string
err error
)
pw, err = Get("keyring-test", "jack")
if err != nil {
// ok on initial invokation
fmt.Println("Get() error:", err)
}
err = Set("keyring-test", "jack", "test")
if err != nil {
t.Error("Set() error:", err)
}
pw, err = Get("keyring-test", "jack")
if err != nil {
t.Error("Get() error:", err)
}
if pw != "test" {
fmt.Errorf("expected 'test', got '%s'", pw)
}
}