summaryrefslogtreecommitdiff
path: root/plugins/selinux.c
blob: 7c1a0da8d713094ef29260b5351db8ec56a610f5 (plain)
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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
#include "system.h"

#include <selinux/selinux.h>
#include <selinux/context.h>
#include <selinux/label.h>
#include <selinux/avc.h>
#include <rpm/rpmlog.h>
#include <rpm/rpmts.h>
#include "lib/rpmplugin.h"

#include "debug.h"

static struct selabel_handle * sehandle = NULL;

static inline rpmlogLvl loglvl(int iserror)
{
    return iserror ? RPMLOG_ERR : RPMLOG_DEBUG;
}

static void sehandle_fini(int close_status)
{
    if (sehandle) {
	selabel_close(sehandle);
	sehandle = NULL;
    }
    if (close_status) {
	selinux_status_close();
    }
}

static rpmRC sehandle_init(int open_status)
{
    const char * path = selinux_file_context_path();
    struct selinux_opt opts[] = {
	{ .type = SELABEL_OPT_PATH, .value = path }
    };
    
    if (path == NULL)
	return RPMRC_FAIL;

    if (open_status) {
	selinux_status_close();
	if (selinux_status_open(0) < 0) {
	    return RPMRC_FAIL;
	}
    } else if (!selinux_status_updated() && sehandle) {
	return RPMRC_OK;
    }

    if (sehandle)
	sehandle_fini(0);

    sehandle = selabel_open(SELABEL_CTX_FILE, opts, 1);

    rpmlog(loglvl(sehandle == NULL), "selabel_open: (%s) %s\n",
	   path, (sehandle == NULL ? strerror(errno) : ""));

    return (sehandle != NULL) ? RPMRC_OK : RPMRC_FAIL;
}

static rpmRC selinux_tsm_pre(rpmPlugin plugin, rpmts ts)
{
    rpmRC rc = RPMRC_OK;

    /* If SELinux isn't enabled on the system, dont mess with it */
    if (!is_selinux_enabled()) {
	rpmtsSetFlags(ts, (rpmtsFlags(ts) | RPMTRANS_FLAG_NOCONTEXTS));
    }

    /* If not enabled or a test-transaction, dont bother with labels */
    if (!(rpmtsFlags(ts) & (RPMTRANS_FLAG_NOCONTEXTS|RPMTRANS_FLAG_TEST))) {
	rc = sehandle_init(1);
    }

    return rc;
}

static rpmRC selinux_tsm_post(rpmPlugin plugin, rpmts ts, int rc)
{
    if (sehandle) {
	sehandle_fini(1);
    }
    return RPMRC_OK;
}

static rpmRC selinux_psm_pre(rpmPlugin plugin, rpmte te)
{
    rpmRC rc = RPMRC_OK;

    if (sehandle) {
	/* reload the labels if policy changed underneath */
	rc = sehandle_init(0);
    }
    return rc;
}

#ifndef HAVE_SETEXECFILECON
static int setexecfilecon(const char *path, const char *fallback_type)
{
    int rc = -1;
    char *mycon = NULL, fcon = NULL, newcon = NULL;
    context_t con = NULL;

    /* Figure the context to for next exec() */
    if (getcon(&mycon) < 0)
	goto exit;
    if (getfilecon(path, &fcon) < 0)
	goto exit;
    if (security_compute_create(mycon, fcon,
			string_to_security_class("process"), &newcon) < 0)
	goto exit;

    if (rstreq(mycon, newcon)) {
	con = context_new(mycon);
	if (!con)
	    goto exit;
	if (context_type_set(con, fallback_type))
	    goto exit;
	freecon(newcon);
	newcon = xstrdup(context_str(con));
    }

    rc = setexeccon(newcon);

exit:
    context_free(con);
    freecon(newcon);
    freecon(fcon);
    freecon(mycon);
    return rc;
}
#endif

static rpmRC selinux_scriptlet_fork_post(rpmPlugin plugin,
						 const char *path, int type)
{
    /* No default transition, use rpm_script_t for now. */
    const char *script_type  = "rpm_script_t";
    rpmRC rc = RPMRC_FAIL;

    if (sehandle == NULL)
	return RPMRC_OK;

    if (setexecfilecon(path, script_type) == 0)
	rc = RPMRC_OK;

    /* If selinux is not enforcing, we don't care either */
    if (rc && security_getenforce() < 1)
	rc = RPMRC_OK;

    rpmlog(loglvl(rc), "setexecfilecon: (%s, %s) %s\n",
	       path, script_type, rc ? strerror(errno) : "");

    return rc;
}

static rpmRC selinux_fsm_file_prepare(rpmPlugin plugin, rpmfi fi,
					const char *path, const char *dest,
				        mode_t file_mode, rpmFsmOp op)
{
    rpmRC rc = RPMRC_FAIL; /* assume failure */
    rpmFileAction action = XFO_ACTION(op);

    if (sehandle && !XFA_SKIPPING(action)) {
	char *scon = NULL;
	if (selabel_lookup_raw(sehandle, &scon, dest, file_mode) == 0) {
	    int conrc = lsetfilecon(path, scon);

	    if (conrc == 0 || (conrc < 0 && errno == EOPNOTSUPP))
		rc = RPMRC_OK;

	    rpmlog(loglvl(rc != RPMRC_OK), "lsetfilecon: (%s, %s) %s\n",
		       path, scon, (conrc < 0 ? strerror(errno) : ""));

	    freecon(scon);
	} else {
	    /* No context for dest is not our headache */
	    if (errno == ENOENT)
		rc = RPMRC_OK;
	}
    } else {
	rc = RPMRC_OK;
    }

    return rc;
}

struct rpmPluginHooks_s selinux_hooks = {
    .tsm_pre = selinux_tsm_pre,
    .tsm_post = selinux_tsm_post,
    .psm_pre = selinux_psm_pre,
    .scriptlet_fork_post = selinux_scriptlet_fork_post,
    .fsm_file_prepare = selinux_fsm_file_prepare,
};