Code Monkey home page Code Monkey logo

go-keyring's People

Contributors

bitte-ein-bit avatar c-reeder avatar comebackoneyear avatar diamondburned avatar erdaltsksn avatar fiws avatar matisszilard avatar mikkeloscar avatar mislav avatar mrueg avatar nhatthm avatar njuettner avatar piccirello avatar pluralistix avatar renehollander avatar robinino avatar stephengroat avatar szh avatar szuecs avatar timbunce avatar williammartin avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

go-keyring's Issues

Fails to run on docker

UPDATE: I managed to find a solution as I wrote this issue but thought it worth sharing in case someone else comes across it


This is similar to #8 that discusses headless operation, but this may be docker specific.

Error
When running example code from: https://github.com/zalando/go-keyring#linux it returns error:
failed to unlock correct collection '/org/freedesktop/secrets/aliases/default'

Steps to reproduce

docker run --privileged -it ubuntu:latest /bin/bash
apt update && apt install -y curl dbus-x11 gnome-keyring git vim golang
go get github.com/zalando/go-keyring
vi test.go
add code example from: https://github.com/zalando/go-keyring#linux
go run test.go

gnome keyring is running

root@fed2abd272d4:/# ps -ef
UID        PID  PPID  C STIME TTY          TIME CMD
root      6320     1  0 15:09 ?        00:00:00 /usr/bin/dbus-daemon --syslog --fork --print-pid 4 --print-address 6 --session
root      6322     1  0 15:09 ?        00:00:00 /usr/bin/gnome-keyring-daemon --start --foreground --components=secrets
...

It seems it is possible to run headless using a python keyring library: https://keyring.readthedocs.io/en/latest/#using-keyring-on-headless-linux-systems
So, I followed the examples:

root@fed2abd272d4:/# dbus-run-session -- sh
# gnome-keyring-daemon --unlock
password
GNOME_KEYRING_CONTROL=/root/.cache/keyring-HWJLG0
SSH_AUTH_SOCK=/root/.cache/keyring-HWJLG0/ssh
# go run test.go
2020/03/01 15:12:43 secret

Ambiguous error message

Hi zalando,

I'm using the Arch Linux distribution and don't use a desktop environment, just a window manager (AwesomeWM). I ran into an issue when using a tool that included go-keyring where it was returning "ERROR: failed to unlock correct collection". I didn't know what specific collection it was referring to, so I had to trace it back to here and saw that you included the helpful information in your readme. I believe it would be better to include the name of the collection you are trying to unlock so if anyone runs into the same problem, they don't have to track it down.

Thanks,
Joey

-

/edit: sent mail according to the contribution guideline.

Tests fail under headless Centos7

Hello,

I have downloaded and tried this package under Centos7 (headless server) but the tests failed, any idea ?

[vagrant@localhost go-keyring]$ go test
--- FAIL: TestSet (0.04s)
        keyring_test.go:15: Should not fail, got: The name org.freedesktop.secrets was not provided by any .service files
--- FAIL: TestGet (0.00s)
        keyring_test.go:23: Should not fail, got: The name org.freedesktop.secrets was not provided by any .service files
        keyring_test.go:28: Should not fail, got: The name org.freedesktop.secrets was not provided by any .service files
        keyring_test.go:32: Expected password test-password, got
--- FAIL: TestGetNonExisting (0.00s)
        keyring_test.go:40: Expected error ErrNotFound, got The name org.freedesktop.secrets was not provided by any .service files
--- FAIL: TestDelete (0.00s)
        keyring_test.go:48: Should not fail, got: The name org.freedesktop.secrets was not provided by any .service files
FAIL
exit status 1
FAIL    github.com/zalando/go-keyring   0.043s

Thanks :)

How to design an encrypted file with this ?

I have a configuration file that holds many details that my app uses for various purposes such a SMTP password, db encryption token, etc. That configuration file is encrypted against a password using some golang code i wrote. Lets call it the "Config password"

Context and Intent:
This is a linux server in a data center with highly sensitive information stored on it.
The intent here is that if someone gets remote or physical access to the machine they cannot decrypt the configuration data stored in the config file.
Currently i store the password to decrypt the configuration file in Systemd file, and so anyone that gets physical access can find it.

How can i make it so that only that app can access the "Config password" that i stored into the TPM / KeyRing ?
The only way i can see this working is if the ServiceName and UserName is stored in the golang binary. It would then know the args to pass into go-keyring to retrieve the "Config password".

I feel like there is something i am missing as any attacker can decompile the golang binary and find the ServiceName and UserName.

Stable API release

The version referenced in a go.mod is a pseudo-version based on the commit (v0.0.0-20200121091418-667557018717).

Pseudo-versions are a special type of pre-release version. Is it possible to release a stable API?

You can release v1.0.0 using git tags.

git tag v1.0.0
git push --tags

NOTE: In my projects, I prefer to use v0.1.0 as a initial version.

Reference: https://blog.golang.org/publishing-go-modules

Not possible to access objects which do not have username and service parameters

At least in with git gnome keyring helper the github credentials are stored with user, server and protocol parameter so it's not possible to access password with hard coded username and service search parameters.

I was planning to overcome this issue by extending current interface with FreeGet and FreeSet functions which take instead of username and service the map of parameters.
What do you think about this approach?

Support Pass with GPG as a backend for Linux

I would also like support for Pass with GPG instead of only supporting secretservice. Secretservice is a pain to work with on headless servers with no gui. Pass on the other hand is very simple to use via cli.

I can work on a PR to help speed things along.

DecodeString corrupts valid single line hex strings

Assumption taken in the following breaking change 41dbcc1 is problematic as it corrupts single line valid hex strings (that were not encoded) and returns them in a decoded form.

In this case it is not enough to check whenever dec, err := hex.DecodeString(trimStr) does return an error to determine whenever value was stored in encoded form.

One of the propositions might be to perform decoding as an optional operation during obtaining stored value, or keep additional metadata after storing the value which will indicate whenever the value was encoded or not.

Test Case:

// TestGetSingleLineHexString tests getting a single line hex string password from the keyring.
func TestGetSingleLineHexString(t *testing.T) {
	hexPassword :=  "abcdef123abcdef123"
	err := Set(service, user, hexPassword)
	if err != nil {
		t.Errorf("Should not fail, got: %s", err)
	}

	pw, err := Get(service, user)
	if err != nil {
		t.Errorf("Should not fail, got: %s", err)
	}

	if hexPassword != pw {
		t.Errorf("Expected password %s, got %s", hexPassword, pw)
	}
}

Umlaut passwords fail to decode correctly on MacOS

#60 adds a test case for this. I'm unsure how this should be fixed correctly while maintaining the best possible backward compatibility. Options that came to mind:

  • check for non-ascii characters and encode them all
  • simply encode all passwords on MacOS

I happily implement but would request some guidance on which direction it should be.

Cannot get/set keys with account

I have another application that has to interact with keyring.
The application saves the keys in the format of account: "account_name" and service: "service_name" as can be seen on gui keyring manager.
But when i try to get the key with go-keyring it cannot find and neither can i set the account name with it. it only lets me set it in the service and username format.
anyways to enable this?

Using GitHub features to manage repository easily

Github offers a lot of features via special .github folder. Do you have any plan to adapt any of them? For example:

  • Pull Request Template
  • Issue Templates (multiple)
  • Workflows (instead of travis)
  • Dependabot (beta - Check go.mod file and automatically create a pull request if one of the dependency is outdated.)

All of the above used by my repositories. You can check https://github.com/erdaltsksn/gh-label/tree/master/.github to see them.
NOTE: This app gh-label uses go-keyring so I link it.

If you are willing to adapt, I'm ready to work on it and create a pull request for it.

[Linux/Ubuntu]When running compiled binary with sudo an error shows up

This is the error I get when I try to run the binary with sudo

failed to unlock correct collection '/org/freedesktop/secrets/aliases/default'

Now, I don't know enough about keyring to know where the issue exactly is. Is it the issue with gnome-keyring or this lib?
Also, is there anything I can do to mitigate this problem?
The reason I'm running the binary with sudo is since it's being used to connect to vpn, and I can't to that without sudo.
I wanted to keep the username/password pair in keyring for future vpn connections. Is that even possible?

Ubuntu: 21.04
Gnome: 3.38.5

KeepassXC: freezes and org.freedesktop.Secret.IsLocked

Heya ๐Ÿ‘‹

I was checking out gh (the github cli) and it got stuck after running gh auth login. So I dug a little and localized the issue in the interaction between this library and keepassxc, which I use as a keyring.

As far as I can tell, both this library and keepassxc are weird with the secret service protocol, so I'll cross-post it to their issue tracker.

While playring around, I've encountered unhandled errors and freeze.

Not sure about the freezes, but the secret service api spec this regarding IsLocked:

A client application should always be ready to unlock the items for the secrets it needs, or objects it must modify. It must not assume that an item is already unlocked for whatever reason.
A service may lock previously unlocked items for any reason at any time. Usually this is done in response to user actions, timeouts, or external actions (such as the computer sleeping). The inherent race conditions present due to this are unavoidable, and must be handled gracefully.

gh auth login freezes instead of exiting, but it does get a new token from github and saves it. It doesn't seem to apply, but as far as I can tell, that's because it fails to read the secret from the store. (Which arguably should be reported to the user, but it should also work.)

dbus-monitor, with messages not containing org.freedesktop.Secret removed
# 1.241 is gh
method call time=1681678963.125907 sender=:1.241 -> destination=org.freedesktop.secrets serial=2 path=/org/freedesktop/secrets; interface=org.freedesktop.DBus.Properties; member=Get
   string "org.freedesktop.Secret.Service"
   string "Collections"
method return time=1681678963.130388 sender=:1.28 -> destination=:1.241 serial=1575 reply_serial=2
   variant       array [
         object path "/org/freedesktop/secrets/collection/passwords"
      ]
method call time=1681678963.130985 sender=:1.241 -> destination=org.freedesktop.secrets serial=3 path=/org/freedesktop/secrets; interface=org.freedesktop.Secret.Service; member=Unlock
   array [
      object path "/org/freedesktop/secrets/aliases/default"
   ]
method return time=1681678963.131601 sender=:1.28 -> destination=:1.241 serial=1576 reply_serial=3
   array [
      object path "/org/freedesktop/secrets/collection/passwords"
   ]
   object path "/"
method call time=1681678963.132177 sender=:1.241 -> destination=org.freedesktop.secrets serial=4 path=/org/freedesktop/secrets/aliases/default; interface=org.freedesktop.Secret.Collection; member=SearchItems
   array [
      dict entry(
         string "username"
         string ""
      )
      dict entry(
         string "service"
         string "gh:github.com"
      )
   ]
method return time=1681678963.133262 sender=:1.28 -> destination=:1.241 serial=1577 reply_serial=4
   array [
      object path "/org/freedesktop/secrets/collection/passwords/d72fb5ce71ea4196835b86ff53826c9b"
   ]
method call time=1681678963.133718 sender=:1.241 -> destination=org.freedesktop.secrets serial=5 path=/org/freedesktop/secrets; interface=org.freedesktop.Secret.Service; member=OpenSession
   string "plain"
   variant       string ""
method return time=1681678963.134195 sender=:1.28 -> destination=:1.241 serial=1578 reply_serial=5
   variant       string ""
   object path "/org/freedesktop/secrets/session/f4c480f8b9e64673ae53238eee74a4d6"
method call time=1681678963.134689 sender=:1.241 -> destination=org.freedesktop.secrets serial=6 path=/org/freedesktop/secrets/collection/passwords/d72fb5ce71ea4196835b86ff53826c9b; interface=org.freedesktop.Secret.Item; member=GetSecret
   object path "/org/freedesktop/secrets/session/f4c480f8b9e64673ae53238eee74a4d6"
error time=1681678963.135092 sender=:1.28 -> destination=:1.241 error_name=org.freedesktop.Secret.Error.IsLocked reply_serial=6
method call time=1681678963.135611 sender=:1.241 -> destination=org.freedesktop.secrets serial=7 path=/org/freedesktop/secrets/session/f4c480f8b9e64673ae53238eee74a4d6; interface=org.freedesktop.Secret.Session; member=Close
method return time=1681678963.136162 sender=:1.28 -> destination=:1.241 serial=1580 reply_serial=7
method call time=1681678963.136718 sender=:1.241 -> destination=org.freedesktop.secrets serial=8 path=/org/freedesktop/secrets; interface=org.freedesktop.DBus.Properties; member=Get
   string "org.freedesktop.Secret.Service"
   string "Collections"
method return time=1681678963.137231 sender=:1.28 -> destination=:1.241 serial=1581 reply_serial=8
   variant       array [
         object path "/org/freedesktop/secrets/collection/passwords"
      ]
method call time=1681678963.137773 sender=:1.241 -> destination=org.freedesktop.secrets serial=9 path=/org/freedesktop/secrets; interface=org.freedesktop.Secret.Service; member=Unlock
   array [
      object path "/org/freedesktop/secrets/aliases/default"
   ]
method return time=1681678963.138249 sender=:1.28 -> destination=:1.241 serial=1582 reply_serial=9
   array [
      object path "/org/freedesktop/secrets/collection/passwords"
   ]
   object path "/"
method call time=1681678963.138731 sender=:1.241 -> destination=org.freedesktop.secrets serial=10 path=/org/freedesktop/secrets/aliases/default; interface=org.freedesktop.Secret.Collection; member=SearchItems
   array [
      dict entry(
         string "service"
         string "gh:github.com"
      )
      dict entry(
         string "username"
         string ""
      )
   ]
method return time=1681678963.139639 sender=:1.28 -> destination=:1.241 serial=1583 reply_serial=10
   array [
      object path "/org/freedesktop/secrets/collection/passwords/d72fb5ce71ea4196835b86ff53826c9b"
   ]
method call time=1681678963.140248 sender=:1.241 -> destination=org.freedesktop.secrets serial=11 path=/org/freedesktop/secrets; interface=org.freedesktop.Secret.Service; member=OpenSession
   string "plain"
   variant       string ""
method return time=1681678963.140984 sender=:1.28 -> destination=:1.241 serial=1584 reply_serial=11
   variant       string ""
   object path "/org/freedesktop/secrets/session/3754810ae21f448091efecb69caf0fa5"
method call time=1681678963.141441 sender=:1.241 -> destination=org.freedesktop.secrets serial=12 path=/org/freedesktop/secrets/collection/passwords/d72fb5ce71ea4196835b86ff53826c9b; interface=org.freedesktop.Secret.Item; member=GetSecret
   object path "/org/freedesktop/secrets/session/3754810ae21f448091efecb69caf0fa5"
error time=1681678963.142054 sender=:1.28 -> destination=:1.241 error_name=org.freedesktop.Secret.Error.IsLocked reply_serial=12
method call time=1681678963.142504 sender=:1.241 -> destination=org.freedesktop.secrets serial=13 path=/org/freedesktop/secrets/session/3754810ae21f448091efecb69caf0fa5; interface=org.freedesktop.Secret.Session; member=Close
method return time=1681678963.142976 sender=:1.28 -> destination=:1.241 serial=1586 reply_serial=13

Tests

Here's my test tool:

package main

import (
	"fmt"
    "os"
	"github.com/zalando/go-keyring"
)

func main()  {
    var err error;
    switch os.Args[1] {
    case "set":
        err = keyring.Set("gh:github.com", "foo", "aloha!")
    case "get":
        var secret string;
        secret, err = keyring.Get("gh:github.com", "foo")
        if err == nil {
            fmt.Println(secret)
        }
    }
    if err != nil {
        fmt.Printf("err, %s\n", err)
    }
}

These have a single password file opened and unlocked

Actual test cases have to go in comments because of github length restrictions ๐Ÿผ

Versions and stuff

Go stuff:

> cat go.mod
module test

go 1.20

require (
	github.com/alessio/shellescape v1.4.1 // indirect
	github.com/danieljoos/wincred v1.1.2 // indirect
	github.com/godbus/dbus/v5 v5.1.0 // indirect
	github.com/zalando/go-keyring v0.2.2 // indirect
	golang.org/x/sys v0.0.0-20210819135213-f52c844e1c1c // indirect
)

Keepassxc debug output:

KeePassXC - Version 2.7.4

Qt 5.15.8
Debugging mode is disabled.

Operating system: Arch Linux
CPU architecture: x86_64
Kernel: linux 6.2.8-arch1-1

Enabled extensions:
- Auto-Type
- Browser Integration
- SSH Agent
- KeeShare
- YubiKey
- Secret Service Integration

Cryptographic libraries:
- Botan 2.19.3

dbus-daemon is 1.14.6, just in case.
Generally, I'm on arch linux with a lot of stuff, including keepassxc, gh and go, pulled in via nix and home-manager

[Windows] Get, Set and Delete have unexpected and limiting behaviours

Hi,
I've found an issue while retrieving credentials set in the Windows Credential Manager by another application (not using go-keyring).

Let's take this scenario, the main application sets a new credential to a specific service and username:

  • serviceName = service:name
  • username = $YOUR_USERNAME

When trying to call keyring.Get("service:name","$YOUR_USERNAME"), the searched service name is created merging serviceName and username (searchedServiceName = service:name:$YOUR_USERNAME), this behaviour is implemented in the function credName.

This problem is the same also with keyring.Delete and keyring.Set.

Since changing this behavior would break every windows application using this library, I'd propose:

  • add a "raw" counterpart to the public apis (RawGet, RawSet and RawDelete) that skips this behaviour on Windows (on other systems this would be a proxy to the original calls)
  • expose a new public api that can set/toggle a raw mode on Windows, but this will create problems with concurrent goroutines (on other systems that would be a NOOP)

I'm open to work on this as soon as a decision is taken.
Thanks ๐Ÿ˜„

Suspected race condition in secret service prompt handler

Description

Hello, I am from the GitHub CLI team and we use this library to store user tokens in secret storage. Historically we've had some issues around the CLI hanging when trying to interact with a secret service and recently we had a number of reports about the CLI failing to use the secret service on the first attempt after machine restart on linux.

After some investigation I believe the culprit is a race condition in how this library handles prompts. I'll explain the investigation in further detail below but the specific issue is in the following code, which runs when an attempt to Unlock results in a prompt:

func (s *SecretService) handlePrompt(prompt dbus.ObjectPath) (bool, dbus.Variant, error) {
if prompt != dbus.ObjectPath("/") {
err := s.Object(serviceName, prompt).Call(promptInterface+".Prompt", 0, "").Err
if err != nil {
return false, dbus.MakeVariant(""), err
}
err = s.AddMatchSignal(dbus.WithMatchObjectPath(prompt),
dbus.WithMatchInterface(promptInterface),
)
if err != nil {
return false, dbus.MakeVariant(""), err
}
defer func(s *SecretService, options ...dbus.MatchOption) {
_ = s.RemoveMatchSignal(options...)
}(s, dbus.WithMatchObjectPath(prompt), dbus.WithMatchInterface(promptInterface))
promptSignal := make(chan *dbus.Signal, 1)
s.Signal(promptSignal)
signal := <-promptSignal
switch signal.Name {
case promptInterface + ".Completed":
dismissed := signal.Body[0].(bool)
result := signal.Body[1].(dbus.Variant)
return dismissed, result, nil
}
}
return false, dbus.MakeVariant(""), nil
}

This code makes a call with suffix .Prompt, then registers a MatchSignal for the successful completion of the prompt, at which point it blocks until it receives the signal with name suffix .Completed. Once it receives the signal, unlocking can proceed and secrets can be interacted with.

I believe there is an issue here that the signal handling is registered after the .Prompt call has been made. This leaves a short window for the .Completed response to appear on the dbus before we have registered the handler. Typically, with the user filling in a password this would not be an issue, but if the user has set up automatic unlocking, it appears that the response can come fast enough to trigger a race.

Suggested fix

Naively, I think this is as simple as delaying the .Prompt call until right before we block awaiting the signal. I've opened a PR to demonstrate this: #105

I can't say for sure that it hasn't had some other unintended side effect but we've had three reports that it has resolved the original issue.

More details

In this collapsed section you can find the dbus-monitor log showing two attempts to use gh in a way that accessed the secret service, right after machine restart. Below the details section I will detail the specific lines of interest.

signal time=1710441087.030272 sender=org.freedesktop.DBus -> destination=:1.76 serial=2 path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=NameAcquired
   string ":1.76"
signal time=1710441087.030324 sender=org.freedesktop.DBus -> destination=:1.76 serial=4 path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=NameLost
   string ":1.76"
method call time=1710441095.361593 sender=:1.77 -> destination=org.freedesktop.DBus serial=1 path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=Hello
method return time=1710441095.361603 sender=org.freedesktop.DBus -> destination=:1.77 serial=1 reply_serial=1
   string ":1.77"
signal time=1710441095.361606 sender=org.freedesktop.DBus -> destination=(null destination) serial=71 path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=NameOwnerChanged
   string ":1.77"
   string ""
   string ":1.77"
signal time=1710441095.361610 sender=org.freedesktop.DBus -> destination=:1.77 serial=2 path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=NameAcquired
   string ":1.77"
method call time=1710441095.361697 sender=:1.77 -> destination=org.freedesktop.secrets serial=2 path=/org/freedesktop/secrets; interface=org.freedesktop.DBus.Properties; member=Get
   string "org.freedesktop.Secret.Service"
   string "Collections"
method return time=1710441095.361955 sender=:1.18 -> destination=:1.77 serial=38 reply_serial=2
   variant       array [
         object path "/org/freedesktop/secrets/collection/kdewallet"
      ]
method call time=1710441095.362390 sender=:1.77 -> destination=org.freedesktop.secrets serial=3 path=/org/freedesktop/secrets; interface=org.freedesktop.Secret.Service; member=Unlock
   array [
      object path "/org/freedesktop/secrets/aliases/default"
   ]
method return time=1710441095.362684 sender=:1.18 -> destination=:1.77 serial=39 reply_serial=3
   array [
   ]
   object path "/org/freedesktop/secrets/prompt/p0"
method call time=1710441095.362792 sender=:1.77 -> destination=org.freedesktop.secrets serial=4 path=/org/freedesktop/secrets/prompt/p0; interface=org.freedesktop.Secret.Prompt; member=Prompt
   string ""
method return time=1710441095.362801 sender=:1.18 -> destination=:1.77 serial=40 reply_serial=4
signal time=1710441095.362957 sender=:1.18 -> destination=(null destination) serial=41 path=/modules/kwalletd5; interface=org.kde.KWallet; member=walletAsyncOpened
   int32 1
   int32 1966752915
signal time=1710441095.362963 sender=:1.18 -> destination=(null destination) serial=42 path=/modules/kwalletd6; interface=org.kde.KWallet; member=walletAsyncOpened
   int32 1
   int32 1966752915
signal time=1710441095.362965 sender=:1.18 -> destination=(null destination) serial=43 path=/org/freedesktop/secrets; interface=org.freedesktop.Secret.Service; member=CollectionChanged
   object path "/org/freedesktop/secrets/collection/kdewallet"
signal time=1710441095.362967 sender=:1.18 -> destination=:1.77 serial=44 path=/org/freedesktop/secrets/prompt/p0; interface=org.freedesktop.Secret.Prompt; member=Completed
   boolean false
   variant       array [
         object path "/org/freedesktop/secrets/aliases/default"
      ]
method call time=1710441095.362970 sender=:1.77 -> destination=org.freedesktop.DBus serial=5 path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=AddMatch
   string "type='signal',path='/org/freedesktop/secrets/prompt/p0',interface='org.freedesktop.Secret.Prompt'"
method return time=1710441095.362972 sender=org.freedesktop.DBus -> destination=:1.77 serial=3 reply_serial=5
signal time=1710441098.632789 sender=org.freedesktop.DBus -> destination=:1.77 serial=4 path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=NameLost
   string ":1.77"
signal time=1710441098.632838 sender=org.freedesktop.DBus -> destination=(null destination) serial=72 path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=NameOwnerChanged
   string ":1.77"
   string ":1.77"
   string ""
method call time=1710441103.840776 sender=:1.78 -> destination=org.freedesktop.DBus serial=1 path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=Hello
method return time=1710441103.840787 sender=org.freedesktop.DBus -> destination=:1.78 serial=1 reply_serial=1
   string ":1.78"
signal time=1710441103.840803 sender=org.freedesktop.DBus -> destination=(null destination) serial=73 path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=NameOwnerChanged
   string ":1.78"
   string ""
   string ":1.78"
signal time=1710441103.840807 sender=org.freedesktop.DBus -> destination=:1.78 serial=2 path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=NameAcquired
   string ":1.78"
method call time=1710441103.840847 sender=:1.78 -> destination=org.freedesktop.secrets serial=2 path=/org/freedesktop/secrets; interface=org.freedesktop.DBus.Properties; member=Get
   string "org.freedesktop.Secret.Service"
   string "Collections"
method return time=1710441103.841219 sender=:1.18 -> destination=:1.78 serial=45 reply_serial=2
   variant       array [
         object path "/org/freedesktop/secrets/collection/kdewallet"
      ]
method call time=1710441103.841232 sender=:1.78 -> destination=org.freedesktop.secrets serial=3 path=/org/freedesktop/secrets; interface=org.freedesktop.Secret.Service; member=Unlock
   array [
      object path "/org/freedesktop/secrets/aliases/default"
   ]
method return time=1710441103.841579 sender=:1.18 -> destination=:1.78 serial=46 reply_serial=3
   array [
      object path "/org/freedesktop/secrets/aliases/default"
   ]
   object path "/"
method call time=1710441103.841591 sender=:1.78 -> destination=org.freedesktop.secrets serial=4 path=/org/freedesktop/secrets/aliases/default; interface=org.freedesktop.Secret.Collection; member=SearchItems
   array [
      dict entry(
         string "username"
         string ""
      )
      dict entry(
         string "service"
         string "gh:github.com"
      )
   ]
method return time=1710441103.841886 sender=:1.18 -> destination=:1.78 serial=47 reply_serial=4
   array [
      object path "/org/freedesktop/secrets/collection/kdewallet/0"
   ]
method call time=1710441103.841893 sender=:1.78 -> destination=org.freedesktop.secrets serial=5 path=/org/freedesktop/secrets; interface=org.freedesktop.Secret.Service; member=OpenSession
   string "plain"
   variant       string ""
method call time=1710441103.842137 sender=:1.18 -> destination=org.freedesktop.DBus serial=48 path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=AddMatch
   string "type='signal',sender='org.freedesktop.DBus',interface='org.freedesktop.DBus',member='NameOwnerChanged',arg0=':1.78'"
method return time=1710441103.842293 sender=:1.18 -> destination=:1.78 serial=49 reply_serial=5
   variant       array [
      ]
   object path "/org/freedesktop/secrets/session/1"
method call time=1710441103.842300 sender=:1.78 -> destination=org.freedesktop.secrets serial=6 path=/org/freedesktop/secrets/collection/kdewallet/0; interface=org.freedesktop.Secret.Item; member=GetSecret
   object path "/org/freedesktop/secrets/session/1"
method return time=1710441103.842411 sender=:1.18 -> destination=:1.78 serial=50 reply_serial=6
   struct {
      object path "/org/freedesktop/secrets/session/1"
      array [
      ]
      array of bytes "TOKEN-REDACTED"
      string "text/plain; charset=utf8"
   }
method call time=1710441103.842426 sender=:1.78 -> destination=org.freedesktop.secrets serial=7 path=/org/freedesktop/secrets/session/1; interface=org.freedesktop.Secret.Session; member=Close
method return time=1710441103.842569 sender=:1.18 -> destination=:1.78 serial=51 reply_serial=7
method call time=1710441103.842574 sender=:1.18 -> destination=org.freedesktop.DBus serial=52 path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=RemoveMatch
   string "type='signal',sender='org.freedesktop.DBus',interface='org.freedesktop.DBus',member='NameOwnerChanged',arg0=':1.78'"
method call time=1710441103.842576 sender=:1.18 -> destination=org.freedesktop.DBus serial=53 path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=RemoveMatch
   string "type='signal',sender='org.freedesktop.DBus',interface='org.freedesktop.DBus',member='NameOwnerChanged',arg0='org.freedesktop.DBus'"
error time=1710441103.842578 sender=org.freedesktop.DBus -> destination=:1.18 error_name=org.freedesktop.DBus.Error.MatchRuleNotFound reply_serial=53
   string "The given match rule wasn't found and can't be removed"
signal time=1710441104.135900 sender=org.freedesktop.DBus -> destination=:1.78 serial=3 path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=NameLost
   string ":1.78"
signal time=1710441104.135944 sender=org.freedesktop.DBus -> destination=(null destination) serial=74 path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=NameOwnerChanged
   string ":1.78"
   string ":1.78"
   string ""

In the first usage of gh we see the call to .Unlock returning with a need to prompt:

method call time=1710441095.362390 sender=:1.77 -> destination=org.freedesktop.secrets serial=3 path=/org/freedesktop/secrets; interface=org.freedesktop.Secret.Service; member=Unlock
   array [
      object path "/org/freedesktop/secrets/aliases/default"
   ]
method return time=1710441095.362684 sender=:1.18 -> destination=:1.77 serial=39 reply_serial=3
   array [
   ]
   object path "/org/freedesktop/secrets/prompt/p0"

Immediately after this we see the call to .Prompt:

method call time=1710441095.362792 sender=:1.77 -> destination=org.freedesktop.secrets serial=4 path=/org/freedesktop/secrets/prompt/p0; interface=org.freedesktop.Secret.Prompt; member=Prompt
   string ""
method return time=1710441095.362801 sender=:1.18 -> destination=:1.77 serial=40 reply_serial=4

Then shortly after we see the .Completed response:

signal time=1710441095.362967 sender=:1.18 -> destination=:1.77 serial=44 path=/org/freedesktop/secrets/prompt/p0; interface=org.freedesktop.Secret.Prompt; member=Completed
   boolean false
   variant       array [
         object path "/org/freedesktop/secrets/aliases/default"
      ]

And following that we see the attempt to AddMatch on this interface:

method call time=1710441095.362970 sender=:1.77 -> destination=org.freedesktop.DBus serial=5 path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=AddMatch
   string "type='signal',path='/org/freedesktop/secrets/prompt/p0',interface='org.freedesktop.Secret.Prompt'"

3 seconds later (which is the length of time the CLI waits before reporting a timeout), we see the client close:

signal time=1710441098.632789 sender=org.freedesktop.DBus -> destination=:1.77 serial=4 path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=NameLost
   string ":1.77"

In the second attempt to use gh, there is no prompt required and everything completes as expected:

method call time=1710441103.842300 sender=:1.78 -> destination=org.freedesktop.secrets serial=6 path=/org/freedesktop/secrets/collection/kdewallet/0; interface=org.freedesktop.Secret.Item; member=GetSecret
   object path "/org/freedesktop/secrets/session/1"
method return time=1710441103.842411 sender=:1.18 -> destination=:1.78 serial=50 reply_serial=6
   struct {
      object path "/org/freedesktop/secrets/session/1"
      array [
      ]
      array of bytes "TOKEN-REDACTED"
      string "text/plain; charset=utf8"
   }

[Windows] naive (incorrect) conversion of byte slice to Go string

First, thanks for this package!

There is a bug on Windows.

This line converts a byte slice to a Go string. This is fine if the byte slice is utf8, however Windows likes to store strings as utf16 it seems like the content of this blob is application defined. Nonetheless when the content is utf16, the result of the cast is an invalid Go string.

The fix would be to validate the byte slice and convert it to utf8 prior to casting it into a Go string.

Edit: I've looked into this somewhat and have realized there's no easy way to do this conversion in general, without knowing the encoding ahead of time.

[Question] Persistent Keyring

Hi, First of all, thanks to the maintainer team for the wonderful library.

Does go-keyring provide a persistent mechanism? Is it cross-platform? e.g. I want to persist some secret which should remain across machine restarts for n number of days. I've observed that stored secrets were getting removed when restarting the machine. Is it possible to achieve this with this library?

Build issue in 0.2.0

In 0.2.0, the file keyring_linux.go was renamed to keyring_unix.go to enable dbus on more platforms.

Unfortunately, the dbus implementation in godbus/dbus requires cgo for both dragonflybsd and freebsd. This means that if you are building with cgo disabled to build static binaries or for easier cross-compilation, upgrading to v0.2.0 results in build failures, with no way to disable this functionality.

One option would be to put a build tag constraint on cgo in keyring_unix.go to not compile in the code if cgo is disabled. That would probably require a copy of the file to be available for non-dragonflybsd/freebsd platforms that still compiles if cgo is disabled.

No such interface 'org.freedesktop.Secret.Collection' on object at path /org/freedesktop/secrets/collection/login

The example from the readme file throws an error on keyring.Set(service, user, password)``:
No such interface 'org.freedesktop.Secret.Collection' on object at path /org/freedesktop/secrets/collection/login

Any idea why is that? If I run it with sudo the program just get stuck

package main

import (
    "log"

    "github.com/zalando/go-keyring"
)

func main() {
    service := "my-app"
    user := "anon"
    password := "secret"

    // set password
    err := keyring.Set(service, user, password)
    if err != nil {
        log.Fatal(err)
    }

    // get password
    secret, err := keyring.Get(service, user)
    if err != nil {
        log.Fatal(err)
    }

    log.Println(secret)
}

Panic when calling Set() on Ubuntu 20.04 on WSL

Calling keyring.Set() on Ubuntu 20.04 on WSL results in the following panic: panic: runtime error: slice bounds out of range. On Ubuntu 18.04 on the same machine, keyring.Set() fails more gracefully, returning the error exec: "dbus-launch": executable file not found in $PATH. I've included a minimal PoC to reproduce.

OS: Microsoft Windows Server Version 1809

package main

import (
	"fmt"

	"github.com/zalando/go-keyring"
)

func main() {
	if err := keyring.Set("my-service", "foo", "bar"); err != nil {
		fmt.Printf("Error: %s", err)
	} else {
		fmt.Printf("Success!")
	}
}

Ubuntu 20.04 on WSL:

thomas@windows-server-2019:~$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 20.04 LTS
Release:        20.04
Codename:       focal
thomas@windows-server-2019:~$ /mnt/c/Users/thomas/Desktop/keyring
panic: runtime error: slice bounds out of range [308:282]

goroutine 1 [running]:
github.com/godbus/dbus.getSessionBusPlatformAddress(0x59090f, 0x18, 0x0, 0x0)
        /Users/thomas/Documents/projects/go/pkg/mod/github.com/godbus/[email protected]+incompatible/conn_other.go:30 +0x295
github.com/godbus/dbus.getSessionBusAddress(0x0, 0x0, 0x0, 0x0)
        /Users/thomas/Documents/projects/go/pkg/mod/github.com/godbus/[email protected]+incompatible/conn.go:96 +0xf8
github.com/godbus/dbus.SessionBusPrivate(0x7f85a58ece20, 0xc00008cd18, 0x40d430)
        /Users/thomas/Documents/projects/go/pkg/mod/github.com/godbus/[email protected]+incompatible/conn.go:101 +0x25
github.com/godbus/dbus.SessionBus(0x0, 0x0, 0x0)
        /Users/thomas/Documents/projects/go/pkg/mod/github.com/godbus/[email protected]+incompatible/conn.go:73 +0xb5
github.com/zalando/go-keyring/secret_service.NewSecretService(0x40d430, 0xc00008cd80, 0x44bfa5)
        /Users/thomas/Documents/projects/go/pkg/mod/github.com/zalando/[email protected]/secret_service/secret_service.go:50 +0x26
github.com/zalando/go-keyring.secretServiceProvider.Set(0x58d634, 0xa, 0x58c369, 0x3, 0x58c357, 0x3, 0x0, 0x0)
        /Users/thomas/Documents/projects/go/pkg/mod/github.com/zalando/[email protected]/keyring_linux.go:14 +0x5d
github.com/zalando/go-keyring.Set(...)
        /Users/thomas/Documents/projects/go/pkg/mod/github.com/zalando/[email protected]/keyring.go:27
main.main()
        /Users/thomas/Documents/projects/go/src/github.com/DopplerHQ/cli/keyring-poc.go:10 +0x78

Ubuntu 18.04 on WSL:

thomas@windows-server-2019:~$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 18.04.2 LTS
Release:        18.04
Codename:       bionic
thomas@windows-server-2019:~$ /mnt/c/Users/thomas/Desktop/keyring
Error: exec: "dbus-launch": executable file not found in $PATH

Missing gitignore file

The purpose of .gitignore file is to ensure that certain files not tracked by git remain untracked.

panic in windowsKeychain.Get

ref kopia/kopia#732

I got a panic in Kopia, which uses go-keyring:

> "kopia.exe" snapshot create --description "abcdefg" "C:\some\path"
panic: runtime error: invalid memory address or nil pointer dereference
[signal 0xc0000005 code=0x0 addr=0x38 pc=0x136cc7a]
goroutine 1 [running]:
github.com/zalando/go-keyring.windowsKeychain.Get(0xc000167860, 0x22, 0xc00042f2ee, 0x8, 0x1a4eba0, 0xc00042f2e0, 0x1a3b740, 0xc00050f880)
	/home/travis/gopath/pkg/mod/github.com/zalando/[email protected]/keyring_windows.go:20 +0x11a
github.com/zalando/go-keyring.Get(...)
	/home/travis/gopath/pkg/mod/github.com/zalando/[email protected]/keyring.go:32
github.com/kopia/kopia/repo.GetPersistedPassword(0x1d8fbc0, 0xc000534120, 0xc0004be200, 0x39, 0xc00050f7e0, 0x0, 0xc000534150)
	/home/travis/gopath/src/github.com/kopia/kopia/repo/password.go:26 +0x375
github.com/kopia/kopia/cli.getPasswordFromFlags(0x1d8fbc0, 0xc000534120, 0x1d70100, 0xc0000720d0, 0x0, 0x0, 0x1a4cd60)
	/home/travis/gopath/src/github.com/kopia/kopia/cli/password.go:56 +0x145
github.com/kopia/kopia/cli.openRepository(0x1d8fbc0, 0xc000534120, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0)
	/home/travis/gopath/src/github.com/kopia/kopia/cli/config.go:60 +0x165
github.com/kopia/kopia/cli.maybeRepositoryAction.func1.1(0x0, 0x0)
	/home/travis/gopath/src/github.com/kopia/kopia/cli/app.go:141 +0xf0
github.com/kopia/kopia/cli.withProfiling(...)
	/home/travis/gopath/src/github.com/kopia/kopia/cli/profile_disabled.go:7
github.com/kopia/kopia/cli.maybeRepositoryAction.func1(0xc0001a2d80, 0xe8bcaa, 0x1ac45e0)
	/home/travis/gopath/src/github.com/kopia/kopia/cli/app.go:125 +0x64
gopkg.in/alecthomas/kingpin%2ev2.(*actionMixin).applyActions(0xc0004f9518, 0xc0001a2d80, 0x0, 0x0)
	/home/travis/gopath/pkg/mod/gopkg.in/alecthomas/[email protected]/actions.go:28 +0x74
gopkg.in/alecthomas/kingpin%2ev2.(*Application).applyActions(0xc000152870, 0xc0001a2d80, 0x0, 0x0)
	/home/travis/gopath/pkg/mod/gopkg.in/alecthomas/[email protected]/app.go:557 +0xe3
gopkg.in/alecthomas/kingpin%2ev2.(*Application).execute(0xc000152870, 0xc0001a2d80, 0xc00050f620, 0x2, 0x2, 0x0, 0x0, 0x0, 0xc000007dd8)
	/home/travis/gopath/pkg/mod/gopkg.in/alecthomas/[email protected]/app.go:390 +0xa5
gopkg.in/alecthomas/kingpin%2ev2.(*Application).Parse(0xc000152870, 0xc000130010, 0x5, 0x7, 0x1, 0xc000007de0, 0x0, 0x1)
	/home/travis/gopath/pkg/mod/gopkg.in/alecthomas/[email protected]/app.go:222 +0x213
main.main()
	/home/travis/gopath/src/github.com/kopia/kopia/main.go:77 +0x188

It crashes here in line 20:

func (k windowsKeychain) Get(service, username string) (string, error) {
cred, err := wincred.GetGenericCredential(k.credName(service, username))
if err != nil {
if err == syscall.ERROR_NOT_FOUND {
return "", ErrNotFound
}
return "", err
}
return string(cred.CredentialBlob), nil

Now, I don't know much about golang, but one thing that might be related is that go-keyring checks whether err != nil, whereas wincred checks if cred != nil.

In case err was nil and ret was 0 here https://github.com/danieljoos/wincred/blob/78f93c1f8b99b0c2f6e7f3d2bdc4993cf87bddff/sys.go#L70, I believe that it would cause nil, nil to be returned.

Another way nil, nil might be returned was if cred is null:
https://github.com/danieljoos/wincred/blob/78f93c1f8b99b0c2f6e7f3d2bdc4993cf87bddff/conversion.go#L91-L94

See danieljoos/wincred#5 (comment).

So I'd change the check from if err != nil to if cred == nil.
That wouldn't solve the underlying issue, but might convert the panic into a normal, handleable error.


Update:

I took a look at the logs, and, running on two different computers, I had two errors out of 290 runs (0.69%).
Each time it was executing a bunch of times successfully in sequence and then failed.

Ref danieljoos/wincred#5 (comment), the CreadRead API seems to be a bit flakey, do you think it might make sense to check if cred, err are nil, nil, and then try a second time, or would that be better handled at the calling application (Kopia in this case)?


Update 2:

4 failures in 439 runs (0.91%)

Set username in wincred

It is possible to set the username in wincred package Credential struct.

It does not add new features, only the Credential Manager will show the username properly.

use errors.New("not found") instead of fmt.Errorf(...)

fmt.Errorf is usually used with the first parameter being a format string.
In your case there is a plain string used, so errors.New(..) should suffice.

If you want to be more fancy, you may implement the error interface like this

type Error string

func (e Error) Error() string {
  return string(e)
}
const (
  ErrNotFound = Error("not found")
)

Comparison with 99designs/keyring

Hey there! It appears to me that 99designs/keyring supports more backends compared to go-keyring. Other than that, are there any differences between both of them? Looks like 99designs/keyring also supports KWallet as well custom keyring names while go-keyring has open PRs for a while now.

Password exposed in command line on Mac

On Mac, the use of the security utility to add a generic password to the Keychain insecurely passes the password on the command line, allowing any other process on the system to observe the password. To make it more secure, pass the password on STDIN to security or use the Keychain API directly.

Support secret transfer via dh-ietf1024-sha256-aes128-cbc-pkcs7

As of now, go-keyring only supports "plain" to transfer secrets.
It would be nice if go-keyring would support dh-ietf1024-sha256-aes128-cbc-pkcs7 as well as defined in here:
https://specifications.freedesktop.org/secret-service/latest/ch07s03.html

Further context: #66 (comment)

Kwallet recently added support for Secret Service API.

Looks like this does not work as "plain" Algorithm is not supported. Trying to use it, I get: Algorithm plain is not supported. (only dh-ietf1024-sha256-aes128-cbc-pkcs7 is supported) See: https://invent.kde.org/frameworks/kwallet/-/blob/master/src/runtime/kwalletd/kwalletfreedesktopservice.cpp#L265

Probably either kwallet should add support for plain, or maybe

err := s.object.Call(serviceInterface+".OpenSession", 0, "plain", dbus.MakeVariant("")).Store(&disregard, &sessionPath)

could be changed to use other algorithms as well.

What if i want to use an app relying on this library on wslv2 and have no ui/gnome/seahorse?

Really struggling using an app that uses this library on wslv2.

Installed keyring, but have no gnome, no gkeyring, no seahorse ... how can i create a keyring via the cli?

seahorse tells me:
(seahorse:23589): Gtk-WARNING **: 23:33:20.817: cannot open display:

gkeyring isn't maintained anymore

It would be nice if you could give me a hint, how i can initialize the default keyring via a terminal without x and gnome.

Update:

Found:
https://www.reddit.com/r/ProtonMail/comments/81zmto/linux_keyring_issues/
microsoft/WSL#4254

Ok, after installing some dbus, gnome keyring etc. i think this is working, even if it is no good solution:

dbus-run-session -- zsh (or your prefered sh)
gnome-keyring-daemon --unlock
<your password> return, ctrl+d
... use your program that relies on this library ...

if anyone knows how to "automate" this, please let me know

ok, best thin i found til now:

Add this to your bashrc, or zshrc or ...

if [ "$DBUS_SESSION_BUS_ADDRESS" = "" ]
then
   exec dbus-run-session -- zsh
else
   echo "$(/lib/cryptsetup/askpass 'Password: ')" | gnome-keyring-daemon --unlock
fi

!!! modify the exec statement to your usecase !!!

Cons:

  • one extra sh session?
  • password prompt on every sh session
  • extra dbus session for every sh

Adding IsAvailable to the interface

I am integrating this library in a project of mine and I am running into the issue that I want to bypass caching if it is not available locally.

A method added to the interface would be helpful. The method IsAvailable would check if the service is available. I looked at the source and saw that it might have been already tried to add a function like IsAvailable. I see a commented out function:

// func (*MacOSXKeychain) IsAvailable() bool {
// 	return exec.Command(execPathKeychain).Run() != exec.ErrNotFound
// }

Before I go into developing this more deeply, I was wondering if you tried to adding this method and have come into fundamental problems that prevent implementation.

We can also discuss if this is a good feature to be merged upstream into this library.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.