mirror of
https://github.com/quexten/goldwarden.git
synced 2025-02-09 08:38:41 +01:00
Implement initial pinentry
This commit is contained in:
parent
8b08d5841a
commit
ac0e84a46f
@ -8,7 +8,7 @@ import (
|
|||||||
"github.com/twpayne/go-pinentry"
|
"github.com/twpayne/go-pinentry"
|
||||||
)
|
)
|
||||||
|
|
||||||
func GetPassword(title string, description string) (string, error) {
|
func getPassword(title string, description string) (string, error) {
|
||||||
client, err := pinentry.NewClient(
|
client, err := pinentry.NewClient(
|
||||||
pinentry.WithBinaryNameFromGnuPGAgentConf(),
|
pinentry.WithBinaryNameFromGnuPGAgentConf(),
|
||||||
pinentry.WithGPGTTY(),
|
pinentry.WithGPGTTY(),
|
||||||
@ -38,7 +38,7 @@ func GetPassword(title string, description string) (string, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetApproval(title string, description string) (bool, error) {
|
func getApproval(title string, description string) (bool, error) {
|
||||||
if systemAuthDisabled {
|
if systemAuthDisabled {
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,7 @@ import (
|
|||||||
pinentry "github.com/quexten/goldwarden/agent/systemauth/pinentry/keybase-pinentry"
|
pinentry "github.com/quexten/goldwarden/agent/systemauth/pinentry/keybase-pinentry"
|
||||||
)
|
)
|
||||||
|
|
||||||
func GetPassword(title string, description string) (string, error) {
|
func getPassword(title string, description string) (string, error) {
|
||||||
pinentryInstance := pinentry.New("", logger.New(""), "")
|
pinentryInstance := pinentry.New("", logger.New(""), "")
|
||||||
result, err := pinentryInstance.Get(keybase1.SecretEntryArg{
|
result, err := pinentryInstance.Get(keybase1.SecretEntryArg{
|
||||||
Prompt: title,
|
Prompt: title,
|
||||||
@ -28,7 +28,7 @@ func GetPassword(title string, description string) (string, error) {
|
|||||||
return result.Text, nil
|
return result.Text, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetApproval(title string, description string) (bool, error) {
|
func getApproval(title string, description string) (bool, error) {
|
||||||
pinentryInstance := pinentry.New("", logger.New(""), "")
|
pinentryInstance := pinentry.New("", logger.New(""), "")
|
||||||
result, err := pinentryInstance.Get(keybase1.SecretEntryArg{
|
result, err := pinentryInstance.Get(keybase1.SecretEntryArg{
|
||||||
Prompt: title,
|
Prompt: title,
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package pinentry
|
package pinentry
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/quexten/goldwarden/logging"
|
"github.com/quexten/goldwarden/logging"
|
||||||
@ -9,8 +10,52 @@ import (
|
|||||||
var log = logging.GetLogger("Goldwarden", "Pinentry")
|
var log = logging.GetLogger("Goldwarden", "Pinentry")
|
||||||
var systemAuthDisabled = false
|
var systemAuthDisabled = false
|
||||||
|
|
||||||
|
type Pinentry struct {
|
||||||
|
GetPassword func(title string, description string) (string, error)
|
||||||
|
GetApproval func(title string, description string) (bool, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
var externalPinentry Pinentry = Pinentry{}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
if os.Getenv("GOLDWARDEN_SYSTEM_AUTH_DISABLED") == "true" {
|
if os.Getenv("GOLDWARDEN_SYSTEM_AUTH_DISABLED") == "true" {
|
||||||
systemAuthDisabled = true
|
systemAuthDisabled = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func SetExternalPinentry(pinentry Pinentry) error {
|
||||||
|
if externalPinentry.GetPassword != nil {
|
||||||
|
return errors.New("External pinentry already set")
|
||||||
|
}
|
||||||
|
|
||||||
|
externalPinentry = pinentry
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetPassword(title string, description string) (string, error) {
|
||||||
|
// password, err := getPassword(title, description)
|
||||||
|
// if err == nil {
|
||||||
|
// return password, nil
|
||||||
|
// }
|
||||||
|
|
||||||
|
if externalPinentry.GetPassword != nil {
|
||||||
|
return externalPinentry.GetPassword(title, description)
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", errors.New("Not implemented")
|
||||||
|
// return password, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetApproval(title string, description string) (bool, error) {
|
||||||
|
// approval, err := getApproval(title, description)
|
||||||
|
// if err == nil {
|
||||||
|
// return approval, nil
|
||||||
|
// }
|
||||||
|
|
||||||
|
if externalPinentry.GetApproval != nil {
|
||||||
|
return externalPinentry.GetApproval(title, description)
|
||||||
|
}
|
||||||
|
|
||||||
|
// return approval, nil
|
||||||
|
return true, errors.New("Not implemented")
|
||||||
|
}
|
||||||
|
@ -4,12 +4,12 @@ package pinentry
|
|||||||
|
|
||||||
import "errors"
|
import "errors"
|
||||||
|
|
||||||
func GetPassword(title string, description string) (string, error) {
|
func getPassword(title string, description string) (string, error) {
|
||||||
log.Info("Asking for password is not implemented on this platform")
|
log.Info("Asking for password is not implemented on this platform")
|
||||||
return "", errors.New("Not implemented")
|
return "", errors.New("Not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetApproval(title string, description string) (bool, error) {
|
func getApproval(title string, description string) (bool, error) {
|
||||||
log.Info("Asking for approval is not implemented on this platform")
|
log.Info("Asking for approval is not implemented on this platform")
|
||||||
return true, errors.New("Not implemented")
|
return true, errors.New("Not implemented")
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,7 @@ import (
|
|||||||
"github.com/quexten/goldwarden/agent/sockets"
|
"github.com/quexten/goldwarden/agent/sockets"
|
||||||
"github.com/quexten/goldwarden/agent/ssh"
|
"github.com/quexten/goldwarden/agent/ssh"
|
||||||
"github.com/quexten/goldwarden/agent/systemauth"
|
"github.com/quexten/goldwarden/agent/systemauth"
|
||||||
|
"github.com/quexten/goldwarden/agent/systemauth/pinentry"
|
||||||
"github.com/quexten/goldwarden/agent/vault"
|
"github.com/quexten/goldwarden/agent/vault"
|
||||||
"github.com/quexten/goldwarden/ipc/messages"
|
"github.com/quexten/goldwarden/ipc/messages"
|
||||||
"github.com/quexten/goldwarden/logging"
|
"github.com/quexten/goldwarden/logging"
|
||||||
@ -62,6 +63,7 @@ func serveAgentSession(c net.Conn, vault *vault.Vault, cfg *config.Config) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// todo refactor to other file
|
||||||
if msg.Type == messages.MessageTypeForEmptyPayload(messages.SessionAuthRequest{}) {
|
if msg.Type == messages.MessageTypeForEmptyPayload(messages.SessionAuthRequest{}) {
|
||||||
log.Info("Received session auth request")
|
log.Info("Received session auth request")
|
||||||
req := messages.ParsePayload(msg).(messages.SessionAuthRequest)
|
req := messages.ParsePayload(msg).(messages.SessionAuthRequest)
|
||||||
@ -93,6 +95,125 @@ func serveAgentSession(c net.Conn, vault *vault.Vault, cfg *config.Config) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// todo refactor to other file
|
||||||
|
if msg.Type == messages.MessageTypeForEmptyPayload(messages.PinentryRegistrationRequest{}) {
|
||||||
|
log.Info("Received pinentry registration request")
|
||||||
|
|
||||||
|
getPasswordChan := make(chan struct {
|
||||||
|
title string
|
||||||
|
description string
|
||||||
|
})
|
||||||
|
getPasswordReturnChan := make(chan struct {
|
||||||
|
password string
|
||||||
|
err error
|
||||||
|
})
|
||||||
|
getApprovalChan := make(chan struct {
|
||||||
|
title string
|
||||||
|
description string
|
||||||
|
})
|
||||||
|
getApprovalReturnChan := make(chan struct {
|
||||||
|
approved bool
|
||||||
|
err error
|
||||||
|
})
|
||||||
|
|
||||||
|
pe := pinentry.Pinentry{
|
||||||
|
GetPassword: func(title string, description string) (string, error) {
|
||||||
|
getPasswordChan <- struct {
|
||||||
|
title string
|
||||||
|
description string
|
||||||
|
}{title, description}
|
||||||
|
returnValue := <-getPasswordReturnChan
|
||||||
|
return returnValue.password, returnValue.err
|
||||||
|
},
|
||||||
|
GetApproval: func(title string, description string) (bool, error) {
|
||||||
|
getApprovalChan <- struct {
|
||||||
|
title string
|
||||||
|
description string
|
||||||
|
}{title, description}
|
||||||
|
returnValue := <-getApprovalReturnChan
|
||||||
|
return returnValue.approved, returnValue.err
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
pinnentrySetError := pinentry.SetExternalPinentry(pe)
|
||||||
|
payload := messages.PinentryRegistrationResponse{
|
||||||
|
Success: pinnentrySetError == nil,
|
||||||
|
}
|
||||||
|
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())
|
||||||
|
}
|
||||||
|
_, err = c.Write([]byte("\n"))
|
||||||
|
time.Sleep(50 * time.Millisecond) //todo fix properly
|
||||||
|
|
||||||
|
if pinnentrySetError != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
fmt.Println("Waiting for pinentry request")
|
||||||
|
select {
|
||||||
|
case getPasswordRequest := <-getPasswordChan:
|
||||||
|
log.Info("Received getPassword request")
|
||||||
|
payload := messages.PinentryPinRequest{
|
||||||
|
Message: getPasswordRequest.description,
|
||||||
|
}
|
||||||
|
payloadPayload, err := messages.IPCMessageFromPayload(payload)
|
||||||
|
if err != nil {
|
||||||
|
writeError(c, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
payloadBytes, err := json.Marshal(payloadPayload)
|
||||||
|
if err != nil {
|
||||||
|
writeError(c, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = c.Write(payloadBytes)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Failed writing to socket " + err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := make([]byte, 1024*1024)
|
||||||
|
nr, err := c.Read(buf)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
data := buf[0:nr]
|
||||||
|
|
||||||
|
var msg messages.IPCMessage
|
||||||
|
err = json.Unmarshal(data, &msg)
|
||||||
|
if err != nil {
|
||||||
|
writeError(c, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if msg.Type == messages.MessageTypeForEmptyPayload(messages.PinentryPinResponse{}) {
|
||||||
|
getPasswordResponse := messages.ParsePayload(msg).(messages.PinentryPinResponse)
|
||||||
|
getPasswordReturnChan <- struct {
|
||||||
|
password string
|
||||||
|
err error
|
||||||
|
}{getPasswordResponse.Pin, nil}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
var responseBytes []byte
|
var responseBytes []byte
|
||||||
if action, actionFound := actions.AgentActionsRegistry.Get(msg.Type); actionFound {
|
if action, actionFound := actions.AgentActionsRegistry.Get(msg.Type); actionFound {
|
||||||
callingContext := sockets.GetCallingContext(c)
|
callingContext := sockets.GetCallingContext(c)
|
||||||
|
@ -1,169 +0,0 @@
|
|||||||
package agent
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"os/user"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/quexten/goldwarden/agent/actions"
|
|
||||||
"github.com/quexten/goldwarden/agent/bitwarden"
|
|
||||||
"github.com/quexten/goldwarden/agent/bitwarden/crypto"
|
|
||||||
"github.com/quexten/goldwarden/agent/config"
|
|
||||||
"github.com/quexten/goldwarden/agent/processsecurity"
|
|
||||||
"github.com/quexten/goldwarden/agent/sockets"
|
|
||||||
"github.com/quexten/goldwarden/agent/vault"
|
|
||||||
"github.com/quexten/goldwarden/ipc/messages"
|
|
||||||
)
|
|
||||||
|
|
||||||
func writeErrorToLog(err error) {
|
|
||||||
log.Error(err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
func serveVirtualAgent(recv chan []byte, send chan []byte, ctx context.Context, vault *vault.Vault, cfg *config.Config) {
|
|
||||||
for {
|
|
||||||
data := <-recv
|
|
||||||
|
|
||||||
var msg messages.IPCMessage
|
|
||||||
err := json.Unmarshal(data, &msg)
|
|
||||||
if err != nil {
|
|
||||||
writeErrorToLog(err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
responseBytes := []byte{}
|
|
||||||
if action, actionFound := actions.AgentActionsRegistry.Get(msg.Type); actionFound {
|
|
||||||
user, _ := user.Current()
|
|
||||||
process := "goldwarden"
|
|
||||||
parent := "SINGLE_PROC_MODE"
|
|
||||||
grandparent := "SINGLE_PROC_MODE"
|
|
||||||
callingContext := sockets.CallingContext{
|
|
||||||
UserName: user.Name,
|
|
||||||
ProcessName: process,
|
|
||||||
ParentProcessName: parent,
|
|
||||||
GrandParentProcessName: grandparent,
|
|
||||||
}
|
|
||||||
payload, err := action(msg, cfg, vault, &callingContext)
|
|
||||||
if err != nil {
|
|
||||||
writeErrorToLog(err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
responseBytes, err = json.Marshal(payload)
|
|
||||||
if err != nil {
|
|
||||||
writeErrorToLog(err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
payload := messages.ActionResponse{
|
|
||||||
Success: false,
|
|
||||||
Message: "Action not found",
|
|
||||||
}
|
|
||||||
payloadBytes, err := json.Marshal(payload)
|
|
||||||
if err != nil {
|
|
||||||
writeErrorToLog(err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
responseBytes = payloadBytes
|
|
||||||
}
|
|
||||||
|
|
||||||
send <- responseBytes
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func StartVirtualAgent(runtimeConfig config.RuntimeConfig) (chan []byte, chan []byte) {
|
|
||||||
ctx := context.Background()
|
|
||||||
|
|
||||||
var keyring crypto.Keyring
|
|
||||||
if runtimeConfig.UseMemguard {
|
|
||||||
keyring = crypto.NewMemguardKeyring(nil)
|
|
||||||
} else {
|
|
||||||
keyring = crypto.NewMemoryKeyring(nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
var vault = vault.NewVault(&keyring)
|
|
||||||
cfg, err := config.ReadConfig(runtimeConfig)
|
|
||||||
if err != nil {
|
|
||||||
var cfg = config.DefaultConfig(runtimeConfig.UseMemguard)
|
|
||||||
cfg.WriteConfig()
|
|
||||||
}
|
|
||||||
cfg.ConfigFile.RuntimeConfig = runtimeConfig
|
|
||||||
if cfg.ConfigFile.RuntimeConfig.ApiURI != "" {
|
|
||||||
cfg.ConfigFile.ApiUrl = cfg.ConfigFile.RuntimeConfig.ApiURI
|
|
||||||
}
|
|
||||||
if cfg.ConfigFile.RuntimeConfig.IdentityURI != "" {
|
|
||||||
cfg.ConfigFile.IdentityUrl = cfg.ConfigFile.RuntimeConfig.IdentityURI
|
|
||||||
}
|
|
||||||
if cfg.ConfigFile.RuntimeConfig.NotificationsURI != "" {
|
|
||||||
cfg.ConfigFile.NotificationsUrl = cfg.ConfigFile.RuntimeConfig.NotificationsURI
|
|
||||||
}
|
|
||||||
if cfg.ConfigFile.RuntimeConfig.DeviceUUID != "" {
|
|
||||||
cfg.ConfigFile.DeviceUUID = cfg.ConfigFile.RuntimeConfig.DeviceUUID
|
|
||||||
}
|
|
||||||
|
|
||||||
if !cfg.IsLocked() && !cfg.ConfigFile.RuntimeConfig.DoNotPersistConfig {
|
|
||||||
log.Warn("Config is not locked. SET A PIN!!")
|
|
||||||
token, err := cfg.GetToken()
|
|
||||||
if err == nil {
|
|
||||||
if token.AccessToken != "" {
|
|
||||||
bitwarden.RefreshToken(ctx, &cfg)
|
|
||||||
userSymmetricKey, err := cfg.GetUserSymmetricKey()
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
}
|
|
||||||
var protectedUserSymetricKey crypto.SymmetricEncryptionKey
|
|
||||||
if keyring.IsMemguard {
|
|
||||||
protectedUserSymetricKey, err = crypto.MemguardSymmetricEncryptionKeyFromBytes(userSymmetricKey)
|
|
||||||
} else {
|
|
||||||
protectedUserSymetricKey, err = crypto.MemorySymmetricEncryptionKeyFromBytes(userSymmetricKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = bitwarden.DoFullSync(context.WithValue(ctx, bitwarden.AuthToken{}, token.AccessToken), vault, &cfg, &protectedUserSymetricKey, true)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
processsecurity.DisableDumpable()
|
|
||||||
err = processsecurity.MonitorLocks(func() {
|
|
||||||
cfg.Lock()
|
|
||||||
vault.Clear()
|
|
||||||
vault.Keyring.Lock()
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
log.Warn("Could not monitor screensaver: %s", err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
for {
|
|
||||||
time.Sleep(TokenRefreshInterval)
|
|
||||||
if !cfg.IsLocked() {
|
|
||||||
bitwarden.RefreshToken(ctx, &cfg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
for {
|
|
||||||
time.Sleep(FullSyncInterval)
|
|
||||||
if !cfg.IsLocked() {
|
|
||||||
token, err := cfg.GetToken()
|
|
||||||
if err != nil {
|
|
||||||
log.Warn("Could not get token: %s", err.Error())
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
bitwarden.DoFullSync(context.WithValue(ctx, bitwarden.AuthToken{}, token.AccessToken), vault, &cfg, nil, false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
recv := make(chan []byte)
|
|
||||||
send := make(chan []byte)
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
go serveVirtualAgent(recv, send, ctx, vault, &cfg)
|
|
||||||
}()
|
|
||||||
return recv, send
|
|
||||||
}
|
|
@ -1,5 +0,0 @@
|
|||||||
package client
|
|
||||||
|
|
||||||
type Client interface {
|
|
||||||
SendToAgent(request interface{}) (interface{}, error)
|
|
||||||
}
|
|
@ -3,7 +3,6 @@ package client
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
|
||||||
"net"
|
"net"
|
||||||
|
|
||||||
"github.com/quexten/goldwarden/agent/config"
|
"github.com/quexten/goldwarden/agent/config"
|
||||||
@ -16,13 +15,17 @@ type UnixSocketClient struct {
|
|||||||
runtimeConfig *config.RuntimeConfig
|
runtimeConfig *config.RuntimeConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type UnixSocketConnection struct {
|
||||||
|
conn net.Conn
|
||||||
|
}
|
||||||
|
|
||||||
func NewUnixSocketClient(runtimeConfig *config.RuntimeConfig) UnixSocketClient {
|
func NewUnixSocketClient(runtimeConfig *config.RuntimeConfig) UnixSocketClient {
|
||||||
return UnixSocketClient{
|
return UnixSocketClient{
|
||||||
runtimeConfig: runtimeConfig,
|
runtimeConfig: runtimeConfig,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func reader(r io.Reader) interface{} {
|
func Reader(r io.Reader) interface{} {
|
||||||
buf := make([]byte, READ_BUFFER)
|
buf := make([]byte, READ_BUFFER)
|
||||||
for {
|
for {
|
||||||
n, err := r.Read(buf[:])
|
n, err := r.Read(buf[:])
|
||||||
@ -40,25 +43,55 @@ func reader(r io.Reader) interface{} {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (client UnixSocketClient) SendToAgent(request interface{}) (interface{}, error) {
|
func (client UnixSocketClient) SendToAgent(request interface{}) (interface{}, error) {
|
||||||
c, err := net.Dial("unix", client.runtimeConfig.GoldwardenSocketPath)
|
c, err := client.Connect()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer c.Close()
|
defer c.Close()
|
||||||
|
return c.SendCommand(request)
|
||||||
message, err := messages.IPCMessageFromPayload(request)
|
}
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
func (client UnixSocketClient) Connect() (UnixSocketConnection, error) {
|
||||||
}
|
c, err := net.Dial("unix", client.runtimeConfig.GoldwardenSocketPath)
|
||||||
messageJson, err := json.Marshal(message)
|
if err != nil {
|
||||||
if err != nil {
|
return UnixSocketConnection{}, err
|
||||||
panic(err)
|
}
|
||||||
}
|
return UnixSocketConnection{conn: c}, nil
|
||||||
|
}
|
||||||
_, err = c.Write(messageJson)
|
|
||||||
if err != nil {
|
func (conn UnixSocketConnection) SendCommand(request interface{}) (interface{}, error) {
|
||||||
log.Fatal("write error:", err)
|
err := conn.WriteMessage(request)
|
||||||
}
|
if err != nil {
|
||||||
result := reader(c)
|
return nil, err
|
||||||
return messages.ParsePayload(result.(messages.IPCMessage)), nil
|
}
|
||||||
|
return conn.ReadMessage(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (conn UnixSocketConnection) ReadMessage() interface{} {
|
||||||
|
result := Reader(conn.conn)
|
||||||
|
// fmt.Println("ReadMessag")
|
||||||
|
// fmt.Println(result)
|
||||||
|
payload := messages.ParsePayload(result.(messages.IPCMessage))
|
||||||
|
// fmt.Println(payload)
|
||||||
|
return payload
|
||||||
|
}
|
||||||
|
|
||||||
|
func (conn UnixSocketConnection) WriteMessage(message interface{}) error {
|
||||||
|
// fmt.Println("WriteMessage")
|
||||||
|
messagePacket, err := messages.IPCMessageFromPayload(message)
|
||||||
|
// fmt.Println(messagePacket)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
messageJson, err := json.Marshal(messagePacket)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
// fmt.Println(messageJson)
|
||||||
|
_, err = conn.conn.Write(messageJson)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (conn UnixSocketConnection) Close() {
|
||||||
|
conn.conn.Close()
|
||||||
}
|
}
|
||||||
|
@ -1,45 +0,0 @@
|
|||||||
package client
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
|
|
||||||
"github.com/quexten/goldwarden/ipc/messages"
|
|
||||||
)
|
|
||||||
|
|
||||||
func NewVirtualClient(recv chan []byte, send chan []byte) VirtualClient {
|
|
||||||
return VirtualClient{
|
|
||||||
recv,
|
|
||||||
send,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type VirtualClient struct {
|
|
||||||
recv chan []byte
|
|
||||||
send chan []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
func virtualReader(recv chan []byte) interface{} {
|
|
||||||
for {
|
|
||||||
var message messages.IPCMessage
|
|
||||||
err := json.Unmarshal(<-recv, &message)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return message
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (client VirtualClient) SendToAgent(request interface{}) (interface{}, error) {
|
|
||||||
message, err := messages.IPCMessageFromPayload(request)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
messageJson, err := json.Marshal(message)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
client.send <- messageJson
|
|
||||||
result := virtualReader(client.recv)
|
|
||||||
return messages.ParsePayload(result.(messages.IPCMessage)), nil
|
|
||||||
}
|
|
@ -89,7 +89,7 @@ var listLoginsCmd = &cobra.Command{
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func ListLogins(client client.Client) ([]messages.DecryptedLoginCipher, error) {
|
func ListLogins(client client.UnixSocketClient) ([]messages.DecryptedLoginCipher, error) {
|
||||||
resp, err := client.SendToAgent(messages.ListLoginsRequest{})
|
resp, err := client.SendToAgent(messages.ListLoginsRequest{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return []messages.DecryptedLoginCipher{}, err
|
return []messages.DecryptedLoginCipher{}, err
|
||||||
|
11
cmd/root.go
11
cmd/root.go
@ -4,14 +4,13 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/quexten/goldwarden/agent"
|
|
||||||
"github.com/quexten/goldwarden/agent/config"
|
"github.com/quexten/goldwarden/agent/config"
|
||||||
"github.com/quexten/goldwarden/client"
|
"github.com/quexten/goldwarden/client"
|
||||||
"github.com/quexten/goldwarden/ipc/messages"
|
"github.com/quexten/goldwarden/ipc/messages"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
var commandClient client.Client
|
var commandClient client.UnixSocketClient
|
||||||
var runtimeConfig config.RuntimeConfig
|
var runtimeConfig config.RuntimeConfig
|
||||||
|
|
||||||
var rootCmd = &cobra.Command{
|
var rootCmd = &cobra.Command{
|
||||||
@ -25,13 +24,7 @@ var rootCmd = &cobra.Command{
|
|||||||
func Execute(cfg config.RuntimeConfig) {
|
func Execute(cfg config.RuntimeConfig) {
|
||||||
runtimeConfig = cfg
|
runtimeConfig = cfg
|
||||||
|
|
||||||
goldwardenSingleProcess := os.Getenv("GOLDWARDEN_SINGLE_PROCESS")
|
commandClient = client.NewUnixSocketClient(&cfg)
|
||||||
if goldwardenSingleProcess == "true" {
|
|
||||||
recv, send := agent.StartVirtualAgent(runtimeConfig)
|
|
||||||
commandClient = client.NewVirtualClient(send, recv)
|
|
||||||
} else {
|
|
||||||
commandClient = client.NewUnixSocketClient(&cfg)
|
|
||||||
}
|
|
||||||
|
|
||||||
err := rootCmd.Execute()
|
err := rootCmd.Execute()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -34,7 +34,37 @@ var pinentry = &cobra.Command{
|
|||||||
Short: "Registers as a pinentry program",
|
Short: "Registers as a pinentry program",
|
||||||
Long: `Registers as a pinentry program.`,
|
Long: `Registers as a pinentry program.`,
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
conn, err := commandClient.Connect()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
defer conn.Close()
|
||||||
|
_, err = conn.SendCommand(messages.PinentryRegistrationRequest{})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
response := conn.ReadMessage()
|
||||||
|
switch response.(type) {
|
||||||
|
case messages.PinentryPinRequest:
|
||||||
|
fmt.Println("pin-request" + "," + response.(messages.PinentryPinRequest).Message)
|
||||||
|
case messages.PinentryApprovalRequest:
|
||||||
|
fmt.Println("approval-request" + "," + response.(messages.PinentryApprovalRequest).Message)
|
||||||
|
}
|
||||||
|
|
||||||
|
// read line
|
||||||
|
reader := bufio.NewReader(os.Stdin)
|
||||||
|
text, _ := reader.ReadString('\n')
|
||||||
|
text = strings.TrimSuffix(text, "\n")
|
||||||
|
|
||||||
|
switch response.(type) {
|
||||||
|
case messages.PinentryPinRequest:
|
||||||
|
err = conn.WriteMessage(messages.PinentryPinResponse{Pin: text})
|
||||||
|
case messages.PinentryApprovalRequest:
|
||||||
|
err = conn.WriteMessage(messages.PinentryApprovalResponse{Approved: text == "true"})
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,6 +10,29 @@ type SessionAuthResponse struct {
|
|||||||
Verified bool
|
Verified bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type PinentryRegistrationRequest struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
type PinentryRegistrationResponse struct {
|
||||||
|
Success bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type PinentryPinRequest struct {
|
||||||
|
Message string
|
||||||
|
}
|
||||||
|
|
||||||
|
type PinentryPinResponse struct {
|
||||||
|
Pin string
|
||||||
|
}
|
||||||
|
|
||||||
|
type PinentryApprovalRequest struct {
|
||||||
|
Message string
|
||||||
|
}
|
||||||
|
|
||||||
|
type PinentryApprovalResponse struct {
|
||||||
|
Approved bool
|
||||||
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
registerPayloadParser(func(payload []byte) (interface{}, error) {
|
registerPayloadParser(func(payload []byte) (interface{}, error) {
|
||||||
var req SessionAuthRequest
|
var req SessionAuthRequest
|
||||||
@ -28,4 +51,58 @@ func init() {
|
|||||||
}
|
}
|
||||||
return req, nil
|
return req, nil
|
||||||
}, SessionAuthResponse{})
|
}, SessionAuthResponse{})
|
||||||
|
|
||||||
|
registerPayloadParser(func(payload []byte) (interface{}, error) {
|
||||||
|
var req PinentryRegistrationRequest
|
||||||
|
err := json.Unmarshal(payload, &req)
|
||||||
|
if err != nil {
|
||||||
|
panic("Unmarshal: " + err.Error())
|
||||||
|
}
|
||||||
|
return req, nil
|
||||||
|
}, PinentryRegistrationRequest{})
|
||||||
|
|
||||||
|
registerPayloadParser(func(payload []byte) (interface{}, error) {
|
||||||
|
var req PinentryRegistrationResponse
|
||||||
|
err := json.Unmarshal(payload, &req)
|
||||||
|
if err != nil {
|
||||||
|
panic("Unmarshal: " + err.Error())
|
||||||
|
}
|
||||||
|
return req, nil
|
||||||
|
}, PinentryRegistrationResponse{})
|
||||||
|
|
||||||
|
registerPayloadParser(func(payload []byte) (interface{}, error) {
|
||||||
|
var req PinentryPinRequest
|
||||||
|
err := json.Unmarshal(payload, &req)
|
||||||
|
if err != nil {
|
||||||
|
panic("Unmarshal: " + err.Error())
|
||||||
|
}
|
||||||
|
return req, nil
|
||||||
|
}, PinentryPinRequest{})
|
||||||
|
|
||||||
|
registerPayloadParser(func(payload []byte) (interface{}, error) {
|
||||||
|
var req PinentryPinResponse
|
||||||
|
err := json.Unmarshal(payload, &req)
|
||||||
|
if err != nil {
|
||||||
|
panic("Unmarshal: " + err.Error())
|
||||||
|
}
|
||||||
|
return req, nil
|
||||||
|
}, PinentryPinResponse{})
|
||||||
|
|
||||||
|
registerPayloadParser(func(payload []byte) (interface{}, error) {
|
||||||
|
var req PinentryApprovalRequest
|
||||||
|
err := json.Unmarshal(payload, &req)
|
||||||
|
if err != nil {
|
||||||
|
panic("Unmarshal: " + err.Error())
|
||||||
|
}
|
||||||
|
return req, nil
|
||||||
|
}, PinentryApprovalRequest{})
|
||||||
|
|
||||||
|
registerPayloadParser(func(payload []byte) (interface{}, error) {
|
||||||
|
var req PinentryApprovalResponse
|
||||||
|
err := json.Unmarshal(payload, &req)
|
||||||
|
if err != nil {
|
||||||
|
panic("Unmarshal: " + err.Error())
|
||||||
|
}
|
||||||
|
return req, nil
|
||||||
|
}, PinentryApprovalResponse{})
|
||||||
}
|
}
|
||||||
|
65
ui/src/ui/pinentry.py
Normal file
65
ui/src/ui/pinentry.py
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
import gi
|
||||||
|
gi.require_version('Gtk', '4.0')
|
||||||
|
gi.require_version('Adw', '1')
|
||||||
|
import gc
|
||||||
|
import time
|
||||||
|
from gi.repository import Gtk, Adw, GLib, Notify, Gdk
|
||||||
|
from threading import Thread
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
|
||||||
|
message = sys.stdin.readline()
|
||||||
|
|
||||||
|
class MyApp(Adw.Application):
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
super().__init__(**kwargs)
|
||||||
|
self.connect('activate', self.on_activate)
|
||||||
|
|
||||||
|
def on_activate(self, app):
|
||||||
|
self.pinentry_window = MainWindow(application=app)
|
||||||
|
self.pinentry_window.present()
|
||||||
|
self.app = app
|
||||||
|
|
||||||
|
class MainWindow(Gtk.ApplicationWindow):
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
self.stack = Gtk.Stack()
|
||||||
|
self.stack.set_transition_type(Gtk.StackTransitionType.SLIDE_LEFT_RIGHT)
|
||||||
|
self.set_child(self.stack)
|
||||||
|
|
||||||
|
box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6)
|
||||||
|
self.stack.add_child(box)
|
||||||
|
|
||||||
|
label = Gtk.Label(label=message)
|
||||||
|
box.append(label)
|
||||||
|
|
||||||
|
# Create the password entry
|
||||||
|
self.password_entry = Gtk.Entry()
|
||||||
|
self.password_entry.set_placeholder_text("Enter your password")
|
||||||
|
self.password_entry.set_visibility(False) # Hide the password
|
||||||
|
box.append(self.password_entry)
|
||||||
|
|
||||||
|
# Create a button box for cancel and approve buttons
|
||||||
|
button_box = Gtk.Box(spacing=6)
|
||||||
|
box.append(button_box)
|
||||||
|
|
||||||
|
# Cancel button
|
||||||
|
cancel_button = Gtk.Button(label="Cancel")
|
||||||
|
cancel_button.set_hexpand(True) # Make the button expand horizontally
|
||||||
|
button_box.append(cancel_button)
|
||||||
|
|
||||||
|
# Approve button
|
||||||
|
approve_button = Gtk.Button(label="Approve")
|
||||||
|
approve_button.set_hexpand(True) # Make the button expand horizontally
|
||||||
|
def on_approve_button_clicked(button):
|
||||||
|
print(self.password_entry.get_text())
|
||||||
|
os._exit(0)
|
||||||
|
approve_button.connect("clicked", on_approve_button_clicked)
|
||||||
|
button_box.append(approve_button)
|
||||||
|
|
||||||
|
self.set_default_size(700, 200)
|
||||||
|
self.set_title("Goldwarden Pinentry")
|
||||||
|
|
||||||
|
app = MyApp(application_id="com.quexten.Goldwarden.pinentry")
|
||||||
|
app.run(sys.argv)
|
63
ui/src/ui/pinentry_approval.py
Normal file
63
ui/src/ui/pinentry_approval.py
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
import gi
|
||||||
|
gi.require_version('Gtk', '4.0')
|
||||||
|
gi.require_version('Adw', '1')
|
||||||
|
import gc
|
||||||
|
import time
|
||||||
|
from gi.repository import Gtk, Adw, GLib, Notify, Gdk
|
||||||
|
from threading import Thread
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
|
||||||
|
message = sys.stdin.readline()
|
||||||
|
|
||||||
|
class MyApp(Adw.Application):
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
super().__init__(**kwargs)
|
||||||
|
self.connect('activate', self.on_activate)
|
||||||
|
|
||||||
|
def on_activate(self, app):
|
||||||
|
self.pinentry_window = MainWindow(application=app)
|
||||||
|
self.pinentry_window.present()
|
||||||
|
self.app = app
|
||||||
|
|
||||||
|
class MainWindow(Gtk.ApplicationWindow):
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
self.stack = Gtk.Stack()
|
||||||
|
self.stack.set_transition_type(Gtk.StackTransitionType.SLIDE_LEFT_RIGHT)
|
||||||
|
self.set_child(self.stack)
|
||||||
|
|
||||||
|
box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6)
|
||||||
|
self.stack.add_child(box)
|
||||||
|
|
||||||
|
label = Gtk.Label(label=message)
|
||||||
|
box.append(label)
|
||||||
|
|
||||||
|
# Create a button box for cancel and approve buttons
|
||||||
|
button_box = Gtk.Box(spacing=6)
|
||||||
|
box.append(button_box)
|
||||||
|
|
||||||
|
# Cancel button
|
||||||
|
cancel_button = Gtk.Button(label="Cancel")
|
||||||
|
cancel_button.set_hexpand(True) # Make the button expand horizontally
|
||||||
|
def on_cancel_button_clicked(button):
|
||||||
|
print("false")
|
||||||
|
os._exit(0)
|
||||||
|
cancel_button.connect("clicked", on_cancel_button_clicked)
|
||||||
|
button_box.append(cancel_button)
|
||||||
|
|
||||||
|
# Approve button
|
||||||
|
approve_button = Gtk.Button(label="Approve")
|
||||||
|
approve_button.set_hexpand(True) # Make the button expand horizontally
|
||||||
|
def on_approve_button_clicked(button):
|
||||||
|
print("true")
|
||||||
|
os._exit(0)
|
||||||
|
approve_button.connect("clicked", on_approve_button_clicked)
|
||||||
|
button_box.append(approve_button)
|
||||||
|
|
||||||
|
self.set_default_size(700, 200)
|
||||||
|
self.set_title("Goldwarden Approval")
|
||||||
|
|
||||||
|
app = MyApp(application_id="com.quexten.Goldwarden.pinentry")
|
||||||
|
app.run(sys.argv)
|
Loading…
x
Reference in New Issue
Block a user