chiark / gitweb /
selinux: minor error handling fix
[elogind.git] / src / util.c
index e5d845609defb295e7e03f3dc087f3337f1c51f8..ee7749be5ef9c4751acdb4bc3ffcf8de64890dd7 100644 (file)
 #include "log.h"
 #include "strv.h"
 
+#ifdef HAVE_SELINUX
+#include <selinux/selinux.h>
+#include <selinux/label.h>
+
+static struct selabel_handle *label_hnd = NULL;
+
+static inline bool use_selinux(void) {
+        static int use_selinux_ind = -1;
+
+        if (use_selinux_ind < 0)
+                use_selinux_ind = is_selinux_enabled() > 0;
+
+        return use_selinux_ind;
+}
+
+static int label_get_file_label_from_path(
+                const char *label,
+                const char *path,
+                const char *class,
+                security_context_t *fcon) {
+
+        security_context_t dir_con = NULL;
+        security_class_t sclass;
+        int r = 0;
+
+        r = getfilecon(path, &dir_con);
+        if (r >= 0) {
+                r = -1;
+                errno = EINVAL;
+
+                if ((sclass = string_to_security_class(class)) != 0)
+                        r = security_compute_create((security_context_t) label, dir_con, sclass, fcon);
+        }
+        if (r < 0)
+                r = -errno;
+
+        freecon(dir_con);
+        return r;
+}
+
+#endif
+
+int label_init(void) {
+        int r = 0;
+
+#ifdef HAVE_SELINUX
+
+        if (!use_selinux())
+                return 0;
+
+        label_hnd = selabel_open(SELABEL_CTX_FILE, NULL, 0);
+        if (!label_hnd) {
+                log_full(security_getenforce() == 1 ? LOG_ERR : LOG_DEBUG,
+                         "Failed to initialize SELinux context: %m");
+                r = (security_getenforce() == 1) ? -errno : 0;
+        }
+#endif
+
+        return r;
+}
+
+int label_fix(const char *path) {
+        int r = 0;
+
+#ifdef HAVE_SELINUX
+        struct stat st;
+        security_context_t fcon;
+
+        if (!use_selinux() || !label_hnd)
+                return 0;
+
+        r = lstat(path, &st);
+        if (r == 0) {
+                r = selabel_lookup_raw(label_hnd, &fcon, path, st.st_mode);
+
+                if (r == 0) {
+                        r = setfilecon(path, fcon);
+                        freecon(fcon);
+                }
+        }
+        if (r < 0) {
+                log_full(security_getenforce() == 1 ? LOG_ERR : LOG_DEBUG,
+                         "Unable to fix label of %s: %m", path);
+                r = (security_getenforce() == 1) ? -errno : 0;
+        }
+#endif
+
+        return r;
+}
+
+void label_finish(void) {
+
+#ifdef HAVE_SELINUX
+        if (use_selinux() && label_hnd)
+                selabel_close(label_hnd);
+#endif
+}
+
+int label_get_socket_label_from_exe(const char *exe, char **label) {
+
+        int r = 0;
+
+#ifdef HAVE_SELINUX
+        security_context_t mycon = NULL, fcon = NULL;
+        security_class_t sclass;
+
+        if (!use_selinux()) {
+                *label = NULL;
+                return 0;
+        }
+
+        r = getcon(&mycon);
+        if (r < 0)
+                goto fail;
+
+        r = getfilecon(exe, &fcon);
+        if (r < 0)
+                goto fail;
+
+        sclass = string_to_security_class("process");
+        r = security_compute_create(mycon, fcon, sclass, (security_context_t *) label);
+        if (r == 0)
+                log_debug("SELinux Socket context for %s will be set to %s", exe, *label);
+
+fail:
+        if (r < 0 && security_getenforce() == 1)
+                r = -errno;
+
+        freecon(mycon);
+        freecon(fcon);
+#endif
+
+        return r;
+}
+
+int label_fifofile_set(const char *label, const char *path) {
+        int r = 0;
+
+#ifdef HAVE_SELINUX
+        security_context_t filecon = NULL;
+
+        if (!use_selinux() || !label)
+                return 0;
+
+        if (((r = label_get_file_label_from_path(label, path, "fifo_file", &filecon)) == 0)) {
+                if ((r = setfscreatecon(filecon)) < 0) {
+                        log_error("Failed to set SELinux file context (%s) on %s: %m", label, path);
+                        r = -errno;
+                }
+
+                freecon(filecon);
+        }
+
+        if (r < 0 && security_getenforce() == 0)
+                r = 0;
+#endif
+
+        return r;
+}
+
+int label_socket_set(const char *label) {
+
+#ifdef HAVE_SELINUX
+        if (!use_selinux())
+                return 0;
+
+        if (setsockcreatecon((security_context_t) label) < 0) {
+                log_full(security_getenforce() == 1 ? LOG_ERR : LOG_DEBUG,
+                         "Failed to set SELinux context (%s) on socket: %m", label);
+
+                if (security_getenforce() == 1)
+                        return -errno;
+        }
+#endif
+
+        return 0;
+}
+
+void label_file_clear(void) {
+
+#ifdef HAVE_SELINUX
+        if (!use_selinux())
+                return;
+
+        setfscreatecon(NULL);
+#endif
+}
+
+void label_socket_clear(void) {
+
+#ifdef HAVE_SELINUX
+        if (!use_selinux())
+                return;
+
+        setsockcreatecon(NULL);
+#endif
+}
+
+void label_free(const char *label) {
+
+#ifdef HAVE_SELINUX
+        if (!use_selinux())
+                return;
+
+        freecon((security_context_t) label);
+#endif
+}
+
+static int label_mkdir(
+        const char *path,
+        mode_t mode) {
+
+#ifdef HAVE_SELINUX
+        int r;
+        security_context_t fcon = NULL;
+
+        if (use_selinux() && label_hnd) {
+                if (path[0] == '/') {
+                        r = selabel_lookup_raw(label_hnd, &fcon, path, mode);
+                }
+                else {
+                        char *cwd = NULL;
+                        char *newpath = NULL;
+                        cwd = getcwd(NULL,0);
+                        if ((! cwd) || (asprintf(&newpath, "%s/%s",cwd,path) < 0)) {
+                                free(cwd);
+                                return -errno;
+                        }
+                        r = selabel_lookup_raw(label_hnd, &fcon, newpath, mode);
+                        free(cwd);
+                        free(newpath);
+                }
+
+                if (r == 0)
+                        r = setfscreatecon(fcon);
+
+                if ((r < 0) && (errno != ENOENT)) {
+                        log_error("Failed to set security context %s for %s", fcon, path);
+
+                        if (security_getenforce() == 1)
+                                goto finish;
+                }
+        }
+        r = mkdir(path, mode);
+
+finish:
+        if (use_selinux() && label_hnd) {
+                setfscreatecon(NULL);
+                freecon(fcon);
+        }
+
+        return r;
+#else
+        return mkdir(path, mode);
+#endif
+}
+
 bool streq_ptr(const char *a, const char *b) {
 
         /* Like streq(), but tries to make sense of NULL pointers */
@@ -969,7 +1226,7 @@ char *file_in_same_dir(const char *path, const char *filename) {
 int safe_mkdir(const char *path, mode_t mode, uid_t uid, gid_t gid) {
         struct stat st;
 
-        if (mkdir(path, mode) >= 0)
+        if (label_mkdir(path, mode) >= 0)
                 if (chmod_and_chown(path, mode, uid, gid) < 0)
                         return -errno;
 
@@ -1012,7 +1269,7 @@ int mkdir_parents(const char *path, mode_t mode) {
                 if (!(t = strndup(path, e - path)))
                         return -ENOMEM;
 
-                r = mkdir(t, mode);
+                r = label_mkdir(t, mode);
                 free(t);
 
                 if (r < 0 && errno != EEXIST)
@@ -1028,7 +1285,7 @@ int mkdir_p(const char *path, mode_t mode) {
         if ((r = mkdir_parents(path, mode)) < 0)
                 return r;
 
-        if (mkdir(path, mode) < 0 && errno != EEXIST)
+        if (label_mkdir(path, mode) < 0 && errno != EEXIST)
                 return -errno;
 
         return 0;
@@ -2565,7 +2822,8 @@ static int rm_rf_children(int fd, bool only_dirs) {
 
         if (!(d = fdopendir(fd))) {
                 close_nointr_nofail(fd);
-                return -errno;
+
+                return errno == ENOENT ? 0 : -errno;
         }
 
         for (;;) {
@@ -2589,7 +2847,7 @@ static int rm_rf_children(int fd, bool only_dirs) {
                         struct stat st;
 
                         if (fstatat(fd, de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) {
-                                if (ret == 0)
+                                if (ret == 0 && errno != ENOENT)
                                         ret = -errno;
                                 continue;
                         }
@@ -2602,7 +2860,7 @@ static int rm_rf_children(int fd, bool only_dirs) {
                         int subdir_fd;
 
                         if ((subdir_fd = openat(fd, de->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC)) < 0) {
-                                if (ret == 0)
+                                if (ret == 0 && errno != ENOENT)
                                         ret = -errno;
                                 continue;
                         }
@@ -2613,13 +2871,13 @@ static int rm_rf_children(int fd, bool only_dirs) {
                         }
 
                         if (unlinkat(fd, de->d_name, AT_REMOVEDIR) < 0) {
-                                if (ret == 0)
+                                if (ret == 0 && errno != ENOENT)
                                         ret = -errno;
                         }
                 } else  if (!only_dirs) {
 
                         if (unlinkat(fd, de->d_name, 0) < 0) {
-                                if (ret == 0)
+                                if (ret == 0 && errno != ENOENT)
                                         ret = -errno;
                         }
                 }
@@ -2777,7 +3035,7 @@ void status_welcome(void) {
 char *replace_env(const char *format, char **env) {
         enum {
                 WORD,
-                DOLLAR,
+                CURLY,
                 VARIABLE
         } state = WORD;
 
@@ -2792,11 +3050,11 @@ char *replace_env(const char *format, char **env) {
 
                 case WORD:
                         if (*e == '$')
-                                state = DOLLAR;
+                                state = CURLY;
                         break;
 
-                case DOLLAR:
-                        if (*e == '(') {
+                case CURLY:
+                        if (*e == '{') {
                                 if (!(k = strnappend(r, word, e-word-1)))
                                         goto fail;
 
@@ -2820,7 +3078,7 @@ char *replace_env(const char *format, char **env) {
                         break;
 
                 case VARIABLE:
-                        if (*e == ')') {
+                        if (*e == '}') {
                                 char *t;
 
                                 if ((t = strv_env_get_with_length(env, word+2, e-word-2))) {
@@ -2852,12 +3110,49 @@ fail:
 
 char **replace_env_argv(char **argv, char **env) {
         char **r, **i;
-        unsigned k = 0;
+        unsigned k = 0, l = 0;
 
-        if (!(r = new(char*, strv_length(argv)+1)))
+        l = strv_length(argv);
+
+        if (!(r = new(char*, l+1)))
                 return NULL;
 
         STRV_FOREACH(i, argv) {
+
+                /* If $FOO appears as single word, replace it by the split up variable */
+                if ((*i)[0] == '$') {
+                        char *e = strv_env_get(env, *i+1);
+
+                        if (e) {
+                                char **w, **m;
+                                unsigned q;
+
+                                if (!(m = strv_split_quoted(e))) {
+                                        r[k] = NULL;
+                                        strv_free(r);
+                                        return NULL;
+                                }
+
+                                q = strv_length(m);
+                                l = l + q - 1;
+
+                                if (!(w = realloc(r, sizeof(char*) * (l+1)))) {
+                                        r[k] = NULL;
+                                        strv_free(r);
+                                        strv_free(m);
+                                        return NULL;
+                                }
+
+                                r = w;
+                                memcpy(r + k, m, q * sizeof(char*));
+                                free(m);
+
+                                k += q;
+                                continue;
+                        }
+                }
+
+                /* If ${FOO} appears as part of a word, replace it by the variable as-is */
                 if (!(r[k++] = replace_env(*i, env))) {
                         strv_free(r);
                         return NULL;
@@ -2911,6 +3206,38 @@ int running_in_chroot(void) {
                 a.st_ino != b.st_ino;
 }
 
+char *ellipsize(const char *s, unsigned length, unsigned percent) {
+        size_t l, x;
+        char *r;
+
+        assert(s);
+        assert(percent <= 100);
+        assert(length >= 3);
+
+        l = strlen(s);
+
+        if (l <= 3 || l <= length)
+                return strdup(s);
+
+        if (!(r = new0(char, length+1)))
+                return r;
+
+        x = (length * percent) / 100;
+
+        if (x > length - 3)
+                x = length - 3;
+
+        memcpy(r, s, x);
+        r[x] = '.';
+        r[x+1] = '.';
+        r[x+2] = '.';
+        memcpy(r + x + 3,
+               s + l - (length - x - 3),
+               length - x - 3);
+
+        return r;
+}
+
 static const char *const ioprio_class_table[] = {
         [IOPRIO_CLASS_NONE] = "none",
         [IOPRIO_CLASS_RT] = "realtime",