This commit is contained in:
github-actions[bot] 2023-12-30 20:06:05 +00:00 committed by GitHub
commit 8499783206
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 211 additions and 68 deletions

View File

@ -1,8 +1,8 @@
pkgname=goldwarden
pkgver=0.2.6
pkgver=0.2.7
pkgrel=1
pkgdesc='A feature-packed Bitwarden compatible desktop integration'
arch=('x86_64')
arch=('x86_64', 'aarch64')
url="https://github.com/quexten/$pkgname"
license=('MIT')
depends=('libfido2')

View File

@ -55,8 +55,17 @@ func handleSetNotifications(request messages.IPCMessage, cfg *config.Config, vau
})
}
func handleGetRuntimeConfig(request messages.IPCMessage, cfg *config.Config, vault *vault.Vault, ctx *sockets.CallingContext) (response messages.IPCMessage, err error) {
return messages.IPCMessageFromPayload(messages.GetRuntimeConfigResponse{
UseMemguard: cfg.ConfigFile.RuntimeConfig.UseMemguard,
SSHAgentSocketPath: cfg.ConfigFile.RuntimeConfig.SSHAgentSocketPath,
GoldwardenSocketPath: cfg.ConfigFile.RuntimeConfig.GoldwardenSocketPath,
})
}
func init() {
AgentActionsRegistry.Register(messages.MessageTypeForEmptyPayload(messages.SetIdentityURLRequest{}), handleSetIdentity)
AgentActionsRegistry.Register(messages.MessageTypeForEmptyPayload(messages.SetApiURLRequest{}), handleSetApiURL)
AgentActionsRegistry.Register(messages.MessageTypeForEmptyPayload(messages.SetNotificationsURLRequest{}), handleSetNotifications)
AgentActionsRegistry.Register(messages.MessageTypeForEmptyPayload(messages.GetRuntimeConfigRequest{}), handleGetRuntimeConfig)
}

View File

@ -46,6 +46,8 @@ type RuntimeConfig struct {
Password string
Pin string
UseMemguard bool
SSHAgentSocketPath string
GoldwardenSocketPath string
}
type ConfigFile struct {

View File

@ -144,6 +144,7 @@ func (vaultAgent) Unlock(passphrase []byte) error {
type SSHAgentServer struct {
vault *vault.Vault
config *config.Config
runtimeConfig *config.RuntimeConfig
unlockRequestAction func() bool
}
@ -151,10 +152,11 @@ func (v *SSHAgentServer) SetUnlockRequestAction(action func() bool) {
v.unlockRequestAction = action
}
func NewVaultAgent(vault *vault.Vault, config *config.Config) SSHAgentServer {
func NewVaultAgent(vault *vault.Vault, config *config.Config, runtimeConfig *config.RuntimeConfig) SSHAgentServer {
return SSHAgentServer{
vault: vault,
config: config,
vault: vault,
config: config,
runtimeConfig: runtimeConfig,
unlockRequestAction: func() bool {
log.Info("Unlock Request, but no action defined")
return false
@ -163,13 +165,7 @@ func NewVaultAgent(vault *vault.Vault, config *config.Config) SSHAgentServer {
}
func (v SSHAgentServer) Serve() {
home, err := os.UserHomeDir()
if err != nil {
panic(err)
}
path := home + "/.goldwarden-ssh-agent.sock"
path := v.runtimeConfig.SSHAgentSocketPath
if _, err := os.Stat(path); err == nil {
if err := os.Remove(path); err != nil {
log.Error("Could not remove old socket file: %s", err)

View File

@ -203,7 +203,7 @@ func StartUnixAgent(path string, runtimeConfig config.RuntimeConfig) error {
}()
if !runtimeConfig.DisableSSHAgent {
vaultAgent := ssh.NewVaultAgent(vault, &cfg)
vaultAgent := ssh.NewVaultAgent(vault, &cfg, &runtimeConfig)
vaultAgent.SetUnlockRequestAction(func() bool {
err := cfg.TryUnlock(vault)
if err == nil {

View File

@ -6,44 +6,88 @@ import (
"path/filepath"
"strings"
"github.com/quexten/goldwarden/agent/config"
"github.com/quexten/goldwarden/browserbiometrics/logging"
)
var chromiumPaths = []string{
"~/.config/google-chrome/",
"~/.config/google-chrome-beta/",
"~/.config/google-chrome-unstable/",
"~/.config/chromium/",
"~/.config/BraveSoftware/Brave-Browser/",
"~/.config/thorium/",
"~/.config/microsoft-edge-beta/",
"~/.config/microsoft-edge-dev/",
}
var mozillaPaths = []string{"~/.mozilla/", "~/.librewolf/", "~/.waterfox/"}
const appID = "com.quexten.bw-bio-handler"
var transportKey []byte
func Main() {
if os.Args[1] == "install" {
var err error
err = detectAndInstallBrowsers(".config")
if err != nil {
panic("Failed to detect browsers: " + err.Error())
}
err = detectAndInstallBrowsers(".mozilla")
if err != nil {
panic("Failed to detect browsers: " + err.Error())
}
return
}
func Main(rtCfg *config.RuntimeConfig) {
logging.Debugf("Starting browserbiometrics")
transportKey = generateTransportKey()
logging.Debugf("Generated transport key")
setupCommunication()
readLoop()
readLoop(rtCfg)
}
func DetectAndInstallBrowsers() error {
var err error
// first, ensure the native messaging hosts dirs exist
for _, path := range chromiumPaths {
path = strings.ReplaceAll(path, "~", os.Getenv("HOME"))
_, err = os.Stat(path)
if err != nil {
continue
}
_, err = os.Stat(path + "NativeMessagingHosts/")
if err == nil {
fmt.Println("Native messaging host directory already exists: " + path + "NativeMessagingHosts/")
continue
}
err = os.MkdirAll(path+"NativeMessagingHosts/", 0755)
if err != nil {
fmt.Println("Error creating native messaging host directory: " + err.Error())
} else {
fmt.Println("Created native messaging host directory: " + path + "NativeMessagingHosts/")
}
}
for _, path := range mozillaPaths {
path = strings.ReplaceAll(path, "~", os.Getenv("HOME"))
_, err = os.Stat(path)
if err != nil {
continue
}
_, err = os.Stat(path + "native-messaging-hosts/")
if err == nil {
fmt.Println("Native messaging host directory already exists: " + path + "native-messaging-hosts/")
continue
}
err = os.MkdirAll(path+"native-messaging-hosts/", 0755)
if err != nil {
fmt.Println("Error creating native messaging host directory: " + err.Error())
} else {
fmt.Println("Created native messaging host directory: " + path + "native-messaging-hosts/")
}
}
err = detectAndInstallBrowsers(".config")
if err != nil {
return err
}
err = detectAndInstallBrowsers(".mozilla")
if err != nil {
return err
for _, path := range mozillaPaths {
path = strings.ReplaceAll(path, "~/", "")
err = detectAndInstallBrowsers(path)
if err != nil {
return err
}
}
return nil
}

View File

@ -6,12 +6,16 @@ import (
"io"
"os"
"github.com/quexten/goldwarden/agent/config"
"github.com/quexten/goldwarden/browserbiometrics/logging"
"github.com/quexten/goldwarden/client"
"github.com/quexten/goldwarden/ipc/messages"
)
func readLoop() {
var runtimeConfig *config.RuntimeConfig
func readLoop(rtCfg *config.RuntimeConfig) {
runtimeConfig = rtCfg
v := bufio.NewReader(os.Stdin)
s := bufio.NewReaderSize(v, bufferSize)
@ -101,7 +105,7 @@ func handlePayloadMessage(msg PayloadMessage, appID string) {
case "biometricUnlock":
logging.Debugf("Biometric unlock requested")
// logging.Debugf("Biometrics authorized: %t", isAuthorized)
result, err := client.NewUnixSocketClient().SendToAgent(messages.GetBiometricsKeyRequest{})
result, err := client.NewUnixSocketClient(runtimeConfig).SendToAgent(messages.GetBiometricsKeyRequest{})
if err != nil {
logging.Errorf("Unable to send message to agent: %s", err.Error())
return

View File

@ -5,18 +5,21 @@ import (
"io"
"log"
"net"
"os"
"github.com/quexten/goldwarden/agent/config"
"github.com/quexten/goldwarden/ipc/messages"
)
const READ_BUFFER = 1 * 1024 * 1024 // 1MB
type UnixSocketClient struct {
runtimeConfig *config.RuntimeConfig
}
func NewUnixSocketClient() UnixSocketClient {
return UnixSocketClient{}
func NewUnixSocketClient(runtimeConfig *config.RuntimeConfig) UnixSocketClient {
return UnixSocketClient{
runtimeConfig: runtimeConfig,
}
}
func reader(r io.Reader) interface{} {
@ -37,12 +40,7 @@ func reader(r io.Reader) interface{} {
}
func (client UnixSocketClient) SendToAgent(request interface{}) (interface{}, error) {
home, err := os.UserHomeDir()
if err != nil {
panic(err)
}
c, err := net.Dial("unix", home+"/.goldwarden.sock")
c, err := net.Dial("unix", client.runtimeConfig.GoldwardenSocketPath)
if err != nil {
return nil, err
}

View File

@ -1,6 +1,8 @@
package cmd
import (
"fmt"
"github.com/quexten/goldwarden/ipc/messages"
"github.com/spf13/cobra"
)
@ -104,6 +106,32 @@ var setNotificationsURLCmd = &cobra.Command{
},
}
var getRuntimeConfigCmd = &cobra.Command{
Use: "get-runtime-config",
Short: "Get the runtime config",
Long: `Get the runtime config.`,
Run: func(cmd *cobra.Command, args []string) {
request := messages.GetRuntimeConfigRequest{}
result, err := commandClient.SendToAgent(request)
if err != nil {
handleSendToAgentError(err)
return
}
switch result := result.(type) {
case messages.GetRuntimeConfigResponse:
fmt.Println("{")
fmt.Println(" \"useMemguard\": " + fmt.Sprintf("%t", result.UseMemguard) + ",")
fmt.Println(" \"SSHAgentSocketPath\": \"" + result.SSHAgentSocketPath + "\",")
fmt.Println(" \"goldwardenSocketPath\": \"" + result.GoldwardenSocketPath + "\"")
fmt.Println("}")
default:
println("Wrong IPC response type")
}
},
}
var configCmd = &cobra.Command{
Use: "config",
Short: "Manage the configuration",
@ -115,4 +143,5 @@ func init() {
configCmd.AddCommand(setApiUrlCmd)
configCmd.AddCommand(setIdentityURLCmd)
configCmd.AddCommand(setNotificationsURLCmd)
configCmd.AddCommand(getRuntimeConfigCmd)
}

View File

@ -3,7 +3,6 @@ package cmd
import (
"os"
"os/signal"
"strings"
"github.com/awnumar/memguard"
"github.com/quexten/goldwarden/agent"
@ -19,15 +18,6 @@ var daemonizeCmd = &cobra.Command{
websocketDisabled := runtimeConfig.WebsocketDisabled
sshDisabled := runtimeConfig.DisableSSHAgent
_, err := os.Stat("/.flatpak-info")
isFlatpak := err == nil
if isFlatpak {
runtimeConfig.ConfigDirectory = "~/.var/app/com.quexten.Goldwarden/config/goldwarden.json"
userHome, _ := os.UserHomeDir()
runtimeConfig.ConfigDirectory = strings.ReplaceAll(runtimeConfig.ConfigDirectory, "~", userHome)
println("Flatpak Config directory: " + runtimeConfig.ConfigDirectory)
}
if websocketDisabled {
println("Websocket disabled")
}
@ -42,11 +32,7 @@ var daemonizeCmd = &cobra.Command{
<-signalChannel
memguard.SafeExit(0)
}()
home, err := os.UserHomeDir()
if err != nil {
panic(err)
}
err = agent.StartUnixAgent(home+"/.goldwarden.sock", runtimeConfig)
err := agent.StartUnixAgent(runtimeConfig.GoldwardenSocketPath, runtimeConfig)
if err != nil {
panic(err)
}

View File

@ -29,7 +29,7 @@ func Execute(cfg config.RuntimeConfig) {
recv, send := agent.StartVirtualAgent(runtimeConfig)
commandClient = client.NewVirtualClient(send, recv)
} else {
commandClient = client.NewUnixSocketClient()
commandClient = client.NewUnixSocketClient(&cfg)
}
err := rootCmd.Execute()

View File

@ -14,6 +14,14 @@ type SetNotificationsURLRequest struct {
Value string
}
type GetRuntimeConfigRequest struct{}
type GetRuntimeConfigResponse struct {
UseMemguard bool
SSHAgentSocketPath string
GoldwardenSocketPath string
}
func init() {
registerPayloadParser(func(payload []byte) (interface{}, error) {
var req SetApiURLRequest
@ -41,4 +49,22 @@ func init() {
}
return req, nil
}, SetNotificationsURLRequest{})
registerPayloadParser(func(payload []byte) (interface{}, error) {
var req GetRuntimeConfigRequest
err := json.Unmarshal(payload, &req)
if err != nil {
panic("Unmarshal: " + err.Error())
}
return req, nil
}, GetRuntimeConfigRequest{})
registerPayloadParser(func(payload []byte) (interface{}, error) {
var req GetRuntimeConfigResponse
err := json.Unmarshal(payload, &req)
if err != nil {
panic("Unmarshal: " + err.Error())
}
return req, nil
}, GetRuntimeConfigResponse{})
}

34
main.go
View File

@ -10,11 +10,6 @@ import (
)
func main() {
if len(os.Args) > 1 && (strings.Contains(os.Args[1], "com.8bit.bitwarden.json") || strings.Contains(os.Args[1], "chrome-extension://")) {
browserbiometrics.Main()
return
}
var configPath string
if path, found := os.LookupEnv("GOLDWARDEN_CONFIG_DIRECTORY"); found {
configPath = path
@ -39,10 +34,39 @@ func main() {
Password: os.Getenv("GOLDWARDEN_AUTH_PASSWORD"),
Pin: os.Getenv("GOLDWARDEN_PIN"),
UseMemguard: os.Getenv("GOLDWARDEN_NO_MEMGUARD") != "true",
SSHAgentSocketPath: os.Getenv("GOLDWARDEN_SSH_AUTH_SOCK"),
GoldwardenSocketPath: os.Getenv("GOLDWARDEN_SOCKET_PATH"),
ConfigDirectory: configPath,
}
if len(os.Args) > 1 && (strings.Contains(os.Args[1], "com.8bit.bitwarden.json") || strings.Contains(os.Args[1], "chrome-extension://")) {
browserbiometrics.Main(&runtimeConfig)
return
}
home, err := os.UserHomeDir()
if err != nil {
panic(err)
}
if runtimeConfig.SSHAgentSocketPath == "" {
runtimeConfig.SSHAgentSocketPath = home + "/.goldwarden-ssh-agent.sock"
}
if runtimeConfig.GoldwardenSocketPath == "" {
runtimeConfig.GoldwardenSocketPath = home + "/.goldwarden.sock"
}
_, err = os.Stat("/.flatpak-info")
isFlatpak := err == nil
if isFlatpak {
userHome, _ := os.UserHomeDir()
runtimeConfig.ConfigDirectory = userHome + "/.var/app/com.quexten.Goldwarden/config/goldwarden.json"
runtimeConfig.ConfigDirectory = strings.ReplaceAll(runtimeConfig.ConfigDirectory, "~", userHome)
println("Flatpak Config directory: " + runtimeConfig.ConfigDirectory)
runtimeConfig.SSHAgentSocketPath = userHome + "/.var/app/com.quexten.Goldwarden/data/ssh-auth-sock"
runtimeConfig.GoldwardenSocketPath = userHome + "/.var/app/com.quexten.Goldwarden/data/goldwarden.sock"
}
if runtimeConfig.SingleProcess {
runtimeConfig.DisablePinRequirement = true
runtimeConfig.DisableAuth = true

View File

@ -3,12 +3,13 @@ gi.require_version('Gtk', '4.0')
gi.require_version('Adw', '1')
import gc
import time
from gi.repository import Gtk, Adw, GLib
from gi.repository import Gtk, Adw, GLib, Notify
import goldwarden
import clipboard
from threading import Thread
import sys
import os
Notify.init("Goldwarden")
class MyApp(Adw.Application):
def __init__(self, **kwargs):
@ -93,9 +94,13 @@ class MainWindow(Gtk.ApplicationWindow):
if keyval == 112:
print("copy password")
clipboard.write(self.history_list.get_selected_row().password)
Notify.Notification.new("Goldwarden", "Password Copied", "dialog-information").show()
elif keyval == 117:
print("copy username")
clipboard.write(self.history_list.get_selected_row().username)
notification=Notify.Notification.new("Goldwarden", "Username Copied", "dialog-information")
notification.set_timeout(5)
notification.show()
keycont.connect('key-pressed', handle_keypress, self)
self.add_controller(keycont)

View File

@ -104,6 +104,17 @@ def get_vault_logins():
print(e)
return None
def get_runtime_config():
restic_cmd = f"{BINARY_PATH} config get-runtime-config"
result = subprocess.run(restic_cmd.split(), capture_output=True, text=True)
if result.returncode != 0:
return None
try:
return json.loads(result.stdout)
except Exception as e:
print(e)
return None
def autotype(username, password):
# environment
env = os.environ.copy()

View File

@ -30,11 +30,15 @@ class SettingsWinvdow(Gtk.ApplicationWindow):
self.ssh_row = Adw.ActionRow()
self.ssh_row.set_title("SSH Daemon")
self.ssh_row.set_subtitle("Listening at ~/.goldwarden-ssh-agent.sock")
self.ssh_row.set_subtitle("Getting status...")
self.ssh_row.set_icon_name("emblem-default")
self.preferences_group.add(self.ssh_row)
self.icon = components.status_icon_ok("emblem-default")
self.ssh_row.add_prefix(self.icon)
self.goldwarden_daemon_row = Adw.ActionRow()
self.goldwarden_daemon_row.set_title("Goldwarden Daemon")
self.goldwarden_daemon_row.set_subtitle("Getting status...")
self.goldwarden_daemon_row.set_icon_name("emblem-default")
self.preferences_group.add(self.goldwarden_daemon_row)
self.login_with_device = Adw.ActionRow()
self.login_with_device.set_title("Login with device")
@ -168,6 +172,11 @@ class SettingsWinvdow(Gtk.ApplicationWindow):
pin_set = goldwarden.is_pin_enabled()
status = goldwarden.get_vault_status()
runtimeCfg = goldwarden.get_runtime_config()
if runtimeCfg != None:
self.ssh_row.set_subtitle("Listening at "+runtimeCfg["SSHAgentSocketPath"])
self.goldwarden_daemon_row.set_subtitle("Listening at "+runtimeCfg["goldwardenSocketPath"])
if status != None:
if pin_set:
self.unlock_button.set_sensitive(True)
@ -210,7 +219,7 @@ class SettingsWinvdow(Gtk.ApplicationWindow):
else:
self.websocket_connected_status_icon.set_icon("dialog-error", "error")
self.last_sync_row.set_subtitle(str(status["lastSynced"]))
if status["lastSynced"].startswith("1970"):
if status["lastSynced"].startswith("1970") or status["lastSynced"].startswith("1969"):
self.last_sync_row.set_subtitle("Never")
self.unlock_button.set_label("Unlock" if locked else "Lock")
else: