| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336 |
- package keychainlinker
- import (
- "fmt"
- "path"
- "strconv"
- "time"
- "github.com/godbus/dbus/v5"
- "github.com/godbus/dbus/v5/introspect"
- )
- const DEFAULT_COLLECTION = "/org/freedesktop/secrets/collections/default"
- type Cache struct {
- Collections map[dbus.ObjectPath]Collection
- }
- type Service struct {
- /*
- Working on implementing the org.freedesktop.Secret.Service interface, from their v0.2 spec:
- https://specifications.freedesktop.org/secret-service-spec/latest-single/#org.freedesktop.Secret.Service
- */
- Cache map[dbus.ObjectPath]*Collection
- Collections []dbus.ObjectPath
- PathCount int
- Alias map[string]dbus.ObjectPath
- SessionBase string // e.g. "/org/freedesktop/secrets/session/"
- CollectionBase string // e.g. "/org/freedesktop/secrets/collection/"
- }
- // implementing method to read the object property
- func (s *Service) Get(iface, property string) (dbus.Variant, *dbus.Error) {
- if iface != "/org/freedesktop/secrets/service" {
- return dbus.Variant{}, dbus.MakeFailedError(dbus.ErrMsgUnknownInterface)
- }
- switch property {
- case "Collections":
- return dbus.MakeVariant(s.Collections), nil
- default:
- return dbus.Variant{}, dbus.MakeFailedError(dbus.ErrMsgUnknownMethod)
- }
- }
- // implementing method to read the object property
- func (s *Service) Set(iface, property string, value dbus.Variant) *dbus.Error {
- if iface != "/org/freedesktop/secrets/service" {
- return dbus.MakeFailedError(dbus.ErrMsgUnknownInterface)
- }
- switch property {
- case "Collections":
- collections, ok := value.Value().([]dbus.ObjectPath)
- if !ok {
- return dbus.MakeFailedError(dbus.ErrMsgInvalidArg)
- }
- s.Collections = collections
- return nil
- default:
- return dbus.MakeFailedError(dbus.ErrMsgUnknownMethod)
- }
- }
- // implementing the get all method for the dbus interface
- func (s *Service) GetAll(iface string) (map[string]dbus.Variant, *dbus.Error) {
- if iface != "/org/freedesktop/secrets/service" {
- return nil, dbus.MakeFailedError(dbus.ErrMsgUnknownInterface)
- }
- return map[string]dbus.Variant{
- "Collections": dbus.MakeVariant(s.Collections),
- }, nil
- }
- /*
- implementing the properties interface
- func (s *Service) Get(iface, property string) (dbus.Variant, *dbus.Error) {
- if iface == "org.freedesktop.Secret.Service" && property == "Collections" {
- return dbus.MakeVariant(s.Collections), nil
- }
- return dbus.Variant{}, dbus.MakeFailedError(fmt.Errorf("no such property"))
- }
- func (s *Service) GetAll(iface string) (map[string]dbus.Variant, *dbus.Error) {
- if iface == "org.freedesktop.Secret.Service" {
- return map[string]dbus.Variant{
- "Collections": dbus.MakeVariant(s.Collections),
- }, nil
- }
- return nil, dbus.MakeFailedError(fmt.Errorf("no such interface"))
- }
- /*
- Create a new service interface
- */
- func NewService(base dbus.ObjectPath) *Service {
- return &Service{
- Cache: map[dbus.ObjectPath]*Collection{
- DEFAULT_COLLECTION: {
- Items: []dbus.ObjectPath{
- "/",
- },
- Label: "default",
- Cache: map[dbus.ObjectPath]CacheItem{},
- PathCount: 0,
- PathBase: fmt.Sprintf("%s/collection/default", base),
- Private: "true",
- Created: uint64(time.Now().Unix()),
- Modified: uint64(time.Now().Unix()),
- },
- },
- Collections: []dbus.ObjectPath{
- DEFAULT_COLLECTION,
- },
- PathCount: 0,
- Alias: map[string]dbus.ObjectPath{
- "default": DEFAULT_COLLECTION,
- },
- SessionBase: fmt.Sprintf("%s/session", base),
- CollectionBase: fmt.Sprintf("%s/collection", base),
- }
- }
- /*
- Opens a session for the Secret Service Interface
- :param algorithm: the encryption algorithm to use with the client
- :param input: the data used when implementing more advanced encryption algos
- */
- func (s *Service) OpenSession(algorithm string, input dbus.Variant) (dbus.Variant, dbus.ObjectPath, *dbus.Error) {
- if algorithm != "PLAIN" {
- return dbus.Variant{}, "/", dbus.MakeFailedError(fmt.Errorf("only PLAIN is supported"))
- }
- sessionPath := dbus.ObjectPath(path.Join(s.SessionBase, "1"))
- fmt.Println(sessionPath)
- return input, sessionPath, nil
- }
- /*
- Creates a collection with the Service object
- :param properties: a set of properties that are used by client apps
- :param alias: the shortname of the collection
- */
- func (s *Service) CreateCollection(properties map[string]dbus.Variant, alias string) (dbus.ObjectPath, dbus.ObjectPath, *dbus.Error) {
- fmt.Printf("CreateCollection called with: %+v (alias: %s)\n", properties, alias)
- collPath := dbus.ObjectPath(path.Join(s.CollectionBase, strconv.Itoa(s.PathCount+1)))
- s.Collections = append(s.Collections, collPath)
- s.PathCount = s.PathCount + 1
- s.Alias[alias] = collPath
- return collPath, "/", nil
- }
- /*
- search for items in the keychain that satisfy 'attrs'
- :param attrs: a map of search criteria
- */
- func (s *Service) SearchItems(attrs map[string]string) ([]dbus.ObjectPath, []dbus.ObjectPath, *dbus.Error) {
- // Just return empty results for now
- found := []dbus.ObjectPath{}
- for _, v := range s.Cache {
- f, err := v.SearchItems(attrs)
- if err != nil {
- return []dbus.ObjectPath{}, []dbus.ObjectPath{}, err
- }
- found = append(found, f...)
- }
- return found, []dbus.ObjectPath{}, nil
- }
- /*
- attempts to return secrets that were either already unlocked, or unlocked without a prompt, in addition to
- a prompt path that can be used to unlock all remaining locked objects
- :param objects: a slice of dbus.Objects to unlock
- */
- func (s *Service) Unlock(objects []dbus.ObjectPath) ([]dbus.ObjectPath, dbus.ObjectPath, *dbus.Error) {
- return []dbus.ObjectPath{}, dbus.ObjectPath("/"), nil // No prompt
- }
- /*
- Sets all dbus.Objects in 'objects' to the 'locked' position
- :param objects: a slice of dbus.Objects to unlock
- */
- func (s *Service) Lock(objects []dbus.ObjectPath) ([]dbus.ObjectPath, dbus.ObjectPath, *dbus.Error) {
- return []dbus.ObjectPath{}, dbus.ObjectPath("/"), nil // No prompt
- }
- /*
- retrives secrets from an array of items/collections
- :param items: a slice of dbus.ObjectPath that will have their secrets returned
- */
- func (s *Service) GetSecrets(items []dbus.ObjectPath, session dbus.ObjectPath) (map[dbus.ObjectPath]SecretStruct, *dbus.Error) {
- return map[dbus.ObjectPath]SecretStruct{}, nil
- }
- /*
- Return a collection based on the alias name
- :param name: the alias to search for
- */
- func (s *Service) ReadAlias(name string) (dbus.ObjectPath, *dbus.Error) {
- objectPath, ok := s.Alias[name]
- if !ok {
- return dbus.ObjectPath("/"), &dbus.ErrMsgNoObject
- }
- return objectPath, nil
- }
- /*
- set the alias of the passed in collection
- :param name: the alias to set the collection to
- :param collection: the collection to modify
- */
- func (s *Service) SetAlias(name string, collection dbus.ObjectPath) *dbus.Error {
- s.Alias[name] = collection
- return nil
- }
- var ServiceNode = introspect.Node{
- Interfaces: []introspect.Interface{
- {
- Name: "org.freedesktop.Secret.Service",
- Methods: []introspect.Method{
- {
- Name: "OpenSession",
- Args: []introspect.Arg{
- {Name: "algorithm", Type: "s", Direction: "in"},
- {Name: "input", Type: "v", Direction: "in"},
- {Name: "output", Type: "(sv)", Direction: "out"},
- {Name: "session_path", Type: "o", Direction: "out"},
- },
- },
- {
- Name: "CreateCollection",
- Args: []introspect.Arg{
- {Name: "properties", Type: "a{sv}", Direction: "in"},
- {Name: "alias", Type: "s", Direction: "in"},
- {Name: "collection", Type: "o", Direction: "out"},
- {Name: "prompt", Type: "o", Direction: "out"},
- },
- },
- {
- Name: "SearchItems",
- Args: []introspect.Arg{
- {Name: "attributes", Type: "a{ss}", Direction: "in"},
- {Name: "locked", Type: "ao", Direction: "out"},
- {Name: "unlocked", Type: "ao", Direction: "out"},
- },
- },
- {
- Name: "Unlock",
- Args: []introspect.Arg{
- {Name: "objects", Type: "ao", Direction: "in"},
- {Name: "unlocked", Type: "ao", Direction: "out"},
- {Name: "prompt", Type: "o", Direction: "out"},
- },
- },
- {
- Name: "Lock",
- Args: []introspect.Arg{
- {Name: "objects", Type: "ao", Direction: "in"},
- {Name: "locked", Type: "ao", Direction: "out"},
- {Name: "prompt", Type: "o", Direction: "out"},
- },
- },
- {
- Name: "GetSecrets",
- Args: []introspect.Arg{
- {Name: "items", Type: "ao", Direction: "in"},
- {Name: "session", Type: "o", Direction: "in"},
- {Name: "secrets", Type: "a{oa(yays)}", Direction: "out"},
- },
- },
- {
- Name: "ReadAlias",
- Args: []introspect.Arg{
- {Name: "name", Type: "s", Direction: "in"},
- {Name: "collection", Type: "o", Direction: "out"},
- },
- },
- {
- Name: "SetAlias",
- Args: []introspect.Arg{
- {Name: "name", Type: "s", Direction: "in"},
- {Name: "collection", Type: "o", Direction: "in"},
- },
- },
- },
- Properties: []introspect.Property{
- {
- Name: "Collections",
- Type: "ao",
- Access: "read",
- },
- },
- },
- {
- Name: "org.freedesktop.DBus.Properties",
- Methods: []introspect.Method{
- {
- Name: "Get",
- Args: []introspect.Arg{
- {Name: "interface_name", Type: "s", Direction: "in"},
- {Name: "property_name", Type: "s", Direction: "in"},
- {Name: "value", Type: "v", Direction: "out"},
- },
- },
- {
- Name: "Set",
- Args: []introspect.Arg{
- {Name: "interface_name", Type: "s", Direction: "in"},
- {Name: "property_name", Type: "s", Direction: "in"},
- {Name: "value", Type: "v", Direction: "in"},
- },
- },
- {
- Name: "GetAll",
- Args: []introspect.Arg{
- {Name: "interface_name", Type: "s", Direction: "in"},
- {Name: "props", Type: "a{sv}", Direction: "out"},
- },
- },
- },
- },
- introspect.IntrospectData,
- },
- }
|