Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow configuring pinentry binary #322

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions cli/agent/actions/browserbiometrics.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ func handleGetBiometricsKey(request messages.IPCMessage, cfg *config.Config, vau
if !authenticated {
// todo, skip when explicitly denied instead of error
actionsLog.Info("Browser Biometrics: Biometrics not approved, asking for pin...")
pin, err := pinentry.GetPassword("Goldwarden", "Enter your pin to unlock your vault")
pin, err := pinentry.GetPassword(cfg, "Goldwarden", "Enter your pin to unlock your vault")
if err == nil {
authenticated = cfg.VerifyPin(pin)
if !authenticated {
Expand All @@ -66,7 +66,7 @@ func handleGetBiometricsKey(request messages.IPCMessage, cfg *config.Config, vau
}

actionsLog.Info("Browser Biometrics: Biometrics verified, asking for approval...")
if approved, err := pinentry.GetApproval("Approve Credential Access", fmt.Sprintf("%s on %s>%s>%s is trying to access your vault encryption key for browser biometric unlock.", ctx.UserName, ctx.GrandParentProcessName, ctx.ParentProcessName, ctx.ProcessName)); err != nil || !approved {
if approved, err := pinentry.GetApproval(cfg, "Approve Credential Access", fmt.Sprintf("%s on %s>%s>%s is trying to access your vault encryption key for browser biometric unlock.", ctx.UserName, ctx.GrandParentProcessName, ctx.ParentProcessName, ctx.ProcessName)); err != nil || !approved {
response, err = messages.IPCMessageFromPayload(messages.ActionResponse{
Success: false,
Message: "not approved",
Expand Down
18 changes: 18 additions & 0 deletions cli/agent/actions/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,23 @@ func handleSetVaultURL(request messages.IPCMessage, cfg *config.Config, vault *v
})
}

func handleSetPinentryBinary(request messages.IPCMessage, cfg *config.Config, vault *vault.Vault, ctx *sockets.CallingContext) (response messages.IPCMessage, err error) {
binary := messages.ParsePayload(request).(messages.SetPinentryBinaryRequest).Value
cfg.ConfigFile.PinentryBinary = binary
err = cfg.WriteConfig()
if err != nil {
return messages.IPCMessageFromPayload(messages.ActionResponse{
Success: false,
Message: err.Error(),
})
}

return messages.IPCMessageFromPayload(messages.ActionResponse{
Success: true,
})
}


type ConfigResponse struct {
Version string `json:"version"`
GitHash string `json:"gitHash"`
Expand Down Expand Up @@ -201,6 +218,7 @@ func init() {
AgentActionsRegistry.Register(messages.MessageTypeForEmptyPayload(messages.SetApiURLRequest{}), handleSetApiURL)
AgentActionsRegistry.Register(messages.MessageTypeForEmptyPayload(messages.SetNotificationsURLRequest{}), handleSetNotifications)
AgentActionsRegistry.Register(messages.MessageTypeForEmptyPayload(messages.SetVaultURLRequest{}), handleSetVaultURL)
AgentActionsRegistry.Register(messages.MessageTypeForEmptyPayload(messages.SetPinentryBinaryRequest{}), handleSetPinentryBinary)
AgentActionsRegistry.Register(messages.MessageTypeForEmptyPayload(messages.SetURLsAutomaticallyRequest{}), handleSetURLsAutomatically)
AgentActionsRegistry.Register(messages.MessageTypeForEmptyPayload(messages.GetConfigEnvironmentRequest{}), handleGetConfigEnvironment)
AgentActionsRegistry.Register(messages.MessageTypeForEmptyPayload(messages.GetRuntimeConfigRequest{}), handleGetRuntimeConfig)
Expand Down
2 changes: 1 addition & 1 deletion cli/agent/actions/getclicredentials.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import (
func handleGetCliCredentials(request messages.IPCMessage, cfg *config.Config, vault *vault.Vault, ctx *sockets.CallingContext) (response messages.IPCMessage, err error) {
req := messages.ParsePayload(request).(messages.GetCLICredentialsRequest)

if approved, err := pinentry.GetApproval("Approve Credential Access", fmt.Sprintf("%s on %s>%s>%s is trying to access credentials for %s", ctx.UserName, ctx.GrandParentProcessName, ctx.ParentProcessName, ctx.ProcessName, req.ApplicationName)); err != nil || !approved {
if approved, err := pinentry.GetApproval(cfg, "Approve Credential Access", fmt.Sprintf("%s on %s>%s>%s is trying to access credentials for %s", ctx.UserName, ctx.GrandParentProcessName, ctx.ParentProcessName, ctx.ProcessName, req.ApplicationName)); err != nil || !approved {
response, err = messages.IPCMessageFromPayload(messages.ActionResponse{
Success: false,
Message: "not approved",
Expand Down
2 changes: 1 addition & 1 deletion cli/agent/actions/logins.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ func handleGetLoginCipher(request messages.IPCMessage, cfg *config.Config, vault
}
}

if approved, err := pinentry.GetApproval("Approve Credential Access", fmt.Sprintf("%s on %s>%s>%s is trying to access credentials for user %s on entry %s", ctx.UserName, ctx.GrandParentProcessName, ctx.ParentProcessName, ctx.ProcessName, decryptedLogin.Username, decryptedLogin.Name)); err != nil || !approved {
if approved, err := pinentry.GetApproval(cfg, "Approve Credential Access", fmt.Sprintf("%s on %s>%s>%s is trying to access credentials for user %s on entry %s", ctx.UserName, ctx.GrandParentProcessName, ctx.ParentProcessName, ctx.ProcessName, decryptedLogin.Username, decryptedLogin.Name)); err != nil || !approved {
response, err = messages.IPCMessageFromPayload(messages.ActionResponse{
Success: false,
Message: "not approved",
Expand Down
4 changes: 2 additions & 2 deletions cli/agent/actions/vault.go
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ func handleUpdateVaultPin(request messages.IPCMessage, cfg *config.Config, vault
if !authenticated {
// todo, skip when explicitly denied instead of error
actionsLog.Info("Browser Biometrics: Biometrics not approved, asking for pin...")
pin, err := pinentry.GetPassword("Goldwarden", "Enter your pin to unlock your vault")
pin, err := pinentry.GetPassword(cfg, "Goldwarden", "Enter your pin to unlock your vault")
if err == nil {
authenticated = cfg.VerifyPin(pin)
if !authenticated {
Expand All @@ -213,7 +213,7 @@ func handleUpdateVaultPin(request messages.IPCMessage, cfg *config.Config, vault
}
}

pin, err := pinentry.GetPassword("Pin Change", "Enter your desired pin")
pin, err := pinentry.GetPassword(cfg, "Pin Change", "Enter your desired pin")
if err != nil {
response, err = messages.IPCMessageFromPayload(messages.ActionResponse{
Success: false,
Expand Down
4 changes: 2 additions & 2 deletions cli/agent/bitwarden/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ func LoginWithApiKey(ctx context.Context, email string, cfg *config.Config, vaul
return LoginResponseToken{}, crypto.MasterKey{}, "", fmt.Errorf("could not login via API key: %v", err)
}

password, err := pinentry.GetPassword("Bitwarden Password", "Enter your Bitwarden password")
password, err := pinentry.GetPassword(cfg, "Bitwarden Password", "Enter your Bitwarden password")
if err != nil {
notify.Notify("Goldwarden", fmt.Sprintf("Could not get password: %v", err), "", 0, func() {})
return LoginResponseToken{}, crypto.MasterKey{}, "", err
Expand Down Expand Up @@ -129,7 +129,7 @@ func LoginWithMasterpassword(ctx context.Context, email string, cfg *config.Conf
var hashedPassword string

fmt.Println("Getting password")
password, err := pinentry.GetPassword("Bitwarden Password", "Enter your Bitwarden password")
password, err := pinentry.GetPassword(cfg, "Bitwarden Password", "Enter your Bitwarden password")
if err != nil {
notify.Notify("Goldwarden", fmt.Sprintf("Could not get password: %v", err), "", 0, func() {})
return LoginResponseToken{}, crypto.MasterKey{}, "", err
Expand Down
2 changes: 1 addition & 1 deletion cli/agent/bitwarden/twofactor/fido2twofactor.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ func Fido2TwoFactor(challengeB64 string, credentials []string, config *config.Co

var assertion *libfido2.Assertion
if hasPin {
pin, err := pinentry.GetPassword("Fido2 PIN", "Enter your token's PIN")
pin, err := pinentry.GetPassword(config, "Fido2 PIN", "Enter your token's PIN")
if err != nil {
return "", err
}
Expand Down
4 changes: 2 additions & 2 deletions cli/agent/bitwarden/twofactor/twofactor.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,15 @@ func PerformSecondFactor(resp *TwoFactorResponse, cfg *config.Config) (TwoFactor
}
}
if _, isInMap := resp.TwoFactorProviders2[Authenticator]; isInMap {
token, err := pinentry.GetPassword("Authenticator Second Factor", "Enter your two-factor auth code")
token, err := pinentry.GetPassword(cfg, "Authenticator Second Factor", "Enter your two-factor auth code")
if err != nil {
twofactorLog.Error("Error during authenticator two-factor authentication: %s", err)
} else {
return Authenticator, []byte(token), err
}
}
if _, isInMap := resp.TwoFactorProviders2[Email]; isInMap {
token, err := pinentry.GetPassword("Email Second Factor", "Enter your two-factor auth code")
token, err := pinentry.GetPassword(cfg, "Email Second Factor", "Enter your two-factor auth code")
if err == nil {
return Email, []byte(token), err
}
Expand Down
2 changes: 1 addition & 1 deletion cli/agent/bitwarden/websocket.go
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ func connectToWebsocket(ctx context.Context, vault *vault.Vault, cfg *config.Con

notify.Notify("Passwordless Login Request", authRequest.RequestIpAddress+" - "+authRequest.RequestDeviceType, "", 0, func() {
var message = "Do you want to allow " + authRequest.RequestIpAddress + " (" + authRequest.RequestDeviceType + ") to login to your account?"
if approved, err := pinentry.GetApproval("Paswordless Login Request", message); err != nil || !approved {
if approved, err := pinentry.GetApproval(cfg, "Paswordless Login Request", message); err != nil || !approved {
websocketLog.Info("AuthRequest denied")
return
}
Expand Down
7 changes: 6 additions & 1 deletion cli/agent/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ type ConfigFile struct {
EncryptedUserSymmetricKey string
EncryptedMasterPasswordHash string
EncryptedMasterKey string
PinentryBinary string
RuntimeConfig RuntimeConfig `json:"-"`
}

Expand Down Expand Up @@ -116,6 +117,10 @@ func (c *Config) IsLoggedIn() bool {
return c.ConfigFile.EncryptedMasterPasswordHash != ""
}

func (c *Config) GetPinentryBinary() string {
return c.ConfigFile.PinentryBinary
}

func (c *Config) Unlock(password string) bool {
c.mu.Lock()
defer c.mu.Unlock()
Expand Down Expand Up @@ -577,7 +582,7 @@ func (cfg *Config) TryUnlock(vault *vault.Vault) error {
pin = string(pinBytes)
} else {
var err error
pin, err = pinentry.GetPassword("Unlock Goldwarden", "Enter the vault PIN")
pin, err = pinentry.GetPassword(cfg, "Unlock Goldwarden", "Enter the vault PIN")
if err != nil {
return err
}
Expand Down
2 changes: 1 addition & 1 deletion cli/agent/ssh/ssh.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ func (vaultAgent vaultAgent) SignWithFlags(key ssh.PublicKey, data []byte, flags

// todo refactor
if !systemauth.GetSSHSession(vaultAgent.context) {
if approved, err := pinentry.GetApproval("SSH Key Signing Request", message); err != nil || !approved {
if approved, err := pinentry.GetApproval(vaultAgent.config, "SSH Key Signing Request", message); err != nil || !approved {
log.Info("Sign Request for key: %s denied", sshKey.Name)
return nil, errors.New("Approval not given")
}
Expand Down
14 changes: 8 additions & 6 deletions cli/agent/systemauth/pinentry/go-pinentry.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,18 @@ import (
"github.com/twpayne/go-pinentry"
)

func getBinaryClientOption() (clientOption pinentry.ClientOption) {
func getBinaryClientOption(cfg PinentryConfig) (clientOption pinentry.ClientOption) {
binaryClientOption := pinentry.WithBinaryNameFromGnuPGAgentConf()
if runtime.GOOS == "darwin" {
if cfg.GetPinentryBinary() != "" {
binaryClientOption = pinentry.WithBinaryName(cfg.GetPinentryBinary())
} else if runtime.GOOS == "darwin" {
binaryClientOption = pinentry.WithBinaryName("pinentry-mac")
}
return binaryClientOption
}

func getPassword(title string, description string) (string, error) {
binaryClientOption := getBinaryClientOption()
func getPassword(cfg PinentryConfig, title string, description string) (string, error) {
binaryClientOption := getBinaryClientOption(cfg)

client, err := pinentry.NewClient(
binaryClientOption,
Expand Down Expand Up @@ -49,12 +51,12 @@ func getPassword(title string, description string) (string, error) {
}
}

func getApproval(title string, description string) (bool, error) {
func getApproval(cfg PinentryConfig, title string, description string) (bool, error) {
if systemAuthDisabled {
return true, nil
}

binaryClientOption := getBinaryClientOption()
binaryClientOption := getBinaryClientOption(cfg)

client, err := pinentry.NewClient(
binaryClientOption,
Expand Down
12 changes: 8 additions & 4 deletions cli/agent/systemauth/pinentry/pinentry.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ type Pinentry struct {
GetApproval func(title string, description string) (bool, error)
}

type PinentryConfig interface {
GetPinentryBinary() string
}

var externalPinentry Pinentry = Pinentry{}

func init() {
Expand All @@ -32,8 +36,8 @@ func SetExternalPinentry(pinentry Pinentry) error {
return nil
}

func GetPassword(title string, description string) (string, error) {
password, err := getPassword(title, description)
func GetPassword(cfg PinentryConfig, title string, description string) (string, error) {
password, err := getPassword(cfg, title, description)
if err == nil {
return password, nil
}
Expand All @@ -45,8 +49,8 @@ func GetPassword(title string, description string) (string, error) {
return password, err
}

func GetApproval(title string, description string) (bool, error) {
approval, err := getApproval(title, description)
func GetApproval(cfg PinentryConfig, title string, description string) (bool, error) {
approval, err := getApproval(cfg, title, description)
if err == nil {
return approval, nil
}
Expand Down
4 changes: 2 additions & 2 deletions cli/agent/systemauth/pinentry/unimplemented.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@ package pinentry

import "errors"

func getPassword(title string, description string) (string, error) {
func getPassword(cfg PinentryConfig, 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(cfg PinentryConfig, title string, description string) (bool, error) {
log.Info("Asking for approval is not implemented on this platform")
return true, errors.New("Not implemented")
}
6 changes: 3 additions & 3 deletions cli/agent/systemauth/systemauth.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ func GetPermission(sessionType SessionType, ctx sockets.CallingContext, config *
}
} else {
log.Warn("Biometrics is not available, asking for pin")
pin, err := pinentry.GetPassword("Enter PIN", "Biometrics is not available. Enter your pin to authorize this action. "+message)
pin, err := pinentry.GetPassword(config, "Enter PIN", "Biometrics is not available. Enter your pin to authorize this action. "+message)
if err != nil {
return false, err
}
Expand All @@ -119,14 +119,14 @@ func GetPermission(sessionType SessionType, ctx sockets.CallingContext, config *
}

// no session
func CheckBiometrics(callingContext *sockets.CallingContext, approvalType biometrics.Approval) bool {
func CheckBiometrics(callingContext *sockets.CallingContext, approvalType biometrics.Approval, cfg *config.Config) bool {
var message = fmt.Sprintf("Do you want to grant %s>%s>%s one-time access your vault?", callingContext.GrandParentProcessName, callingContext.ParentProcessName, callingContext.ProcessName)
var bioApproval = biometrics.CheckBiometrics(approvalType)
if !bioApproval {
return false
}

approval, err := pinentry.GetApproval("Goldwarden authorization", message)
approval, err := pinentry.GetApproval(cfg, "Goldwarden authorization", message)
if err != nil {
log.Error(err.Error())
}
Expand Down
34 changes: 34 additions & 0 deletions cli/cmd/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,39 @@ var setApiSecretCmd = &cobra.Command{
},
}

var setPinentryBinaryCmd = &cobra.Command{
Use: "set-pinentry-binary",
Short: "Set the pinentry binary",
Long: `Set the pinentry binary.`,
Run: func(cmd *cobra.Command, args []string) {
if len(args) == 0 {
return
}

binary := args[0]
request := messages.SetPinentryBinaryRequest{}
request.Value = binary

result, err := commandClient.SendToAgent(request)
if err != nil {
handleSendToAgentError(err)
return
}

switch result.(type) {
case messages.ActionResponse:
if result.(messages.ActionResponse).Success {
fmt.Println("Done")
} else {
fmt.Println("Setting vault url failed: " + result.(messages.ActionResponse).Message)
}
default:
fmt.Println("Wrong IPC response type")
}

},
}

var getRuntimeConfigCmd = &cobra.Command{
Use: "get-runtime-config",
Short: "Get the runtime config",
Expand Down Expand Up @@ -315,6 +348,7 @@ func init() {
configCmd.AddCommand(setIdentityURLCmd)
configCmd.AddCommand(setNotificationsURLCmd)
configCmd.AddCommand(setVaultURLCmd)
configCmd.AddCommand(setPinentryBinaryCmd)
configCmd.AddCommand(setURLsAutomaticallyCmd)
configCmd.AddCommand(getEnvironmentCmd)
configCmd.AddCommand(getRuntimeConfigCmd)
Expand Down
13 changes: 13 additions & 0 deletions cli/ipc/messages/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ type SetURLsAutomaticallyRequest struct {
Value string
}

type SetPinentryBinaryRequest struct {
Value string
}

type GetConfigEnvironmentRequest struct {
}

Expand Down Expand Up @@ -121,6 +125,15 @@ func init() {
return req, nil
}, SetVaultURLRequest{})

registerPayloadParser(func(payload []byte) (interface{}, error) {
var req SetPinentryBinaryRequest
err := json.Unmarshal(payload, &req)
if err != nil {
panic("Unmarshal: " + err.Error())
}
return req, nil
}, SetPinentryBinaryRequest{})

registerPayloadParser(func(payload []byte) (interface{}, error) {
var req SetURLsAutomaticallyRequest
err := json.Unmarshal(payload, &req)
Expand Down