Skip to content

Commit 4757ff7

Browse files
committed
Use semodule -lfull --checksum instead of the datastore
SELinux userspace release 3.4 introduced a new command line option [-m|--checksum] to `semodule` which adds sha256 checksum of modules to its output. It can be used to check whether the same module is already installed or not. Given that selinuxd installed modules use priority 350 we can use semodule checksum and priority 350 as an indicator whether a module was already installed by selinuxd or not and therefore there's no need to track the state of modules in a separate datastore. `semodule --checksum` is supported since Red Hat Enterprise Linux 8.6 Signed-off-by: Petr Lautrbach <[email protected]>
1 parent 85c0a85 commit 4757ff7

File tree

8 files changed

+133
-55
lines changed

8 files changed

+133
-55
lines changed

pkg/daemon/action.go

+3-31
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
package daemon
22

33
import (
4-
"bytes"
5-
"errors"
64
"fmt"
75

86
"github.com/containers/selinuxd/pkg/datastore"
@@ -40,34 +38,14 @@ func (pi *policyInstall) do(modulePath string, sh seiface.Handler, ds datastore.
4038
return "", fmt.Errorf("installing policy: %w", csErr)
4139
}
4240

43-
p, getErr := ds.Get(policyName)
41+
module, getErr := sh.GetPolicyModule(policyName)
4442
// If the checksums are equal, the policy is already installed
4543
// and in an appropriate state
46-
if getErr == nil && bytes.Equal(p.Checksum, cs) {
44+
if getErr == nil && module.Checksum == cs {
4745
return "", nil
48-
} else if getErr != nil && !errors.Is(getErr, datastore.ErrPolicyNotFound) {
49-
return "", fmt.Errorf("installing policy: couldn't access datastore: %w", getErr)
5046
}
5147

5248
installErr := sh.Install(pi.path)
53-
status := datastore.InstalledStatus
54-
var msg string
55-
56-
if installErr != nil {
57-
status = datastore.FailedStatus
58-
msg = installErr.Error()
59-
}
60-
61-
ps := datastore.PolicyStatus{
62-
Policy: policyName,
63-
Status: status,
64-
Message: msg,
65-
Checksum: cs,
66-
}
67-
puterr := ds.Put(ps)
68-
if puterr != nil {
69-
return "", fmt.Errorf("failed persisting status in datastore: %w", puterr)
70-
}
7149

7250
if installErr != nil {
7351
return "", fmt.Errorf("failed executing install action: %w", installErr)
@@ -96,19 +74,13 @@ func (pi *policyRemove) do(modulePath string, sh seiface.Handler, ds datastore.D
9674
}
9775

9876
if !pi.moduleInstalled(sh, policyArg) {
99-
if err := ds.Remove(policyArg); err != nil {
100-
return "Module is not in the system", fmt.Errorf("failed removing policy from datastore: %w", err)
101-
}
10277
return "No action needed; Module is not in the system", nil
10378
}
10479

10580
if err := sh.Remove(policyArg); err != nil {
10681
return "", fmt.Errorf("failed executing remove action: %w", err)
10782
}
10883

109-
if err := ds.Remove(policyArg); err != nil {
110-
return "", fmt.Errorf("failed removing policy from datastore: %w", err)
111-
}
11284
return "", nil
11385
}
11486

@@ -119,7 +91,7 @@ func (pi *policyRemove) moduleInstalled(sh seiface.Handler, policy string) bool
11991
}
12092

12193
for _, mod := range currentModules {
122-
if policy == mod {
94+
if policy == mod.Name {
12395
return true
12496
}
12597
}

pkg/daemon/daemon.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ func Daemon(opts *SelinuxdOptions, mPath string, sh seiface.Handler, ds datastor
3939
defer ds.Close()
4040
}
4141

42-
ss, err := initStatusServer(opts.StatusServerConfig, ds.GetReadOnly(), l)
42+
ss, err := initStatusServer(opts.StatusServerConfig, ds.GetReadOnly(), sh, l, mPath)
4343
if err != nil {
4444
l.Error(err, "Unable initialize status server")
4545
panic(err)

pkg/daemon/status_server.go

+63-8
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,12 @@ import (
88
"net/http"
99
"net/http/pprof"
1010
"os"
11+
"path/filepath"
1112
"time"
1213

1314
"github.com/containers/selinuxd/pkg/datastore"
15+
seiface "github.com/containers/selinuxd/pkg/semodule/interface"
16+
"github.com/containers/selinuxd/pkg/utils"
1417
"github.com/go-logr/logr"
1518
"github.com/gorilla/mux"
1619
)
@@ -31,12 +34,27 @@ type StatusServerConfig struct {
3134
type statusServer struct {
3235
cfg StatusServerConfig
3336
ds datastore.ReadOnlyDataStore
37+
sh seiface.Handler
3438
l logr.Logger
39+
mPath string
3540
lst net.Listener
3641
ready bool
3742
}
3843

39-
func initStatusServer(cfg StatusServerConfig, ds datastore.ReadOnlyDataStore, l logr.Logger) (*statusServer, error) {
44+
type policyStatus struct {
45+
Policy string `json:"-"`
46+
Status string `json:"status"`
47+
Message string `json:"msg"`
48+
Checksum string `json:"-"`
49+
}
50+
51+
func initStatusServer(
52+
cfg StatusServerConfig,
53+
ds datastore.ReadOnlyDataStore,
54+
sh seiface.Handler,
55+
l logr.Logger,
56+
mPath string,
57+
) (*statusServer, error) {
4058
if cfg.Path == "" {
4159
cfg.Path = DefaultUnixSockAddr
4260
}
@@ -48,7 +66,7 @@ func initStatusServer(cfg StatusServerConfig, ds datastore.ReadOnlyDataStore, l
4866
return nil, fmt.Errorf("setting up socket: %w", err)
4967
}
5068

51-
ss := &statusServer{cfg, ds, l, lst, false}
69+
ss := &statusServer{cfg, ds, sh, l, mPath, lst, false}
5270
return ss, nil
5371
}
5472

@@ -111,13 +129,17 @@ func (ss *statusServer) initializeRoutes(r *mux.Router) {
111129
}
112130

113131
func (ss *statusServer) listPoliciesHandler(w http.ResponseWriter, r *http.Request) {
114-
modules, err := ss.ds.List()
132+
modules, err := ss.sh.List()
115133
if err != nil {
116134
http.Error(w, "Cannot list modules", http.StatusInternalServerError)
117135
return
118136
}
119137

120-
err = json.NewEncoder(w).Encode(modules)
138+
moduleList := []string{}
139+
for _, module := range modules {
140+
moduleList = append(moduleList, module.Name)
141+
}
142+
err = json.NewEncoder(w).Encode(moduleList)
121143
if err != nil {
122144
ss.l.Error(err, "error writing list response")
123145
http.Error(w, "Cannot list modules", http.StatusInternalServerError)
@@ -127,17 +149,50 @@ func (ss *statusServer) listPoliciesHandler(w http.ResponseWriter, r *http.Reque
127149
func (ss *statusServer) getPolicyStatusHandler(w http.ResponseWriter, r *http.Request) {
128150
vars := mux.Vars(r)
129151
policy := vars["policy"]
130-
status, err := ss.ds.Get(policy)
131-
if errors.Is(err, datastore.ErrPolicyNotFound) {
132-
http.Error(w, "couldn't find requested policy", http.StatusNotFound)
152+
module, err := ss.sh.GetPolicyModule(policy)
153+
if errors.Is(err, seiface.ErrPolicyNotFound) {
154+
http.Error(w, "policy is not installed", http.StatusNotFound)
133155
return
134156
} else if err != nil {
135157
ss.l.Error(err, "error getting status")
136158
http.Error(w, "Cannot get status", http.StatusInternalServerError)
137159
return
138160
}
139161

140-
err = json.NewEncoder(w).Encode(status)
162+
var policyFile string
163+
err = filepath.Walk(ss.mPath, func(path string, info os.FileInfo, err error) error {
164+
if info == nil {
165+
return nil
166+
}
167+
if !info.IsDir() && (filepath.Base(path) == policy+".cil" || filepath.Base(path) == policy+".pp") {
168+
policyFile = path
169+
return nil
170+
}
171+
return nil
172+
})
173+
if err != nil {
174+
ss.l.Error(err, "error getting status")
175+
http.Error(w, "Cannot get status", http.StatusInternalServerError)
176+
return
177+
}
178+
179+
cs, csErr := utils.Checksum(policyFile)
180+
181+
if csErr != nil {
182+
http.Error(w, "cannot find policy file "+policyFile, http.StatusNotFound)
183+
return
184+
}
185+
186+
if cs != module.Checksum {
187+
http.Error(w, "Installed policy "+module.Name+" does not much policy file "+policyFile, http.StatusNotFound)
188+
return
189+
}
190+
err = json.NewEncoder(w).Encode(policyStatus{
191+
Policy: policy,
192+
Status: "Installed",
193+
Message: "",
194+
Checksum: module.Checksum,
195+
})
141196
if err != nil {
142197
ss.l.Error(err, "error writing status response")
143198
http.Error(w, "Cannot get status", http.StatusInternalServerError)

pkg/semodule/interface/interface.go

+10-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@ import (
55
"fmt"
66
)
77

8+
type PolicyModule struct {
9+
Name string
10+
Ext string
11+
Checksum string
12+
}
13+
814
// errors
915
var (
1016
// ErrHandleCreate is an error when getting a handle to semanage
@@ -23,6 +29,8 @@ var (
2329
ErrCannotInstallModule = errors.New("cannot install module")
2430
// ErrCommit is an error when committing the changes to the SELinux policy
2531
ErrCommit = errors.New("cannot commit changes to policy")
32+
// ErrPolicyNotFound is an error policy is not found in SELinux policy modules store
33+
ErrPolicyNotFound = errors.New("policy not found in SELinux store")
2634
)
2735

2836
func NewErrCannotRemoveModule(mName string) error {
@@ -42,7 +50,8 @@ func NewErrCommit(origErrVal int, msg string) error {
4250
type Handler interface {
4351
SetAutoCommit(bool)
4452
Install(string) error
45-
List() ([]string, error)
53+
List() ([]PolicyModule, error)
54+
GetPolicyModule(string) (PolicyModule, error)
4655
Remove(string) error
4756
Commit() error
4857
Close() error

pkg/semodule/policycoreutils/policycoreutils.go

+25-5
Original file line numberDiff line numberDiff line change
@@ -46,22 +46,42 @@ func (smt *SEModulePcuHandler) Install(modulePath string) error {
4646
return nil
4747
}
4848

49-
func (smt *SEModulePcuHandler) List() ([]string, error) {
50-
out, err := runSemodule("-lfull")
49+
func (smt *SEModulePcuHandler) List() ([]seiface.PolicyModule, error) {
50+
out, err := runSemodule("-lfull", "--checksum")
5151
if err != nil {
5252
smt.logger.Error(err, "Listing policies")
5353
return nil, seiface.ErrList
5454
}
55-
modules := make([]string, 0)
55+
modules := make([]seiface.PolicyModule, 0)
5656
for _, line := range strings.Split(string(out), "\n") {
57-
module := strings.Split(line, " ")
57+
module := strings.Fields(line)
58+
if len(module) != 4 {
59+
continue
60+
}
5861
if module[0] == "350" {
59-
modules = append(modules, module[1])
62+
policyModule := seiface.PolicyModule{module[1], module[2], module[3]}
63+
modules = append(modules, policyModule)
6064
}
6165
}
6266
return modules, nil
6367
}
6468

69+
func (smt *SEModulePcuHandler) GetPolicyModule(moduleName string) (seiface.PolicyModule, error) {
70+
modules, err := smt.List()
71+
if err != nil {
72+
smt.logger.Error(err, "Getting module checksum")
73+
return seiface.PolicyModule{}, seiface.ErrList
74+
}
75+
for _, module := range modules {
76+
// 350 module cil sha256:dadb16b11a1d298e57cbd965f5fc060b7a9263b8d6b23af7763e68ac22fb5265
77+
if module.Name == moduleName {
78+
smt.logger.Info(moduleName, "checksum", module.Checksum)
79+
return module, nil
80+
}
81+
}
82+
return seiface.PolicyModule{}, seiface.ErrPolicyNotFound
83+
}
84+
6585
func (smt *SEModulePcuHandler) Remove(modToRemove string) error {
6686
out, err := runSemodule("-X", "350", "-r", modToRemove)
6787
if err != nil {

pkg/semodule/semanage/semanage.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,13 @@ void wrap_set_cb(semanage_handle_t *handle, void *arg);
1414
1515
*/
1616
import "C"
17+
1718
import (
1819
"bytes"
19-
"github.com/containers/selinuxd/pkg/semodule/interface"
2020
"sync"
2121
"unsafe"
2222

23+
"github.com/containers/selinuxd/pkg/semodule/interface"
2324
"github.com/go-logr/logr"
2425
)
2526

pkg/semodule/test/testhandler.go

+22-2
Original file line numberDiff line numberDiff line change
@@ -49,11 +49,31 @@ func (smt *SEModuleTestHandler) IsModuleInstalled(module string) bool {
4949
return false
5050
}
5151

52-
func (smt *SEModuleTestHandler) List() ([]string, error) {
52+
func (smt *SEModuleTestHandler) List() ([]seiface.PolicyModule, error) {
5353
// Return a copy
5454
smt.mu.Lock()
5555
defer smt.mu.Unlock()
56-
return append([]string(nil), smt.modules...), nil
56+
policyModules := []seiface.PolicyModule{}
57+
for _, module := range smt.modules {
58+
m, err := smt.GetPolicyModule(module)
59+
if err != nil {
60+
return policyModules, err
61+
}
62+
policyModules = append(policyModules, m)
63+
}
64+
return policyModules, nil
65+
}
66+
67+
func (smt *SEModuleTestHandler) GetPolicyModule(moduleName string) (seiface.PolicyModule, error) {
68+
if smt.IsModuleInstalled(moduleName) {
69+
return seiface.PolicyModule{
70+
Name: moduleName,
71+
Ext: "cil",
72+
Checksum: "sha256:e6c4d9c235af5d3ca50f660fc2283ecc330ebea73ec35241cc9cc47878dab68c",
73+
}, nil
74+
} else {
75+
return seiface.PolicyModule{}, seiface.ErrPolicyNotFound
76+
}
5777
}
5878

5979
func (smt *SEModuleTestHandler) Remove(modToRemove string) error {

pkg/utils/utils.go

+7-6
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
package utils
22

33
import (
4-
"crypto/sha512"
4+
"crypto/sha256"
5+
"encoding/hex"
56
"errors"
67
"fmt"
78
"io"
@@ -35,17 +36,17 @@ func PolicyNameFromPath(path string) (string, error) {
3536
}
3637

3738
// Checksum returns a checksum for a file on a given path
38-
func Checksum(path string) ([]byte, error) {
39+
func Checksum(path string) (string, error) {
3940
f, err := os.Open(path)
4041
if err != nil {
41-
return nil, fmt.Errorf("unable to calculate checksum: %w", err)
42+
return "", fmt.Errorf("unable to calculate checksum: %w", err)
4243
}
4344
defer f.Close()
4445

45-
h := sha512.New()
46+
h := sha256.New()
4647
if _, err := io.Copy(h, f); err != nil {
47-
return nil, fmt.Errorf("unable to calculate checksum: %w", err)
48+
return "", fmt.Errorf("unable to calculate checksum: %w", err)
4849
}
4950

50-
return h.Sum(nil), nil
51+
return fmt.Sprintf("sha256:%s", hex.EncodeToString(h.Sum(nil))), nil
5152
}

0 commit comments

Comments
 (0)