Subject: bug#53631: closed (Re: bug#53631: coreutils id(1) incorrect behavior)

From: [email-masked] (GNU bug Tracking System)
Date: Fri, 04 Feb 2022 23:00:03 +0000

Your bug report

#53631: coreutils id(1) incorrect behavior

which was filed against the coreutils package, has been closed.

The explanation is attached below, along with your original report.
If you require more details, please reply to [email-masked].

-- 
53631: http://debbugs.gnu.org/cgi/bugreport.cgi?bug=53631
GNU Bug Tracking System
Contact [email-masked] with problems

Subject: Re: bug#53631: coreutils id(1) incorrect behavior
From: Paul Eggert <[email-masked]>
To: "Vladimir D. Seleznev" <[email-masked]>
Cc: [email-masked]
Date: Fri, 4 Feb 2022 14:59:20 -0800
List-Id: <debbugs-submit.debbugs.gnu.org>

Thanks for the bug report. I installed the attached patch, which I hope 
fixes things for you, and am boldly closing the bug report.

This fix depends on the latest lib/userspec.c from Gnulib; see 
<https://lists.gnu.org/r/bug-gnulib/2022-02/msg00000.html>.

From 1204c5132d61efbb966fb2a94b4dc7463beddfe1 Mon Sep 17 00:00:00 2001
From: Paul Eggert <[email-masked]>
Date: Fri, 4 Feb 2022 14:43:31 -0800
Subject: [PATCH] id: print groups of listed name

Problem reported by Vladimir D. Seleznev (Bug#53631).
* src/id.c (main): Do not canonicalize user name before
deciding what groups the user belongs to.
---
 NEWS     |  3 +++
 src/id.c | 30 ++++++++++++++----------------
 2 files changed, 17 insertions(+), 16 deletions(-)

diff --git a/NEWS b/NEWS
index a4ba0fce6..fcf31fe39 100644
--- a/NEWS
+++ b/NEWS
@@ -21,6 +21,9 @@ GNU coreutils NEWS                                    -*- outline -*-
   and B is in some other file system.
   [bug introduced in coreutils-9.0]
 
+  'id xyz' now uses the name 'xyz' to determine groups, instead of xyz's uid.
+  [bug introduced in coreutils-8.22]
+
   On macOS, 'mv A B' no longer fails with "Operation not supported"
   when A and B are in the same tmpfs file system.
   [bug introduced in coreutils-9.0]
diff --git a/src/id.c b/src/id.c
index 2d969cc1e..f7625b6fb 100644
--- a/src/id.c
+++ b/src/id.c
@@ -127,7 +127,6 @@ main (int argc, char **argv)
   int optc;
   int selinux_enabled = (is_selinux_enabled () > 0);
   bool smack_enabled = is_smack_enabled ();
-  char *pw_name = NULL;
 
   initialize_main (&argc, &argv);
   set_program_name (argv[0]);
@@ -235,6 +234,7 @@ main (int argc, char **argv)
       /* For each username/userid to get its pw_name field */
       for (; optind < n_ids; optind++)
         {
+          char *pw_name = NULL;
           struct passwd *pwd = NULL;
           char const *spec = argv[optind];
           /* Disallow an empty spec here as parse_user_spec() doesn't
@@ -242,24 +242,22 @@ main (int argc, char **argv)
              specify a noop or "reset special bits" depending on the system.  */
           if (*spec)
             {
-              if (parse_user_spec (spec, &euid, NULL, NULL, NULL) == NULL)
-                {
-                  /* parse_user_spec will only extract a numeric spec,
-                     so we lookup that here to verify and also retrieve
-                     the PW_NAME used subsequently in group lookup.  */
-                  pwd = getpwuid (euid);
-                }
+              if (parse_user_spec (spec, &euid, NULL, &pw_name, NULL) == NULL)
+                pwd = pw_name ? getpwnam (pw_name) : getpwuid (euid);
             }
           if (pwd == NULL)
             {
-              error (0, errno, _("%s: no such user"), quote (argv[optind]));
+              error (0, errno, _("%s: no such user"), quote (spec));
               ok &= false;
-              continue;
             }
-          pw_name = xstrdup (pwd->pw_name);
-          ruid = euid = pwd->pw_uid;
-          rgid = egid = pwd->pw_gid;
-          print_stuff (pw_name);
+          else
+            {
+              if (!pw_name)
+                pw_name = xstrdup (pwd->pw_name);
+              ruid = euid = pwd->pw_uid;
+              rgid = egid = pwd->pw_gid;
+              print_stuff (pw_name);
+            }
           free (pw_name);
         }
     }
@@ -301,7 +299,7 @@ main (int argc, char **argv)
           if (rgid == NO_GID && errno)
             die (EXIT_FAILURE, errno, _("cannot get real GID"));
         }
-        print_stuff (pw_name);
+        print_stuff (NULL);
     }
 
   return ok ? EXIT_SUCCESS : EXIT_FAILURE;
@@ -434,7 +432,7 @@ print_stuff (char const *pw_name)
   if (just_user)
       print_user (use_real ? ruid : euid);
 
-  /* print_group and print_group_lists functions return true on successful
+  /* print_group and print_group_list return true on successful
      execution but false if something goes wrong. We then AND this value with
      the current value of 'ok' because we want to know if one of the previous
      users faced a problem in these functions. This value of 'ok' is later used
-- 
2.34.1

Subject: coreutils id(1) incorrect behavior
From: "Vladimir D. Seleznev" <[email-masked]>
To: [email-masked]
Cc: "Vladimir D. Seleznev" <[email-masked]>
Date: Sun, 30 Jan 2022 03:52:29 +0300
List-Id: <debbugs-submit.debbugs.gnu.org>

Hi, bug-coreutils@!

Recently I found an issue in the coreutils id(1) behavior. For coreutils
v8.32-165-g18dbcae25, `id username` prints user and group IDs for the
first matched user with the identical UID. In the case where there are
several users with the identical UID in the system it can be misleading.

Example:

	# useradd user1
	# groupadd somegroup
	# useradd -o -u "$(id -u user1)" -g "$(id -G user1) -G somegroup user2
	# id user1
	uid=1027(user1) gid=1027(user1) groups=1027(user1)
	# id user2
	uid=1027(user1) gid=1027(user1) groups=1027(user1)
	# su - user2
	$ id
	uid=1027(user1) gid=1027(user1) groups=1027(user1),1028(somegroup)

Expected behavior is:

	# id user1
	uid=1027(user1) gid=1027(user1) groups=1027(user1)
	# id user2
	uid=1027(user1) gid=1027(user1) groups=1027(user1),1028(somegroup)

Looking at the code, I found that id uses parse_user_spec() to obtain
euid of username (or uid) command line argument, and then it obtains all
the rest needed information by this euid.

	for (; optind < n_ids; optind++)
	{
	  struct passwd *pwd = NULL;
	  char const *spec = argv[optind];
	  /* Disallow an empty spec here as parse_user_spec() doesn't
	     give an error for that as it seems it's a valid way to
	     specify a noop or "reset special bits" depending on the system.  */
	  if (*spec)
	    {
	      if (parse_user_spec (spec, &euid, NULL, NULL, NULL) == NULL)
		{
		  /* parse_user_spec will only extract a numeric spec,
		     so we lookup that here to verify and also retrieve
		     the PW_NAME used subsequently in group lookup.  */
		  pwd = getpwuid (euid);
		}
	    }

-- 
   WBR,
   Vladimir D. Seleznev