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
|