sysusers: do not append entries after the NIS ones
authorFranck Bui <fbui@suse.com>
Thu, 15 Mar 2018 17:46:28 +0000 (18:46 +0100)
committerFranck Bui <fbui@suse.com>
Fri, 16 Mar 2018 09:01:33 +0000 (10:01 +0100)
The NIS-catchall entry switches from files to NIS lookup and never goes back,
so it must be the last entry in /etc/passwd (the other +/-{user,@netgroup}
entries don't have to be).

That's how the nss_compat mode for /etc/passwd (and /etc/group) traditionally
works.

It's age-old historic behaviour that the NIS entry must be the last one.  It
doesn't seem to be specified somewhere, but it worked like this since very
early SunOS when NIS was first included.

Fixes: #8467

src/sysusers/sysusers.c

index cd273ef..5dcc213 100644 (file)
@@ -396,6 +396,7 @@ static const char* default_shell(uid_t uid) {
 static int write_temporary_passwd(const char *passwd_path, FILE **tmpfile, char **tmpfile_path) {
         _cleanup_fclose_ FILE *original = NULL, *passwd = NULL;
         _cleanup_(unlink_and_freep) char *passwd_tmp = NULL;
+        struct passwd *pw = NULL;
         Iterator iterator;
         Item *i;
         int r;
@@ -409,7 +410,6 @@ static int write_temporary_passwd(const char *passwd_path, FILE **tmpfile, char
 
         original = fopen(passwd_path, "re");
         if (original) {
-                struct passwd *pw;
 
                 r = sync_rights(original, passwd);
                 if (r < 0)
@@ -429,6 +429,10 @@ static int write_temporary_passwd(const char *passwd_path, FILE **tmpfile, char
                                 return -EEXIST;
                         }
 
+                        /* Make sure we keep the NIS entries (if any) at the end. */
+                        if (IN_SET(pw->pw_name[0], '+', '-'))
+                                break;
+
                         errno = 0;
                         if (putpwent(pw, passwd) < 0)
                                 return errno ? -errno : -EIO;
@@ -468,6 +472,17 @@ static int write_temporary_passwd(const char *passwd_path, FILE **tmpfile, char
                         return errno ? -errno : -EIO;
         }
 
+        /* Append the remaining NIS entries if any */
+        while (pw) {
+                errno = 0;
+                if (putpwent(pw, passwd) < 0)
+                        return errno ? -errno : -EIO;
+
+                pw = fgetpwent(original);
+        }
+        if (!IN_SET(errno, 0, ENOENT))
+                return -errno;
+
         r = fflush_and_check(passwd);
         if (r < 0)
                 return r;
@@ -567,6 +582,7 @@ static int write_temporary_group(const char *group_path, FILE **tmpfile, char **
         _cleanup_fclose_ FILE *original = NULL, *group = NULL;
         _cleanup_(unlink_and_freep) char *group_tmp = NULL;
         bool group_changed = false;
+        struct group *gr = NULL;
         Iterator iterator;
         Item *i;
         int r;
@@ -580,7 +596,6 @@ static int write_temporary_group(const char *group_path, FILE **tmpfile, char **
 
         original = fopen(group_path, "re");
         if (original) {
-                struct group *gr;
 
                 r = sync_rights(original, group);
                 if (r < 0)
@@ -604,6 +619,10 @@ static int write_temporary_group(const char *group_path, FILE **tmpfile, char **
                                 return  -EEXIST;
                         }
 
+                        /* Make sure we keep the NIS entries (if any) at the end. */
+                        if (IN_SET(gr->gr_name[0], '+', '-'))
+                                break;
+
                         r = putgrent_with_members(gr, group);
                         if (r < 0)
                                 return r;
@@ -636,6 +655,17 @@ static int write_temporary_group(const char *group_path, FILE **tmpfile, char **
                 group_changed = true;
         }
 
+        /* Append the remaining NIS entries if any */
+        while (gr) {
+                errno = 0;
+                if (putgrent(gr, group) != 0)
+                        return errno > 0 ? -errno : -EIO;
+
+                gr = fgetgrent(original);
+        }
+        if (!IN_SET(errno, 0, ENOENT))
+                return -errno;
+
         r = fflush_sync_and_check(group);
         if (r < 0)
                 return r;