chiark / gitweb /
sysusers: fix selinux context of backup files
[elogind.git] / src / sysusers / sysusers.c
index 1209a5a8b411aeed49b6a2e70156b3d3ad1617a8..b7c16092429163b73d00be9aea6b6ded1f801be9 100644 (file)
@@ -24,6 +24,7 @@
 #include <grp.h>
 #include <shadow.h>
 #include <getopt.h>
+#include <utmp.h>
 
 #include "util.h"
 #include "hashmap.h"
@@ -34,6 +35,8 @@
 #include "conf-files.h"
 #include "copy.h"
 #include "utf8.h"
+#include "label.h"
+#include "fileio-label.h"
 
 typedef enum ItemType {
         ADD_USER = 'u',
@@ -61,6 +64,8 @@ typedef struct Item {
 static char *arg_root = NULL;
 
 static const char conf_file_dirs[] =
+        "/etc/sysusers.d\0"
+        "/run/sysusers.d\0"
         "/usr/local/lib/sysusers.d\0"
         "/usr/lib/sysusers.d\0"
 #ifdef HAVE_SPLIT_USR
@@ -190,8 +195,9 @@ static int load_group_database(void) {
         return 0;
 }
 
-static int make_backup(const char *x) {
-        _cleanup_close_ int src = -1, dst = -1;
+static int make_backup(const char *target, const char *x) {
+        _cleanup_close_ int src = -1;
+        _cleanup_fclose_ FILE *dst = NULL;
         char *backup, *temp;
         struct timespec ts[2];
         struct stat st;
@@ -208,30 +214,30 @@ static int make_backup(const char *x) {
         if (fstat(src, &st) < 0)
                 return -errno;
 
-        temp = strappenda(x, ".XXXXXX");
-        dst = mkostemp_safe(temp, O_WRONLY|O_CLOEXEC|O_NOCTTY);
-        if (dst < 0)
-                return dst;
+        r = fopen_temporary_label(target, x, &dst, &temp);
+        if (r < 0)
+                return r;
 
-        r = copy_bytes(src, dst, (off_t) -1);
+        r = copy_bytes(src, fileno(dst), (off_t) -1);
         if (r < 0)
                 goto fail;
 
+        /* Don't fail on chmod() or chown(). If it stays owned by us
+         * and/or unreadable by others, then it isn't too bad... */
+
+        backup = strappenda(x, "-");
+
         /* Copy over the access mask */
-        if (fchmod(dst, st.st_mode & 07777) < 0) {
-                r = -errno;
-                goto fail;
-        }
+        if (fchmod(fileno(dst), st.st_mode & 07777) < 0)
+                log_warning("Failed to change mode on %s: %m", backup);
 
-        /* Don't fail on chmod(). If it stays owned by us, then it
-         * isn't too bad... */
-        fchown(dst, st.st_uid, st.st_gid);
+        if (fchown(fileno(dst), st.st_uid, st.st_gid)< 0)
+                log_warning("Failed to change ownership of %s: %m", backup);
 
         ts[0] = st.st_atim;
         ts[1] = st.st_mtim;
-        futimens(dst, ts);
+        futimens(fileno(dst), ts);
 
-        backup = strappenda(x, "-");
         if (rename(temp, backup) < 0)
                 goto fail;
 
@@ -309,7 +315,7 @@ static int write_files(void) {
                 _cleanup_fclose_ FILE *original = NULL;
 
                 group_path = fix_root("/etc/group");
-                r = fopen_temporary(group_path, &group, &group_tmp);
+                r = fopen_temporary_label("/etc/group", group_path, &group, &group_tmp);
                 if (r < 0)
                         goto finish;
 
@@ -385,7 +391,7 @@ static int write_files(void) {
                 _cleanup_fclose_ FILE *original = NULL;
 
                 passwd_path = fix_root("/etc/passwd");
-                r = fopen_temporary(passwd_path, &passwd, &passwd_tmp);
+                r = fopen_temporary_label("/etc/passwd", passwd_path, &passwd, &passwd_tmp);
                 if (r < 0)
                         goto finish;
 
@@ -464,13 +470,13 @@ static int write_files(void) {
 
         /* Make a backup of the old files */
         if (group && group_changed) {
-                r = make_backup(group_path);
+                r = make_backup("/etc/group", group_path);
                 if (r < 0)
                         goto finish;
         }
 
         if (passwd) {
-                r = make_backup(passwd_path);
+                r = make_backup("/etc/passwd", passwd_path);
                 if (r < 0)
                         goto finish;
         }
@@ -1095,6 +1101,9 @@ static bool valid_user_group_name(const char *u) {
         if ((size_t) (i-u) > (size_t) sz)
                 return false;
 
+        if ((size_t) (i-u) > UT_NAMESIZE - 1)
+                return false;
+
         return true;
 }
 
@@ -1103,7 +1112,11 @@ static bool valid_gecos(const char *d) {
         if (!utf8_is_valid(d))
                 return false;
 
-        if (strpbrk(d, ":\n"))
+        if (string_has_cc(d, NULL))
+                return false;
+
+        /* Colons are used as field separators, and hence not OK */
+        if (strchr(d, ':'))
                 return false;
 
         return true;
@@ -1312,6 +1325,8 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
 
                 h = groups;
                 break;
+        default:
+                return -EBADMSG;
         }
 
         i->type = action[0];
@@ -1377,42 +1392,6 @@ static int read_config_file(const char *fn, bool ignore_enoent) {
         return r;
 }
 
-static int take_lock(void) {
-
-        struct flock flock = {
-                .l_type = F_WRLCK,
-                .l_whence = SEEK_SET,
-                .l_start = 0,
-                .l_len = 0,
-        };
-
-        const char *path;
-        int fd, r;
-
-        /* This is roughly the same as lckpwdf(), but not as awful. We
-         * don't want to use alarm() and signals, hence we implement
-         * our own trivial version of this.
-         *
-         * Note that shadow-utils also takes per-database locks in
-         * addition to lckpwdf(). However, we don't given that they
-         * are redundant as they they invoke lckpwdf() first and keep
-         * it during everything they do. The per-database locks are
-         * awfully racy, and thus we just won't do them. */
-
-        path = fix_root("/etc/.pwd.lock");
-        fd = open(path, O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0600);
-        if (fd < 0)
-                return -errno;
-
-        r = fcntl(fd, F_SETLKW, &flock);
-        if (r < 0) {
-                safe_close(fd);
-                return -errno;
-        }
-
-        return fd;
-}
-
 static void free_database(Hashmap *by_name, Hashmap *by_id) {
         char *name;
 
@@ -1515,7 +1494,11 @@ int main(int argc, char *argv[]) {
 
         umask(0022);
 
-        r = 0;
+        r = label_init(NULL);
+        if (r < 0) {
+                log_error("SELinux setup failed: %s", strerror(-r));
+                goto finish;
+        }
 
         if (optind < argc) {
                 int j;
@@ -1546,7 +1529,7 @@ int main(int argc, char *argv[]) {
         if (r < 0)
                 goto finish;
 
-        lock = take_lock();
+        lock = take_password_lock(arg_root);
         if (lock < 0) {
                 log_error("Failed to take lock: %s", strerror(-lock));
                 goto finish;