LCOV - code coverage report
Current view: top level - lib - userspec.c (source / functions) Hit Total Coverage
Test: coreutils.info Lines: 53 60 88.3 %
Date: 2018-01-30 Functions: 2 2 100.0 %

          Line data    Source code
       1             : /* userspec.c -- Parse a user and group string.
       2             :    Copyright (C) 1989-1992, 1997-1998, 2000, 2002-2007 Free Software
       3             :    Foundation, Inc.
       4             : 
       5             :    This program is free software: you can redistribute it and/or modify
       6             :    it under the terms of the GNU General Public License as published by
       7             :    the Free Software Foundation; either version 3 of the License, or
       8             :    (at your option) any later version.
       9             : 
      10             :    This program is distributed in the hope that it will be useful,
      11             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
      12             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      13             :    GNU General Public License for more details.
      14             : 
      15             :    You should have received a copy of the GNU General Public License
      16             :    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
      17             : 
      18             : /* Written by David MacKenzie <djm@gnu.ai.mit.edu>.  */
      19             : 
      20             : #include <config.h>
      21             : 
      22             : /* Specification.  */
      23             : #include "userspec.h"
      24             : 
      25             : #include <stdbool.h>
      26             : #include <stdio.h>
      27             : #include <sys/types.h>
      28             : #include <pwd.h>
      29             : #include <grp.h>
      30             : 
      31             : #if HAVE_SYS_PARAM_H
      32             : # include <sys/param.h>
      33             : #endif
      34             : 
      35             : #include <limits.h>
      36             : #include <stdlib.h>
      37             : #include <string.h>
      38             : 
      39             : #include <unistd.h>
      40             : 
      41             : #include "intprops.h"
      42             : #include "inttostr.h"
      43             : #include "xalloc.h"
      44             : #include "xstrtol.h"
      45             : 
      46             : #include "gettext.h"
      47             : #define _(msgid) gettext (msgid)
      48             : #define N_(msgid) msgid
      49             : 
      50             : #ifndef HAVE_ENDGRENT
      51             : # define endgrent() ((void) 0)
      52             : #endif
      53             : 
      54             : #ifndef HAVE_ENDPWENT
      55             : # define endpwent() ((void) 0)
      56             : #endif
      57             : 
      58             : #ifndef UID_T_MAX
      59             : # define UID_T_MAX TYPE_MAXIMUM (uid_t)
      60             : #endif
      61             : 
      62             : #ifndef GID_T_MAX
      63             : # define GID_T_MAX TYPE_MAXIMUM (gid_t)
      64             : #endif
      65             : 
      66             : /* MAXUID may come from limits.h or sys/params.h.  */
      67             : #ifndef MAXUID
      68             : # define MAXUID UID_T_MAX
      69             : #endif
      70             : #ifndef MAXGID
      71             : # define MAXGID GID_T_MAX
      72             : #endif
      73             : 
      74             : /* ISDIGIT differs from isdigit, as follows:
      75             :    - Its arg may be any int or unsigned int; it need not be an unsigned char
      76             :      or EOF.
      77             :    - It's typically faster.
      78             :    POSIX says that only '0' through '9' are digits.  Prefer ISDIGIT to
      79             :    isdigit unless it's important to use the locale's definition
      80             :    of `digit' even when the host does not conform to POSIX.  */
      81             : #define ISDIGIT(c) ((unsigned int) (c) - '0' <= 9)
      82             : 
      83             : #ifdef __DJGPP__
      84             : 
      85             : /* Return true if STR represents an unsigned decimal integer.  */
      86             : 
      87             : static bool
      88             : is_number (const char *str)
      89             : {
      90             :   do
      91             :     {
      92             :       if (!ISDIGIT (*str))
      93             :         return false;
      94             :     }
      95             :   while (*++str);
      96             : 
      97             :   return true;
      98             : }
      99             : #endif
     100             : 
     101             : static char const *
     102          45 : parse_with_separator (char const *spec, char const *separator,
     103             :                       uid_t *uid, gid_t *gid,
     104             :                       char **username, char **groupname)
     105             : {
     106             :   static const char *E_invalid_user = N_("invalid user");
     107             :   static const char *E_invalid_group = N_("invalid group");
     108             :   static const char *E_bad_spec = N_("invalid spec");
     109             : 
     110             :   const char *error_msg;
     111             :   struct passwd *pwd;
     112             :   struct group *grp;
     113             :   char *u;
     114             :   char const *g;
     115          45 :   char *gname = NULL;
     116          45 :   uid_t unum = *uid;
     117          45 :   gid_t gnum = *gid;
     118             : 
     119          45 :   error_msg = NULL;
     120          45 :   *username = *groupname = NULL;
     121             : 
     122             :   /* Set U and G to nonzero length strings corresponding to user and
     123             :      group specifiers or to NULL.  If U is not NULL, it is a newly
     124             :      allocated string.  */
     125             : 
     126          45 :   u = NULL;
     127          45 :   if (separator == NULL)
     128             :     {
     129          10 :       if (*spec)
     130           7 :         u = xstrdup (spec);
     131             :     }
     132             :   else
     133             :     {
     134          35 :       size_t ulen = separator - spec;
     135          35 :       if (ulen != 0)
     136             :         {
     137           3 :           u = xmemdup (spec, ulen + 1);
     138           3 :           u[ulen] = '\0';
     139             :         }
     140             :     }
     141             : 
     142          80 :   g = (separator == NULL || *(separator + 1) == '\0'
     143             :        ? NULL
     144          56 :        : separator + 1);
     145             : 
     146             : #ifdef __DJGPP__
     147             :   /* Pretend that we are the user U whose group is G.  This makes
     148             :      pwd and grp functions ``know'' about the UID and GID of these.  */
     149             :   if (u && !is_number (u))
     150             :     setenv ("USER", u, 1);
     151             :   if (g && !is_number (g))
     152             :     setenv ("GROUP", g, 1);
     153             : #endif
     154             : 
     155          45 :   if (u != NULL)
     156             :     {
     157             :       /* If it starts with "+", skip the look-up.  */
     158          10 :       pwd = (*u == '+' ? NULL : getpwnam (u));
     159          10 :       if (pwd == NULL)
     160             :         {
     161          10 :           bool use_login_group = (separator != NULL && g == NULL);
     162          10 :           if (use_login_group)
     163             :             {
     164             :               /* If there is no group,
     165             :                  then there may not be a trailing ":", either.  */
     166           2 :               error_msg = E_bad_spec;
     167             :             }
     168             :           else
     169             :             {
     170             :               unsigned long int tmp;
     171           8 :               if (xstrtoul (u, NULL, 10, &tmp, "") == LONGINT_OK
     172           1 :                   && tmp <= MAXUID)
     173           1 :                 unum = tmp;
     174             :               else
     175           7 :                 error_msg = E_invalid_user;
     176             :             }
     177             :         }
     178             :       else
     179             :         {
     180           0 :           unum = pwd->pw_uid;
     181           0 :           if (g == NULL && separator != NULL)
     182             :             {
     183             :               /* A separator was given, but a group was not specified,
     184             :                  so get the login group.  */
     185             :               char buf[INT_BUFSIZE_BOUND (uintmax_t)];
     186           0 :               gnum = pwd->pw_gid;
     187           0 :               grp = getgrgid (gnum);
     188           0 :               gname = xstrdup (grp ? grp->gr_name : umaxtostr (gnum, buf));
     189           0 :               endgrent ();
     190             :             }
     191             :         }
     192          10 :       endpwent ();
     193             :     }
     194             : 
     195          45 :   if (g != NULL && error_msg == NULL)
     196             :     {
     197             :       /* Explicit group.  */
     198             :       /* If it starts with "+", skip the look-up.  */
     199          10 :       grp = (*g == '+' ? NULL : getgrnam (g));
     200          10 :       if (grp == NULL)
     201             :         {
     202             :           unsigned long int tmp;
     203           8 :           if (xstrtoul (g, NULL, 10, &tmp, "") == LONGINT_OK && tmp <= MAXGID)
     204           1 :             gnum = tmp;
     205             :           else
     206           7 :             error_msg = E_invalid_group;
     207             :         }
     208             :       else
     209           2 :         gnum = grp->gr_gid;
     210          10 :       endgrent ();              /* Save a file descriptor.  */
     211          10 :       gname = xstrdup (g);
     212             :     }
     213             : 
     214          45 :   if (error_msg == NULL)
     215             :     {
     216          29 :       *uid = unum;
     217          29 :       *gid = gnum;
     218          29 :       *username = u;
     219          29 :       *groupname = gname;
     220          29 :       u = NULL;
     221             :     }
     222             :   else
     223          16 :     free (gname);
     224             : 
     225          45 :   free (u);
     226          45 :   return _(error_msg);
     227             : }
     228             : 
     229             : /* Extract from SPEC, which has the form "[user][:.][group]",
     230             :    a USERNAME, UID U, GROUPNAME, and GID G.
     231             :    Either user or group, or both, must be present.
     232             :    If the group is omitted but the separator is given,
     233             :    use the given user's login group.
     234             :    If SPEC contains a `:', then use that as the separator, ignoring
     235             :    any `.'s.  If there is no `:', but there is a `.', then first look
     236             :    up the entire SPEC as a login name.  If that look-up fails, then
     237             :    try again interpreting the `.'  as a separator.
     238             : 
     239             :    USERNAME and GROUPNAME will be in newly malloc'd memory.
     240             :    Either one might be NULL instead, indicating that it was not
     241             :    given and the corresponding numeric ID was left unchanged.
     242             : 
     243             :    Return NULL if successful, a static error message string if not.  */
     244             : 
     245             : char const *
     246          44 : parse_user_spec (char const *spec, uid_t *uid, gid_t *gid,
     247             :                  char **username, char **groupname)
     248             : {
     249          44 :   char const *colon = strchr (spec, ':');
     250          44 :   char const *error_msg =
     251             :     parse_with_separator (spec, colon, uid, gid, username, groupname);
     252             : 
     253          44 :   if (!colon && error_msg)
     254             :     {
     255             :       /* If there's no colon but there is a dot, and if looking up the
     256             :          whole spec failed (i.e., the spec is not a owner name that
     257             :          includes a dot), then try again, but interpret the dot as a
     258             :          separator.  This is a compatible extension to POSIX, since
     259             :          the POSIX-required behavior is always tried first.  */
     260             : 
     261           6 :       char const *dot = strchr (spec, '.');
     262           6 :       if (dot
     263           1 :           && ! parse_with_separator (spec, dot, uid, gid, username, groupname))
     264           0 :         error_msg = NULL;
     265             :     }
     266             : 
     267          44 :   return error_msg;
     268             : }
     269             : 
     270             : #ifdef TEST
     271             : 
     272             : # define NULL_CHECK(s) ((s) == NULL ? "(null)" : (s))
     273             : 
     274             : int
     275             : main (int argc, char **argv)
     276             : {
     277             :   int i;
     278             : 
     279             :   for (i = 1; i < argc; i++)
     280             :     {
     281             :       const char *e;
     282             :       char *username, *groupname;
     283             :       uid_t uid;
     284             :       gid_t gid;
     285             :       char *tmp;
     286             : 
     287             :       tmp = strdup (argv[i]);
     288             :       e = parse_user_spec (tmp, &uid, &gid, &username, &groupname);
     289             :       free (tmp);
     290             :       printf ("%s: %lu %lu %s %s %s\n",
     291             :               argv[i],
     292             :               (unsigned long int) uid,
     293             :               (unsigned long int) gid,
     294             :               NULL_CHECK (username),
     295             :               NULL_CHECK (groupname),
     296             :               NULL_CHECK (e));
     297             :     }
     298             : 
     299             :   exit (0);
     300             : }
     301             : 
     302             : #endif

Generated by: LCOV version 1.10