1 /* id -- print real and effective UIDs and GIDs
2    Copyright (C) 1989-2018 Free Software Foundation, Inc.
3 
4    This program is free software: you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation, either version 3 of the License, or
7    (at your option) any later version.
8 
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13 
14    You should have received a copy of the GNU General Public License
15    along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
16 
17 /* Written by Arnold Robbins.
18    Major rewrite by David MacKenzie, djm@gnu.ai.mit.edu. */
19 
20 #include <config.h>
21 #include <stdio.h>
22 #include <sys/types.h>
23 #include <pwd.h>
24 #include <grp.h>
25 #include <getopt.h>
26 #include <selinux/selinux.h>
27 
28 #include "system.h"
29 #include "die.h"
30 #include "error.h"
31 #include "mgetgroups.h"
32 #include "quote.h"
33 #include "group-list.h"
34 #include "smack.h"
35 #include "userspec.h"
36 
37 /* The official name of this program (e.g., no 'g' prefix).  */
38 #define PROGRAM_NAME "id"
39 
40 #define AUTHORS \
41   proper_name ("Arnold Robbins"), \
42   proper_name ("David MacKenzie")
43 
44 /* If nonzero, output only the SELinux context.  */
45 static bool just_context = 0;
46 
47 static void print_user (uid_t uid);
48 static void print_full_info (const char *username);
49 
50 /* If true, output user/group name instead of ID number. -n */
51 static bool use_name = false;
52 
53 /* The real and effective IDs of the user to print. */
54 static uid_t ruid, euid;
55 static gid_t rgid, egid;
56 
57 /* True unless errors have been encountered.  */
58 static bool ok = true;
59 
60 /* The SELinux context.  Start with a known invalid value so print_full_info
61    knows when 'context' has not been set to a meaningful value.  */
62 static char *context = NULL;
63 
64 static struct option const longopts[] =
65 {
66   {"context", no_argument, NULL, 'Z'},
67   {"group", no_argument, NULL, 'g'},
68   {"groups", no_argument, NULL, 'G'},
69   {"name", no_argument, NULL, 'n'},
70   {"real", no_argument, NULL, 'r'},
71   {"user", no_argument, NULL, 'u'},
72   {"zero", no_argument, NULL, 'z'},
73   {GETOPT_HELP_OPTION_DECL},
74   {GETOPT_VERSION_OPTION_DECL},
75   {NULL, 0, NULL, 0}
76 };
77 
78 void
usage(int status)79 usage (int status)
80 {
81   if (status != EXIT_SUCCESS)
82     emit_try_help ();
83   else
84     {
85       printf (_("Usage: %s [OPTION]... [USER]\n"), program_name);
86       fputs (_("\
87 Print user and group information for the specified USER,\n\
88 or (when USER omitted) for the current user.\n\
89 \n"),
90              stdout);
91       fputs (_("\
92   -a             ignore, for compatibility with other versions\n\
93   -Z, --context  print only the security context of the process\n\
94   -g, --group    print only the effective group ID\n\
95   -G, --groups   print all group IDs\n\
96   -n, --name     print a name instead of a number, for -ugG\n\
97   -r, --real     print the real ID instead of the effective ID, with -ugG\n\
98   -u, --user     print only the effective user ID\n\
99   -z, --zero     delimit entries with NUL characters, not whitespace;\n\
100                    not permitted in default format\n\
101 "), stdout);
102       fputs (HELP_OPTION_DESCRIPTION, stdout);
103       fputs (VERSION_OPTION_DESCRIPTION, stdout);
104       fputs (_("\
105 \n\
106 Without any OPTION, print some useful set of identified information.\n\
107 "), stdout);
108       emit_ancillary_info (PROGRAM_NAME);
109     }
110   exit (status);
111 }
112 
113 int
main(int argc,char ** argv)114 main (int argc, char **argv)
115 {
116   int optc;
117   int selinux_enabled = (is_selinux_enabled () > 0);
118   bool smack_enabled = is_smack_enabled ();
119   bool opt_zero = false;
120   char *pw_name = NULL;
121 
122   /* If true, output the list of all group IDs. -G */
123   bool just_group_list = false;
124   /* If true, output only the group ID(s). -g */
125   bool just_group = false;
126   /* If true, output real UID/GID instead of default effective UID/GID. -r */
127   bool use_real = false;
128   /* If true, output only the user ID(s). -u */
129   bool just_user = false;
130 
131   initialize_main (&argc, &argv);
132   set_program_name (argv[0]);
133   setlocale (LC_ALL, "");
134   bindtextdomain (PACKAGE, LOCALEDIR);
135   textdomain (PACKAGE);
136 
137   atexit (close_stdout);
138 
139   while ((optc = getopt_long (argc, argv, "agnruzGZ", longopts, NULL)) != -1)
140     {
141       switch (optc)
142         {
143         case 'a':
144           /* Ignore -a, for compatibility with SVR4.  */
145           break;
146 
147         case 'Z':
148           /* politely decline if we're not on a SELinux/SMACK-enabled kernel. */
149 #ifdef HAVE_SMACK
150           if (!selinux_enabled && !smack_enabled)
151             die (EXIT_FAILURE, 0,
152                  _("--context (-Z) works only on "
153                    "an SELinux/SMACK-enabled kernel"));
154 #else
155           if (!selinux_enabled)
156             die (EXIT_FAILURE, 0,
157                  _("--context (-Z) works only on an SELinux-enabled kernel"));
158 #endif
159           just_context = true;
160           break;
161 
162         case 'g':
163           just_group = true;
164           break;
165         case 'n':
166           use_name = true;
167           break;
168         case 'r':
169           use_real = true;
170           break;
171         case 'u':
172           just_user = true;
173           break;
174         case 'z':
175           opt_zero = true;
176           break;
177         case 'G':
178           just_group_list = true;
179           break;
180         case_GETOPT_HELP_CHAR;
181         case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
182         default:
183           usage (EXIT_FAILURE);
184         }
185     }
186 
187   size_t n_ids = argc - optind;
188   if (1 < n_ids)
189     {
190       error (0, 0, _("extra operand %s"), quote (argv[optind + 1]));
191       usage (EXIT_FAILURE);
192     }
193 
194   if (n_ids && just_context)
195     die (EXIT_FAILURE, 0,
196          _("cannot print security context when user specified"));
197 
198   if (just_user + just_group + just_group_list + just_context > 1)
199     die (EXIT_FAILURE, 0, _("cannot print \"only\" of more than one choice"));
200 
201   bool default_format = ! (just_user
202                            || just_group
203                            || just_group_list
204                            || just_context);
205 
206   if (default_format && (use_real || use_name))
207     die (EXIT_FAILURE, 0,
208          _("cannot print only names or real IDs in default format"));
209 
210   if (default_format && opt_zero)
211     die (EXIT_FAILURE, 0,
212          _("option --zero not permitted in default format"));
213 
214   /* If we are on a SELinux/SMACK-enabled kernel, no user is specified, and
215      either --context is specified or none of (-u,-g,-G) is specified,
216      and we're not in POSIXLY_CORRECT mode, get our context.  Otherwise,
217      leave the context variable alone - it has been initialized to an
218      invalid value that will be not displayed in print_full_info().  */
219   if (n_ids == 0
220       && (just_context
221           || (default_format && ! getenv ("POSIXLY_CORRECT"))))
222     {
223       /* Report failure only if --context (-Z) was explicitly requested.  */
224       if ((selinux_enabled && getcon (&context) && just_context)
225           || (smack_enabled
226               && smack_new_label_from_self (&context) < 0
227               && just_context))
228         die (EXIT_FAILURE, 0, _("can't get process context"));
229     }
230 
231   if (n_ids == 1)
232     {
233       struct passwd *pwd = NULL;
234       const char *spec = argv[optind];
235       /* Disallow an empty spec here as parse_user_spec() doesn't
236          give an error for that as it seems it's a valid way to
237          specify a noop or "reset special bits" depending on the system.  */
238       if (*spec)
239         {
240           if (parse_user_spec (spec, &euid, NULL, NULL, NULL) == NULL)
241             {
242               /* parse_user_spec will only extract a numeric spec,
243                  so we lookup that here to verify and also retrieve
244                  the PW_NAME used subsequently in group lookup.  */
245               pwd = getpwuid (euid);
246             }
247         }
248       if (pwd == NULL)
249         die (EXIT_FAILURE, 0, _("%s: no such user"), quote (spec));
250       pw_name = xstrdup (pwd->pw_name);
251       ruid = euid = pwd->pw_uid;
252       rgid = egid = pwd->pw_gid;
253     }
254   else
255     {
256       /* POSIX says identification functions (getuid, getgid, and
257          others) cannot fail, but they can fail under GNU/Hurd and a
258          few other systems.  Test for failure by checking errno.  */
259       uid_t NO_UID = -1;
260       gid_t NO_GID = -1;
261 
262       if (just_user ? !use_real
263           : !just_group && !just_group_list && !just_context)
264         {
265           errno = 0;
266           euid = geteuid ();
267           if (euid == NO_UID && errno)
268             die (EXIT_FAILURE, errno, _("cannot get effective UID"));
269         }
270 
271       if (just_user ? use_real
272           : !just_group && (just_group_list || !just_context))
273         {
274           errno = 0;
275           ruid = getuid ();
276           if (ruid == NO_UID && errno)
277             die (EXIT_FAILURE, errno, _("cannot get real UID"));
278         }
279 
280       if (!just_user && (just_group || just_group_list || !just_context))
281         {
282           errno = 0;
283           egid = getegid ();
284           if (egid == NO_GID && errno)
285             die (EXIT_FAILURE, errno, _("cannot get effective GID"));
286 
287           errno = 0;
288           rgid = getgid ();
289           if (rgid == NO_GID && errno)
290             die (EXIT_FAILURE, errno, _("cannot get real GID"));
291         }
292     }
293 
294   if (just_user)
295     {
296       print_user (use_real ? ruid : euid);
297     }
298   else if (just_group)
299     {
300       if (!print_group (use_real ? rgid : egid, use_name))
301         ok = false;
302     }
303   else if (just_group_list)
304     {
305       if (!print_group_list (pw_name, ruid, rgid, egid, use_name,
306                              opt_zero ? '\0' : ' '))
307         ok = false;
308     }
309   else if (just_context)
310     {
311       fputs (context, stdout);
312     }
313   else
314     {
315       print_full_info (pw_name);
316     }
317   putchar (opt_zero ? '\0' : '\n');
318 
319   IF_LINT (free (pw_name));
320   return ok ? EXIT_SUCCESS : EXIT_FAILURE;
321 }
322 
323 /* Convert a gid_t to string.  Do not use this function directly.
324    Instead, use it via the gidtostr macro.
325    Beware that it returns a pointer to static storage.  */
326 static char *
gidtostr_ptr(gid_t const * gid)327 gidtostr_ptr (gid_t const *gid)
328 {
329   static char buf[INT_BUFSIZE_BOUND (uintmax_t)];
330   return umaxtostr (*gid, buf);
331 }
332 #define gidtostr(g) gidtostr_ptr (&(g))
333 
334 /* Convert a uid_t to string.  Do not use this function directly.
335    Instead, use it via the uidtostr macro.
336    Beware that it returns a pointer to static storage.  */
337 static char *
uidtostr_ptr(uid_t const * uid)338 uidtostr_ptr (uid_t const *uid)
339 {
340   static char buf[INT_BUFSIZE_BOUND (uintmax_t)];
341   return umaxtostr (*uid, buf);
342 }
343 #define uidtostr(u) uidtostr_ptr (&(u))
344 
345 /* Print the name or value of user ID UID. */
346 
347 static void
print_user(uid_t uid)348 print_user (uid_t uid)
349 {
350   struct passwd *pwd = NULL;
351 
352   if (use_name)
353     {
354       pwd = getpwuid (uid);
355       if (pwd == NULL)
356         {
357           error (0, 0, _("cannot find name for user ID %s"),
358                  uidtostr (uid));
359           ok = false;
360         }
361     }
362 
363   char *s = pwd ? pwd->pw_name : uidtostr (uid);
364   fputs (s, stdout);
365 }
366 
367 /* Print all of the info about the user's user and group IDs. */
368 
369 static void
print_full_info(const char * username)370 print_full_info (const char *username)
371 {
372   struct passwd *pwd;
373   struct group *grp;
374 
375   printf (_("uid=%s"), uidtostr (ruid));
376   pwd = getpwuid (ruid);
377   if (pwd)
378     printf ("(%s)", pwd->pw_name);
379 
380   printf (_(" gid=%s"), gidtostr (rgid));
381   grp = getgrgid (rgid);
382   if (grp)
383     printf ("(%s)", grp->gr_name);
384 
385   if (euid != ruid)
386     {
387       printf (_(" euid=%s"), uidtostr (euid));
388       pwd = getpwuid (euid);
389       if (pwd)
390         printf ("(%s)", pwd->pw_name);
391     }
392 
393   if (egid != rgid)
394     {
395       printf (_(" egid=%s"), gidtostr (egid));
396       grp = getgrgid (egid);
397       if (grp)
398         printf ("(%s)", grp->gr_name);
399     }
400 
401   {
402     gid_t *groups;
403 
404     gid_t primary_group;
405     if (username)
406       primary_group = pwd ? pwd->pw_gid : -1;
407     else
408       primary_group = egid;
409 
410     int n_groups = xgetgroups (username, primary_group, &groups);
411     if (n_groups < 0)
412       {
413         if (username)
414           error (0, errno, _("failed to get groups for user %s"),
415                  quote (username));
416         else
417           error (0, errno, _("failed to get groups for the current process"));
418         ok = false;
419         return;
420       }
421 
422     if (n_groups > 0)
423       fputs (_(" groups="), stdout);
424     for (int i = 0; i < n_groups; i++)
425       {
426         if (i > 0)
427           putchar (',');
428         fputs (gidtostr (groups[i]), stdout);
429         grp = getgrgid (groups[i]);
430         if (grp)
431           printf ("(%s)", grp->gr_name);
432       }
433     free (groups);
434   }
435 
436   /* POSIX mandates the precise output format, and that it not include
437      any context=... part, so skip that if POSIXLY_CORRECT is set.  */
438   if (context)
439     printf (_(" context=%s"), context);
440 }
441