From 3a1e47ff673be229b9c15883f35e3661dec29938 Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Fri, 22 Dec 2023 10:44:49 +0100 Subject: [PATCH] Add more logging --- agent/actions/browserbiometrics.go | 6 ++++++ agent/actions/vault.go | 10 ++++++++++ agent/bitwarden/crypto/keyring.go | 7 +++++++ agent/bitwarden/sync.go | 10 +++++++--- agent/bitwarden/websocket.go | 10 ++++++---- agent/processsecurity/unix.go | 5 ++--- agent/vault/vault.go | 30 ++++++++++++++++++------------ cmd/vault.go | 28 ++++++++++++++++++++++++++++ ipc/messages/vault.go | 29 +++++++++++++++++++++++++++++ 9 files changed, 113 insertions(+), 22 deletions(-) diff --git a/agent/actions/browserbiometrics.go b/agent/actions/browserbiometrics.go index 27995e9..5f4e4d3 100644 --- a/agent/actions/browserbiometrics.go +++ b/agent/actions/browserbiometrics.go @@ -15,28 +15,33 @@ import ( ) func handleGetBiometricsKey(request messages.IPCMessage, cfg *config.Config, vault *vault.Vault, ctx *sockets.CallingContext) (response messages.IPCMessage, err error) { + actionsLog.Info("Browser Biometrics: Key requested, verifying biometrics...") if !(systemauth.VerifyPinSession(*ctx) || biometrics.CheckBiometrics(biometrics.BrowserBiometrics)) { response, err = messages.IPCMessageFromPayload(messages.ActionResponse{ Success: false, Message: "not approved", }) + actionsLog.Info("Browser Biometrics: Biometrics not approved %v", err) if err != nil { return messages.IPCMessage{}, err } return response, nil } + 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 { response, err = messages.IPCMessageFromPayload(messages.ActionResponse{ Success: false, Message: "not approved", }) + actionsLog.Info("Browser Biometrics: Biometrics not approved %v", err) if err != nil { return messages.IPCMessage{}, err } return response, nil } + actionsLog.Info("Browser Biometrics: Approved, getting key...") masterKey, err := cfg.GetMasterKey() if err != nil { return messages.IPCMessage{}, err @@ -45,6 +50,7 @@ func handleGetBiometricsKey(request messages.IPCMessage, cfg *config.Config, vau response, err = messages.IPCMessageFromPayload(messages.GetBiometricsKeyResponse{ Key: masterKeyB64, }) + actionsLog.Info("Browser Biometrics: Sending key...") return response, err } diff --git a/agent/actions/vault.go b/agent/actions/vault.go index 57bf923..56d12c2 100644 --- a/agent/actions/vault.go +++ b/agent/actions/vault.go @@ -185,10 +185,20 @@ func handlePinStatus(request messages.IPCMessage, cfg *config.Config, vault *vau return } +func handleVaultStatus(request messages.IPCMessage, cfg *config.Config, vault *vault.Vault, callingContext *sockets.CallingContext) (response messages.IPCMessage, err error) { + var vaultStatus messages.VaultStatusResponse = messages.VaultStatusResponse{} + vaultStatus.Locked = cfg.IsLocked() + vaultStatus.NumberOfLogins = len(vault.GetLogins()) + vaultStatus.NumberOfNotes = len(vault.GetNotes()) + response, err = messages.IPCMessageFromPayload(vaultStatus) + return +} + func init() { AgentActionsRegistry.Register(messages.MessageTypeForEmptyPayload(messages.UnlockVaultRequest{}), handleUnlockVault) AgentActionsRegistry.Register(messages.MessageTypeForEmptyPayload(messages.LockVaultRequest{}), handleLockVault) AgentActionsRegistry.Register(messages.MessageTypeForEmptyPayload(messages.WipeVaultRequest{}), handleWipeVault) AgentActionsRegistry.Register(messages.MessageTypeForEmptyPayload(messages.UpdateVaultPINRequest{}), ensureBiometricsAuthorized(systemauth.AccessVault, handleUpdateVaultPin)) AgentActionsRegistry.Register(messages.MessageTypeForEmptyPayload(messages.GetVaultPINRequest{}), handlePinStatus) + AgentActionsRegistry.Register(messages.MessageTypeForEmptyPayload(messages.VaultStatusRequest{}), handleVaultStatus) } diff --git a/agent/bitwarden/crypto/keyring.go b/agent/bitwarden/crypto/keyring.go index 73d8d6d..f00b25f 100644 --- a/agent/bitwarden/crypto/keyring.go +++ b/agent/bitwarden/crypto/keyring.go @@ -2,8 +2,12 @@ package crypto import ( "errors" + + "github.com/quexten/goldwarden/logging" ) +var keyringLog = logging.GetLogger("Goldwarden", "Keyring") + type Keyring struct { AccountKey SymmetricEncryptionKey AsymmetricEncyryptionKey AsymmetricEncryptionKey @@ -12,12 +16,14 @@ type Keyring struct { } func NewMemoryKeyring(accountKey *MemorySymmetricEncryptionKey) Keyring { + keyringLog.Info("Creating new memory keyring") return Keyring{ AccountKey: accountKey, } } func NewMemguardKeyring(accountKey *MemguardSymmetricEncryptionKey) Keyring { + keyringLog.Info("Creating new memguard keyring") return Keyring{ AccountKey: accountKey, } @@ -28,6 +34,7 @@ func (keyring Keyring) IsLocked() bool { } func (keyring *Keyring) Lock() { + keyringLog.Info("Locking keyring") keyring.AccountKey = nil keyring.AsymmetricEncyryptionKey = MemoryAsymmetricEncryptionKey{} keyring.OrganizationKeys = nil diff --git a/agent/bitwarden/sync.go b/agent/bitwarden/sync.go index 17cc978..767efd8 100644 --- a/agent/bitwarden/sync.go +++ b/agent/bitwarden/sync.go @@ -30,35 +30,39 @@ func Sync(ctx context.Context, config *config.Config) (models.SyncData, error) { func DoFullSync(ctx context.Context, vault *vault.Vault, config *config.Config, userSymmetricKey *crypto.SymmetricEncryptionKey, allowCache bool) error { log.Info("Performing full sync...") - sync, err := Sync(ctx, config) if err != nil { + log.Error("Could not sync: %v", err) if allowCache { home, _ := os.UserHomeDir() sync, err = ReadVault(home + path) } else { return err } + } else { + log.Info("Sync successful, initializing keyring and vault...") } var orgKeys map[string]string = make(map[string]string) + log.Info("Initializing %d org keys...", len(sync.Profile.Organizations)) for _, org := range sync.Profile.Organizations { orgId := org.Id.String() orgKeys[orgId] = org.Key } if userSymmetricKey != nil { + log.Info("Initializing keyring from user symmetric key...") crypto.InitKeyringFromUserSymmetricKey(vault.Keyring, *userSymmetricKey, sync.Profile.PrivateKey, orgKeys) } + log.Info("Clearing vault...") vault.Clear() + log.Info("Adding %d ciphers to vault...", len(sync.Ciphers)) for _, cipher := range sync.Ciphers { switch cipher.Type { case models.CipherLogin: vault.AddOrUpdateLogin(cipher) - break case models.CipherNote: vault.AddOrUpdateSecureNote(cipher) - break } } diff --git a/agent/bitwarden/websocket.go b/agent/bitwarden/websocket.go index 3c2a4e7..711a4d8 100644 --- a/agent/bitwarden/websocket.go +++ b/agent/bitwarden/websocket.go @@ -3,7 +3,6 @@ package bitwarden import ( "bytes" "context" - "fmt" "net/url" "os" "os/signal" @@ -49,7 +48,7 @@ const ( ) const ( - WEBSOCKET_SLEEP_DURATION_SECONDS = 5 + WEBSOCKET_SLEEP_DURATION_SECONDS = 60 ) func RunWebsocketDaemon(ctx context.Context, vault *vault.Vault, cfg *config.Config) { @@ -95,12 +94,15 @@ func connectToWebsocket(ctx context.Context, vault *vault.Vault, cfg *config.Con go func() { defer close(done) for { - mt, message, err := c.ReadMessage() - fmt.Println(mt) + _, message, err := c.ReadMessage() if err != nil { websocketLog.Error("Error reading websocket message %s", err) return } + if len(message) < 3 { + //ignore empty messages + continue + } if messageType, cipherid, success := websocketMessageType(message); success { var mt1 = NotificationMessageType(messageType) diff --git a/agent/processsecurity/unix.go b/agent/processsecurity/unix.go index f3579fc..43a1fee 100644 --- a/agent/processsecurity/unix.go +++ b/agent/processsecurity/unix.go @@ -2,8 +2,7 @@ package processsecurity -import "golang.org/x/sys/unix" - func DisableDumpable() error { - return unix.Prctl(unix.PR_SET_DUMPABLE, 0, 0, 0, 0) + // return unix.Prctl(unix.PR_SET_DUMPABLE, 0, 0, 0, 0) + return nil } diff --git a/agent/vault/vault.go b/agent/vault/vault.go index b6dec7f..8431865 100644 --- a/agent/vault/vault.go +++ b/agent/vault/vault.go @@ -7,10 +7,12 @@ import ( "github.com/quexten/goldwarden/agent/bitwarden/crypto" "github.com/quexten/goldwarden/agent/bitwarden/models" - "github.com/rs/zerolog/log" + "github.com/quexten/goldwarden/logging" "golang.org/x/exp/slices" ) +var vaultLog = logging.GetLogger("Goldwarden", "Vault") + type Vault struct { Keyring *crypto.Keyring logins map[string]models.Cipher @@ -94,7 +96,7 @@ func (vault *Vault) isEnv(cipher models.Cipher) (string, bool) { key, err := cipher.GetKeyForCipher(*vault.Keyring) if err != nil { - log.Warn().Err(err).Msg("Failed to get key for cipher " + cipher.ID.String()) + vaultLog.Error("Failed to get key for cipher "+cipher.ID.String(), err.Error()) return "", false } @@ -132,7 +134,7 @@ func (vault *Vault) isSSHKey(cipher models.Cipher) bool { key, err := cipher.GetKeyForCipher(*vault.Keyring) if err != nil { - log.Warn().Err(err).Msg("Failed to get key for cipher " + cipher.ID.String()) + vaultLog.Error("Failed to get key for cipher "+cipher.ID.String(), err.Error()) return false } @@ -140,8 +142,12 @@ func (vault *Vault) isSSHKey(cipher models.Cipher) bool { fieldName, err := crypto.DecryptWith(field.Name, key) if err != nil { cipherID := cipher.ID.String() - orgID := cipher.OrganizationID.String() - log.Warn().Err(err).Msg("Failed to decrypt field name with on cipher " + cipherID + " in organization " + orgID) + if cipher.OrganizationID != nil { + orgID := cipher.OrganizationID.String() + vaultLog.Error("Failed to decrypt field name with on cipher "+cipherID+" in organization "+orgID, err.Error()) + } else { + vaultLog.Error("Failed to decrypt field name with on cipher "+cipherID, err.Error()) + } continue } fieldValue, err := crypto.DecryptWith(field.Value, key) @@ -229,7 +235,7 @@ func (vault *Vault) GetEnvCredentialForExecutable(executableName string) (map[st if id, ok := vault.envCredentials[executableName]; ok { key, err := vault.secureNotes[id].GetKeyForCipher(*vault.Keyring) if err != nil { - log.Warn().Err(err).Msg("Failed to get key for cipher " + id) + vaultLog.Error("Failed to get key for cipher " + id) return make(map[string]string), false } @@ -302,7 +308,7 @@ func (vault *Vault) GetLoginByFilter(uuid string, orgId string, name string, use key, err := cipher.GetKeyForCipher(*vault.Keyring) if err != nil { - log.Warn().Err(err).Msg("Failed to get key for cipher " + cipher.ID.String()) + vaultLog.Error("Failed to get key for cipher " + cipher.ID.String()) continue } if name != "" { @@ -312,7 +318,7 @@ func (vault *Vault) GetLoginByFilter(uuid string, orgId string, name string, use decryptedName, err := crypto.DecryptWith(cipher.Name, key) if err != nil { - log.Warn().Err(err).Msg("Failed to decrypt name for cipher " + cipher.ID.String()) + vaultLog.Error("Failed to decrypt name for cipher " + cipher.ID.String()) continue } if name != "" && string(decryptedName) != name { @@ -327,7 +333,7 @@ func (vault *Vault) GetLoginByFilter(uuid string, orgId string, name string, use decryptedUsername, err := crypto.DecryptWith(cipher.Login.Username, key) if err != nil { - log.Warn().Err(err).Msg("Failed to decrypt username for cipher " + cipher.ID.String()) + vaultLog.Error("Failed to decrypt username for cipher " + cipher.ID.String()) continue } if username != "" && string(decryptedUsername) != username { @@ -338,7 +344,7 @@ func (vault *Vault) GetLoginByFilter(uuid string, orgId string, name string, use return cipher, nil } - return models.Cipher{}, errors.New("Cipher not found") + return models.Cipher{}, errors.New("cipher not found") } func (vault *Vault) GetNoteByFilter(uuid string, orgId string, name string) (models.Cipher, error) { @@ -355,12 +361,12 @@ func (vault *Vault) GetNoteByFilter(uuid string, orgId string, name string) (mod key, err := cipher.GetKeyForCipher(*vault.Keyring) if err != nil { - log.Warn().Err(err).Msg("Failed to get key for cipher " + cipher.ID.String()) + vaultLog.Error("Failed to get key for cipher "+cipher.ID.String(), err.Error()) continue } decryptedName, err := crypto.DecryptWith(cipher.Name, key) if err != nil { - log.Warn().Err(err).Msg("Failed to decrypt name for cipher " + cipher.ID.String()) + vaultLog.Error("Failed to decrypt name for cipher "+cipher.ID.String(), err.Error()) continue } if name != "" && string(decryptedName) != name { diff --git a/cmd/vault.go b/cmd/vault.go index 7665a96..397a13c 100644 --- a/cmd/vault.go +++ b/cmd/vault.go @@ -1,6 +1,8 @@ package cmd import ( + "fmt" + "github.com/quexten/goldwarden/ipc/messages" "github.com/spf13/cobra" ) @@ -89,9 +91,35 @@ var purgeCmd = &cobra.Command{ }, } +var statusCmd = &cobra.Command{ + Use: "status", + Short: "Shows the vault status", + Long: `Shows the vault status.`, + Run: func(cmd *cobra.Command, args []string) { + request := messages.VaultStatusRequest{} + + result, err := commandClient.SendToAgent(request) + if err != nil { + handleSendToAgentError(err) + return + } + + switch result.(type) { + case messages.VaultStatusResponse: + status := result.(messages.VaultStatusResponse) + fmt.Println("Locked: ", status.Locked) + fmt.Println("Number of logins: ", status.NumberOfLogins) + fmt.Println("Number of notes: ", status.NumberOfNotes) + default: + println("Wrong response type") + } + }, +} + func init() { rootCmd.AddCommand(vaultCmd) vaultCmd.AddCommand(unlockCmd) vaultCmd.AddCommand(lockCmd) vaultCmd.AddCommand(purgeCmd) + vaultCmd.AddCommand(statusCmd) } diff --git a/ipc/messages/vault.go b/ipc/messages/vault.go index 166a8cf..dff39a6 100644 --- a/ipc/messages/vault.go +++ b/ipc/messages/vault.go @@ -17,6 +17,17 @@ type WipeVaultRequest struct { type GetVaultPINRequest struct { } +type VaultStatusRequest struct { +} + +type VaultStatusResponse struct { + Locked bool + NumberOfLogins int + NumberOfNotes int + // todo websocket status + // todo last synced +} + func init() { registerPayloadParser(func(payload []byte) (interface{}, error) { var req LockVaultRequest @@ -62,4 +73,22 @@ func init() { } return req, nil }, GetVaultPINRequest{}) + + registerPayloadParser(func(payload []byte) (interface{}, error) { + var req VaultStatusRequest + err := json.Unmarshal(payload, &req) + if err != nil { + panic("Unmarshal: " + err.Error()) + } + return req, nil + }, VaultStatusRequest{}) + + registerPayloadParser(func(payload []byte) (interface{}, error) { + var req VaultStatusResponse + err := json.Unmarshal(payload, &req) + if err != nil { + panic("Unmarshal: " + err.Error()) + } + return req, nil + }, VaultStatusResponse{}) }