diff options
Diffstat (limited to 'src/selinux.c')
-rw-r--r-- | src/selinux.c | 327 |
1 files changed, 327 insertions, 0 deletions
diff --git a/src/selinux.c b/src/selinux.c new file mode 100644 index 0000000..cd01dd1 --- /dev/null +++ b/src/selinux.c @@ -0,0 +1,327 @@ +/* selinux - core functions for maintaining SELinux labeling + Copyright (C) 2012-2022 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + 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 for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. */ + +/* Written by Daniel Walsh <dwalsh@redhat.com> */ + +#include <config.h> +#include <selinux/label.h> +#include <selinux/context.h> +#include <sys/types.h> + +#include "system.h" +#include "canonicalize.h" +#include "xfts.h" +#include "selinux.h" + +#if HAVE_SELINUX_LABEL_H + +# if ! HAVE_MODE_TO_SECURITY_CLASS +/* + This function has been added to libselinux-2.1.12-5, but is here + for support with older versions of SELinux + + Translates a mode into an Internal SELinux security_class definition. + Returns 0 on failure, with errno set to EINVAL. +*/ +static security_class_t +mode_to_security_class (mode_t m) +{ + + if (S_ISREG (m)) + return string_to_security_class ("file"); + if (S_ISDIR (m)) + return string_to_security_class ("dir"); + if (S_ISCHR (m)) + return string_to_security_class ("chr_file"); + if (S_ISBLK (m)) + return string_to_security_class ("blk_file"); + if (S_ISFIFO (m)) + return string_to_security_class ("fifo_file"); + if (S_ISLNK (m)) + return string_to_security_class ("lnk_file"); + if (S_ISSOCK (m)) + return string_to_security_class ("sock_file"); + + errno = EINVAL; + return 0; +} +# endif + +/* + This function takes a PATH and a MODE and then asks SELinux what the label + of the path object would be if the current process label created it. + It then returns the label. + + Returns -1 on failure. errno will be set appropriately. +*/ + +static int +computecon (char const *path, mode_t mode, char **con) +{ + char *scon = NULL; + char *tcon = NULL; + security_class_t tclass; + int rc = -1; + + char *dir = dir_name (path); + if (!dir) + goto quit; + if (getcon (&scon) < 0) + goto quit; + if (getfilecon (dir, &tcon) < 0) + goto quit; + tclass = mode_to_security_class (mode); + if (!tclass) + goto quit; + rc = security_compute_create (scon, tcon, tclass, con); + + quit:; + int err = errno; + free (dir); + freecon (scon); + freecon (tcon); + errno = err; + return rc; +} + +/* + This function takes a handle, path and mode, it calls computecon to get the + label of the path object if the current process created it, then it calls + selabel_lookup to get the default type for the object. It substitutes the + default type into label. It tells the SELinux Kernel to label all new file + system objects created by the current process with this label. + + Returns -1 on failure. errno will be set appropriately. +*/ +int +defaultcon (struct selabel_handle *selabel_handle, + char const *path, mode_t mode) +{ + int rc = -1; + char *scon = NULL; + char *tcon = NULL; + context_t scontext = 0, tcontext = 0; + char const *contype; + char *constr; + char *newpath = NULL; + + if (! IS_ABSOLUTE_FILE_NAME (path)) + { + /* Generate absolute name as required by subsequent selabel_lookup. */ + newpath = canonicalize_filename_mode (path, CAN_MISSING); + if (! newpath) + goto quit; + path = newpath; + } + + if (selabel_lookup (selabel_handle, &scon, path, mode) < 0) + { + /* "No such file or directory" is a confusing error, + when processing files, when in fact it was the + associated default context that was not found. + Therefore map the error to something more appropriate + to the context in which we're using selabel_lookup(). */ + if (errno == ENOENT) + errno = ENODATA; + goto quit; + } + if (computecon (path, mode, &tcon) < 0) + goto quit; + if (!(scontext = context_new (scon))) + goto quit; + if (!(tcontext = context_new (tcon))) + goto quit; + + if (!(contype = context_type_get (scontext))) + goto quit; + if (context_type_set (tcontext, contype)) + goto quit; + if (!(constr = context_str (tcontext))) + goto quit; + + rc = setfscreatecon (constr); + + quit:; + int err = errno; + context_free (scontext); + context_free (tcontext); + freecon (scon); + freecon (tcon); + free (newpath); + errno = err; + return rc; +} + +/* + If SELABEL_HANDLE is null, set PATH's label to the default to the + local process. Otherwise use selabel_lookup to determine the + default label, extract the type field and then modify the file + system object. Note only the type field is updated, thus preserving MLS + levels and user identity etc. of the PATH. + + Returns -1 on failure. errno will be set appropriately. +*/ +static int +restorecon_private (struct selabel_handle *selabel_handle, char const *path) +{ + int rc = -1; + struct stat sb; + char *scon = NULL; + char *tcon = NULL; + context_t scontext = 0, tcontext = 0; + char const *contype; + char *constr; + int fd; + + if (!selabel_handle) + { + if (getfscreatecon (&tcon) < 0) + return rc; + if (!tcon) + { + errno = ENODATA; + return rc; + } + rc = lsetfilecon (path, tcon); + int err = errno; + freecon (tcon); + errno = err; + return rc; + } + + fd = open (path, O_RDONLY | O_NOFOLLOW); + if (fd == -1 && (errno != ELOOP)) + goto quit; + + if (fd != -1) + { + if (fstat (fd, &sb) < 0) + goto quit; + } + else + { + if (lstat (path, &sb) < 0) + goto quit; + } + + if (selabel_lookup (selabel_handle, &scon, path, sb.st_mode) < 0) + { + /* "No such file or directory" is a confusing error, + when processing files, when in fact it was the + associated default context that was not found. + Therefore map the error to something more appropriate + to the context in which we're using selabel_lookup. */ + if (errno == ENOENT) + errno = ENODATA; + goto quit; + } + if (!(scontext = context_new (scon))) + goto quit; + + if (fd != -1) + { + if (fgetfilecon (fd, &tcon) < 0) + goto quit; + } + else + { + if (lgetfilecon (path, &tcon) < 0) + goto quit; + } + + if (!(tcontext = context_new (tcon))) + goto quit; + + if (!(contype = context_type_get (scontext))) + goto quit; + if (context_type_set (tcontext, contype)) + goto quit; + if (!(constr = context_str (tcontext))) + goto quit; + + if (fd != -1) + rc = fsetfilecon (fd, constr); + else + rc = lsetfilecon (path, constr); + + quit:; + int err = errno; + if (fd != -1) + close (fd); + context_free (scontext); + context_free (tcontext); + freecon (scon); + freecon (tcon); + errno = err; + return rc; +} + +/* + This function takes three parameters: + + SELABEL_HANDLE for selabel_lookup, or null to preserve. + + PATH of an existing file system object. + + A RECURSE boolean which if the file system object is a directory, will + call restorecon_private on every file system object in the directory. + + Return false on failure. errno will be set appropriately. +*/ +bool +restorecon (struct selabel_handle *selabel_handle, + char const *path, bool recurse) +{ + char *newpath = NULL; + + if (! IS_ABSOLUTE_FILE_NAME (path)) + { + /* Generate absolute name as required by subsequent selabel_lookup. + When RECURSE, this also generates absolute names in the + fts entries, which may be quicker to process in any case. */ + newpath = canonicalize_filename_mode (path, CAN_MISSING); + if (! newpath) + return false; + path = newpath; + } + + if (! recurse) + { + bool ok = restorecon_private (selabel_handle, path) != -1; + int err = errno; + free (newpath); + errno = err; + return ok; + } + + char const *ftspath[2] = { path, NULL }; + FTS *fts = xfts_open ((char *const *) ftspath, FTS_PHYSICAL, NULL); + + int err = 0; + for (FTSENT *ent; (ent = fts_read (fts)); ) + if (restorecon_private (selabel_handle, fts->fts_path) < 0) + err = errno; + + if (errno != 0) + err = errno; + + if (fts_close (fts) != 0) + err = errno; + + free (newpath); + return !err; +} +#endif |