Whamcloud - gitweb
LU-11546 utils: enable large_dir for ldiskfs
[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_PERROR | LOG_PID, LOG_AUTHPRIV);
69
70         va_start(args, fmt);
71         vsyslog(LOG_NOTICE, fmt, args);
72         va_end(args);
73
74         closelog();
75 }
76
77 /* Retrieve name of policy loaded, and version */
78 static int sepol_get_policy_info(char **policyname)
79 {
80         char *pol_path;
81
82         /* Name of loaded policy can be retrieved from policy root path */
83         pol_path = strdup(selinux_policy_root());
84
85         if (!pol_path) {
86                 *policyname = NULL;
87                 errlog("can't get policy name: %s\n", strerror(errno));
88                 return -errno;
89         }
90
91         *policyname = strdup(basename(pol_path));
92         free(pol_path);
93
94         return 0;
95 }
96
97 /* Read binary SELinux policy, and compute hash */
98 static int sepol_get_policy_data(const char *pol_bin_path,
99                                  unsigned char **mdval, unsigned int *mdsize)
100 {
101         int fd;
102         char buffer[1024];
103         ssize_t count = 1024;
104         EVP_MD_CTX *mdctx;
105         const EVP_MD *md = EVP_sha256(); /* use SHA-256 */
106         int rc;
107
108         /* Open policy file */
109         fd = open(pol_bin_path, O_RDONLY);
110         if (fd < 0) {
111                 errlog("can't open SELinux policy file %s: %s\n", pol_bin_path,
112                        strerror(errno));
113                 rc = -ENOENT;
114                 goto out;
115         }
116
117         /* Read policy file */
118         mdctx = EVP_MD_CTX_create();
119         EVP_DigestInit_ex(mdctx, md, NULL);
120         while (count == 1024) {
121                 count = read(fd, buffer, count);
122                 if (count < 0) {
123                         errlog("can't read SELinux policy file %s\n",
124                                pol_bin_path);
125                         rc = -errno;
126                         close(fd);
127                         goto out;
128                 }
129                 EVP_DigestUpdate(mdctx, buffer, count);
130         }
131
132         /* Close policy file */
133         rc = close(fd);
134         if (rc < 0) {
135                 rc = -errno;
136                 goto out;
137         }
138
139         *mdsize = EVP_MD_size(md);
140         *mdval = malloc(*mdsize);
141         if (*mdval == NULL) {
142                 rc = -ENOMEM;
143                 goto out;
144         }
145
146         EVP_DigestFinal_ex(mdctx, *mdval, NULL);
147         EVP_MD_CTX_destroy(mdctx);
148
149 out:
150         return rc;
151 }
152
153 int get_opts(int argc, char *const argv[])
154 {
155         static struct option long_opts[] = {
156                 { .val = 'o', .name =  "obd_type",
157                   .has_arg = required_argument},
158                 { .val = 'n', .name =  "obd_name",
159                   .has_arg = required_argument},
160                 { .val = 't', .name =  "sel_mtime",
161                   .has_arg = required_argument},
162                 { .val = 'm', .name =  "sel_mode",
163                   .has_arg = required_argument},
164                 { .name = NULL } };
165         char *short_opts = "o:n:t:m:";
166         int opt;
167         int longidx;
168         char *sel_mtime = NULL, *sel_mode = NULL;
169         char *res;
170
171         optind = 0;
172         while ((opt = getopt_long(argc, argv, short_opts, long_opts,
173                                   &longidx)) != EOF) {
174                 switch (opt) {
175                 case 'o':
176                         obd_type = optarg;
177                         break;
178                 case 'n':
179                         obd_name = optarg;
180                         break;
181                 case 't':
182                         sel_mtime = optarg;
183                         break;
184                 case 'm':
185                         sel_mode = optarg;
186                         break;
187                 default:
188                         if (opt != '?')
189                                 fprintf(stderr, "Unknown option '%c'\n", opt);
190                         return -EINVAL;
191                 }
192         }
193
194         if (optind != argc) {
195                 errlog("incorrect arguments\n");
196                 return -EINVAL;
197         }
198
199         if (!obd_type || !obd_name)
200                 /* called without arg (presumably from command line):
201                  * ignore everything */
202                 return 0;
203
204         if (sel_mtime) {
205                 ref_pol_mtime = (time_t)strtoul(sel_mtime, &res, 0);
206                 if (*res != '\0') {
207                         /* not a valid number */
208                         errlog("invalid sel_mtime");
209                         return -EINVAL;
210                 }
211         }
212
213         if (sel_mode) {
214                 ref_selinux_mode = sel_mode[0] - '0';
215                 if (ref_selinux_mode != 0 && ref_selinux_mode != 1) {
216                         /* not a valid enforcing mode */
217                         errlog("invalid sel_mode");
218                         return -EINVAL;
219                 }
220         }
221
222         return 0;
223 }
224
225 /**
226  * Calculate SELinux status information.
227  * String that represents SELinux status info has the following format:
228  * <mode>:<policy name>:<policy version>:<policy hash>
229  * <mode> is a digit equal to 0 for SELinux Permissive mode,
230  * and 1 for Enforcing mode.
231  * When called from kernel space, it requires 4 args:
232  * - obd type
233  * - obd name
234  * - SELinux policy mtime
235  * - SELinux enforcing mode
236  * When called from command line (in this case without proper args), it prints
237  * SELinux status info to stdout.
238  */
239 int main(int argc, char **argv)
240 {
241         int policyver = 0;
242         char pol_bin_path[PATH_MAX + 1];
243         struct stat st;
244         time_t policymtime;
245         int enforce;
246         struct sepol_downcall_data *data = NULL;
247         glob_t path;
248         int fd, size;
249         char *policy_type = NULL;
250         unsigned char *mdval = NULL;
251         unsigned int mdsize = 0;
252         char *p;
253         int idx;
254         int rc;
255
256         progname = basename(argv[0]);
257
258         rc = get_opts(argc, argv);
259         if (rc < 0)
260                 goto out;
261
262         /* Version of loaded policy */
263         policyver = security_policyvers();
264         if (policyver < 0) {
265                 errlog("unknown policy version: %s\n", strerror(errno));
266                 rc = -errno;
267                 goto out;
268         }
269
270         /* Path of binary policy file */
271         snprintf(pol_bin_path, sizeof(pol_bin_path), "%s.%d",
272                  selinux_binary_policy_path(), policyver);
273
274         /* Stat binary policy file */
275         if (stat(pol_bin_path, &st)) {
276                 errlog("can't stat %s: %s\n", pol_bin_path, strerror(errno));
277                 rc = -errno;
278                 goto out;
279         }
280         policymtime = st.st_mtime;
281
282         /* Determine if SELinux is in permissive or enforcing mode */
283         enforce = security_getenforce();
284         if (enforce < 0) {
285                 errlog("can't getenforce: %s\n", strerror(errno));
286                 rc = -errno;
287                 goto out;
288         }
289
290         if (ref_pol_mtime == policymtime && ref_selinux_mode == enforce) {
291                 /* Policy has not changed: return immediately */
292                 rc = 0;
293                 goto out;
294         }
295
296         /* Now we need to calculate SELinux status information */
297         size = offsetof(struct sepol_downcall_data,
298                         sdd_sepol[LUSTRE_NODEMAP_SEPOL_LENGTH + 1]);
299         data = malloc(size);
300         if (!data) {
301                 errlog("malloc sepol downcall data(%d) failed!\n", size);
302                 rc = -ENOMEM;
303                 goto out;
304         }
305         memset(data, 0, size);
306
307         /* Get policy name */
308         rc = sepol_get_policy_info(&policy_type);
309         if (rc < 0)
310                 goto out_data;
311
312         /* Read binary SELinux policy, and compute hash */
313         rc = sepol_get_policy_data(pol_bin_path, &mdval, &mdsize);
314         if (rc < 0)
315                 goto out_poltyp;
316
317         /* Put all info together and generate string
318          * to represent SELinux policy information
319          */
320         rc = snprintf(data->sdd_sepol, LUSTRE_NODEMAP_SEPOL_LENGTH + 1,
321                       "%.1d:%s:%u:", enforce, policy_type, policyver);
322         if (rc >= LUSTRE_NODEMAP_SEPOL_LENGTH + 1) {
323                 rc = -EMSGSIZE;
324                 goto out_mdval;
325         }
326
327         p = data->sdd_sepol + strlen(data->sdd_sepol);
328         size = LUSTRE_NODEMAP_SEPOL_LENGTH + 1 - strlen(data->sdd_sepol);
329         for (idx = 0; idx < mdsize; idx++) {
330                 rc = snprintf(p, size, "%02x",
331                               (unsigned char)(mdval[idx]));
332                 p += 2;
333                 size -= 2;
334                 if (size < 0 || rc >= size) {
335                         rc = -EMSGSIZE;
336                         goto out_mdval;
337                 }
338         }
339         data->sdd_sepol_len = p - data->sdd_sepol;
340
341         size = offsetof(struct sepol_downcall_data,
342                         sdd_sepol[data->sdd_sepol_len]);
343
344         if (!obd_type || !obd_name) {
345                 /* called without arg (presumably from command line):
346                  * print SELinux status and exit
347                  */
348                 printf("SELinux status info: %.*s\n",
349                        data->sdd_sepol_len, data->sdd_sepol);
350                 return 0;
351         }
352
353         data->sdd_magic = SEPOL_DOWNCALL_MAGIC;
354         data->sdd_sepol_mtime = policymtime;
355         /* Send SELinux policy info to kernelspace */
356         rc = cfs_get_param_paths(&path, "%s/%s/srpc_sepol", obd_type, obd_name);
357         if (rc != 0) {
358                 errlog("can't get param '%s/%s/srpc_sepol': %s\n",
359                        obd_type, obd_name, strerror(errno));
360                 rc = -errno;
361                 goto out_mdval;
362         }
363
364         fd = open(path.gl_pathv[0], O_WRONLY);
365         if (fd < 0) {
366                 errlog("can't open file '%s':%s\n", path.gl_pathv[0],
367                        strerror(errno));
368                 rc = -errno;
369                 goto out_params;
370         }
371
372         rc = write(fd, data, size);
373         close(fd);
374         if (rc != size) {
375                 errlog("partial write ret %d: %s\n", rc, strerror(errno));
376                 rc = -errno;
377         } else {
378                 rc = 0;
379         }
380
381 out_params:
382         cfs_free_param_data(&path);
383 out_mdval:
384         free(mdval);
385 out_poltyp:
386         free(policy_type);
387 out_data:
388         free(data);
389 out:
390         if (isatty(STDIN_FILENO))
391                 /* we are called from the command line */
392                 return rc < 0 ? -rc : rc;
393         else
394                 return rc;
395 }