/* * GPL HEADER START * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 only, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License version 2 for more details (a copy is included * in the LICENSE file that accompanied this code). * * You should have received a copy of the GNU General Public License * version 2 along with this program; If not, see http://www.gnu.org/licenses * * GPL HEADER END */ /* * Copyright (c) 2016 DDN Storage * Author: Sebastien Buisson sbuisson@ddn.com */ /* * lustre/utils/l_getsepol.c * Userland helper to retrieve SELinux policy information. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static char *progname; static char *obd_type = NULL, *obd_name = NULL; static time_t ref_pol_mtime = 0; static char ref_selinux_mode = -1; static void errlog(const char *fmt, ...) { va_list args; openlog(progname, LOG_PERROR | LOG_PID, LOG_AUTHPRIV); va_start(args, fmt); vsyslog(LOG_NOTICE, fmt, args); va_end(args); closelog(); } /* Retrieve name of policy loaded, and version */ static int sepol_get_policy_info(char **policyname) { char *pol_path; /* Name of loaded policy can be retrieved from policy root path */ pol_path = strdup(selinux_policy_root()); if (!pol_path) { *policyname = NULL; errlog("can't get policy name: %s\n", strerror(errno)); return -errno; } *policyname = strdup(basename(pol_path)); free(pol_path); return 0; } /* Read binary SELinux policy, and compute hash */ static int sepol_get_policy_data(const char *pol_bin_path, unsigned char **mdval, unsigned int *mdsize) { int fd; char buffer[1024]; ssize_t count = 1024; EVP_MD_CTX *mdctx; const EVP_MD *md = EVP_sha256(); /* use SHA-256 */ int rc; /* Open policy file */ fd = open(pol_bin_path, O_RDONLY); if (fd < 0) { errlog("can't open SELinux policy file %s: %s\n", pol_bin_path, strerror(errno)); rc = -ENOENT; goto out; } /* Read policy file */ mdctx = EVP_MD_CTX_create(); EVP_DigestInit_ex(mdctx, md, NULL); while (count == 1024) { count = read(fd, buffer, count); if (count < 0) { errlog("can't read SELinux policy file %s\n", pol_bin_path); rc = -errno; close(fd); goto out; } EVP_DigestUpdate(mdctx, buffer, count); } /* Close policy file */ rc = close(fd); if (rc < 0) { rc = -errno; goto out; } *mdsize = EVP_MD_size(md); *mdval = malloc(*mdsize); if (*mdval == NULL) { rc = -ENOMEM; goto out; } EVP_DigestFinal_ex(mdctx, *mdval, NULL); EVP_MD_CTX_destroy(mdctx); out: return rc; } int get_opts(int argc, char *const argv[]) { static struct option long_opts[] = { { .val = 'o', .name = "obd_type", .has_arg = required_argument}, { .val = 'n', .name = "obd_name", .has_arg = required_argument}, { .val = 't', .name = "sel_mtime", .has_arg = required_argument}, { .val = 'm', .name = "sel_mode", .has_arg = required_argument}, { .name = NULL } }; char *short_opts = "o:n:t:m:"; int opt; int longidx; char *sel_mtime = NULL, *sel_mode = NULL; char *res; optind = 0; while ((opt = getopt_long(argc, argv, short_opts, long_opts, &longidx)) != EOF) { switch (opt) { case 'o': obd_type = optarg; break; case 'n': obd_name = optarg; break; case 't': sel_mtime = optarg; break; case 'm': sel_mode = optarg; break; default: if (opt != '?') fprintf(stderr, "Unknown option '%c'\n", opt); return -EINVAL; } } if (optind != argc) { errlog("incorrect arguments\n"); return -EINVAL; } if (!obd_type || !obd_name) /* called without arg (presumably from command line): * ignore everything */ return 0; if (sel_mtime) { ref_pol_mtime = (time_t)strtoul(sel_mtime, &res, 0); if (*res != '\0') { /* not a valid number */ errlog("invalid sel_mtime"); return -EINVAL; } } if (sel_mode) { ref_selinux_mode = sel_mode[0] - '0'; if (ref_selinux_mode != 0 && ref_selinux_mode != 1) { /* not a valid enforcing mode */ errlog("invalid sel_mode"); return -EINVAL; } } return 0; } /** * Calculate SELinux status information. * String that represents SELinux status info has the following format: * ::: * is a digit equal to 0 for SELinux Permissive mode, * and 1 for Enforcing mode. * When called from kernel space, it requires 4 args: * - obd type * - obd name * - SELinux policy mtime * - SELinux enforcing mode * When called from command line (in this case without proper args), it prints * SELinux status info to stdout. */ int main(int argc, char **argv) { int policyver = 0; char pol_bin_path[PATH_MAX + 1]; struct stat st; time_t policymtime; int enforce; struct sepol_downcall_data *data = NULL; glob_t path; int fd, size; char *policy_type = NULL; unsigned char *mdval = NULL; unsigned int mdsize = 0; char *p; int idx; int rc; progname = basename(argv[0]); rc = get_opts(argc, argv); if (rc < 0) goto out; /* Version of loaded policy */ policyver = security_policyvers(); if (policyver < 0) { errlog("unknown policy version: %s\n", strerror(errno)); rc = -errno; goto out; } /* Path of binary policy file */ snprintf(pol_bin_path, sizeof(pol_bin_path), "%s.%d", selinux_binary_policy_path(), policyver); /* Stat binary policy file */ if (stat(pol_bin_path, &st)) { errlog("can't stat %s: %s\n", pol_bin_path, strerror(errno)); rc = -errno; goto out; } policymtime = st.st_mtime; /* Determine if SELinux is in permissive or enforcing mode */ enforce = security_getenforce(); if (enforce < 0) { errlog("can't getenforce: %s\n", strerror(errno)); rc = -errno; goto out; } if (ref_pol_mtime == policymtime && ref_selinux_mode == enforce) { /* Policy has not changed: return immediately */ rc = 0; goto out; } /* Now we need to calculate SELinux status information */ size = offsetof(struct sepol_downcall_data, sdd_sepol[LUSTRE_NODEMAP_SEPOL_LENGTH + 1]); data = malloc(size); if (!data) { errlog("malloc sepol downcall data(%d) failed!\n", size); rc = -ENOMEM; goto out; } memset(data, 0, size); /* Get policy name */ rc = sepol_get_policy_info(&policy_type); if (rc < 0) goto out_data; /* Read binary SELinux policy, and compute hash */ rc = sepol_get_policy_data(pol_bin_path, &mdval, &mdsize); if (rc < 0) goto out_poltyp; /* Put all info together and generate string * to represent SELinux policy information */ rc = snprintf(data->sdd_sepol, LUSTRE_NODEMAP_SEPOL_LENGTH + 1, "%.1d:%s:%u:", enforce, policy_type, policyver); if (rc >= LUSTRE_NODEMAP_SEPOL_LENGTH + 1) { rc = -EMSGSIZE; goto out_mdval; } p = data->sdd_sepol + strlen(data->sdd_sepol); size = LUSTRE_NODEMAP_SEPOL_LENGTH + 1 - strlen(data->sdd_sepol); for (idx = 0; idx < mdsize; idx++) { rc = snprintf(p, size, "%02x", (unsigned char)(mdval[idx])); p += 2; size -= 2; if (size < 0 || rc >= size) { rc = -EMSGSIZE; goto out_mdval; } } data->sdd_sepol_len = p - data->sdd_sepol; size = offsetof(struct sepol_downcall_data, sdd_sepol[data->sdd_sepol_len]); if (!obd_type || !obd_name) { /* called without arg (presumably from command line): * print SELinux status and exit */ printf("SELinux status info: %.*s\n", data->sdd_sepol_len, data->sdd_sepol); return 0; } data->sdd_magic = SEPOL_DOWNCALL_MAGIC; data->sdd_sepol_mtime = policymtime; /* Send SELinux policy info to kernelspace */ rc = cfs_get_param_paths(&path, "%s/%s/srpc_sepol", obd_type, obd_name); if (rc != 0) { errlog("can't get param '%s/%s/srpc_sepol': %s\n", obd_type, obd_name, strerror(errno)); rc = -errno; goto out_mdval; } fd = open(path.gl_pathv[0], O_WRONLY); if (fd < 0) { errlog("can't open file '%s':%s\n", path.gl_pathv[0], strerror(errno)); rc = -errno; goto out_params; } rc = write(fd, data, size); close(fd); if (rc != size) { errlog("partial write ret %d: %s\n", rc, strerror(errno)); rc = -errno; } else { rc = 0; } out_params: cfs_free_param_data(&path); out_mdval: free(mdval); out_poltyp: free(policy_type); out_data: free(data); out: if (isatty(STDIN_FILENO)) /* we are called from the command line */ return rc < 0 ? -rc : rc; else return rc; }