]>
Commit | Line | Data |
---|---|---|
b168057a | 1 | /* Copyright (C) 1998-2015 Free Software Foundation, Inc. |
899d423e | 2 | This file is part of the GNU C Library. |
3b965a7d | 3 | Contributed by Thorsten Kukuk <kukuk@suse.de>, 1998. |
899d423e UD |
4 | |
5 | The GNU C Library is free software; you can redistribute it and/or | |
41bdb6e2 AJ |
6 | modify it under the terms of the GNU Lesser General Public |
7 | License as published by the Free Software Foundation; either | |
8 | version 2.1 of the License, or (at your option) any later version. | |
899d423e UD |
9 | |
10 | The GNU C Library 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 GNU | |
41bdb6e2 | 13 | Lesser General Public License for more details. |
899d423e | 14 | |
41bdb6e2 | 15 | You should have received a copy of the GNU Lesser General Public |
59ba27a6 PE |
16 | License along with the GNU C Library; if not, see |
17 | <http://www.gnu.org/licenses/>. */ | |
899d423e | 18 | |
b9b9a51e | 19 | #include <alloca.h> |
899d423e UD |
20 | #include <ctype.h> |
21 | #include <errno.h> | |
b9b9a51e UD |
22 | #include <grp.h> |
23 | #include <nss.h> | |
ff0913d3 | 24 | #include <pwd.h> |
899d423e UD |
25 | #include <string.h> |
26 | #include <unistd.h> | |
27 | #include <rpcsvc/yp.h> | |
28 | #include <rpcsvc/ypclnt.h> | |
7603ea28 | 29 | #include <sys/param.h> |
899d423e UD |
30 | |
31 | #include "nss-nis.h" | |
433f49c4 | 32 | #include <libnsl.h> |
899d423e UD |
33 | |
34 | /* Get the declaration of the parser function. */ | |
35 | #define ENTNAME grent | |
36 | #define STRUCTURE group | |
37 | #define EXTERN_PARSER | |
38 | #include <nss/nss_files/files-parse.c> | |
39 | ||
899d423e UD |
40 | |
41 | static enum nss_status | |
ff0913d3 | 42 | internal_setgrent (char *domainname, intern_t *intern) |
899d423e | 43 | { |
899d423e UD |
44 | struct ypall_callback ypcb; |
45 | enum nss_status status; | |
46 | ||
e0d4e91d | 47 | ypcb.foreach = _nis_saveit; |
899d423e UD |
48 | ypcb.data = (char *) intern; |
49 | status = yperr2nss (yp_all (domainname, "group.byname", &ypcb)); | |
e0d4e91d UD |
50 | |
51 | /* Mark the last buffer as full. */ | |
52 | if (intern->next != NULL) | |
53 | intern->next->size = intern->offset; | |
54 | ||
899d423e | 55 | intern->next = intern->start; |
e0d4e91d | 56 | intern->offset = 0; |
899d423e UD |
57 | |
58 | return status; | |
59 | } | |
60 | ||
e0d4e91d | 61 | |
899d423e UD |
62 | static enum nss_status |
63 | internal_getgrent_r (struct group *grp, char *buffer, size_t buflen, | |
64 | int *errnop, intern_t *intern) | |
65 | { | |
899d423e | 66 | if (intern->start == NULL) |
ff0913d3 | 67 | return NSS_STATUS_NOTFOUND; |
899d423e UD |
68 | |
69 | /* Get the next entry until we found a correct one. */ | |
ab9a9ff8 | 70 | int parse_res; |
899d423e UD |
71 | do |
72 | { | |
e0d4e91d UD |
73 | struct response_t *bucket = intern->next; |
74 | ||
a1ffb40e | 75 | if (__glibc_unlikely (intern->offset >= bucket->size)) |
e0d4e91d UD |
76 | { |
77 | if (bucket->next == NULL) | |
78 | return NSS_STATUS_NOTFOUND; | |
79 | ||
80 | /* We look at all the content in the current bucket. Go on | |
81 | to the next. */ | |
82 | bucket = intern->next = bucket->next; | |
83 | intern->offset = 0; | |
84 | } | |
34816665 | 85 | |
e0d4e91d UD |
86 | char *p; |
87 | for (p = &bucket->mem[intern->offset]; isspace (*p); ++p) | |
88 | ++intern->offset; | |
89 | ||
90 | size_t len = strlen (p) + 1; | |
a1ffb40e | 91 | if (__glibc_unlikely (len > buflen)) |
e0d4e91d UD |
92 | { |
93 | *errnop = ERANGE; | |
94 | return NSS_STATUS_TRYAGAIN; | |
95 | } | |
96 | ||
97 | /* We unfortunately have to copy the data in the user-provided | |
98 | buffer because that buffer might be around for a very long | |
99 | time and the servent structure must remain valid. If we would | |
100 | rely on the BUCKET memory the next 'setservent' or 'endservent' | |
101 | call would destroy it. | |
102 | ||
103 | The important thing is that it is a single NUL-terminated | |
104 | string. This is what the parsing routine expects. */ | |
105 | p = memcpy (buffer, &bucket->mem[intern->offset], len); | |
899d423e | 106 | |
ab9a9ff8 UD |
107 | parse_res = _nss_files_parse_grent (p, grp, (void *) buffer, buflen, |
108 | errnop); | |
a1ffb40e | 109 | if (__glibc_unlikely (parse_res == -1)) |
899d423e | 110 | return NSS_STATUS_TRYAGAIN; |
e0d4e91d UD |
111 | |
112 | intern->offset += len; | |
899d423e UD |
113 | } |
114 | while (!parse_res); | |
115 | ||
116 | return NSS_STATUS_SUCCESS; | |
117 | } | |
118 | ||
ff0913d3 | 119 | |
ff0913d3 UD |
120 | static int |
121 | get_uid (const char *user, uid_t *uidp) | |
122 | { | |
123 | size_t buflen = sysconf (_SC_GETPW_R_SIZE_MAX); | |
124 | char *buf = (char *) alloca (buflen); | |
125 | ||
126 | while (1) | |
127 | { | |
128 | struct passwd result; | |
129 | struct passwd *resp; | |
130 | ||
131 | int r = getpwnam_r (user, &result, buf, buflen, &resp); | |
132 | if (r == 0 && resp != NULL) | |
133 | { | |
134 | *uidp = resp->pw_uid; | |
135 | return 0; | |
136 | } | |
137 | ||
138 | if (r != ERANGE) | |
139 | break; | |
140 | ||
caa78cf8 | 141 | buf = extend_alloca (buf, buflen, 2 * buflen); |
ff0913d3 UD |
142 | } |
143 | ||
144 | return 1; | |
145 | } | |
146 | ||
147 | ||
148 | static enum nss_status | |
149 | initgroups_netid (uid_t uid, gid_t group, long int *start, long int *size, | |
150 | gid_t **groupsp, long int limit, int *errnop, | |
151 | const char *domainname) | |
152 | { | |
315eb1d8 AS |
153 | /* Limit domainname length to the maximum size of an RPC packet. */ |
154 | if (strlen (domainname) > UDPMSGSIZE) | |
155 | { | |
156 | *errnop = ERANGE; | |
157 | return NSS_STATUS_UNAVAIL; | |
158 | } | |
159 | ||
ff0913d3 UD |
160 | /* Prepare the key. The form is "unix.UID@DOMAIN" with the UID and |
161 | DOMAIN field filled in appropriately. */ | |
162 | char key[sizeof ("unix.@") + sizeof (uid_t) * 3 + strlen (domainname)]; | |
163 | ssize_t keylen = snprintf (key, sizeof (key), "unix.%lu@%s", | |
164 | (unsigned long int) uid, domainname); | |
165 | ||
ff0913d3 UD |
166 | char *result; |
167 | int reslen; | |
ab9a9ff8 UD |
168 | int yperr = yp_match (domainname, "netid.byname", key, keylen, &result, |
169 | &reslen); | |
a1ffb40e | 170 | if (__glibc_unlikely (yperr != YPERR_SUCCESS)) |
ab9a9ff8 | 171 | return yperr2nss (yperr); |
ff0913d3 UD |
172 | |
173 | /* Parse the result: following the colon is a comma separated list of | |
174 | group IDs. */ | |
175 | char *cp = strchr (result, ':'); | |
176 | if (cp == NULL) | |
177 | { | |
178 | errout: | |
179 | free (result); | |
180 | return NSS_STATUS_NOTFOUND; | |
181 | } | |
182 | /* Skip the colon. */ | |
183 | ++cp; | |
184 | ||
185 | gid_t *groups = *groupsp; | |
186 | while (*cp != '\0') | |
187 | { | |
188 | char *endp; | |
189 | unsigned long int gid = strtoul (cp, &endp, 0); | |
190 | if (cp == endp) | |
191 | goto errout; | |
192 | if (*endp == ',') | |
193 | ++endp; | |
194 | else if (*endp != '\0') | |
195 | goto errout; | |
196 | cp = endp; | |
197 | ||
198 | if (gid == group) | |
199 | /* We do not need this group again. */ | |
200 | continue; | |
201 | ||
202 | /* Insert this group. */ | |
203 | if (*start == *size) | |
204 | { | |
205 | /* Need a bigger buffer. */ | |
ff0913d3 UD |
206 | long int newsize; |
207 | ||
208 | if (limit > 0 && *size == limit) | |
209 | /* We reached the maximum. */ | |
210 | break; | |
211 | ||
212 | if (limit <= 0) | |
213 | newsize = 2 * *size; | |
214 | else | |
215 | newsize = MIN (limit, 2 * *size); | |
216 | ||
ab9a9ff8 | 217 | gid_t *newgroups = realloc (groups, newsize * sizeof (*groups)); |
ff0913d3 UD |
218 | if (newgroups == NULL) |
219 | goto errout; | |
220 | *groupsp = groups = newgroups; | |
221 | *size = newsize; | |
222 | } | |
223 | ||
224 | groups[*start] = gid; | |
225 | *start += 1; | |
226 | } | |
227 | ||
228 | free (result); | |
229 | ||
230 | return NSS_STATUS_SUCCESS; | |
231 | } | |
232 | ||
233 | ||
899d423e | 234 | enum nss_status |
cf9e9ad9 | 235 | _nss_nis_initgroups_dyn (const char *user, gid_t group, long int *start, |
7603ea28 UD |
236 | long int *size, gid_t **groupsp, long int limit, |
237 | int *errnop) | |
899d423e | 238 | { |
ff0913d3 UD |
239 | /* We always need the domain name. */ |
240 | char *domainname; | |
241 | if (yp_get_default_domain (&domainname)) | |
242 | return NSS_STATUS_UNAVAIL; | |
243 | ||
244 | /* Check whether we are supposed to use the netid.byname map. */ | |
433f49c4 | 245 | if (_nsl_default_nss () & NSS_FLAG_NETID_AUTHORITATIVE) |
ff0913d3 UD |
246 | { |
247 | /* We need the user ID. */ | |
248 | uid_t uid; | |
249 | ||
250 | if (get_uid (user, &uid) == 0 | |
251 | && initgroups_netid (uid, group, start, size, groupsp, limit, | |
252 | errnop, domainname) == NSS_STATUS_SUCCESS) | |
253 | return NSS_STATUS_SUCCESS; | |
254 | } | |
255 | ||
899d423e UD |
256 | struct group grpbuf, *g; |
257 | size_t buflen = sysconf (_SC_GETPW_R_SIZE_MAX); | |
258 | char *tmpbuf; | |
259 | enum nss_status status; | |
e0d4e91d | 260 | intern_t intern = { NULL, NULL, 0 }; |
cf9e9ad9 | 261 | gid_t *groups = *groupsp; |
899d423e | 262 | |
ff0913d3 | 263 | status = internal_setgrent (domainname, &intern); |
899d423e UD |
264 | if (status != NSS_STATUS_SUCCESS) |
265 | return status; | |
266 | ||
267 | tmpbuf = __alloca (buflen); | |
268 | ||
269 | do | |
270 | { | |
271 | while ((status = | |
272 | internal_getgrent_r (&grpbuf, tmpbuf, buflen, errnop, | |
273 | &intern)) == NSS_STATUS_TRYAGAIN | |
274 | && *errnop == ERANGE) | |
b9b9a51e | 275 | tmpbuf = extend_alloca (tmpbuf, buflen, 2 * buflen); |
899d423e UD |
276 | |
277 | if (status != NSS_STATUS_SUCCESS) | |
278 | goto done; | |
279 | ||
280 | ||
281 | g = &grpbuf; | |
282 | if (g->gr_gid != group) | |
283 | { | |
284 | char **m; | |
285 | ||
286 | for (m = g->gr_mem; *m != NULL; ++m) | |
287 | if (strcmp (*m, user) == 0) | |
288 | { | |
289 | /* Matches user. Insert this group. */ | |
cf9e9ad9 | 290 | if (*start == *size) |
899d423e UD |
291 | { |
292 | /* Need a bigger buffer. */ | |
cf9e9ad9 | 293 | gid_t *newgroups; |
7603ea28 UD |
294 | long int newsize; |
295 | ||
296 | if (limit > 0 && *size == limit) | |
297 | /* We reached the maximum. */ | |
298 | goto done; | |
299 | ||
300 | if (limit <= 0) | |
301 | newsize = 2 * *size; | |
302 | else | |
303 | newsize = MIN (limit, 2 * *size); | |
304 | ||
305 | newgroups = realloc (groups, newsize * sizeof (*groups)); | |
cf9e9ad9 | 306 | if (newgroups == NULL) |
899d423e | 307 | goto done; |
cf9e9ad9 | 308 | *groupsp = groups = newgroups; |
7603ea28 | 309 | *size = newsize; |
899d423e UD |
310 | } |
311 | ||
312 | groups[*start] = g->gr_gid; | |
313 | *start += 1; | |
314 | ||
899d423e UD |
315 | break; |
316 | } | |
317 | } | |
318 | } | |
319 | while (status == NSS_STATUS_SUCCESS); | |
320 | ||
321 | done: | |
322 | while (intern.start != NULL) | |
323 | { | |
899d423e UD |
324 | intern.next = intern.start; |
325 | intern.start = intern.start->next; | |
326 | free (intern.next); | |
327 | } | |
328 | ||
329 | return NSS_STATUS_SUCCESS; | |
330 | } |