Просмотр исходного кода

started implementing core org.freedesktop.Secret interfaces

aeth 6 дней назад
Родитель
Сommit
369f24bf96
4 измененных файлов с 285 добавлено и 0 удалено
  1. 16 0
      go.mod
  2. 24 0
      go.sum
  3. 39 0
      main.go
  4. 206 0
      pkg/listener.go

+ 16 - 0
go.mod

@@ -0,0 +1,16 @@
+module git.aetherial.dev/aeth/keychain-linker
+
+go 1.24.2
+
+require (
+	github.com/godbus/dbus/v5 v5.1.0 // indirect
+	github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
+	github.com/hashicorp/go-retryablehttp v0.7.7 // indirect
+	github.com/hashicorp/go-rootcerts v1.0.2 // indirect
+	github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 // indirect
+	github.com/hashicorp/vault-client-go v0.4.3 // indirect
+	github.com/mitchellh/go-homedir v1.1.0 // indirect
+	github.com/ryanuber/go-glob v1.0.0 // indirect
+	golang.org/x/sys v0.32.0 // indirect
+	golang.org/x/time v0.11.0 // indirect
+)

+ 24 - 0
go.sum

@@ -0,0 +1,24 @@
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/godbus/dbus v4.1.0+incompatible h1:WqqLRTsQic3apZUK9qC5sGNfXthmPXzUZ7nQPrNITa4=
+github.com/godbus/dbus v4.1.0+incompatible/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
+github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
+github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
+github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
+github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
+github.com/hashicorp/go-retryablehttp v0.7.7 h1:C8hUCYzor8PIfXHa4UrZkU4VvK8o9ISHxT2Q8+VepXU=
+github.com/hashicorp/go-retryablehttp v0.7.7/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk=
+github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc=
+github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
+github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 h1:kes8mmyCpxJsI7FTwtzRqEy9CdjCtrXrXGuOpxEA7Ts=
+github.com/hashicorp/go-secure-stdlib/strutil v0.1.2/go.mod h1:Gou2R9+il93BqX25LAKCLuM+y9U2T4hlwvT1yprcna4=
+github.com/hashicorp/vault-client-go v0.4.3 h1:zG7STGVgn/VK6rnZc0k8PGbfv2x/sJExRKHSUg3ljWc=
+github.com/hashicorp/vault-client-go v0.4.3/go.mod h1:4tDw7Uhq5XOxS1fO+oMtotHL7j4sB9cp0T7U6m4FzDY=
+github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
+github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk=
+github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc=
+golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
+golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
+golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0=
+golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=

+ 39 - 0
main.go

@@ -0,0 +1,39 @@
+package main
+
+import (
+	"fmt"
+	"os"
+
+	keychainlinker "git.aetherial.dev/aeth/keychain-linker/pkg"
+	"github.com/godbus/dbus/v5"
+	"github.com/godbus/dbus/v5/introspect"
+)
+
+func main() {
+	conn, err := dbus.ConnectSessionBus()
+	if err != nil {
+		panic(err)
+	}
+	defer conn.Close()
+
+	path := "/dev/aetherial/KeychainLinker"
+	session := &keychainlinker.SecretService{SessionBase: "/dev/aetherial/KeychainLinker/session/",
+		CollectionBase: "/dev/aetherial/KeychainLinker/collection/",
+		Collections:    []dbus.ObjectPath{}}
+
+	conn.Export(session, dbus.ObjectPath(path), "dev.aetherial.git.KeychainLinker.Service")
+	conn.Export(introspect.Introspectable(keychainlinker.DbusAdv), dbus.ObjectPath(path),
+		"org.freedesktop.DBus.Introspectable")
+
+	reply, err := conn.RequestName("dev.aetherial.git.KeychainLinker.Service",
+		dbus.NameFlagDoNotQueue)
+	if err != nil {
+		panic(err)
+	}
+	if reply != dbus.RequestNameReplyPrimaryOwner {
+		fmt.Fprintln(os.Stderr, "name already taken")
+		os.Exit(1)
+	}
+	fmt.Println("Listening on dev.aetherial.git.KeychainLinker.Service / /dev/aetherial/git/KeychainLinker/Service ...")
+	select {}
+}

+ 206 - 0
pkg/listener.go

@@ -0,0 +1,206 @@
+package keychainlinker
+
+import (
+	"fmt"
+	"path"
+	"strconv"
+
+	"github.com/godbus/dbus/v5"
+	"github.com/godbus/dbus/v5/introspect"
+)
+
+const DbusAdv = `
+<node>
+	<interface name="dev.aetherial.git.KeychainLinker.Service">
+		<method name="OpenSession">
+		  <arg name="algorithm" direction="in" type="s"/>
+		  <arg name="input" direction="in" type="v"/>
+		  <arg name="output" direction="out" type="v"/>
+		  <arg name="result" direction="out" type="o"/>
+		</method>
+		<method name="CreateCollection">
+		  <arg name="properties" direction="in" type="a{sv}"/>
+		  <arg name="alias" direction="in" type="s"/>
+		  <arg name="collection" direction="out" type="o"/>
+		  <arg name="prompt" direction="out" type="o"/>
+		</method>
+		<method name="SearchItems">
+		  <arg name="attributes" direction="in" type="a{ss}"/>
+		  <arg name="unlocked" direction="out" type="ao"/>
+		  <arg name="locked" direction="out" type="ao"/>
+		</method>
+		<method name="Unlock">
+		  <arg name="objects" direction="in" type="ao"/>
+		  <arg name="unlocked" direction="out" type="ao"/>
+		  <arg name="prompt" direction="out" type="o"/>
+		</method>
+		<method name="Lock">
+		  <arg name="objects" direction="in" type="ao"/>
+		  <arg name="locked" direction="out" type="ao"/>
+		  <arg name="prompt" direction="out" type="o"/>
+		</method>
+		<method name="GetSecrets">
+		  <arg name="items" direction="in" type="ao"/>
+		  <arg name="session" direction="in" type="o"/>
+		  <arg name="secrets" direction="out" type="a{o(ayays)}"/>
+		</method>
+		<method name="ReadAlias">
+		  <arg name="name" direction="in" type="s"/>
+		  <arg name="collection" direction="out" type="o"/>
+		</method>
+		<method name="SetAlias">
+		  <arg name="name" direction="in" type="s"/>
+		  <arg name="collection" direction="in" type="o"/>
+		</method>
+		<property name="Collections" type="ao" access="read"/>
+	</interface>` + introspect.IntrospectDataString + `</node> `
+
+type Session struct {
+	Path string
+	Open int
+}
+
+/*
+Get the next session
+*/
+func (s *Session) next() int {
+	s.Open = s.Open + 1
+	return s.Open
+
+}
+
+func (s *Session) OpenSession(algorithm string, input dbus.Variant) (dbus.Variant, dbus.ObjectPath, *dbus.Error) {
+	if algorithm != "PLAIN" {
+		return dbus.Variant{}, dbus.ObjectPath(""), &dbus.ErrMsgInvalidArg
+	}
+	nextPath := path.Join(s.Path, strconv.Itoa(s.next()))
+	fmt.Println("recieved algorithm: ", algorithm, "\nresponding with path: ", nextPath)
+	return dbus.MakeVariant(algorithm), dbus.ObjectPath(nextPath), nil
+}
+
+type SecretStruct struct {
+	Session     dbus.ObjectPath
+	Parameters  []byte
+	Value       []byte
+	ContentType string
+}
+
+type SecretService struct {
+	Collections    []dbus.ObjectPath
+	SessionBase    string // e.g. "/org/freedesktop/secrets/session/"
+	CollectionBase string // e.g. "/org/freedesktop/secrets/collection/"
+}
+
+type Collection struct {
+}
+
+// deletes the collection
+func (c *Collection) Delete() (dbus.ObjectPath, *dbus.Error) {
+	return dbus.ObjectPath("prompt"), nil
+}
+
+/*
+Searches the collection for matching items
+
+	:param attr: the attributes to attempt to match to a key in the collection
+*/
+func (c *Collection) SearchItems(attr map[string]string) ([]dbus.ObjectPath, *dbus.Error) {
+	// implement a recursive searching thing
+	return []dbus.ObjectPath{}, nil
+}
+
+/*
+Creates a new item in the collection with the properties defined in 'props'.
+Returns the items dbus object path, as well as a path to a dbus prompt
+
+	:param props: a map of properties to assign to the item
+	:param secret: the secret to encode into the collection
+	:param replace: replace secret if a matching one is found in the store
+*/
+func (c *Collection) CreateItem(props map[string]dbus.Variant, secret SecretStruct, replace bool) (dbus.ObjectPath, dbus.ObjectPath) {
+	return dbus.ObjectPath("/"), dbus.ObjectPath("/")
+}
+
+/*
+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 *SecretService) 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"))
+	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 *SecretService) CreateCollection(properties map[string]dbus.Variant, alias string) (dbus.ObjectPath, dbus.ObjectPath, *dbus.Error) {
+	collPath := dbus.ObjectPath(path.Join(s.CollectionBase, "login"))
+	s.Collections = append(s.Collections, collPath)
+	return collPath, "/", nil
+}
+
+/*
+search for items in the keychain that satisfy 'attrs'
+
+	:param attrs: a map of search criteria
+*/
+func (s *SecretService) SearchItems(attrs map[string]string) ([]dbus.ObjectPath, []dbus.ObjectPath, *dbus.Error) {
+	// Just return empty results for now
+	return []dbus.ObjectPath{}, []dbus.ObjectPath{}, nil
+}
+
+/*
+sets all dbus.Objects in 'objects' to the 'unlocked' position
+
+	:param objects: a slice of dbus.Objects to unlock
+*/
+func (s *SecretService) Unlock(objects []dbus.ObjectPath) ([]dbus.ObjectPath, dbus.ObjectPath, *dbus.Error) {
+	return objects, "/", nil // No prompt
+}
+
+/*
+Sets all dbus.Objects in 'objects' to the 'locked' position
+
+	:param objects: a slice of dbus.Objects to unlock
+*/
+func (s *SecretService) Lock(objects []dbus.ObjectPath) ([]dbus.ObjectPath, dbus.ObjectPath, *dbus.Error) {
+	return objects, "/", 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 *SecretService) GetSecrets(items []dbus.ObjectPath, session dbus.ObjectPath) (map[dbus.ObjectPath]SecretStruct, *dbus.Error) {
+	return map[dbus.ObjectPath]SecretStruct{}, nil
+}
+
+/*
+returns the collection with the given alias 'name'
+
+	:param name: the name of the alias to return
+*/
+func (s *SecretService) ReadAlias(name string) (dbus.ObjectPath, *dbus.Error) {
+	return dbus.ObjectPath("/dev/aetherial/KeychainLinker/login"), nil
+}
+
+/*
+sets the collections alias name to the specified value in 'name'
+
+	:param name: the alias name to assign
+	:param collection: the dbus.ObjectPath to assign the alias name to
+*/
+func (s *SecretService) SetAlias(name string, collection dbus.ObjectPath) *dbus.Error {
+	// will implement later
+	return nil
+}