1 /* userspec.c -- Parse a user and group string.
2 Copyright (C) 1989-1992, 2001, 2004-2005, 2007, 2010, 2014-2015, 2017
3 Free Software 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, or (at your option)
8 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
16 License along with this program; if not, write to the Free
17 Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 Boston, MA 02110-1301 USA. */
19
20 /* Written by David MacKenzie <djm@gnu.ai.mit.edu>. */
21
22 #include <system.h>
23 #include <alloca.h>
24 #include <stdio.h>
25 #include <ctype.h>
26 #include <sys/types.h>
27
28 #ifndef HAVE_ENDPWENT
29 # define endpwent()
30 #endif
31 #ifndef HAVE_ENDGRENT
32 # define endgrent()
33 #endif
34
35 /* Perform the equivalent of the statement `dest = strdup (src);',
36 but obtaining storage via alloca instead of from the heap. */
37
38 #define V_STRDUP(dest, src) \
39 do \
40 { \
41 int _len = strlen ((src)); \
42 (dest) = (char *) alloca (_len + 1); \
43 strcpy (dest, src); \
44 } \
45 while (0)
46
47 /* Return nonzero if STR represents an unsigned decimal integer,
48 otherwise return 0. */
49
50 static int
isnumber_p(const char * str)51 isnumber_p (const char *str)
52 {
53 for (; *str; str++)
54 if (!isdigit (*str))
55 return 0;
56 return 1;
57 }
58
59 /* Extract from NAME, which has the form "[user][:.][group]",
60 a USERNAME, UID U, GROUPNAME, and GID G.
61 Either user or group, or both, must be present.
62 If the group is omitted but the ":" or "." separator is given,
63 use the given user's login group.
64
65 USERNAME and GROUPNAME will be in newly malloc'd memory.
66 Either one might be NULL instead, indicating that it was not
67 given and the corresponding numeric ID was left unchanged.
68
69 Return NULL if successful, a static error message string if not. */
70
71 const char *
parse_user_spec(const char * spec_arg,uid_t * uid,gid_t * gid,char ** username_arg,char ** groupname_arg)72 parse_user_spec (const char *spec_arg, uid_t *uid, gid_t *gid,
73 char **username_arg, char **groupname_arg)
74 {
75 static const char *tired = "virtual memory exhausted";
76 const char *error_msg;
77 char *spec; /* A copy we can write on. */
78 struct passwd *pwd;
79 struct group *grp;
80 char *g, *u, *separator;
81 char *groupname;
82
83 error_msg = NULL;
84 *username_arg = *groupname_arg = NULL;
85 groupname = NULL;
86
87 V_STRDUP (spec, spec_arg);
88
89 /* Find the separator if there is one. */
90 separator = strchr (spec, ':');
91 if (separator == NULL)
92 separator = strchr (spec, '.');
93
94 /* Replace separator with a NUL. */
95 if (separator != NULL)
96 *separator = '\0';
97
98 /* Set U and G to non-zero length strings corresponding to user and
99 group specifiers or to NULL. */
100 u = (*spec == '\0' ? NULL : spec);
101
102 g = (separator == NULL || *(separator + 1) == '\0'
103 ? NULL
104 : separator + 1);
105
106 if (u == NULL && g == NULL)
107 return "can not omit both user and group";
108
109 if (u != NULL)
110 {
111 if (*u == '+')
112 {
113 pwd = NULL;
114 ++u;
115 }
116 else
117 pwd = getpwnam (u);
118
119 if (pwd == NULL)
120 {
121 if (!isnumber_p (u))
122 error_msg = _("invalid user");
123 else
124 {
125 int use_login_group;
126 use_login_group = (separator != NULL && g == NULL);
127 if (use_login_group)
128 error_msg = _("cannot get the login group of a numeric UID");
129 else
130 *uid = atoi (u);
131 }
132 }
133 else
134 {
135 *uid = pwd->pw_uid;
136 if (g == NULL && separator != NULL)
137 {
138 /* A separator was given, but a group was not specified,
139 so get the login group. */
140 *gid = pwd->pw_gid;
141 grp = getgrgid (pwd->pw_gid);
142 if (grp == NULL)
143 {
144 /* This is enough room to hold the unsigned decimal
145 representation of any 32-bit quantity and the trailing
146 zero byte. */
147 char uint_buf[21];
148 sprintf (uint_buf, "%u", (unsigned) (pwd->pw_gid));
149 V_STRDUP (groupname, uint_buf);
150 }
151 else
152 {
153 V_STRDUP (groupname, grp->gr_name);
154 }
155 endgrent ();
156 }
157 }
158 endpwent ();
159 }
160
161 if (g != NULL && error_msg == NULL)
162 {
163 /* Explicit group. */
164 if (*g == '+')
165 {
166 grp = NULL;
167 ++g;
168 }
169 else
170 grp = getgrnam (g);
171
172 if (grp == NULL)
173 {
174 if (!isnumber_p (g))
175 error_msg = _("invalid group");
176 else
177 *gid = atoi (g);
178 }
179 else
180 *gid = grp->gr_gid;
181 endgrent (); /* Save a file descriptor. */
182
183 if (error_msg == NULL)
184 V_STRDUP (groupname, g);
185 }
186
187 if (error_msg == NULL)
188 {
189 if (u != NULL)
190 {
191 *username_arg = strdup (u);
192 if (*username_arg == NULL)
193 error_msg = tired;
194 }
195
196 if (groupname != NULL && error_msg == NULL)
197 {
198 *groupname_arg = strdup (groupname);
199 if (*groupname_arg == NULL)
200 {
201 if (*username_arg != NULL)
202 {
203 free (*username_arg);
204 *username_arg = NULL;
205 }
206 error_msg = tired;
207 }
208 }
209 }
210
211 return error_msg;
212 }
213
214 #ifdef TEST
215
216 #define NULL_CHECK(s) ((s) == NULL ? "(null)" : (s))
217
218 int
main(int argc,char ** argv)219 main (int argc, char **argv)
220 {
221 int i;
222
223 for (i = 1; i < argc; i++)
224 {
225 const char *e;
226 char *username, *groupname;
227 uid_t uid;
228 gid_t gid;
229 char *tmp;
230
231 tmp = strdup (argv[i]);
232 e = parse_user_spec (tmp, &uid, &gid, &username, &groupname);
233 free (tmp);
234 printf ("%s: %u %u %s %s %s\n",
235 argv[i],
236 (unsigned int) uid,
237 (unsigned int) gid,
238 NULL_CHECK (username),
239 NULL_CHECK (groupname),
240 NULL_CHECK (e));
241 }
242
243 exit (0);
244 }
245
246 #endif
247