4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 only,
8 * as published by the Free Software Foundation.
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License version 2 for more details (a copy is included
14 * in the LICENSE file that accompanied this code).
16 * You should have received a copy of the GNU General Public License
17 * version 2 along with this program; If not, see http://www.gnu.org/licenses
23 * Copyright (c) 2016 DDN Storage
24 * Author: Sebastien Buisson sbuisson@ddn.com
28 * lustre/utils/l_getsepol.c
29 * Userland helper to retrieve SELinux policy information.
32 #include <sys/types.h>
50 #include <openssl/evp.h>
52 #include <selinux/selinux.h>
54 #include <libcfs/util/param.h>
55 #include <linux/lustre/lustre_user.h>
56 #include <linux/lustre/lustre_idl.h>
59 static char *progname;
60 static char *obd_type = NULL, *obd_name = NULL;
61 static time_t ref_pol_mtime = 0;
62 static char ref_selinux_mode = -1;
64 static void errlog(const char *fmt, ...)
68 openlog(progname, LOG_PID, LOG_AUTHPRIV);
71 vsyslog(LOG_NOTICE, fmt, args);
72 if (isatty(STDIN_FILENO))
73 vfprintf(stderr, fmt, args);
79 /* Retrieve name of policy loaded, and version */
80 static int sepol_get_policy_info(char **policyname)
84 /* Name of loaded policy can be retrieved from policy root path */
85 pol_path = strdup(selinux_policy_root());
89 errlog("can't get policy name: %s\n", strerror(errno));
93 *policyname = strdup(basename(pol_path));
99 /* Read binary SELinux policy, and compute hash */
100 static int sepol_get_policy_data(const char *pol_bin_path,
101 unsigned char **mdval, unsigned int *mdsize)
105 ssize_t count = 1024;
107 const EVP_MD *md = EVP_sha256(); /* use SHA-256 */
110 /* Open policy file */
111 fd = open(pol_bin_path, O_RDONLY);
113 errlog("can't open SELinux policy file %s: %s\n", pol_bin_path,
119 /* Read policy file */
120 mdctx = EVP_MD_CTX_create();
121 EVP_DigestInit_ex(mdctx, md, NULL);
122 while (count == 1024) {
123 count = read(fd, buffer, count);
125 errlog("can't read SELinux policy file %s\n",
131 EVP_DigestUpdate(mdctx, buffer, count);
134 /* Close policy file */
141 *mdsize = EVP_MD_size(md);
142 *mdval = malloc(*mdsize);
143 if (*mdval == NULL) {
148 EVP_DigestFinal_ex(mdctx, *mdval, NULL);
149 EVP_MD_CTX_destroy(mdctx);
155 int get_opts(int argc, char *const argv[])
157 static struct option long_opts[] = {
158 { .val = 'o', .name = "obd_type",
159 .has_arg = required_argument},
160 { .val = 'n', .name = "obd_name",
161 .has_arg = required_argument},
162 { .val = 't', .name = "sel_mtime",
163 .has_arg = required_argument},
164 { .val = 'm', .name = "sel_mode",
165 .has_arg = required_argument},
167 char *short_opts = "o:n:t:m:";
170 char *sel_mtime = NULL, *sel_mode = NULL;
174 while ((opt = getopt_long(argc, argv, short_opts, long_opts,
191 fprintf(stderr, "Unknown option '%c'\n", opt);
196 if (optind != argc) {
197 errlog("incorrect arguments\n");
201 if (!obd_type || !obd_name)
202 /* called without arg (presumably from command line):
203 * ignore everything */
207 ref_pol_mtime = (time_t)strtoul(sel_mtime, &res, 0);
209 /* not a valid number */
210 errlog("invalid sel_mtime\n");
216 ref_selinux_mode = sel_mode[0] - '0';
217 if (ref_selinux_mode != 0 && ref_selinux_mode != 1) {
218 /* not a valid enforcing mode */
219 errlog("invalid sel_mode\n");
227 #define sepol_downcall(type_t, magic) ({ \
230 struct type_t *data; \
234 size = offsetof(struct type_t, \
235 sdd_sepol[LUSTRE_NODEMAP_SEPOL_LENGTH + 1]); \
236 data = malloc(size); \
238 errlog("malloc sepol downcall data(%d) failed!\n", size); \
242 memset(data, 0, size); \
244 /* Put all info together and generate string \
245 * to represent SELinux policy information \
247 rc = snprintf(data->sdd_sepol, LUSTRE_NODEMAP_SEPOL_LENGTH + 1, \
248 "%.1d:%s:%u:", enforce, policy_type, policyver); \
249 if (rc >= LUSTRE_NODEMAP_SEPOL_LENGTH + 1) { \
251 goto out_data_ ## type_t ; \
254 p = data->sdd_sepol + strlen(data->sdd_sepol); \
255 size = LUSTRE_NODEMAP_SEPOL_LENGTH + 1 - strlen(data->sdd_sepol); \
256 for (idx = 0; idx < mdsize; idx++) { \
257 rc = snprintf(p, size, "%02x", \
258 (unsigned char)(mdval[idx])); \
261 if (size < 0 || rc >= size) { \
263 goto out_data_ ## type_t ; \
266 data->sdd_sepol_len = p - data->sdd_sepol; \
268 size = offsetof(struct type_t, \
269 sdd_sepol[data->sdd_sepol_len]); \
271 if (!obd_type || !obd_name) { \
272 /* called without arg (presumably from command line): \
273 * print SELinux status and exit \
275 printf("SELinux status info: %.*s\n", \
276 data->sdd_sepol_len, data->sdd_sepol); \
280 data->sdd_magic = magic; \
281 data->sdd_sepol_mtime = policymtime; \
282 /* Send SELinux policy info to kernelspace */ \
283 rc = cfs_get_param_paths(&path, "%s/%s/srpc_sepol", obd_type, \
286 errlog("can't get param '%s/%s/srpc_sepol': %s\n", \
287 obd_type, obd_name, strerror(errno)); \
289 goto out_data_ ## type_t ; \
292 fd = open(path.gl_pathv[0], O_WRONLY); \
294 errlog("can't open file '%s':%s\n", path.gl_pathv[0], \
297 goto out_params_ ## type_t ; \
300 rc = write(fd, data, size); \
303 errlog("partial write ret %d: %s\n", rc, strerror(errno)); \
309 out_params_ ## type_t : \
310 cfs_free_param_data(&path); \
311 out_data_ ## type_t : \
316 * Calculate SELinux status information.
317 * String that represents SELinux status info has the following format:
318 * <mode>:<policy name>:<policy version>:<policy hash>
319 * <mode> is a digit equal to 0 for SELinux Permissive mode,
320 * and 1 for Enforcing mode.
321 * When called from kernel space, it requires 4 args:
324 * - SELinux policy mtime
325 * - SELinux enforcing mode
326 * When called from command line (in this case without proper args), it prints
327 * SELinux status info to stdout.
329 int main(int argc, char **argv)
332 char pol_bin_path[PATH_MAX + 1];
334 time_t policymtime = 0;
337 char *policy_type = NULL;
338 unsigned char *mdval = NULL;
339 unsigned int mdsize = 0;
342 progname = basename(argv[0]);
344 rc = get_opts(argc, argv);
348 is_selinux = is_selinux_enabled();
349 if (is_selinux < 0) {
350 errlog("is_selinux_enabled() failed\n");
356 errlog("SELinux is disabled, ptlrpc 'send_sepol' value should be set to 0\n");
361 /* Max version of loaded policy */
362 policyver = security_policyvers();
364 errlog("unknown policy version: %s\n", strerror(errno));
369 while (policymtime == 0) {
370 /* Path of binary policy file */
371 snprintf(pol_bin_path, sizeof(pol_bin_path), "%s.%d",
372 selinux_binary_policy_path(), policyver);
374 /* Stat binary policy file */
375 if (stat(pol_bin_path, &st)) {
379 errlog("can't stat %s.*: %s\n",
380 selinux_binary_policy_path(),
386 policymtime = st.st_mtime;
390 /* Determine if SELinux is in permissive or enforcing mode */
391 enforce = security_getenforce();
393 errlog("can't getenforce: %s\n", strerror(errno));
398 if (ref_pol_mtime == policymtime && ref_selinux_mode == enforce) {
399 /* Policy has not changed: return immediately */
404 /* Now we need to calculate SELinux status information */
405 /* Get policy name */
406 rc = sepol_get_policy_info(&policy_type);
410 /* Read binary SELinux policy, and compute hash */
411 rc = sepol_get_policy_data(pol_bin_path, &mdval, &mdsize);
415 sepol_downcall(sepol_downcall_data, SEPOL_DOWNCALL_MAGIC);
416 #if LUSTRE_VERSION_CODE < OBD_OCD_VERSION(2, 16, 53, 0)
418 /* try with old magic */
419 sepol_downcall(sepol_downcall_data_old,
420 SEPOL_DOWNCALL_MAGIC_OLD);
428 if (isatty(STDIN_FILENO))
429 /* we are called from the command line */
430 return rc < 0 ? -rc : rc;