File: utils.go

package info (click to toggle)
crowdsec 1.4.6-10.1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 18,500 kB
  • sloc: sh: 2,870; makefile: 386; python: 74
file content (137 lines) | stat: -rw-r--r-- 3,465 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
//go:build linux || freebsd || netbsd || openbsd || solaris || !windows

package csplugin

import (
	"fmt"
	"io/fs"
	"math"
	"os"
	"os/exec"
	"os/user"
	"path/filepath"
	"strconv"
	"strings"
	"syscall"

	"github.com/pkg/errors"
)

func CheckCredential(uid int, gid int) *syscall.SysProcAttr {
	return &syscall.SysProcAttr{
		Credential: &syscall.Credential{
			Uid: uint32(uid),
			Gid: uint32(gid),
		},
	}
}

func (pb *PluginBroker) CreateCmd(binaryPath string) (*exec.Cmd, error) {
	var err error
	cmd := exec.Command(binaryPath)
	if pb.pluginProcConfig.User != "" || pb.pluginProcConfig.Group != "" {
		if !(pb.pluginProcConfig.User != "" && pb.pluginProcConfig.Group != "") {
			return nil, errors.New("while getting process attributes: both plugin user and group must be set")
		}
		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
	}
	return cmd, err
}

func getUID(username string) (uint32, error) {
	u, err := user.Lookup(username)
	if err != nil {
		return 0, err
	}
	uid, err := strconv.ParseInt(u.Uid, 10, 32)
	if err != nil {
		return 0, err
	}
	if uid < 0 || uid > math.MaxInt32 {
		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 0, err
	}
	if gid < 0 || gid > math.MaxInt32 {
		return 0, fmt.Errorf("out of bound gid")
	}
	return uint32(gid), nil
}

func getPluginTypeAndSubtypeFromPath(path string) (string, string, error) {
	pluginFileName := filepath.Base(path)
	parts := strings.Split(pluginFileName, "-")
	if len(parts) < 2 {
		return "", "", fmt.Errorf("plugin name %s is invalid. Name should be like {type-name}", path)
	}
	return strings.Join(parts[:len(parts)-1], "-"), parts[len(parts)-1], 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: uid,
			Gid: gid,
		},
	}, nil
}

func pluginIsValid(path string) error {
	var details fs.FileInfo
	var err error

	// check if it exists
	if details, err = os.Stat(path); err != nil {
		return errors.Wrap(err, fmt.Sprintf("plugin at %s does not exist", path))
	}

	// check if it is owned by current user
	currentUser, err := user.Current()
	if err != nil {
		return errors.Wrap(err, "while getting current user")
	}
	currentUID, err := getUID(currentUser.Username)
	if err != nil {
		return errors.Wrap(err, "while looking up the current uid")
	}
	stat := details.Sys().(*syscall.Stat_t)
	if stat.Uid != currentUID {
		return fmt.Errorf("plugin at %s is not owned by user '%s'", path, currentUser.Username)
	}

	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
}