Introduce session cache

This commit is contained in:
Bernd Schoolmann 2023-09-12 18:56:35 +02:00
parent 032da6cdf1
commit 88526058c3
No known key found for this signature in database
15 changed files with 128 additions and 34 deletions

View File

@ -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",

View File

@ -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
}

View File

@ -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()

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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 {

View File

@ -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"

View File

@ -43,6 +43,7 @@ type RuntimeConfig struct {
User string
Password string
Pin string
SessionToken string
}
type ConfigFile struct {

View File

@ -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(),
}
}

View File

@ -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")
}

View File

@ -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)
}

View File

@ -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

View File

@ -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

View File

@ -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,
}