github.com/crowdsecurity/crowdsec@v1.6.1/pkg/csplugin/utils.go (about) 1 //go:build linux || freebsd || netbsd || openbsd || solaris || (!windows && !js) 2 3 package csplugin 4 5 import ( 6 "errors" 7 "fmt" 8 "io/fs" 9 "math" 10 "os" 11 "os/exec" 12 "os/user" 13 "path/filepath" 14 "strconv" 15 "strings" 16 "syscall" 17 ) 18 19 func CheckCredential(uid int, gid int) *syscall.SysProcAttr { 20 return &syscall.SysProcAttr{ 21 Credential: &syscall.Credential{ 22 Uid: uint32(uid), 23 Gid: uint32(gid), 24 }, 25 } 26 } 27 28 func (pb *PluginBroker) CreateCmd(binaryPath string) (*exec.Cmd, error) { 29 var err error 30 cmd := exec.Command(binaryPath) 31 if pb.pluginProcConfig.User != "" || pb.pluginProcConfig.Group != "" { 32 if pb.pluginProcConfig.User == "" || pb.pluginProcConfig.Group == "" { 33 return nil, errors.New("while getting process attributes: both plugin user and group must be set") 34 } 35 cmd.SysProcAttr, err = getProcessAttr(pb.pluginProcConfig.User, pb.pluginProcConfig.Group) 36 if err != nil { 37 return nil, fmt.Errorf("while getting process attributes: %w", err) 38 } 39 cmd.SysProcAttr.Credential.NoSetGroups = true 40 } 41 return cmd, err 42 } 43 44 func getUID(username string) (uint32, error) { 45 u, err := user.Lookup(username) 46 if err != nil { 47 return 0, err 48 } 49 uid, err := strconv.ParseInt(u.Uid, 10, 32) 50 if err != nil { 51 return 0, err 52 } 53 if uid < 0 || uid > math.MaxInt32 { 54 return 0, fmt.Errorf("out of bound uid") 55 } 56 return uint32(uid), nil 57 } 58 59 func getGID(groupname string) (uint32, error) { 60 g, err := user.LookupGroup(groupname) 61 if err != nil { 62 return 0, err 63 } 64 gid, err := strconv.ParseInt(g.Gid, 10, 32) 65 if err != nil { 66 return 0, err 67 } 68 if gid < 0 || gid > math.MaxInt32 { 69 return 0, fmt.Errorf("out of bound gid") 70 } 71 return uint32(gid), nil 72 } 73 74 func getPluginTypeAndSubtypeFromPath(path string) (string, string, error) { 75 pluginFileName := filepath.Base(path) 76 parts := strings.Split(pluginFileName, "-") 77 if len(parts) < 2 { 78 return "", "", fmt.Errorf("plugin name %s is invalid. Name should be like {type-name}", path) 79 } 80 return strings.Join(parts[:len(parts)-1], "-"), parts[len(parts)-1], nil 81 } 82 83 func getProcessAttr(username string, groupname string) (*syscall.SysProcAttr, error) { 84 uid, err := getUID(username) 85 if err != nil { 86 return nil, err 87 } 88 gid, err := getGID(groupname) 89 if err != nil { 90 return nil, err 91 } 92 93 return &syscall.SysProcAttr{ 94 Credential: &syscall.Credential{ 95 Uid: uid, 96 Gid: gid, 97 }, 98 }, nil 99 } 100 101 func pluginIsValid(path string) error { 102 var details fs.FileInfo 103 var err error 104 105 // check if it exists 106 if details, err = os.Stat(path); err != nil { 107 return fmt.Errorf("plugin at %s does not exist: %w", path, err) 108 } 109 110 // check if it is owned by current user 111 currentUser, err := user.Current() 112 if err != nil { 113 return fmt.Errorf("while getting current user: %w", err) 114 } 115 currentUID, err := getUID(currentUser.Username) 116 if err != nil { 117 return fmt.Errorf("while looking up the current uid: %w", err) 118 } 119 stat := details.Sys().(*syscall.Stat_t) 120 if stat.Uid != currentUID { 121 return fmt.Errorf("plugin at %s is not owned by user '%s'", path, currentUser.Username) 122 } 123 124 mode := details.Mode() 125 perm := uint32(mode) 126 if (perm & 00002) != 0 { 127 return fmt.Errorf("plugin at %s is world writable, world writable plugins are invalid", path) 128 } 129 if (perm & 00020) != 0 { 130 return fmt.Errorf("plugin at %s is group writable, group writable plugins are invalid", path) 131 } 132 if (mode & os.ModeSetgid) != 0 { 133 return fmt.Errorf("plugin at %s has setgid permission, which is not allowed", path) 134 } 135 return nil 136 }