X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Futil.c;h=ec5c7ca91aacbb19c807015c402fc5baed38cba1;hp=e5d845609defb295e7e03f3dc087f3337f1c51f8;hb=6319292f15478f763777738c9a98faa99ae819c1;hpb=3fe5e5d476f6a653e303913aff6c438807b80b3b diff --git a/src/util.c b/src/util.c index e5d845609..ec5c7ca91 100644 --- a/src/util.c +++ b/src/util.c @@ -56,6 +56,261 @@ #include "log.h" #include "strv.h" +#ifdef HAVE_SELINUX +#include +#include + +static struct selabel_handle *label_hnd = NULL; + +static inline int use_selinux(void) { + static int use_selinux_ind = -1; + + if (use_selinux_ind == -1) + use_selinux_ind = (is_selinux_enabled() == 1); + + 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; + 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 +1224,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 +1267,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 +1283,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 +2820,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 +2845,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 +2858,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 +2869,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 +3033,7 @@ void status_welcome(void) { char *replace_env(const char *format, char **env) { enum { WORD, - DOLLAR, + CURLY, VARIABLE } state = WORD; @@ -2792,11 +3048,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 +3076,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 +3108,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 +3204,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",