From 88526058c30e18cbaec1fde1e00b12053b33955c Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Tue, 12 Sep 2023 18:56:35 +0200 Subject: [PATCH] Introduce session cache --- agent/actions/actions.go | 13 +++-- agent/actions/browserbiometrics.go | 4 +- agent/actions/config.go | 6 +-- agent/actions/getclicredentials.go | 6 +-- agent/actions/login.go | 12 ++--- agent/actions/logins.go | 8 +-- agent/actions/ssh.go | 4 +- agent/actions/vault.go | 12 ++--- agent/config/config.go | 1 + agent/sockets/callingcontext.go | 6 +++ agent/ssh/ssh.go | 5 +- agent/systemauth/systemauth.go | 80 ++++++++++++++++++++++++++++++ agent/unixsocketagent.go | 2 +- agent/virtualagent.go | 2 +- main.go | 1 + 15 files changed, 128 insertions(+), 34 deletions(-) create mode 100644 agent/systemauth/systemauth.go diff --git a/agent/actions/actions.go b/agent/actions/actions.go index 1de62c3..c1bdd3e 100644 --- a/agent/actions/actions.go +++ b/agent/actions/actions.go @@ -7,6 +7,7 @@ import ( "github.com/quexten/goldwarden/agent/bitwarden/crypto" "github.com/quexten/goldwarden/agent/config" "github.com/quexten/goldwarden/agent/sockets" + "github.com/quexten/goldwarden/agent/systemauth" "github.com/quexten/goldwarden/agent/systemauth/biometrics" "github.com/quexten/goldwarden/agent/vault" "github.com/quexten/goldwarden/ipc" @@ -14,7 +15,7 @@ import ( var AgentActionsRegistry = newActionsRegistry() -type Action func(ipc.IPCMessage, *config.Config, *vault.Vault, sockets.CallingContext) (interface{}, error) +type Action func(ipc.IPCMessage, *config.Config, *vault.Vault, *sockets.CallingContext) (ipc.IPCMessage, error) type ActionsRegistry struct { actions map[ipc.IPCMessageType]Action } @@ -35,7 +36,7 @@ func (registry *ActionsRegistry) Get(messageType ipc.IPCMessageType) (Action, bo } func ensureIsLoggedIn(action Action) Action { - return func(request ipc.IPCMessage, cfg *config.Config, vault *vault.Vault, ctx sockets.CallingContext) (interface{}, error) { + return func(request ipc.IPCMessage, cfg *config.Config, vault *vault.Vault, ctx *sockets.CallingContext) (ipc.IPCMessage, error) { if hash, err := cfg.GetMasterPasswordHash(); err != nil || len(hash) == 0 { return ipc.IPCMessageFromPayload(ipc.ActionResponse{ Success: false, @@ -68,7 +69,7 @@ func sync(ctx context.Context, vault *vault.Vault, cfg *config.Config) bool { } func ensureIsNotLocked(action Action) Action { - return func(request ipc.IPCMessage, cfg *config.Config, vault *vault.Vault, ctx sockets.CallingContext) (interface{}, error) { + return func(request ipc.IPCMessage, cfg *config.Config, vault *vault.Vault, ctx *sockets.CallingContext) (ipc.IPCMessage, error) { if cfg.IsLocked() { err := cfg.TryUnlock(vault) ctx1 := context.Background() @@ -79,6 +80,8 @@ func ensureIsNotLocked(action Action) Action { Message: err.Error(), }) } + + systemauth.CreateSession(*ctx) } return action(request, cfg, vault, ctx) @@ -86,8 +89,8 @@ func ensureIsNotLocked(action Action) Action { } func ensureBiometricsAuthorized(approvalType biometrics.Approval, action Action) Action { - return func(request ipc.IPCMessage, cfg *config.Config, vault *vault.Vault, ctx sockets.CallingContext) (interface{}, error) { - if !biometrics.CheckBiometrics(approvalType) { + return func(request ipc.IPCMessage, cfg *config.Config, vault *vault.Vault, ctx *sockets.CallingContext) (ipc.IPCMessage, error) { + if !systemauth.CheckBiometrics(ctx, approvalType) { return ipc.IPCMessageFromPayload(ipc.ActionResponse{ Success: false, Message: "Polkit authorization failed required", diff --git a/agent/actions/browserbiometrics.go b/agent/actions/browserbiometrics.go index 68d3cd3..92bc3c9 100644 --- a/agent/actions/browserbiometrics.go +++ b/agent/actions/browserbiometrics.go @@ -12,14 +12,14 @@ import ( "github.com/quexten/goldwarden/ipc" ) -func handleGetBiometricsKey(request ipc.IPCMessage, cfg *config.Config, vault *vault.Vault, ctx sockets.CallingContext) (response interface{}, err error) { +func handleGetBiometricsKey(request ipc.IPCMessage, cfg *config.Config, vault *vault.Vault, ctx *sockets.CallingContext) (response ipc.IPCMessage, err error) { 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 = ipc.IPCMessageFromPayload(ipc.ActionResponse{ Success: false, Message: "not approved", }) if err != nil { - return nil, err + return ipc.IPCMessage{}, err } return response, nil } diff --git a/agent/actions/config.go b/agent/actions/config.go index 0684e65..cbd73fa 100644 --- a/agent/actions/config.go +++ b/agent/actions/config.go @@ -7,7 +7,7 @@ import ( "github.com/quexten/goldwarden/ipc" ) -func handleSetApiURL(request ipc.IPCMessage, cfg *config.Config, vault *vault.Vault, ctx sockets.CallingContext) (response interface{}, err error) { +func handleSetApiURL(request ipc.IPCMessage, cfg *config.Config, vault *vault.Vault, ctx *sockets.CallingContext) (response ipc.IPCMessage, err error) { apiURL := request.ParsedPayload().(ipc.SetApiURLRequest).Value cfg.ConfigFile.ApiUrl = apiURL err = cfg.WriteConfig() @@ -23,7 +23,7 @@ func handleSetApiURL(request ipc.IPCMessage, cfg *config.Config, vault *vault.Va }) } -func handleSetIdentity(request ipc.IPCMessage, cfg *config.Config, vault *vault.Vault, ctx sockets.CallingContext) (response interface{}, err error) { +func handleSetIdentity(request ipc.IPCMessage, cfg *config.Config, vault *vault.Vault, ctx *sockets.CallingContext) (response ipc.IPCMessage, err error) { identity := request.ParsedPayload().(ipc.SetIdentityURLRequest).Value cfg.ConfigFile.IdentityUrl = identity err = cfg.WriteConfig() @@ -39,7 +39,7 @@ func handleSetIdentity(request ipc.IPCMessage, cfg *config.Config, vault *vault. }) } -func handleSetNotifications(request ipc.IPCMessage, cfg *config.Config, vault *vault.Vault, ctx sockets.CallingContext) (response interface{}, err error) { +func handleSetNotifications(request ipc.IPCMessage, cfg *config.Config, vault *vault.Vault, ctx *sockets.CallingContext) (response ipc.IPCMessage, err error) { notifications := request.ParsedPayload().(ipc.SetNotificationsURLRequest).Value cfg.ConfigFile.NotificationsUrl = notifications err = cfg.WriteConfig() diff --git a/agent/actions/getclicredentials.go b/agent/actions/getclicredentials.go index 1dcf318..be35fb3 100644 --- a/agent/actions/getclicredentials.go +++ b/agent/actions/getclicredentials.go @@ -11,7 +11,7 @@ import ( "github.com/quexten/goldwarden/ipc" ) -func handleGetCliCredentials(request ipc.IPCMessage, cfg *config.Config, vault *vault.Vault, ctx sockets.CallingContext) (response interface{}, err error) { +func handleGetCliCredentials(request ipc.IPCMessage, cfg *config.Config, vault *vault.Vault, ctx *sockets.CallingContext) (response ipc.IPCMessage, err error) { req := request.ParsedPayload().(ipc.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 { @@ -20,7 +20,7 @@ func handleGetCliCredentials(request ipc.IPCMessage, cfg *config.Config, vault * Message: "not approved", }) if err != nil { - return nil, err + return ipc.IPCMessage{}, err } return response, nil } @@ -32,7 +32,7 @@ func handleGetCliCredentials(request ipc.IPCMessage, cfg *config.Config, vault * Message: "no credentials found for " + req.ApplicationName, }) if err != nil { - return nil, err + return ipc.IPCMessage{}, err } return response, nil } diff --git a/agent/actions/login.go b/agent/actions/login.go index cf8a0e2..6fc2fa1 100644 --- a/agent/actions/login.go +++ b/agent/actions/login.go @@ -12,14 +12,14 @@ import ( "github.com/quexten/goldwarden/ipc" ) -func handleLogin(msg ipc.IPCMessage, cfg *config.Config, vault *vault.Vault, callingContext sockets.CallingContext) (response interface{}, err error) { +func handleLogin(msg ipc.IPCMessage, cfg *config.Config, vault *vault.Vault, callingContext *sockets.CallingContext) (response ipc.IPCMessage, err error) { if !cfg.HasPin() && !cfg.ConfigFile.RuntimeConfig.DisablePinRequirement { response, err = ipc.IPCMessageFromPayload(ipc.ActionResponse{ Success: false, Message: "No pin set. Set a pin first!", }) if err != nil { - return nil, err + return ipc.IPCMessage{}, err } return @@ -44,7 +44,7 @@ func handleLogin(msg ipc.IPCMessage, cfg *config.Config, vault *vault.Vault, cal } response, err = ipc.IPCMessageFromPayload(payload) if err != nil { - return nil, err + return ipc.IPCMessage{}, err } return } @@ -65,7 +65,7 @@ func handleLogin(msg ipc.IPCMessage, cfg *config.Config, vault *vault.Vault, cal } response, err = ipc.IPCMessageFromPayload(payload) if err != nil { - return nil, err + return ipc.IPCMessage{}, err } return } @@ -84,7 +84,7 @@ func handleLogin(msg ipc.IPCMessage, cfg *config.Config, vault *vault.Vault, cal } response, err = ipc.IPCMessageFromPayload(payload) if err != nil { - return nil, err + return ipc.IPCMessage{}, err } return } @@ -100,7 +100,7 @@ func handleLogin(msg ipc.IPCMessage, cfg *config.Config, vault *vault.Vault, cal } response, err = ipc.IPCMessageFromPayload(payload) if err != nil { - return nil, err + return ipc.IPCMessage{}, err } return } diff --git a/agent/actions/logins.go b/agent/actions/logins.go index f62728b..e5158ea 100644 --- a/agent/actions/logins.go +++ b/agent/actions/logins.go @@ -16,7 +16,7 @@ import ( "github.com/quexten/goldwarden/ipc" ) -func handleGetLoginCipher(request ipc.IPCMessage, cfg *config.Config, vault *vault.Vault, ctx sockets.CallingContext) (response interface{}, err error) { +func handleGetLoginCipher(request ipc.IPCMessage, cfg *config.Config, vault *vault.Vault, ctx *sockets.CallingContext) (response ipc.IPCMessage, err error) { req := request.ParsedPayload().(ipc.GetLoginRequest) login, err := vault.GetLoginByFilter(req.UUID, req.OrgId, req.Name, req.Username) if err != nil { @@ -89,7 +89,7 @@ func handleGetLoginCipher(request ipc.IPCMessage, cfg *config.Config, vault *vau Message: "not approved", }) if err != nil { - return nil, err + return ipc.IPCMessage{}, err } return response, nil } @@ -100,14 +100,14 @@ func handleGetLoginCipher(request ipc.IPCMessage, cfg *config.Config, vault *vau }) } -func handleListLoginsRequest(request ipc.IPCMessage, cfg *config.Config, vault *vault.Vault, ctx sockets.CallingContext) (response interface{}, err error) { +func handleListLoginsRequest(request ipc.IPCMessage, cfg *config.Config, vault *vault.Vault, ctx *sockets.CallingContext) (response ipc.IPCMessage, err error) { if approved, err := pinentry.GetApproval("Approve List Credentials", fmt.Sprintf("%s on %s>%s>%s is trying to list credentials (name & username)", ctx.UserName, ctx.GrandParentProcessName, ctx.ParentProcessName, ctx.ProcessName)); err != nil || !approved { response, err = ipc.IPCMessageFromPayload(ipc.ActionResponse{ Success: false, Message: "not approved", }) if err != nil { - return nil, err + return ipc.IPCMessage{}, err } return response, nil } diff --git a/agent/actions/ssh.go b/agent/actions/ssh.go index 224d6e1..c1830c1 100644 --- a/agent/actions/ssh.go +++ b/agent/actions/ssh.go @@ -16,7 +16,7 @@ import ( var actionsLog = logging.GetLogger("Goldwarden", "Actions") -func handleAddSSH(msg ipc.IPCMessage, cfg *config.Config, vault *vault.Vault, callingContext sockets.CallingContext) (response interface{}, err error) { +func handleAddSSH(msg ipc.IPCMessage, cfg *config.Config, vault *vault.Vault, callingContext *sockets.CallingContext) (response ipc.IPCMessage, err error) { req := msg.ParsedPayload().(ipc.CreateSSHKeyRequest) cipher, publicKey := ssh.NewSSHKeyCipher(req.Name, vault.Keyring) @@ -43,7 +43,7 @@ func handleAddSSH(msg ipc.IPCMessage, cfg *config.Config, vault *vault.Vault, ca return } -func handleListSSH(msg ipc.IPCMessage, cfg *config.Config, vault *vault.Vault, callingContext sockets.CallingContext) (response interface{}, err error) { +func handleListSSH(msg ipc.IPCMessage, cfg *config.Config, vault *vault.Vault, callingContext *sockets.CallingContext) (response ipc.IPCMessage, err error) { keys := vault.GetSSHKeys() keyStrings := make([]string, 0) for _, key := range keys { diff --git a/agent/actions/vault.go b/agent/actions/vault.go index 98b4a49..aa6590c 100644 --- a/agent/actions/vault.go +++ b/agent/actions/vault.go @@ -14,7 +14,7 @@ import ( "github.com/quexten/goldwarden/ipc" ) -func handleUnlockVault(request ipc.IPCMessage, cfg *config.Config, vault *vault.Vault, callingContext sockets.CallingContext) (response interface{}, err error) { +func handleUnlockVault(request ipc.IPCMessage, cfg *config.Config, vault *vault.Vault, callingContext *sockets.CallingContext) (response ipc.IPCMessage, err error) { if !cfg.HasPin() { response, err = ipc.IPCMessageFromPayload(ipc.ActionResponse{ Success: false, @@ -85,7 +85,7 @@ func handleUnlockVault(request ipc.IPCMessage, cfg *config.Config, vault *vault. return } -func handleLockVault(request ipc.IPCMessage, cfg *config.Config, vault *vault.Vault, callingContext sockets.CallingContext) (response interface{}, err error) { +func handleLockVault(request ipc.IPCMessage, cfg *config.Config, vault *vault.Vault, callingContext *sockets.CallingContext) (response ipc.IPCMessage, err error) { if !cfg.HasPin() { response, err = ipc.IPCMessageFromPayload(ipc.ActionResponse{ Success: false, @@ -124,7 +124,7 @@ func handleLockVault(request ipc.IPCMessage, cfg *config.Config, vault *vault.Va return } -func handleWipeVault(request ipc.IPCMessage, cfg *config.Config, vault *vault.Vault, callingContext sockets.CallingContext) (response interface{}, err error) { +func handleWipeVault(request ipc.IPCMessage, cfg *config.Config, vault *vault.Vault, callingContext *sockets.CallingContext) (response ipc.IPCMessage, err error) { cfg.Purge() cfg.WriteConfig() vault.Clear() @@ -139,7 +139,7 @@ func handleWipeVault(request ipc.IPCMessage, cfg *config.Config, vault *vault.Va return } -func handleUpdateVaultPin(request ipc.IPCMessage, cfg *config.Config, vault *vault.Vault, callingContext sockets.CallingContext) (response interface{}, err error) { +func handleUpdateVaultPin(request ipc.IPCMessage, cfg *config.Config, vault *vault.Vault, callingContext *sockets.CallingContext) (response ipc.IPCMessage, err error) { pin, err := pinentry.GetPassword("Pin Change", "Enter your desired pin") if err != nil { response, err = ipc.IPCMessageFromPayload(ipc.ActionResponse{ @@ -147,7 +147,7 @@ func handleUpdateVaultPin(request ipc.IPCMessage, cfg *config.Config, vault *vau Message: err.Error(), }) if err != nil { - return nil, err + return ipc.IPCMessage{}, err } else { return response, nil } @@ -161,7 +161,7 @@ func handleUpdateVaultPin(request ipc.IPCMessage, cfg *config.Config, vault *vau return } -func handlePinStatus(request ipc.IPCMessage, cfg *config.Config, vault *vault.Vault, callingContext sockets.CallingContext) (response interface{}, err error) { +func handlePinStatus(request ipc.IPCMessage, cfg *config.Config, vault *vault.Vault, callingContext *sockets.CallingContext) (response ipc.IPCMessage, err error) { var pinStatus string if cfg.HasPin() { pinStatus = "enabled" diff --git a/agent/config/config.go b/agent/config/config.go index d8054a7..d9cdde8 100644 --- a/agent/config/config.go +++ b/agent/config/config.go @@ -43,6 +43,7 @@ type RuntimeConfig struct { User string Password string Pin string + SessionToken string } type ConfigFile struct { diff --git a/agent/sockets/callingcontext.go b/agent/sockets/callingcontext.go index 858799a..c7771d5 100644 --- a/agent/sockets/callingcontext.go +++ b/agent/sockets/callingcontext.go @@ -13,6 +13,9 @@ type CallingContext struct { ProcessName string ParentProcessName string GrandParentProcessName string + ProcessPid int + ParentProcessPid int + GrandParentProcessPid int } func GetCallingContext(connection net.Conn) CallingContext { @@ -48,5 +51,8 @@ func GetCallingContext(connection net.Conn) CallingContext { ProcessName: process.Executable(), ParentProcessName: parentProcess.Executable(), GrandParentProcessName: parentParentProcess.Executable(), + ProcessPid: pid, + ParentProcessPid: ppid, + GrandParentProcessPid: parentParentProcess.PPid(), } } diff --git a/agent/ssh/ssh.go b/agent/ssh/ssh.go index b7ff1ad..a34de51 100644 --- a/agent/ssh/ssh.go +++ b/agent/ssh/ssh.go @@ -9,6 +9,7 @@ import ( "os" "github.com/quexten/goldwarden/agent/sockets" + "github.com/quexten/goldwarden/agent/systemauth" "github.com/quexten/goldwarden/agent/systemauth/biometrics" "github.com/quexten/goldwarden/agent/systemauth/pinentry" "github.com/quexten/goldwarden/agent/vault" @@ -75,6 +76,8 @@ func (vaultAgent vaultAgent) Sign(key ssh.PublicKey, data []byte) (*ssh.Signatur if !vaultAgent.unlockRequestAction() { return nil, errors.New("vault is locked") } + + systemauth.CreateSession(vaultAgent.context) } var signer ssh.Signer @@ -100,7 +103,7 @@ func (vaultAgent vaultAgent) Sign(key ssh.PublicKey, data []byte) (*ssh.Signatur return nil, errors.New("Approval not given") } - if !biometrics.CheckBiometrics(biometrics.SSHKey) { + if !systemauth.CheckBiometrics(&vaultAgent.context, biometrics.SSHKey) { log.Info("Sign Request for key: %s denied", key.Marshal()) return nil, errors.New("Biometrics not checked") } diff --git a/agent/systemauth/systemauth.go b/agent/systemauth/systemauth.go new file mode 100644 index 0000000..5c37d54 --- /dev/null +++ b/agent/systemauth/systemauth.go @@ -0,0 +1,80 @@ +package systemauth + +import ( + "time" + + "github.com/quexten/goldwarden/agent/sockets" + "github.com/quexten/goldwarden/agent/systemauth/biometrics" + "github.com/quexten/goldwarden/agent/systemauth/pinentry" +) + +const tokenExpiry = 10 * time.Minute + +var sessionStore = SessionStore{ + Store: []Session{}, +} + +type Session struct { + Pid int + ParentPid int + GrandParentPid int + Expires time.Time +} + +type SessionStore struct { + Store []Session +} + +func (s *SessionStore) CreateSession(pid int, parentpid int, grandparentpid int) Session { + var session = Session{ + Pid: pid, + ParentPid: parentpid, + GrandParentPid: grandparentpid, + Expires: time.Now().Add(tokenExpiry), + } + s.Store = append(s.Store, session) + return session +} + +func (s *SessionStore) VerifySession(ctx sockets.CallingContext) bool { + for _, session := range s.Store { + if session.ParentPid == ctx.ParentProcessPid && session.GrandParentPid == ctx.GrandParentProcessPid { + if session.Expires.After(time.Now()) { + return true + } + } + } + return false +} + +func GetApproval(title string, description string, requriesBiometrics bool) (bool, error) { + approval, err := pinentry.GetApproval(title, description) + if err != nil { + return false, err + } + if requriesBiometrics { + biometricsApproval := biometrics.CheckBiometrics(biometrics.AccessCredential) + if !biometricsApproval { + return false, nil + } + } + return approval, nil +} + +func CheckBiometrics(callingContext *sockets.CallingContext, approvalType biometrics.Approval) bool { + if sessionStore.VerifySession(*callingContext) { + return true + } + + var approval = biometrics.CheckBiometrics(approvalType) + if !approval { + return false + } + + sessionStore.CreateSession(callingContext.ProcessPid, callingContext.ParentProcessPid, callingContext.GrandParentProcessPid) + return true +} + +func CreateSession(ctx sockets.CallingContext) { + sessionStore.CreateSession(ctx.ProcessPid, ctx.ParentProcessPid, ctx.GrandParentProcessPid) +} diff --git a/agent/unixsocketagent.go b/agent/unixsocketagent.go index b45d7b9..ad8bff7 100644 --- a/agent/unixsocketagent.go +++ b/agent/unixsocketagent.go @@ -63,7 +63,7 @@ func serveAgentSession(c net.Conn, ctx context.Context, vault *vault.Vault, cfg responseBytes := []byte{} if action, actionFound := actions.AgentActionsRegistry.Get(msg.Type); actionFound { callingContext := sockets.GetCallingContext(c) - payload, err := action(msg, cfg, vault, callingContext) + payload, err := action(msg, cfg, vault, &callingContext) if err != nil { writeError(c, err) continue diff --git a/agent/virtualagent.go b/agent/virtualagent.go index b2f2f69..ae48cb1 100644 --- a/agent/virtualagent.go +++ b/agent/virtualagent.go @@ -44,7 +44,7 @@ func serveVirtualAgent(recv chan []byte, send chan []byte, ctx context.Context, ParentProcessName: parent, GrandParentProcessName: grandparent, } - payload, err := action(msg, cfg, vault, callingContext) + payload, err := action(msg, cfg, vault, &callingContext) if err != nil { writeErrorToLog(err) continue diff --git a/main.go b/main.go index fca5af0..72a18fc 100644 --- a/main.go +++ b/main.go @@ -39,6 +39,7 @@ func main() { User: os.Getenv("GOLDWARDEN_AUTH_USER"), Password: os.Getenv("GOLDWARDEN_AUTH_PASSWORD"), Pin: os.Getenv("GOLDWARDEN_PIN"), + SessionToken: os.Getenv("GOLDWARDEN_SESSION_TOKEN"), ConfigDirectory: configPath, }