Whamcloud - gitweb
LU-16210 llite: replace selinux_is_enabled()
[fs/lustre-release.git] / lustre / utils / l_getsepol.c
1 /*
2  * GPL HEADER START
3  *
4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5  *
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.
9  *
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).
15  *
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
18  *
19  * GPL HEADER END
20  */
21
22 /*
23  * Copyright (c) 2016 DDN Storage
24  * Author: Sebastien Buisson sbuisson@ddn.com
25  */
26
27 /*
28  * lustre/utils/l_getsepol.c
29  * Userland helper to retrieve SELinux policy information.
30  */
31
32 #include <sys/types.h>
33 #include <stdbool.h>
34 #include <stdlib.h>
35 #include <stdio.h>
36 #include <libgen.h>
37 #include <sys/stat.h>
38 #include <unistd.h>
39 #include <string.h>
40 #include <errno.h>
41 #include <limits.h>
42 #include <syslog.h>
43 #include <stdarg.h>
44 #include <fcntl.h>
45 #include <stddef.h>
46 #include <ctype.h>
47 #include <dirent.h>
48 #include <getopt.h>
49
50 #include <openssl/evp.h>
51
52 #include <selinux/selinux.h>
53
54 #include <libcfs/util/param.h>
55 #include <linux/lustre/lustre_user.h>
56 #include <linux/lustre/lustre_idl.h>
57
58
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;
63
64 static void errlog(const char *fmt, ...)
65 {
66         va_list args;
67
68         openlog(progname, LOG_PID, LOG_AUTHPRIV);
69
70         va_start(args, fmt);
71         vsyslog(LOG_NOTICE, fmt, args);
72         if (isatty(STDIN_FILENO))
73                 vfprintf(stderr, fmt, args);
74         va_end(args);
75
76         closelog();
77 }
78
79 /* Retrieve name of policy loaded, and version */
80 static int sepol_get_policy_info(char **policyname)
81 {
82         char *pol_path;
83
84         /* Name of loaded policy can be retrieved from policy root path */
85         pol_path = strdup(selinux_policy_root());
86
87         if (!pol_path) {
88                 *policyname = NULL;
89                 errlog("can't get policy name: %s\n", strerror(errno));
90                 return -errno;
91         }
92
93         *policyname = strdup(basename(pol_path));
94         free(pol_path);
95
96         return 0;
97 }
98
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)
102 {
103         int fd;
104         char buffer[1024];
105         ssize_t count = 1024;
106         EVP_MD_CTX *mdctx;
107         const EVP_MD *md = EVP_sha256(); /* use SHA-256 */
108         int rc;
109
110         /* Open policy file */
111         fd = open(pol_bin_path, O_RDONLY);
112         if (fd < 0) {
113                 errlog("can't open SELinux policy file %s: %s\n", pol_bin_path,
114                        strerror(errno));
115                 rc = -ENOENT;
116                 goto out;
117         }
118
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);
124                 if (count < 0) {
125                         errlog("can't read SELinux policy file %s\n",
126                                pol_bin_path);
127                         rc = -errno;
128                         close(fd);
129                         goto out;
130                 }
131                 EVP_DigestUpdate(mdctx, buffer, count);
132         }
133
134         /* Close policy file */
135         rc = close(fd);
136         if (rc < 0) {
137                 rc = -errno;
138                 goto out;
139         }
140
141         *mdsize = EVP_MD_size(md);
142         *mdval = malloc(*mdsize);
143         if (*mdval == NULL) {
144                 rc = -ENOMEM;
145                 goto out;
146         }
147
148         EVP_DigestFinal_ex(mdctx, *mdval, NULL);
149         EVP_MD_CTX_destroy(mdctx);
150
151 out:
152         return rc;
153 }
154
155 int get_opts(int argc, char *const argv[])
156 {
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},
166                 { .name = NULL } };
167         char *short_opts = "o:n:t:m:";
168         int opt;
169         int longidx;
170         char *sel_mtime = NULL, *sel_mode = NULL;
171         char *res;
172
173         optind = 0;
174         while ((opt = getopt_long(argc, argv, short_opts, long_opts,
175                                   &longidx)) != EOF) {
176                 switch (opt) {
177                 case 'o':
178                         obd_type = optarg;
179                         break;
180                 case 'n':
181                         obd_name = optarg;
182                         break;
183                 case 't':
184                         sel_mtime = optarg;
185                         break;
186                 case 'm':
187                         sel_mode = optarg;
188                         break;
189                 default:
190                         if (opt != '?')
191                                 fprintf(stderr, "Unknown option '%c'\n", opt);
192                         return -EINVAL;
193                 }
194         }
195
196         if (optind != argc) {
197                 errlog("incorrect arguments\n");
198                 return -EINVAL;
199         }
200
201         if (!obd_type || !obd_name)
202                 /* called without arg (presumably from command line):
203                  * ignore everything */
204                 return 0;
205
206         if (sel_mtime) {
207                 ref_pol_mtime = (time_t)strtoul(sel_mtime, &res, 0);
208                 if (*res != '\0') {
209                         /* not a valid number */
210                         errlog("invalid sel_mtime\n");
211                         return -EINVAL;
212                 }
213         }
214
215         if (sel_mode) {
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");
220                         return -EINVAL;
221                 }
222         }
223
224         return 0;
225 }
226
227 #define sepol_downcall(type_t, magic) ({ \
228         glob_t path; \
229         int fd, size; \
230         struct type_t *data; \
231         int idx; \
232         char *p; \
233         \
234         size = offsetof(struct type_t, \
235                         sdd_sepol[LUSTRE_NODEMAP_SEPOL_LENGTH + 1]); \
236         data = malloc(size); \
237         if (!data) { \
238                 errlog("malloc sepol downcall data(%d) failed!\n", size); \
239                 rc = -ENOMEM; \
240                 goto out_mdval; \
241         } \
242         memset(data, 0, size); \
243         \
244         /* Put all info together and generate string \
245          * to represent SELinux policy information \
246          */ \
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) { \
250                 rc = -EMSGSIZE; \
251                 goto out_data_ ## type_t ; \
252         } \
253         \
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])); \
259                 p += 2; \
260                 size -= 2; \
261                 if (size < 0 || rc >= size) { \
262                         rc = -EMSGSIZE; \
263                         goto out_data_ ## type_t ; \
264                 } \
265         } \
266         data->sdd_sepol_len = p - data->sdd_sepol; \
267         \
268         size = offsetof(struct type_t, \
269                         sdd_sepol[data->sdd_sepol_len]); \
270         \
271         if (!obd_type || !obd_name) { \
272                 /* called without arg (presumably from command line): \
273                  * print SELinux status and exit \
274                  */ \
275                 printf("SELinux status info: %.*s\n", \
276                        data->sdd_sepol_len, data->sdd_sepol); \
277                 return 0; \
278         } \
279         \
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, \
284                                  obd_name); \
285         if (rc != 0) { \
286                 errlog("can't get param '%s/%s/srpc_sepol': %s\n", \
287                        obd_type, obd_name, strerror(errno)); \
288                 rc = -errno; \
289                 goto out_data_ ## type_t ; \
290         } \
291         \
292         fd = open(path.gl_pathv[0], O_WRONLY); \
293         if (fd < 0) { \
294                 errlog("can't open file '%s':%s\n", path.gl_pathv[0], \
295                         strerror(errno)); \
296                 rc = -errno; \
297                 goto out_params_ ## type_t ; \
298         } \
299         \
300         rc = write(fd, data, size); \
301         close(fd); \
302         if (rc != size) { \
303                 errlog("partial write ret %d: %s\n", rc, strerror(errno)); \
304                 rc = -errno; \
305         } else { \
306                 rc = 0; \
307         } \
308         \
309         out_params_ ## type_t :     \
310         cfs_free_param_data(&path); \
311         out_data_ ## type_t :       \
312         free(data); \
313 })
314
315 /**
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:
322  * - obd type
323  * - obd name
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.
328  */
329 int main(int argc, char **argv)
330 {
331         int policyver = 0;
332         char pol_bin_path[PATH_MAX + 1];
333         struct stat st;
334         time_t policymtime = 0;
335         int enforce;
336         int is_selinux;
337         char *policy_type = NULL;
338         unsigned char *mdval = NULL;
339         unsigned int mdsize = 0;
340         int rc;
341
342         progname = basename(argv[0]);
343
344         rc = get_opts(argc, argv);
345         if (rc < 0)
346                 goto out;
347
348         is_selinux = is_selinux_enabled();
349         if (is_selinux < 0) {
350                 errlog("is_selinux_enabled() failed\n");
351                 rc = -errno;
352                 goto out;
353         }
354
355         if (!is_selinux) {
356                 errlog("SELinux is disabled, ptlrpc 'send_sepol' value should be set to 0\n");
357                 rc = -ENODEV;
358                 goto out;
359         }
360
361         /* Max version of loaded policy */
362         policyver = security_policyvers();
363         if (policyver < 0) {
364                 errlog("unknown policy version: %s\n", strerror(errno));
365                 rc = -errno;
366                 goto out;
367         }
368
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);
373
374                 /* Stat binary policy file */
375                 if (stat(pol_bin_path, &st)) {
376                         if (policyver > 0) {
377                                 policyver--;
378                         } else {
379                                 errlog("can't stat %s.*: %s\n",
380                                        selinux_binary_policy_path(),
381                                        strerror(errno));
382                                 rc = -errno;
383                                 goto out;
384                         }
385                 } else {
386                         policymtime = st.st_mtime;
387                 }
388         }
389
390         /* Determine if SELinux is in permissive or enforcing mode */
391         enforce = security_getenforce();
392         if (enforce < 0) {
393                 errlog("can't getenforce: %s\n", strerror(errno));
394                 rc = -errno;
395                 goto out;
396         }
397
398         if (ref_pol_mtime == policymtime && ref_selinux_mode == enforce) {
399                 /* Policy has not changed: return immediately */
400                 rc = 0;
401                 goto out;
402         }
403
404         /* Now we need to calculate SELinux status information */
405         /* Get policy name */
406         rc = sepol_get_policy_info(&policy_type);
407         if (rc < 0)
408                 goto out;
409
410         /* Read binary SELinux policy, and compute hash */
411         rc = sepol_get_policy_data(pol_bin_path, &mdval, &mdsize);
412         if (rc < 0)
413                 goto out_poltyp;
414
415         sepol_downcall(sepol_downcall_data, SEPOL_DOWNCALL_MAGIC);
416 #if LUSTRE_VERSION_CODE < OBD_OCD_VERSION(2, 16, 53, 0)
417         if (rc == -EINVAL)
418                 /* try with old magic */
419                 sepol_downcall(sepol_downcall_data_old,
420                                SEPOL_DOWNCALL_MAGIC_OLD);
421 #endif
422
423 out_mdval:
424         free(mdval);
425 out_poltyp:
426         free(policy_type);
427 out:
428         if (isatty(STDIN_FILENO))
429                 /* we are called from the command line */
430                 return rc < 0 ? -rc : rc;
431         else
432                 return rc;
433 }