root/maint/gnulib/lib/userspec.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. is_number
  2. parse_with_separator
  3. parse_user_spec
  4. main

   1 /* userspec.c -- Parse a user and group string.
   2    Copyright (C) 1989-1992, 1997-1998, 2000, 2002-2021 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 <https://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 #ifdef __DJGPP__
  75 
  76 /* ISDIGIT differs from isdigit, as follows:
  77    - Its arg may be any int or unsigned int; it need not be an unsigned char
  78      or EOF.
  79    - It's typically faster.
  80    POSIX says that only '0' through '9' are digits.  Prefer ISDIGIT to
  81    isdigit unless it's important to use the locale's definition
  82    of "digit" even when the host does not conform to POSIX.  */
  83 # define ISDIGIT(c) ((unsigned int) (c) - '0' <= 9)
  84 
  85 /* Return true if STR represents an unsigned decimal integer.  */
  86 
  87 static bool
  88 is_number (const char *str)
     /* [previous][next][first][last][top][bottom][index][help] */
  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 parse_with_separator (char const *spec, char const *separator,
     /* [previous][next][first][last][top][bottom][index][help] */
 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   char *gname = NULL;
 116   uid_t unum = *uid;
 117   gid_t gnum = gid ? *gid : -1;
 118 
 119   error_msg = NULL;
 120   if (username)
 121     *username = NULL;
 122   if (groupname)
 123     *groupname = NULL;
 124 
 125   /* Set U and G to nonzero length strings corresponding to user and
 126      group specifiers or to NULL.  If U is not NULL, it is a newly
 127      allocated string.  */
 128 
 129   u = NULL;
 130   if (separator == NULL)
 131     {
 132       if (*spec)
 133         u = xstrdup (spec);
 134     }
 135   else
 136     {
 137       idx_t ulen = separator - spec;
 138       if (ulen != 0)
 139         {
 140           u = ximemdup (spec, ulen + 1);
 141           u[ulen] = '\0';
 142         }
 143     }
 144 
 145   g = (separator == NULL || *(separator + 1) == '\0'
 146        ? NULL
 147        : separator + 1);
 148 
 149 #ifdef __DJGPP__
 150   /* Pretend that we are the user U whose group is G.  This makes
 151      pwd and grp functions "know" about the UID and GID of these.  */
 152   if (u && !is_number (u))
 153     setenv ("USER", u, 1);
 154   if (g && !is_number (g))
 155     setenv ("GROUP", g, 1);
 156 #endif
 157 
 158   if (u != NULL)
 159     {
 160       /* If it starts with "+", skip the look-up.  */
 161       pwd = (*u == '+' ? NULL : getpwnam (u));
 162       if (pwd == NULL)
 163         {
 164           bool use_login_group = (separator != NULL && g == NULL);
 165           if (use_login_group)
 166             {
 167               /* If there is no group,
 168                  then there may not be a trailing ":", either.  */
 169               error_msg = E_bad_spec;
 170             }
 171           else
 172             {
 173               unsigned long int tmp;
 174               if (xstrtoul (u, NULL, 10, &tmp, "") == LONGINT_OK
 175                   && tmp <= MAXUID && (uid_t) tmp != (uid_t) -1)
 176                 unum = tmp;
 177               else
 178                 error_msg = E_invalid_user;
 179             }
 180         }
 181       else
 182         {
 183           unum = pwd->pw_uid;
 184           if (g == NULL && separator != NULL)
 185             {
 186               /* A separator was given, but a group was not specified,
 187                  so get the login group.  */
 188               char buf[INT_BUFSIZE_BOUND (uintmax_t)];
 189               gnum = pwd->pw_gid;
 190               grp = getgrgid (gnum);
 191               gname = xstrdup (grp ? grp->gr_name : umaxtostr (gnum, buf));
 192               endgrent ();
 193             }
 194         }
 195       endpwent ();
 196     }
 197 
 198   if (g != NULL && error_msg == NULL)
 199     {
 200       /* Explicit group.  */
 201       /* If it starts with "+", skip the look-up.  */
 202       grp = (*g == '+' ? NULL : getgrnam (g));
 203       if (grp == NULL)
 204         {
 205           unsigned long int tmp;
 206           if (xstrtoul (g, NULL, 10, &tmp, "") == LONGINT_OK
 207               && tmp <= MAXGID && (gid_t) tmp != (gid_t) -1)
 208             gnum = tmp;
 209           else
 210             error_msg = E_invalid_group;
 211         }
 212       else
 213         gnum = grp->gr_gid;
 214       endgrent ();              /* Save a file descriptor.  */
 215       gname = xstrdup (g);
 216     }
 217 
 218   if (error_msg == NULL)
 219     {
 220       *uid = unum;
 221       if (gid)
 222         *gid = gnum;
 223       if (username)
 224         {
 225           *username = u;
 226           u = NULL;
 227         }
 228       if (groupname)
 229         {
 230           *groupname = gname;
 231           gname = NULL;
 232         }
 233     }
 234 
 235   free (u);
 236   free (gname);
 237   return error_msg ? _(error_msg) : NULL;
 238 }
 239 
 240 /* Extract from SPEC, which has the form "[user][:.][group]",
 241    a USERNAME, UID U, GROUPNAME, and GID G.
 242    If the GID parameter is NULL the entire SPEC is treated as a user.
 243    If the USERNAME and GROUPNAME parameters are NULL they're ignored.
 244    Either user or group, or both, must be present.
 245    If the group is omitted but the separator is given,
 246    use the given user's login group.
 247    If SPEC contains a ':', then use that as the separator, ignoring
 248    any '.'s.  If there is no ':', but there is a '.', then first look
 249    up the entire SPEC as a login name.  If that look-up fails, then
 250    try again interpreting the '.'  as a separator.
 251 
 252    USERNAME and GROUPNAME will be in newly malloc'd memory.
 253    Either one might be NULL instead, indicating that it was not
 254    given and the corresponding numeric ID was left unchanged.
 255 
 256    Return NULL if successful, a static error message string if not.  */
 257 
 258 char const *
 259 parse_user_spec (char const *spec, uid_t *uid, gid_t *gid,
     /* [previous][next][first][last][top][bottom][index][help] */
 260                  char **username, char **groupname)
 261 {
 262   char const *colon = gid ? strchr (spec, ':') : NULL;
 263   char const *error_msg =
 264     parse_with_separator (spec, colon, uid, gid, username, groupname);
 265 
 266   if (gid && !colon && error_msg)
 267     {
 268       /* If there's no colon but there is a dot, and if looking up the
 269          whole spec failed (i.e., the spec is not an owner name that
 270          includes a dot), then try again, but interpret the dot as a
 271          separator.  This is a compatible extension to POSIX, since
 272          the POSIX-required behavior is always tried first.  */
 273 
 274       char const *dot = strchr (spec, '.');
 275       if (dot
 276           && ! parse_with_separator (spec, dot, uid, gid, username, groupname))
 277         error_msg = NULL;
 278     }
 279 
 280   return error_msg;
 281 }
 282 
 283 #ifdef TEST
 284 
 285 # define NULL_CHECK(s) ((s) == NULL ? "(null)" : (s))
 286 
 287 int
 288 main (int argc, char **argv)
     /* [previous][next][first][last][top][bottom][index][help] */
 289 {
 290   int i;
 291 
 292   for (i = 1; i < argc; i++)
 293     {
 294       const char *e;
 295       char *username, *groupname;
 296       uid_t uid;
 297       gid_t gid;
 298       char *tmp;
 299 
 300       tmp = strdup (argv[i]);
 301       e = parse_user_spec (tmp, &uid, &gid, &username, &groupname);
 302       free (tmp);
 303       printf ("%s: %lu %lu %s %s %s\n",
 304               argv[i],
 305               (unsigned long int) uid,
 306               (unsigned long int) gid,
 307               NULL_CHECK (username),
 308               NULL_CHECK (groupname),
 309               NULL_CHECK (e));
 310     }
 311 
 312   exit (0);
 313 }
 314 
 315 #endif
 316 
 317 /*
 318 Local Variables:
 319 indent-tabs-mode: nil
 320 End:
 321 */

/* [previous][next][first][last][top][bottom][index][help] */