From 10ce45c054efe103208a24e1a278a1a46d0b2f27 Mon Sep 17 00:00:00 2001 From: mmetc <92726601+mmetc@users.noreply.github.com> Date: Wed, 9 Mar 2022 12:09:50 +0100 Subject: [PATCH] allow notification plugins to work on freebsd and non-root functional tests (#1253) * random uuid for all platforms * check group writable and setgid; don't check group ownership * allow user to run plugins without changing desired user/group (set them to "") --- go.sum | 1 - pkg/csplugin/broker.go | 77 +++++++++++++++++++++++++++++------------- 2 files changed, 53 insertions(+), 25 deletions(-) diff --git a/go.sum b/go.sum index 8c103a3a0..155fe055a 100644 --- a/go.sum +++ b/go.sum @@ -311,7 +311,6 @@ github.com/gobuffalo/packd v0.1.0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWe github.com/gobuffalo/packr/v2 v2.0.9/go.mod h1:emmyGweYTm6Kdper+iywB6YK5YzuKchGtJQZ0Odn4pQ= github.com/gobuffalo/packr/v2 v2.2.0/go.mod h1:CaAwI0GPIAv+5wKLtv8Afwl+Cm78K/I/VCm/3ptBN+0= github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw= -github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= diff --git a/pkg/csplugin/broker.go b/pkg/csplugin/broker.go index 7285469da..01d16365e 100644 --- a/pkg/csplugin/broker.go +++ b/pkg/csplugin/broker.go @@ -22,6 +22,7 @@ import ( "github.com/crowdsecurity/crowdsec/pkg/models" "github.com/crowdsecurity/crowdsec/pkg/protobufs" "github.com/crowdsecurity/crowdsec/pkg/types" + "github.com/google/uuid" plugin "github.com/hashicorp/go-plugin" "github.com/pkg/errors" log "github.com/sirupsen/logrus" @@ -253,11 +254,13 @@ func (pb *PluginBroker) loadNotificationPlugin(name string, binaryPath string) ( return nil, err } cmd := exec.Command(binaryPath) - cmd.SysProcAttr, err = getProcessAtr(pb.pluginProcConfig.User, pb.pluginProcConfig.Group) - if err != nil { - return nil, errors.Wrap(err, "while getting process attributes") + if pb.pluginProcConfig.User != "" || pb.pluginProcConfig.Group != "" { + cmd.SysProcAttr, err = getProcessAttr(pb.pluginProcConfig.User, pb.pluginProcConfig.Group) + if err != nil { + return nil, errors.Wrap(err, "while getting process attributes") + } + cmd.SysProcAttr.Credential.NoSetGroups = true } - cmd.SysProcAttr.Credential.NoSetGroups = true pb.pluginMap[name] = &NotifierPlugin{} l := log.New() err = types.ConfigureLogger(l) @@ -288,6 +291,7 @@ func (pb *PluginBroker) loadNotificationPlugin(name string, binaryPath string) ( } func (pb *PluginBroker) pushNotificationsToPlugin(pluginName string, alerts []*models.Alert) error { + log.WithField("plugin", pluginName).Debug("pushing alerts to plugin") if len(alerts) == 0 { return nil } @@ -372,18 +376,27 @@ func pluginIsValid(path string) error { if err != nil { return errors.Wrap(err, "while getting current user") } - procAttr, err := getProcessAtr(currentUser.Username, currentUser.Username) + currentUID, err := getUID(currentUser.Username) if err != nil { - return errors.Wrap(err, "while getting process attributes") + return errors.Wrap(err, "while looking up the current uid") } stat := details.Sys().(*syscall.Stat_t) - if stat.Uid != procAttr.Credential.Uid || stat.Gid != procAttr.Credential.Gid { - return fmt.Errorf("plugin at %s is not owned by %s user and group", path, currentUser.Username) + if stat.Uid != currentUID { + return fmt.Errorf("plugin at %s is not owned by user '%s'", path, currentUser.Username) } - if (int(details.Mode()) & 2) != 0 { + mode := details.Mode() + perm := uint32(mode) + + if (perm & 00002) != 0 { return fmt.Errorf("plugin at %s is world writable, world writable plugins are invalid", path) } + if (perm & 00020) != 0 { + return fmt.Errorf("plugin at %s is group writable, group writable plugins are invalid", path) + } + if (mode & os.ModeSetgid) != 0 { + return fmt.Errorf("plugin at %s has setgid permission, which is not allowed", path) + } return nil } @@ -412,43 +425,59 @@ func getPluginTypeAndSubtypeFromPath(path string) (string, string, error) { return strings.Join(parts[:len(parts)-1], "-"), parts[len(parts)-1], nil } -func getProcessAtr(username string, groupname string) (*syscall.SysProcAttr, error) { +func getUID(username string) (uint32, error) { u, err := user.Lookup(username) if err != nil { - return nil, err - } - g, err := user.LookupGroup(groupname) - if err != nil { - return nil, err + return 0, err } uid, err := strconv.ParseInt(u.Uid, 10, 32) if err != nil { - return nil, err + return 0, err } if uid < 0 || uid > math.MaxInt32 { - return nil, fmt.Errorf("out of bound uid") + return 0, fmt.Errorf("out of bound uid") + } + return uint32(uid), nil +} + +func getGID(groupname string) (uint32, error) { + g, err := user.LookupGroup(groupname) + if err != nil { + return 0, err } gid, err := strconv.ParseInt(g.Gid, 10, 32) if err != nil { - return nil, err + return 0, err } if gid < 0 || gid > math.MaxInt32 { - return nil, fmt.Errorf("out of bound gid") + return 0, fmt.Errorf("out of bound gid") + } + return uint32(gid), nil +} + +func getProcessAttr(username string, groupname string) (*syscall.SysProcAttr, error) { + uid, err := getUID(username) + if err != nil { + return nil, err + } + gid, err := getGID(groupname) + if err != nil { + return nil, err } return &syscall.SysProcAttr{ Credential: &syscall.Credential{ - Uid: uint32(uid), - Gid: uint32(gid), + Uid: uid, + Gid: gid, }, }, nil } func getUUID() (string, error) { - if d, err := os.ReadFile("/proc/sys/kernel/random/uuid"); err != nil { + uuidv4, err := uuid.NewRandom() + if err != nil { return "", err - } else { - return string(d), nil } + return uuidv4.String(), nil } func getHandshake() (plugin.HandshakeConfig, error) {