Add initial authenticated connection work

This commit is contained in:
Bernd Schoolmann 2024-02-08 16:35:07 +01:00
parent f3196863bb
commit cf6221c080
No known key found for this signature in database
35 changed files with 513 additions and 328 deletions

3
.gitignore vendored
View file

@ -3,4 +3,5 @@ goldwarden
__pycache__
.flatpak-builder
flatpak-pip-generator
repo
repo
__debug*

View file

@ -10,7 +10,6 @@ import (
"io/ioutil"
"net/http"
"net/url"
"os"
"strings"
"time"
)
@ -114,7 +113,7 @@ func makeAuthenticatedHTTPRequest(ctx context.Context, req *http.Request, recv i
return &errStatusCode{res.StatusCode, body}
}
if err := json.Unmarshal(body, recv); err != nil {
fmt.Fprintln(os.Stderr, string(body))
fmt.Println(string(body))
return err
}
return nil

View file

@ -49,6 +49,7 @@ type RuntimeConfig struct {
UseMemguard bool
SSHAgentSocketPath string
GoldwardenSocketPath string
DaemonAuthToken string
}
type ConfigFile struct {

View file

@ -17,6 +17,7 @@ type CallingContext struct {
ParentProcessPid int
GrandParentProcessPid int
Error bool
Authenticated bool
}
func GetCallingContext(connection net.Conn) CallingContext {
@ -30,6 +31,7 @@ func GetCallingContext(connection net.Conn) CallingContext {
ParentProcessPid: 0,
GrandParentProcessPid: 0,
Error: true,
Authenticated: false,
}
if err != nil {
return errorContext

View file

@ -65,6 +65,10 @@ func (s *SessionStore) verifySession(ctx sockets.CallingContext, sessionType Ses
// with session
func GetPermission(sessionType SessionType, ctx sockets.CallingContext, config *config.Config) (bool, error) {
if ctx.Authenticated {
return true, nil
}
log.Info("Checking permission for " + ctx.ProcessName + " with session type " + string(sessionType))
var actionDescription = ""
biometricsApprovalType := biometrics.AccessVault

View file

@ -16,6 +16,7 @@ import (
"github.com/quexten/goldwarden/agent/processsecurity"
"github.com/quexten/goldwarden/agent/sockets"
"github.com/quexten/goldwarden/agent/ssh"
"github.com/quexten/goldwarden/agent/systemauth"
"github.com/quexten/goldwarden/agent/vault"
"github.com/quexten/goldwarden/ipc/messages"
"github.com/quexten/goldwarden/logging"
@ -44,7 +45,7 @@ func writeError(c net.Conn, errMsg error) error {
return nil
}
func serveAgentSession(c net.Conn, ctx context.Context, vault *vault.Vault, cfg *config.Config) {
func serveAgentSession(c net.Conn, vault *vault.Vault, cfg *config.Config) {
for {
buf := make([]byte, 1024*1024)
nr, err := c.Read(buf)
@ -61,7 +62,38 @@ func serveAgentSession(c net.Conn, ctx context.Context, vault *vault.Vault, cfg
continue
}
responseBytes := []byte{}
if msg.Type == messages.MessageTypeForEmptyPayload(messages.SessionAuthRequest{}) {
log.Info("Received session auth request")
req := messages.ParsePayload(msg).(messages.SessionAuthRequest)
fmt.Println("Daemontoken", cfg.ConfigFile.RuntimeConfig.DaemonAuthToken)
fmt.Println("Token", req.Token)
fmt.Println(len(cfg.ConfigFile.RuntimeConfig.DaemonAuthToken), len(req.Token))
payload := messages.SessionAuthResponse{
Verified: cfg.ConfigFile.RuntimeConfig.DaemonAuthToken == req.Token,
}
log.Info("Verified: %t", payload.Verified)
callingContext := sockets.GetCallingContext(c)
systemauth.CreatePinSession(callingContext)
responsePayload, err := messages.IPCMessageFromPayload(payload)
if err != nil {
writeError(c, err)
continue
}
payloadBytes, err := json.Marshal(responsePayload)
if err != nil {
writeError(c, err)
continue
}
_, err = c.Write(payloadBytes)
if err != nil {
log.Error("Failed writing to socket " + err.Error())
}
continue
}
var responseBytes []byte
if action, actionFound := actions.AgentActionsRegistry.Get(msg.Type); actionFound {
callingContext := sockets.GetCallingContext(c)
payload, err := action(msg, cfg, vault, &callingContext)
@ -291,7 +323,7 @@ func StartUnixAgent(path string, runtimeConfig config.RuntimeConfig) error {
l, err := net.Listen("unix", path)
if err != nil {
println("listen error", err.Error())
fmt.Println("listen error", err.Error())
return err
}
defer l.Close()
@ -301,10 +333,11 @@ func StartUnixAgent(path string, runtimeConfig config.RuntimeConfig) error {
for {
fd, err := l.Accept()
if err != nil {
println("accept error", err.Error())
fmt.Println("accept error", err.Error())
}
fmt.Println("Accepted connection")
go serveAgentSession(fd, ctx, vault, &cfg)
go serveAgentSession(fd, vault, &cfg)
}
}()

View file

@ -10,9 +10,10 @@ import (
)
var autofillCmd = &cobra.Command{
Use: "autotype",
Short: "Autotype credentials",
Long: `Autotype credentials`,
Hidden: true,
Use: "autotype",
Short: "Autotype credentials",
Long: `Autotype credentials`,
Run: func(cmd *cobra.Command, args []string) {
username, _ := cmd.Flags().GetString("username")
// get pasword from env

View file

@ -1,6 +1,7 @@
package cmd
import (
"encoding/json"
"fmt"
"strings"
@ -30,12 +31,12 @@ var setApiUrlCmd = &cobra.Command{
switch result.(type) {
case messages.ActionResponse:
if result.(messages.ActionResponse).Success {
println("Done")
fmt.Println("Done")
} else {
println("Setting api url failed: " + result.(messages.ActionResponse).Message)
fmt.Println("Setting api url failed: " + result.(messages.ActionResponse).Message)
}
default:
println("Wrong IPC response type")
fmt.Println("Wrong IPC response type")
}
},
@ -63,12 +64,12 @@ var setIdentityURLCmd = &cobra.Command{
switch result.(type) {
case messages.ActionResponse:
if result.(messages.ActionResponse).Success {
println("Done")
fmt.Println("Done")
} else {
println("Setting identity url failed: " + result.(messages.ActionResponse).Message)
fmt.Println("Setting identity url failed: " + result.(messages.ActionResponse).Message)
}
default:
println("Wrong IPC response type")
fmt.Println("Wrong IPC response type")
}
},
@ -96,12 +97,12 @@ var setNotificationsURLCmd = &cobra.Command{
switch result.(type) {
case messages.ActionResponse:
if result.(messages.ActionResponse).Success {
println("Done")
fmt.Println("Done")
} else {
println("Setting notifications url failed: " + result.(messages.ActionResponse).Message)
fmt.Println("Setting notifications url failed: " + result.(messages.ActionResponse).Message)
}
default:
println("Wrong IPC response type")
fmt.Println("Wrong IPC response type")
}
},
@ -129,12 +130,12 @@ var setVaultURLCmd = &cobra.Command{
switch result.(type) {
case messages.ActionResponse:
if result.(messages.ActionResponse).Success {
println("Done")
fmt.Println("Done")
} else {
println("Setting vault url failed: " + result.(messages.ActionResponse).Message)
fmt.Println("Setting vault url failed: " + result.(messages.ActionResponse).Message)
}
default:
println("Wrong IPC response type")
fmt.Println("Wrong IPC response type")
}
},
@ -162,12 +163,12 @@ var setURLsAutomaticallyCmd = &cobra.Command{
switch result.(type) {
case messages.ActionResponse:
if result.(messages.ActionResponse).Success {
println("Done")
fmt.Println("Done")
} else {
println("Setting urls automatically failed: " + result.(messages.ActionResponse).Message)
fmt.Println("Setting urls automatically failed: " + result.(messages.ActionResponse).Message)
}
default:
println("Wrong IPC response type")
fmt.Println("Wrong IPC response type")
}
},
@ -188,14 +189,15 @@ var getEnvironmentCmd = &cobra.Command{
switch result := result.(type) {
case messages.GetConfigEnvironmentResponse:
fmt.Println("{")
fmt.Println(" \"api\": \"" + result.ApiURL + "\",")
fmt.Println(" \"identity\": \"" + result.IdentityURL + "\",")
fmt.Println(" \"notifications\": \"" + result.NotificationsURL + "\",")
fmt.Println(" \"vault\": \"" + result.VaultURL + "\"")
fmt.Println("}")
response := map[string]string{}
response["api"] = result.ApiURL
response["identity"] = result.IdentityURL
response["notifications"] = result.NotificationsURL
response["vault"] = result.VaultURL
responseJSON, _ := json.Marshal(response)
fmt.Println(string(responseJSON))
default:
println("Wrong IPC response type")
fmt.Println("Wrong IPC response type")
}
},
}
@ -226,12 +228,12 @@ var setApiClientIDCmd = &cobra.Command{
switch result.(type) {
case messages.ActionResponse:
if result.(messages.ActionResponse).Success {
println("Done")
fmt.Println("Done")
} else {
println("Setting api client id failed: " + result.(messages.ActionResponse).Message)
fmt.Println("Setting api client id failed: " + result.(messages.ActionResponse).Message)
}
default:
println("Wrong IPC response type")
fmt.Println("Wrong IPC response type")
}
},
@ -263,12 +265,12 @@ var setApiSecretCmd = &cobra.Command{
switch result.(type) {
case messages.ActionResponse:
if result.(messages.ActionResponse).Success {
println("Done")
fmt.Println("Done")
} else {
println("Setting api secret failed: " + result.(messages.ActionResponse).Message)
fmt.Println("Setting api secret failed: " + result.(messages.ActionResponse).Message)
}
default:
println("Wrong IPC response type")
fmt.Println("Wrong IPC response type")
}
},
@ -289,13 +291,14 @@ var getRuntimeConfigCmd = &cobra.Command{
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("}")
response := map[string]interface{}{}
response["useMemguard"] = result.UseMemguard
response["SSHAgentSocketPath"] = result.SSHAgentSocketPath
response["goldwardenSocketPath"] = result.GoldwardenSocketPath
responseJSON, _ := json.Marshal(response)
fmt.Println(string(responseJSON))
default:
println("Wrong IPC response type")
fmt.Println("Wrong IPC response type")
}
},
}

View file

@ -19,11 +19,11 @@ var daemonizeCmd = &cobra.Command{
sshDisabled := runtimeConfig.DisableSSHAgent
if websocketDisabled {
println("Websocket disabled")
fmt.Println("Websocket disabled")
}
if sshDisabled {
println("SSH agent disabled")
fmt.Println("SSH agent disabled")
}
cleanup := func() {

View file

@ -4,6 +4,8 @@ Copyright © 2023 NAME HERE <EMAIL ADDRESS>
package cmd
import (
"fmt"
"github.com/quexten/goldwarden/ipc/messages"
"github.com/spf13/cobra"
)
@ -17,7 +19,7 @@ var loginCmd = &cobra.Command{
request := messages.DoLoginRequest{}
email, _ := cmd.Flags().GetString("email")
if email == "" {
println("Error: No email specified")
fmt.Println("Error: No email specified")
return
}
@ -34,12 +36,12 @@ var loginCmd = &cobra.Command{
switch result.(type) {
case messages.ActionResponse:
if result.(messages.ActionResponse).Success {
println("Logged in")
fmt.Println("Logged in")
} else {
println("Login failed: " + result.(messages.ActionResponse).Message)
fmt.Println("Login failed: " + result.(messages.ActionResponse).Message)
}
default:
println("Wrong IPC response type for login")
fmt.Println("Wrong IPC response type for login")
}
},
}

View file

@ -53,7 +53,7 @@ var getLoginCmd = &cobra.Command{
}
break
case messages.ActionResponse:
println("Error: " + resp.(messages.ActionResponse).Message)
fmt.Println("Error: " + resp.(messages.ActionResponse).Message)
return
}
},

View file

@ -1,6 +1,8 @@
package cmd
import (
"fmt"
"github.com/quexten/goldwarden/ipc/messages"
"github.com/spf13/cobra"
)
@ -25,12 +27,12 @@ var setPinCmd = &cobra.Command{
switch result.(type) {
case messages.ActionResponse:
if result.(messages.ActionResponse).Success {
println("Pin updated")
fmt.Println("Pin updated")
} else {
println("Pin updating failed: " + result.(messages.ActionResponse).Message)
fmt.Println("Pin updating failed: " + result.(messages.ActionResponse).Message)
}
default:
println("Wrong response type")
fmt.Println("Wrong response type")
}
},
}
@ -48,9 +50,9 @@ var pinStatusCmd = &cobra.Command{
switch result.(type) {
case messages.ActionResponse:
println("Pin status: " + result.(messages.ActionResponse).Message)
fmt.Println("Pin status: " + result.(messages.ActionResponse).Message)
default:
println("Wrong response type")
fmt.Println("Wrong response type")
}
},
}

View file

@ -1,6 +1,7 @@
package cmd
import (
"fmt"
"os"
"github.com/quexten/goldwarden/agent"
@ -62,8 +63,8 @@ func loginIfRequired() error {
func handleSendToAgentError(err error) {
if err != nil {
println("Error: " + err.Error())
println("Is the daemon running?")
fmt.Println("Error: " + err.Error())
fmt.Println("Is the daemon running?")
return
}
}

View file

@ -4,6 +4,7 @@ Copyright © 2023 NAME HERE <EMAIL ADDRESS>
package cmd
import (
"fmt"
"os"
"os/exec"
@ -19,7 +20,7 @@ var runCmd = &cobra.Command{
The variables are stored as a secure note. Consult the documentation for more information.`,
Run: func(cmd *cobra.Command, args []string) {
if len(args) < 1 {
println("Error: No command specified")
fmt.Println("Error: No command specified")
return
}
@ -43,7 +44,7 @@ var runCmd = &cobra.Command{
env = append(env, key+"="+value)
}
case messages.ActionResponse:
println("Error: " + result.(messages.ActionResponse).Message)
fmt.Println("Error: " + result.(messages.ActionResponse).Message)
return
}

View file

@ -39,7 +39,7 @@ var sendCreateCmd = &cobra.Command{
fmt.Println("Send created: " + result.(messages.CreateSendResponse).URL)
break
case messages.ActionResponse:
println("Error: " + result.(messages.ActionResponse).Message)
fmt.Println("Error: " + result.(messages.ActionResponse).Message)
return
}
},

60
cmd/session.go Normal file
View file

@ -0,0 +1,60 @@
package cmd
import (
"bufio"
"fmt"
"os"
"strings"
"github.com/quexten/goldwarden/ipc/messages"
"github.com/spf13/cobra"
)
// sessionCmd represents the run command
var sessionCmd = &cobra.Command{
Use: "session",
Hidden: true,
Short: "Starts a new session",
Long: `Starts a new session.`,
Run: func(cmd *cobra.Command, args []string) {
for {
reader := bufio.NewReader(os.Stdin)
text, _ := reader.ReadString('\n')
text = strings.TrimSuffix(text, "\n")
args := strings.Split(text, " ")
rootCmd.SetArgs(args)
rootCmd.Execute()
}
},
}
var pinentry = &cobra.Command{
Use: "pinentry",
Hidden: true,
Short: "Registers as a pinentry program",
Long: `Registers as a pinentry program.`,
Run: func(cmd *cobra.Command, args []string) {
},
}
var authenticateSession = &cobra.Command{
Use: "authenticate-session",
Hidden: true,
Short: "Authenticates a session",
Long: `Authenticates a session.`,
Run: func(cmd *cobra.Command, args []string) {
token := args[0]
response, err := commandClient.SendToAgent(messages.SessionAuthRequest{Token: token})
if err != nil {
panic(err)
}
fmt.Println(response.(messages.SessionAuthResponse).Verified)
},
}
func init() {
rootCmd.AddCommand(sessionCmd)
rootCmd.AddCommand(pinentry)
rootCmd.AddCommand(authenticateSession)
}

View file

@ -47,7 +47,7 @@ var sshAddCmd = &cobra.Command{
}
break
case messages.ActionResponse:
println("Error: " + result.(messages.ActionResponse).Message)
fmt.Println("Error: " + result.(messages.ActionResponse).Message)
return
}
},
@ -74,7 +74,7 @@ var listSSHCmd = &cobra.Command{
}
break
case messages.ActionResponse:
println("Error: " + result.(messages.ActionResponse).Message)
fmt.Println("Error: " + result.(messages.ActionResponse).Message)
return
}
},

View file

@ -1,6 +1,7 @@
package cmd
import (
"encoding/json"
"fmt"
"time"
@ -30,12 +31,12 @@ var unlockCmd = &cobra.Command{
switch result.(type) {
case messages.ActionResponse:
if result.(messages.ActionResponse).Success {
println("Unlocked")
fmt.Println("Unlocked")
} else {
println("Not unlocked: " + result.(messages.ActionResponse).Message)
fmt.Println("Not unlocked: " + result.(messages.ActionResponse).Message)
}
default:
println("Wrong response type")
fmt.Println("Wrong response type")
}
},
}
@ -56,12 +57,12 @@ var lockCmd = &cobra.Command{
switch result.(type) {
case messages.ActionResponse:
if result.(messages.ActionResponse).Success {
println("Locked")
fmt.Println("Locked")
} else {
println("Not locked: " + result.(messages.ActionResponse).Message)
fmt.Println("Not locked: " + result.(messages.ActionResponse).Message)
}
default:
println("Wrong response type")
fmt.Println("Wrong response type")
}
},
}
@ -82,12 +83,12 @@ var purgeCmd = &cobra.Command{
switch result.(type) {
case messages.ActionResponse:
if result.(messages.ActionResponse).Success {
println("Purged")
fmt.Println("Purged")
} else {
println("Not purged: " + result.(messages.ActionResponse).Message)
fmt.Println("Not purged: " + result.(messages.ActionResponse).Message)
}
default:
println("Wrong response type")
fmt.Println("Wrong response type")
}
},
}
@ -107,18 +108,19 @@ var statusCmd = &cobra.Command{
switch result.(type) {
case messages.VaultStatusResponse:
response := map[string]interface{}{}
status := result.(messages.VaultStatusResponse)
fmt.Println("{")
fmt.Println(" \"locked\":", status.Locked, ",")
fmt.Println(" \"loginEntries\":", status.NumberOfLogins, ",")
fmt.Println(" \"noteEntries\":", status.NumberOfNotes, ",")
fmt.Println(" \"lastSynced\": \"" + time.Unix(status.LastSynced, 0).String() + "\",")
fmt.Println(" \"websocketConnected\":", status.WebsockedConnected, ",")
fmt.Println(" \"pinSet\":", status.PinSet, ",")
fmt.Println(" \"loggedIn\":", status.LoggedIn)
fmt.Println("}")
response["locked"] = status.Locked
response["loginEntries"] = status.NumberOfLogins
response["noteEntries"] = status.NumberOfNotes
response["lastSynced"] = time.Unix(status.LastSynced, 0).String()
response["websocketConnected"] = status.WebsockedConnected
response["pinSet"] = status.PinSet
response["loggedIn"] = status.LoggedIn
responseJSON, _ := json.Marshal(response)
fmt.Println(string(responseJSON))
default:
println("Wrong response type")
fmt.Println("Wrong response type")
}
},
}

31
ipc/messages/session.go Normal file
View file

@ -0,0 +1,31 @@
package messages
import "encoding/json"
type SessionAuthRequest struct {
Token string
}
type SessionAuthResponse struct {
Verified bool
}
func init() {
registerPayloadParser(func(payload []byte) (interface{}, error) {
var req SessionAuthRequest
err := json.Unmarshal(payload, &req)
if err != nil {
panic("Unmarshal: " + err.Error())
}
return req, nil
}, SessionAuthRequest{})
registerPayloadParser(func(payload []byte) (interface{}, error) {
var req SessionAuthResponse
err := json.Unmarshal(payload, &req)
if err != nil {
panic("Unmarshal: " + err.Error())
}
return req, nil
}, SessionAuthResponse{})
}

View file

@ -1,6 +1,7 @@
package main
import (
"fmt"
"os"
"strings"
@ -36,6 +37,7 @@ func main() {
UseMemguard: os.Getenv("GOLDWARDEN_NO_MEMGUARD") != "true",
SSHAgentSocketPath: os.Getenv("GOLDWARDEN_SSH_AUTH_SOCK"),
GoldwardenSocketPath: os.Getenv("GOLDWARDEN_SOCKET_PATH"),
DaemonAuthToken: os.Getenv("GOLDWARDEN_DAEMON_AUTH_TOKEN"),
ConfigDirectory: configPath,
}
@ -62,7 +64,7 @@ func main() {
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)
fmt.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"
}

View file

@ -1,184 +0,0 @@
import subprocess
import json
import os
# if flatpak
if os.path.exists("/app/bin/goldwarden"):
BINARY_PATH = "/app/bin/goldwarden"
else:
res = subprocess.run(["which", "goldwarden"])
BINARY_PATH = res.stdout.decode("utf-8").strip()
def set_api_url(url):
restic_cmd = f"{BINARY_PATH} config set-api-url {url}"
result = subprocess.run(restic_cmd.split(), capture_output=True, text=True)
if result.returncode != 0:
raise Exception("Failed to initialize repository, err", result.stderr)
def set_identity_url(url):
restic_cmd = f"{BINARY_PATH} config set-identity-url {url}"
result = subprocess.run(restic_cmd.split(), capture_output=True, text=True)
if result.returncode != 0:
raise Exception("Failed to initialize repository, err", result.stderr)
def set_notification_url(url):
restic_cmd = f"{BINARY_PATH} config set-notifications-url {url}"
result = subprocess.run(restic_cmd.split(), capture_output=True, text=True)
if result.returncode != 0:
raise Exception("Failed to initialize repository, err", result.stderr)
def set_vault_url(url):
restic_cmd = f"{BINARY_PATH} config set-vault-url {url}"
result = subprocess.run(restic_cmd.split(), capture_output=True, text=True)
if result.returncode != 0:
raise Exception("Failed err", result.stderr)
def set_url(url):
restic_cmd = f"{BINARY_PATH} config set-url {url}"
result = subprocess.run(restic_cmd.split(), capture_output=True, text=True)
if result.returncode != 0:
raise Exception("Failed err", result.stderr)
def get_environment():
restic_cmd = f"{BINARY_PATH} config get-environment"
result = subprocess.run(restic_cmd.split(), capture_output=True, text=True)
if result.returncode != 0:
return None
try:
result_text = result.stdout
print(result_text)
return json.loads(result_text)
except Exception as e:
print(e)
return None
def set_client_id(client_id):
restic_cmd = f"{BINARY_PATH} config set-client-id \"{client_id}\""
result = subprocess.run(restic_cmd.split(), capture_output=True, text=True)
if result.returncode != 0:
raise Exception("Failed err", result.stderr)
def set_client_secret(client_secret):
restic_cmd = f"{BINARY_PATH} config set-client-secret \"{client_secret}\""
result = subprocess.run(restic_cmd.split(), capture_output=True, text=True)
if result.returncode != 0:
raise Exception("Failed err", result.stderr)
def login_with_password(email, password):
restic_cmd = f"{BINARY_PATH} vault login --email {email}"
result = subprocess.run(restic_cmd.split(), capture_output=True, text=True)
if result.returncode != 0:
raise Exception("Failed to initialize repository, err", result.stderr)
if len(result.stderr.strip()) > 0:
print(result.stderr)
if "password" in result.stderr:
return "badpass"
else:
if "Logged in" in result.stderr:
print("ok")
return "ok"
return "error"
print("ok")
return "ok"
def login_passwordless(email):
restic_cmd = f"{BINARY_PATH} vault login --email {email} --passwordless"
result = subprocess.run(restic_cmd.split(), capture_output=True, text=True)
if result.returncode != 0:
raise Exception("Failed to initialize repository, err", result.stderr)
def is_pin_enabled():
restic_cmd = f"{BINARY_PATH} vault pin status"
result = subprocess.run(restic_cmd.split(), capture_output=True, text=True)
if result.returncode != 0:
raise Exception("Failed to initialize repository, err", result.stderr)
# check if contains enabled
return "enabled" in result.stderr
def enable_pin():
restic_cmd = f"{BINARY_PATH} vault pin set"
result = subprocess.run(restic_cmd.split(), capture_output=True, text=True)
if result.returncode != 0:
raise Exception("Failed to initialize repository, err", result.stderr)
def unlock():
restic_cmd = f"{BINARY_PATH} vault unlock"
result = subprocess.run(restic_cmd.split(), capture_output=True, text=True)
if result.returncode != 0:
raise Exception("Failed to initialize repository, err", result.stderr)
def lock():
restic_cmd = f"{BINARY_PATH} vault lock"
result = subprocess.run(restic_cmd.split(), capture_output=True, text=True)
if result.returncode != 0:
raise Exception("Failed to initialize repository, err", result.stderr)
def purge():
restic_cmd = f"{BINARY_PATH} vault purge"
result = subprocess.run(restic_cmd.split(), capture_output=True, text=True)
if result.returncode != 0:
raise Exception("Failed to initialize repository, err", result.stderr)
def get_vault_status():
restic_cmd = f"{BINARY_PATH} vault status"
result = subprocess.run(restic_cmd.split(), capture_output=True, text=True)
if result.returncode != 0:
raise Exception("Failed to initialize repository, err", result.stderr)
try:
return json.loads(result.stdout)
except Exception as e:
print(e)
return None
def get_vault_logins():
restic_cmd = f"{BINARY_PATH} logins list"
result = subprocess.run(restic_cmd.split(), capture_output=True, text=True)
if result.returncode != 0:
raise Exception("Failed to initialize repository, err", result.stderr)
try:
return json.loads(result.stdout)
except Exception as e:
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()
env["PASSWORD"] = password
restic_cmd = f"{BINARY_PATH} autotype --username {username}"
result = subprocess.run(restic_cmd.split(), capture_output=True, text=True, env=env)
print(result.stderr)
print(result.stdout)
if result.returncode != 0:
raise Exception("Failed to initialize repository, err", result.stderr)
def is_daemon_running():
restic_cmd = f"{BINARY_PATH} vault status"
result = subprocess.run(restic_cmd.split(), capture_output=True, text=True)
if result.returncode != 0:
return False
daemon_not_running = ("daemon running?" in result.stderr or "daemon running" in result.stderr)
return not daemon_not_running
def run_daemon():
restic_cmd = f"{BINARY_PATH} daemonize"
# print while running
result = subprocess.Popen(restic_cmd.split(), stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
if result.returncode != 0:
print("Failed err", result.stderr)
for line in result.stdout:
print(line.decode("utf-8"))
result.wait()
print("quitting goldwarden daemon")
return result.returncode

View file

@ -1,53 +0,0 @@
#!/usr/bin/python
import time
import subprocess
from tendo import singleton
import monitors.dbus_autofill_monitor
import sys
import goldwarden
from threading import Thread
import os
isflatpak = os.path.exists("/.flatpak-info")
pathprefix = "/app/bin/" if isflatpak else "./"
try:
subprocess.Popen(["python3", f'{pathprefix}background.py'], start_new_session=True)
except:
pass
is_hidden = "--hidden" in sys.argv
if not is_hidden:
try:
subprocess.Popen(["python3", f'{pathprefix}settings.py'], start_new_session=True)
except:
subprocess.Popen(["python3", f'{pathprefix}settings.py'], start_new_session=True)
pass
try:
me = singleton.SingleInstance()
except:
exit()
def run_daemon():
# todo: do a proper check
if is_hidden:
time.sleep(20)
print("IS daemon running", goldwarden.is_daemon_running())
if not goldwarden.is_daemon_running():
print("running daemon")
goldwarden.run_daemon()
print("daemon running")
thread = Thread(target=run_daemon)
thread.start()
def on_autofill():
subprocess.Popen(["python3", f'{pathprefix}autofill.py'], start_new_session=True)
monitors.dbus_autofill_monitor.on_autofill = lambda: on_autofill()
monitors.dbus_autofill_monitor.run_daemon()
while True:
time.sleep(60)

View file

@ -0,0 +1,3 @@
import linux.main as linux_main
linux_main.main()

59
ui/src/linux/main.py Normal file
View file

@ -0,0 +1,59 @@
#!/usr/bin/python
import time
import subprocess
from tendo import singleton
from .monitors import dbus_autofill_monitor
from .monitors import dbus_monitor
import sys
from services import goldwarden
from threading import Thread
import os
import secrets
import time
import os
root_path = os.path.abspath(os.path.join(os.path.dirname(os.path.abspath(__file__)), os.pardir, os.pardir))
def main():
token = secrets.token_hex(32)
print("token", token)
if not os.environ.get("GOLDWARDEN_DAEMON_AUTH_TOKEN") == None:
token = os.environ["GOLDWARDEN_DAEMON_AUTH_TOKEN"]
# check if already running
try:
me = singleton.SingleInstance()
except:
import dbus
bus = dbus.SessionBus()
the_object = bus.get_objeect("com.quexten.Goldwarden.dbus", "/com/quexten/Goldwarden")
the_interface = dbus.Interface(the_object, "com.quexten.Goldwarden.Settings")
reply = the_interface.settings()
exit()
# start daemons
dbus_autofill_monitor.run_daemon() # todo: remove after migration
dbus_monitor.run_daemon(token)
if not "--hidden" in sys.argv:
subprocess.Popen(["python3", "-m", "src.ui.settings"], cwd=root_path, start_new_session=True)
# try:
# subprocess.Popen(["python3", f'{source_path}/background.py'], start_new_session=True)
# except Exception as e:
# pass
while True:
time.sleep(60)
# def run_daemon():
# # todo: do a proper check
# if is_hidden:
# time.sleep(20)
# print("IS daemon running", goldwarden.is_daemon_running())
# if not goldwarden.is_daemon_running():
# print("running daemon")
# goldwarden.run_daemon()
# print("daemon running")

View file

@ -19,8 +19,13 @@ class GoldwardenDBUSService(dbus.service.Object):
on_autofill()
return ""
def run_daemon():
def daemon():
DBusGMainLoop(set_as_default=True)
service = GoldwardenDBUSService()
from gi.repository import GLib, GObject as gobject
gobject.MainLoop().run()
def run_daemon():
thread = Thread(target=daemon)
thread.start()

View file

@ -0,0 +1,41 @@
from gi.repository import Gtk
import dbus
import dbus.service
from dbus.mainloop.glib import DBusGMainLoop
from threading import Thread
import subprocess
import os
root_path = os.path.abspath(os.path.join(os.path.dirname(os.path.abspath(__file__)), os.pardir, os.pardir, os.pardir))
daemon_token = None
class GoldwardenDBUSService(dbus.service.Object):
def __init__(self):
bus_name = dbus.service.BusName('com.quexten.Goldwarden.ui', bus=dbus.SessionBus())
dbus.service.Object.__init__(self, bus_name, '/com/quexten/Goldwarden/ui')
@dbus.service.method('com.quexten.Goldwarden.ui.QuickAccess')
def quickaccess(self):
p = subprocess.Popen(["python3", "-m", "src.ui.quickaccess"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, cwd=root_path, start_new_session=True)
print("writing token")
p.stdin.write(f"{daemon_token}\n".encode())
p.stdin.flush()
print("reading token")
return ""
@dbus.service.method('com.quexten.Goldwarden.ui.Settings')
def settings(self):
subprocess.Popen(["python3", "-m", "src.ui.settings"], cwd=root_path, start_new_session=True)
return ""
def daemon():
DBusGMainLoop(set_as_default=True)
service = GoldwardenDBUSService()
from gi.repository import GLib, GObject as gobject
gobject.MainLoop().run()
def run_daemon(token):
global daemon_token
daemon_token = token
thread = Thread(target=daemon)
thread.start()

View file

@ -0,0 +1,159 @@
import subprocess
import json
import os
from pathlib import Path
BINARY_PATHS = [
"/app/bin/goldwarden",
"/usr/bin/goldwarden",
str(Path.home()) + "/go/src/github.com/quexten/goldwarden/goldwarden"
]
authenticated_connection = None
def create_authenticated_connection(token):
global authenticated_connection
BINARY_PATH = None
for path in BINARY_PATHS:
if os.path.exists(path):
BINARY_PATH = path
break
if BINARY_PATH == None:
raise Exception("Could not find goldwarden binary")
authenticated_connection = subprocess.Popen([f"{BINARY_PATH}", "session"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
if not token == None:
authenticated_connection.stdin.write("authenticate-session " + token + "\n")
authenticated_connection.stdin.flush()
# read entire message
result = authenticated_connection.stdout.readline()
if "true" not in result:
raise Exception("Failed to authenticate")
def send_authenticated_command(cmd):
if authenticated_connection == None:
print("No daemon connection running, please restart the application completely.")
return ""
print("sending command", cmd)
authenticated_connection.stdin.write(cmd + "\n")
authenticated_connection.stdin.flush()
result = authenticated_connection.stdout.readline()
print("result", result)
return result
def set_api_url(url):
send_authenticated_command(f"config set-api-url {url}")
def set_identity_url(url):
send_authenticated_command(f"config set-identity-url {url}")
def set_notification_url(url):
send_authenticated_command(f"config set-notifications-url {url}")
def set_vault_url(url):
send_authenticated_command(f"config set-vault-url {url}")
def set_url(url):
send_authenticated_command(f"config set-url {url}")
def get_environment():
result = send_authenticated_command(f"config get-environment")
try:
return json.loads(result)
except Exception as e:
print(e)
return None
def set_client_id(client_id):
send_authenticated_command(f"config set-client-id \"{client_id}\"")
def set_client_secret(client_secret):
send_authenticated_command(f"config set-client-secret \"{client_secret}\"")
def login_with_password(email, password):
result = send_authenticated_command(f"vault login --email {email}")
if result.returncode != 0:
raise Exception("Failed to initialize repository, err", result.stderr)
if len(result.stderr.strip()) > 0:
print(result.stderr)
if "password" in result.stderr:
return "badpass"
else:
if "Logged in" in result.stderr:
print("ok")
return "ok"
return "error"
print("ok")
return "ok"
def login_passwordless(email):
send_authenticated_command(f"vault login --email {email} --passwordless")
def is_pin_enabled():
result = send_authenticated_command("vault pin status")
return "enabled" in result
def enable_pin():
send_authenticated_command(f"vault pin set")
def unlock():
send_authenticated_command(f"vault unlock")
def lock():
send_authenticated_command(f"vault lock")
def purge():
send_authenticated_command(f"vault purge")
def get_vault_status():
result = send_authenticated_command(f"vault status")
try:
return json.loads(result)
except Exception as e:
print(e)
return None
def get_vault_logins():
result = send_authenticated_command(f"logins list")
if result.returncode != 0:
raise Exception("Failed to initialize repository, err", result.stderr)
try:
return json.loads(result)
except Exception as e:
return None
def get_runtime_config():
result = send_authenticated_command(f"config get-runtime-config")
print(result)
try:
return json.loads(result)
except Exception as e:
return None
# def autotype(username, password):
# # environment
# env = os.environ.copy()
# env["PASSWORD"] = password
# restic_cmd = f"{BINARY_PATH} autotype --username {username}"
# result = subprocess.run(restic_cmd.split(), capture_output=True, text=True, env=env)
# print(result.stderr)
# print(result.stdout)
# if result.returncode != 0:
# raise Exception("Failed to initialize repository, err", result.stderr)
def is_daemon_running():
result = send_authenticated_command(f"vault status")
daemon_not_running = ("daemon running" in result)
return not daemon_not_running
# def run_daemon():
# restic_cmd = f"daemonize"
# # print while running
# result = subprocess.Popen(restic_cmd.split(), stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
# if result.returncode != 0:
# print("Failed err", result.stderr)
# for line in result.stdout:
# print(line.decode("utf-8"))
# result.wait()
# print("quitting goldwarden daemon")
# return result.returncode

View file

@ -4,14 +4,17 @@ gi.require_version('Adw', '1')
import gc
import time
from gi.repository import Gtk, Adw, GLib, Notify, Gdk
import goldwarden
import clipboard
from ..services import goldwarden
from ..linux import clipboard
from threading import Thread
import sys
import os
import totp
from ..services import totp
Notify.init("Goldwarden")
token = sys.stdin.readline()
goldwarden.create_authenticated_connection(token)
class MyApp(Adw.Application):
def __init__(self, **kwargs):
super().__init__(**kwargs)
@ -85,6 +88,9 @@ class MainWindow(Gtk.ApplicationWindow):
keycont = Gtk.EventControllerKey()
def handle_keypress(cotroller, keyval, keycode, state, user_data):
# if ctrl is pressed
if state == 4:
print("ctrl")
if keycode == 36:
print("enter")
self.hide()
@ -130,7 +136,8 @@ class MainWindow(Gtk.ApplicationWindow):
self.history_list.get_style_context().add_class("boxed-list")
self.box.append(self.history_list)
self.set_default_size(700, 700)
self.set_title("Goldwarden")
# self.set_title("Goldwarden")
self.set_title(token)
app = MyApp(application_id="com.quexten.Goldwarden.autofill-menu")
app.run(sys.argv)

View file

@ -6,12 +6,14 @@ gi.require_version('Adw', '1')
import gc
from gi.repository import Gtk, Adw, GLib, Gdk
import goldwarden
from ..services import goldwarden
from threading import Thread
import subprocess
import components
from . import components
import os
goldwarden.create_authenticated_connection(None)
class SettingsWinvdow(Gtk.ApplicationWindow):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
@ -201,6 +203,7 @@ class SettingsWinvdow(Gtk.ApplicationWindow):
pin_set = goldwarden.is_pin_enabled()
status = goldwarden.get_vault_status()
print("status", status)
runtimeCfg = goldwarden.get_runtime_config()
if runtimeCfg != None:
self.ssh_row.set_subtitle("Listening at "+runtimeCfg["SSHAgentSocketPath"])