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