mirror of
https://github.com/quexten/goldwarden.git
synced 2024-11-23 21:44:37 +03:00
Implement initial pinentry
This commit is contained in:
parent
8b08d5841a
commit
ac0e84a46f
@ -8,7 +8,7 @@ import (
|
||||
"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(
|
||||
pinentry.WithBinaryNameFromGnuPGAgentConf(),
|
||||
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 {
|
||||
return true, nil
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ import (
|
||||
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(""), "")
|
||||
result, err := pinentryInstance.Get(keybase1.SecretEntryArg{
|
||||
Prompt: title,
|
||||
@ -28,7 +28,7 @@ func GetPassword(title string, description string) (string, error) {
|
||||
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(""), "")
|
||||
result, err := pinentryInstance.Get(keybase1.SecretEntryArg{
|
||||
Prompt: title,
|
||||
|
@ -1,6 +1,7 @@
|
||||
package pinentry
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
|
||||
"github.com/quexten/goldwarden/logging"
|
||||
@ -9,8 +10,52 @@ import (
|
||||
var log = logging.GetLogger("Goldwarden", "Pinentry")
|
||||
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() {
|
||||
if os.Getenv("GOLDWARDEN_SYSTEM_AUTH_DISABLED") == "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"
|
||||
|
||||
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")
|
||||
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")
|
||||
return true, errors.New("Not implemented")
|
||||
}
|
||||
|
@ -17,6 +17,7 @@ import (
|
||||
"github.com/quexten/goldwarden/agent/sockets"
|
||||
"github.com/quexten/goldwarden/agent/ssh"
|
||||
"github.com/quexten/goldwarden/agent/systemauth"
|
||||
"github.com/quexten/goldwarden/agent/systemauth/pinentry"
|
||||
"github.com/quexten/goldwarden/agent/vault"
|
||||
"github.com/quexten/goldwarden/ipc/messages"
|
||||
"github.com/quexten/goldwarden/logging"
|
||||
@ -62,6 +63,7 @@ func serveAgentSession(c net.Conn, vault *vault.Vault, cfg *config.Config) {
|
||||
continue
|
||||
}
|
||||
|
||||
// todo refactor to other file
|
||||
if msg.Type == messages.MessageTypeForEmptyPayload(messages.SessionAuthRequest{}) {
|
||||
log.Info("Received session auth request")
|
||||
req := messages.ParsePayload(msg).(messages.SessionAuthRequest)
|
||||
@ -93,6 +95,125 @@ func serveAgentSession(c net.Conn, vault *vault.Vault, cfg *config.Config) {
|
||||
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
|
||||
if action, actionFound := actions.AgentActionsRegistry.Get(msg.Type); actionFound {
|
||||
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 (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
|
||||
"github.com/quexten/goldwarden/agent/config"
|
||||
@ -16,13 +15,17 @@ type UnixSocketClient struct {
|
||||
runtimeConfig *config.RuntimeConfig
|
||||
}
|
||||
|
||||
type UnixSocketConnection struct {
|
||||
conn net.Conn
|
||||
}
|
||||
|
||||
func NewUnixSocketClient(runtimeConfig *config.RuntimeConfig) UnixSocketClient {
|
||||
return UnixSocketClient{
|
||||
runtimeConfig: runtimeConfig,
|
||||
}
|
||||
}
|
||||
|
||||
func reader(r io.Reader) interface{} {
|
||||
func Reader(r io.Reader) interface{} {
|
||||
buf := make([]byte, READ_BUFFER)
|
||||
for {
|
||||
n, err := r.Read(buf[:])
|
||||
@ -40,25 +43,55 @@ func reader(r io.Reader) interface{} {
|
||||
}
|
||||
|
||||
func (client UnixSocketClient) SendToAgent(request interface{}) (interface{}, error) {
|
||||
c, err := net.Dial("unix", client.runtimeConfig.GoldwardenSocketPath)
|
||||
c, err := client.Connect()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer c.Close()
|
||||
|
||||
message, err := messages.IPCMessageFromPayload(request)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
messageJson, err := json.Marshal(message)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
_, err = c.Write(messageJson)
|
||||
if err != nil {
|
||||
log.Fatal("write error:", err)
|
||||
}
|
||||
result := reader(c)
|
||||
return messages.ParsePayload(result.(messages.IPCMessage)), nil
|
||||
return c.SendCommand(request)
|
||||
}
|
||||
|
||||
func (client UnixSocketClient) Connect() (UnixSocketConnection, error) {
|
||||
c, err := net.Dial("unix", client.runtimeConfig.GoldwardenSocketPath)
|
||||
if err != nil {
|
||||
return UnixSocketConnection{}, err
|
||||
}
|
||||
return UnixSocketConnection{conn: c}, nil
|
||||
}
|
||||
|
||||
func (conn UnixSocketConnection) SendCommand(request interface{}) (interface{}, error) {
|
||||
err := conn.WriteMessage(request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
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{})
|
||||
if err != nil {
|
||||
return []messages.DecryptedLoginCipher{}, err
|
||||
|
11
cmd/root.go
11
cmd/root.go
@ -4,14 +4,13 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/quexten/goldwarden/agent"
|
||||
"github.com/quexten/goldwarden/agent/config"
|
||||
"github.com/quexten/goldwarden/client"
|
||||
"github.com/quexten/goldwarden/ipc/messages"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var commandClient client.Client
|
||||
var commandClient client.UnixSocketClient
|
||||
var runtimeConfig config.RuntimeConfig
|
||||
|
||||
var rootCmd = &cobra.Command{
|
||||
@ -25,13 +24,7 @@ var rootCmd = &cobra.Command{
|
||||
func Execute(cfg config.RuntimeConfig) {
|
||||
runtimeConfig = cfg
|
||||
|
||||
goldwardenSingleProcess := os.Getenv("GOLDWARDEN_SINGLE_PROCESS")
|
||||
if goldwardenSingleProcess == "true" {
|
||||
recv, send := agent.StartVirtualAgent(runtimeConfig)
|
||||
commandClient = client.NewVirtualClient(send, recv)
|
||||
} else {
|
||||
commandClient = client.NewUnixSocketClient(&cfg)
|
||||
}
|
||||
commandClient = client.NewUnixSocketClient(&cfg)
|
||||
|
||||
err := rootCmd.Execute()
|
||||
if err != nil {
|
||||
|
@ -34,7 +34,37 @@ var pinentry = &cobra.Command{
|
||||
Short: "Registers as a pinentry program",
|
||||
Long: `Registers as a pinentry program.`,
|
||||
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
|
||||
}
|
||||
|
||||
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() {
|
||||
registerPayloadParser(func(payload []byte) (interface{}, error) {
|
||||
var req SessionAuthRequest
|
||||
@ -28,4 +51,58 @@ func init() {
|
||||
}
|
||||
return req, nil
|
||||
}, 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…
Reference in New Issue
Block a user