Skip to content

Commit 0335b27

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 The behavior of status_server has been changed: /policies/ return list of policy modules consting of { Name, Ext, Checksum }, e.g [{"Name":"ssh","Ext":"cil","Checksum":"sha256:004955cac8f0714d0c99feead64cd9b904cd798850d96a1881c3d085f934beaf"}] /policies/{policy} - returns { Name, Ext, Checksum } if the policy is installed - returns NotFound if policy is not installed, message: "policy is not installed" - returns NotFound if file in /etc/selinud.d doesn't match the installed module, e.g. when user ran `semodule -X 350 module.cil`, message: "Installed policy ssh does not much policy file /etc/selinux.d/ssh.cil" Signed-off-by: Petr Lautrbach <[email protected]>
1 parent 730e7f7 commit 0335b27

File tree

8 files changed

+91
-54
lines changed

8 files changed

+91
-54
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

+29-7
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import (
1111
"time"
1212

1313
"github.com/containers/selinuxd/pkg/datastore"
14+
seiface "github.com/containers/selinuxd/pkg/semodule/interface"
15+
"github.com/containers/selinuxd/pkg/utils"
1416
"github.com/go-logr/logr"
1517
"github.com/gorilla/mux"
1618
)
@@ -31,12 +33,20 @@ type StatusServerConfig struct {
3133
type statusServer struct {
3234
cfg StatusServerConfig
3335
ds datastore.ReadOnlyDataStore
36+
sh seiface.Handler
3437
l logr.Logger
38+
mPath string
3539
lst net.Listener
3640
ready bool
3741
}
3842

39-
func initStatusServer(cfg StatusServerConfig, ds datastore.ReadOnlyDataStore, l logr.Logger) (*statusServer, error) {
43+
func initStatusServer(
44+
cfg StatusServerConfig,
45+
ds datastore.ReadOnlyDataStore,
46+
sh seiface.Handler,
47+
l logr.Logger,
48+
mPath string,
49+
) (*statusServer, error) {
4050
if cfg.Path == "" {
4151
cfg.Path = DefaultUnixSockAddr
4252
}
@@ -48,7 +58,7 @@ func initStatusServer(cfg StatusServerConfig, ds datastore.ReadOnlyDataStore, l
4858
return nil, fmt.Errorf("setting up socket: %w", err)
4959
}
5060

51-
ss := &statusServer{cfg, ds, l, lst, false}
61+
ss := &statusServer{cfg, ds, sh, l, mPath, lst, false}
5262
return ss, nil
5363
}
5464

@@ -111,7 +121,7 @@ func (ss *statusServer) initializeRoutes(r *mux.Router) {
111121
}
112122

113123
func (ss *statusServer) listPoliciesHandler(w http.ResponseWriter, r *http.Request) {
114-
modules, err := ss.ds.List()
124+
modules, err := ss.sh.List()
115125
if err != nil {
116126
http.Error(w, "Cannot list modules", http.StatusInternalServerError)
117127
return
@@ -127,17 +137,29 @@ func (ss *statusServer) listPoliciesHandler(w http.ResponseWriter, r *http.Reque
127137
func (ss *statusServer) getPolicyStatusHandler(w http.ResponseWriter, r *http.Request) {
128138
vars := mux.Vars(r)
129139
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)
140+
module, err := ss.sh.GetPolicyModule(policy)
141+
if errors.Is(err, seiface.ErrPolicyNotFound) {
142+
http.Error(w, "policy is not installed", http.StatusNotFound)
133143
return
134144
} else if err != nil {
135145
ss.l.Error(err, "error getting status")
136146
http.Error(w, "Cannot get status", http.StatusInternalServerError)
137147
return
138148
}
139149

140-
err = json.NewEncoder(w).Encode(status)
150+
policyFile := ss.mPath + "/" + module.Name + "." + module.Ext
151+
cs, csErr := utils.Checksum(policyFile)
152+
153+
if csErr != nil {
154+
http.Error(w, "cannot find policy file "+policyFile, http.StatusNotFound)
155+
return
156+
}
157+
158+
if cs != module.Checksum {
159+
http.Error(w, "Installed policy "+module.Name+" does not much policy file "+policyFile, http.StatusNotFound)
160+
return
161+
}
162+
err = json.NewEncoder(w).Encode(module)
141163
if err != nil {
142164
ss.l.Error(err, "error writing status response")
143165
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

+14-2
Original file line numberDiff line numberDiff line change
@@ -49,11 +49,23 @@ 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+
return seiface.PolicyModule{Name: "policy", Ext: "cil", Checksum: "sha256"}, nil
5769
}
5870

5971
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)