1 | /* Copyright (C) 1996-2019 Free Software Foundation, Inc. |
2 | This file is part of the GNU C Library. |
3 | Contributed by Thorsten Kukuk <kukuk@vt.uni-paderborn.de>, 1996. |
4 | |
5 | The GNU C Library is free software; you can redistribute it and/or |
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. |
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 |
13 | Lesser General Public License for more details. |
14 | |
15 | You should have received a copy of the GNU Lesser General Public |
16 | License along with the GNU C Library; if not, see |
17 | <http://www.gnu.org/licenses/>. */ |
18 | |
19 | #include <ctype.h> |
20 | #include <errno.h> |
21 | #include <fcntl.h> |
22 | #include <netdb.h> |
23 | #include <nss.h> |
24 | #include <nsswitch.h> |
25 | #include <pwd.h> |
26 | #include <stdio_ext.h> |
27 | #include <string.h> |
28 | #include <libc-lock.h> |
29 | #include <kernel-features.h> |
30 | |
31 | #include "netgroup.h" |
32 | #include "nisdomain.h" |
33 | |
34 | static service_user *ni; |
35 | static enum nss_status (*nss_setpwent) (int stayopen); |
36 | static enum nss_status (*nss_getpwnam_r) (const char *name, |
37 | struct passwd * pwd, char *buffer, |
38 | size_t buflen, int *errnop); |
39 | static enum nss_status (*nss_getpwuid_r) (uid_t uid, struct passwd * pwd, |
40 | char *buffer, size_t buflen, |
41 | int *errnop); |
42 | static enum nss_status (*nss_getpwent_r) (struct passwd * pwd, char *buffer, |
43 | size_t buflen, int *errnop); |
44 | static enum nss_status (*nss_endpwent) (void); |
45 | |
46 | /* Get the declaration of the parser function. */ |
47 | #define ENTNAME pwent |
48 | #define STRUCTURE passwd |
49 | #define EXTERN_PARSER |
50 | #include <nss/nss_files/files-parse.c> |
51 | |
52 | /* Structure for remembering -@netgroup and -user members ... */ |
53 | #define BLACKLIST_INITIAL_SIZE 512 |
54 | #define BLACKLIST_INCREMENT 256 |
55 | struct blacklist_t |
56 | { |
57 | char *data; |
58 | int current; |
59 | int size; |
60 | }; |
61 | |
62 | struct ent_t |
63 | { |
64 | bool netgroup; |
65 | bool first; |
66 | bool files; |
67 | enum nss_status setent_status; |
68 | FILE *stream; |
69 | struct blacklist_t blacklist; |
70 | struct passwd pwd; |
71 | struct __netgrent netgrdata; |
72 | }; |
73 | typedef struct ent_t ent_t; |
74 | |
75 | static ent_t ext_ent = { false, false, true, NSS_STATUS_SUCCESS, NULL, |
76 | { NULL, 0, 0 }, |
77 | { NULL, NULL, 0, 0, NULL, NULL, NULL }}; |
78 | |
79 | /* Protect global state against multiple changers. */ |
80 | __libc_lock_define_initialized (static, lock) |
81 | |
82 | /* Prototypes for local functions. */ |
83 | static void blacklist_store_name (const char *, ent_t *); |
84 | static bool in_blacklist (const char *, int, ent_t *); |
85 | |
86 | /* Initialize the NSS interface/functions. The calling function must |
87 | hold the lock. */ |
88 | static void |
89 | init_nss_interface (void) |
90 | { |
91 | if (__nss_database_lookup ("passwd_compat" , NULL, "nis" , &ni) >= 0) |
92 | { |
93 | nss_setpwent = __nss_lookup_function (ni, "setpwent" ); |
94 | nss_getpwnam_r = __nss_lookup_function (ni, "getpwnam_r" ); |
95 | nss_getpwuid_r = __nss_lookup_function (ni, "getpwuid_r" ); |
96 | nss_getpwent_r = __nss_lookup_function (ni, "getpwent_r" ); |
97 | nss_endpwent = __nss_lookup_function (ni, "endpwent" ); |
98 | } |
99 | } |
100 | |
101 | static void |
102 | give_pwd_free (struct passwd *pwd) |
103 | { |
104 | free (pwd->pw_name); |
105 | free (pwd->pw_passwd); |
106 | free (pwd->pw_gecos); |
107 | free (pwd->pw_dir); |
108 | free (pwd->pw_shell); |
109 | |
110 | memset (pwd, '\0', sizeof (struct passwd)); |
111 | } |
112 | |
113 | static size_t |
114 | pwd_need_buflen (struct passwd *pwd) |
115 | { |
116 | size_t len = 0; |
117 | |
118 | if (pwd->pw_passwd != NULL) |
119 | len += strlen (pwd->pw_passwd) + 1; |
120 | |
121 | if (pwd->pw_gecos != NULL) |
122 | len += strlen (pwd->pw_gecos) + 1; |
123 | |
124 | if (pwd->pw_dir != NULL) |
125 | len += strlen (pwd->pw_dir) + 1; |
126 | |
127 | if (pwd->pw_shell != NULL) |
128 | len += strlen (pwd->pw_shell) + 1; |
129 | |
130 | return len; |
131 | } |
132 | |
133 | static void |
134 | copy_pwd_changes (struct passwd *dest, struct passwd *src, |
135 | char *buffer, size_t buflen) |
136 | { |
137 | if (src->pw_passwd != NULL && strlen (src->pw_passwd)) |
138 | { |
139 | if (buffer == NULL) |
140 | dest->pw_passwd = strdup (src->pw_passwd); |
141 | else if (dest->pw_passwd && |
142 | strlen (dest->pw_passwd) >= strlen (src->pw_passwd)) |
143 | strcpy (dest->pw_passwd, src->pw_passwd); |
144 | else |
145 | { |
146 | dest->pw_passwd = buffer; |
147 | strcpy (dest->pw_passwd, src->pw_passwd); |
148 | buffer += strlen (dest->pw_passwd) + 1; |
149 | buflen = buflen - (strlen (dest->pw_passwd) + 1); |
150 | } |
151 | } |
152 | |
153 | if (src->pw_gecos != NULL && strlen (src->pw_gecos)) |
154 | { |
155 | if (buffer == NULL) |
156 | dest->pw_gecos = strdup (src->pw_gecos); |
157 | else if (dest->pw_gecos && |
158 | strlen (dest->pw_gecos) >= strlen (src->pw_gecos)) |
159 | strcpy (dest->pw_gecos, src->pw_gecos); |
160 | else |
161 | { |
162 | dest->pw_gecos = buffer; |
163 | strcpy (dest->pw_gecos, src->pw_gecos); |
164 | buffer += strlen (dest->pw_gecos) + 1; |
165 | buflen = buflen - (strlen (dest->pw_gecos) + 1); |
166 | } |
167 | } |
168 | if (src->pw_dir != NULL && strlen (src->pw_dir)) |
169 | { |
170 | if (buffer == NULL) |
171 | dest->pw_dir = strdup (src->pw_dir); |
172 | else if (dest->pw_dir && strlen (dest->pw_dir) >= strlen (src->pw_dir)) |
173 | strcpy (dest->pw_dir, src->pw_dir); |
174 | else |
175 | { |
176 | dest->pw_dir = buffer; |
177 | strcpy (dest->pw_dir, src->pw_dir); |
178 | buffer += strlen (dest->pw_dir) + 1; |
179 | buflen = buflen - (strlen (dest->pw_dir) + 1); |
180 | } |
181 | } |
182 | |
183 | if (src->pw_shell != NULL && strlen (src->pw_shell)) |
184 | { |
185 | if (buffer == NULL) |
186 | dest->pw_shell = strdup (src->pw_shell); |
187 | else if (dest->pw_shell && |
188 | strlen (dest->pw_shell) >= strlen (src->pw_shell)) |
189 | strcpy (dest->pw_shell, src->pw_shell); |
190 | else |
191 | { |
192 | dest->pw_shell = buffer; |
193 | strcpy (dest->pw_shell, src->pw_shell); |
194 | buffer += strlen (dest->pw_shell) + 1; |
195 | buflen = buflen - (strlen (dest->pw_shell) + 1); |
196 | } |
197 | } |
198 | } |
199 | |
200 | static enum nss_status |
201 | internal_setpwent (ent_t *ent, int stayopen, int needent) |
202 | { |
203 | enum nss_status status = NSS_STATUS_SUCCESS; |
204 | |
205 | ent->first = ent->netgroup = false; |
206 | ent->files = true; |
207 | ent->setent_status = NSS_STATUS_SUCCESS; |
208 | |
209 | /* If something was left over free it. */ |
210 | if (ent->netgroup) |
211 | __internal_endnetgrent (&ent->netgrdata); |
212 | |
213 | if (ent->blacklist.data != NULL) |
214 | { |
215 | ent->blacklist.current = 1; |
216 | ent->blacklist.data[0] = '|'; |
217 | ent->blacklist.data[1] = '\0'; |
218 | } |
219 | else |
220 | ent->blacklist.current = 0; |
221 | |
222 | if (ent->stream == NULL) |
223 | { |
224 | ent->stream = fopen ("/etc/passwd" , "rme" ); |
225 | |
226 | if (ent->stream == NULL) |
227 | status = errno == EAGAIN ? NSS_STATUS_TRYAGAIN : NSS_STATUS_UNAVAIL; |
228 | else |
229 | /* We take care of locking ourself. */ |
230 | __fsetlocking (ent->stream, FSETLOCKING_BYCALLER); |
231 | } |
232 | else |
233 | rewind (ent->stream); |
234 | |
235 | give_pwd_free (&ent->pwd); |
236 | |
237 | if (needent && status == NSS_STATUS_SUCCESS && nss_setpwent) |
238 | ent->setent_status = nss_setpwent (stayopen); |
239 | |
240 | return status; |
241 | } |
242 | |
243 | |
244 | enum nss_status |
245 | _nss_compat_setpwent (int stayopen) |
246 | { |
247 | enum nss_status result; |
248 | |
249 | __libc_lock_lock (lock); |
250 | |
251 | if (ni == NULL) |
252 | init_nss_interface (); |
253 | |
254 | result = internal_setpwent (&ext_ent, stayopen, 1); |
255 | |
256 | __libc_lock_unlock (lock); |
257 | |
258 | return result; |
259 | } |
260 | |
261 | |
262 | static enum nss_status |
263 | internal_endpwent (ent_t *ent) |
264 | { |
265 | if (ent->stream != NULL) |
266 | { |
267 | fclose (ent->stream); |
268 | ent->stream = NULL; |
269 | } |
270 | |
271 | if (ent->netgroup) |
272 | __internal_endnetgrent (&ent->netgrdata); |
273 | |
274 | ent->first = ent->netgroup = false; |
275 | |
276 | if (ent->blacklist.data != NULL) |
277 | { |
278 | ent->blacklist.current = 1; |
279 | ent->blacklist.data[0] = '|'; |
280 | ent->blacklist.data[1] = '\0'; |
281 | } |
282 | else |
283 | ent->blacklist.current = 0; |
284 | |
285 | give_pwd_free (&ent->pwd); |
286 | |
287 | return NSS_STATUS_SUCCESS; |
288 | } |
289 | |
290 | enum nss_status |
291 | _nss_compat_endpwent (void) |
292 | { |
293 | enum nss_status result; |
294 | |
295 | __libc_lock_lock (lock); |
296 | |
297 | if (nss_endpwent) |
298 | nss_endpwent (); |
299 | |
300 | result = internal_endpwent (&ext_ent); |
301 | |
302 | __libc_lock_unlock (lock); |
303 | |
304 | return result; |
305 | } |
306 | |
307 | |
308 | static enum nss_status |
309 | getpwent_next_nss_netgr (const char *name, struct passwd *result, ent_t *ent, |
310 | char *group, char *buffer, size_t buflen, |
311 | int *errnop) |
312 | { |
313 | char *curdomain = NULL, *host, *user, *domain, *p2; |
314 | int status; |
315 | size_t p2len; |
316 | |
317 | /* Leave function if NSS module does not support getpwnam_r, |
318 | we need this function here. */ |
319 | if (!nss_getpwnam_r) |
320 | return NSS_STATUS_UNAVAIL; |
321 | |
322 | if (ent->first) |
323 | { |
324 | memset (&ent->netgrdata, 0, sizeof (struct __netgrent)); |
325 | __internal_setnetgrent (group, &ent->netgrdata); |
326 | ent->first = false; |
327 | } |
328 | |
329 | while (1) |
330 | { |
331 | status = __internal_getnetgrent_r (&host, &user, &domain, |
332 | &ent->netgrdata, buffer, buflen, |
333 | errnop); |
334 | if (status != 1) |
335 | { |
336 | __internal_endnetgrent (&ent->netgrdata); |
337 | ent->netgroup = 0; |
338 | give_pwd_free (&ent->pwd); |
339 | return NSS_STATUS_RETURN; |
340 | } |
341 | |
342 | if (user == NULL || user[0] == '-') |
343 | continue; |
344 | |
345 | if (domain != NULL) |
346 | { |
347 | if (curdomain == NULL |
348 | && __nss_get_default_domain (&curdomain) != 0) |
349 | { |
350 | __internal_endnetgrent (&ent->netgrdata); |
351 | ent->netgroup = false; |
352 | give_pwd_free (&ent->pwd); |
353 | return NSS_STATUS_UNAVAIL; |
354 | } |
355 | if (strcmp (curdomain, domain) != 0) |
356 | continue; |
357 | } |
358 | |
359 | /* If name != NULL, we are called from getpwnam. */ |
360 | if (name != NULL) |
361 | if (strcmp (user, name) != 0) |
362 | continue; |
363 | |
364 | p2len = pwd_need_buflen (&ent->pwd); |
365 | if (p2len > buflen) |
366 | { |
367 | *errnop = ERANGE; |
368 | return NSS_STATUS_TRYAGAIN; |
369 | } |
370 | p2 = buffer + (buflen - p2len); |
371 | buflen -= p2len; |
372 | |
373 | if (nss_getpwnam_r (user, result, buffer, buflen, errnop) != |
374 | NSS_STATUS_SUCCESS) |
375 | continue; |
376 | |
377 | if (!in_blacklist (result->pw_name, strlen (result->pw_name), ent)) |
378 | { |
379 | /* Store the User in the blacklist for possible the "+" at the |
380 | end of /etc/passwd */ |
381 | blacklist_store_name (result->pw_name, ent); |
382 | copy_pwd_changes (result, &ent->pwd, p2, p2len); |
383 | break; |
384 | } |
385 | } |
386 | |
387 | return NSS_STATUS_SUCCESS; |
388 | } |
389 | |
390 | /* get the next user from NSS (+ entry) */ |
391 | static enum nss_status |
392 | getpwent_next_nss (struct passwd *result, ent_t *ent, char *buffer, |
393 | size_t buflen, int *errnop) |
394 | { |
395 | enum nss_status status; |
396 | char *p2; |
397 | size_t p2len; |
398 | |
399 | /* Return if NSS module does not support getpwent_r. */ |
400 | if (!nss_getpwent_r) |
401 | return NSS_STATUS_UNAVAIL; |
402 | |
403 | /* If the setpwent call failed, say so. */ |
404 | if (ent->setent_status != NSS_STATUS_SUCCESS) |
405 | return ent->setent_status; |
406 | |
407 | p2len = pwd_need_buflen (&ent->pwd); |
408 | if (p2len > buflen) |
409 | { |
410 | *errnop = ERANGE; |
411 | return NSS_STATUS_TRYAGAIN; |
412 | } |
413 | p2 = buffer + (buflen - p2len); |
414 | buflen -= p2len; |
415 | |
416 | if (ent->first) |
417 | ent->first = false; |
418 | |
419 | do |
420 | { |
421 | if ((status = nss_getpwent_r (result, buffer, buflen, errnop)) != |
422 | NSS_STATUS_SUCCESS) |
423 | return status; |
424 | } |
425 | while (in_blacklist (result->pw_name, strlen (result->pw_name), ent)); |
426 | |
427 | copy_pwd_changes (result, &ent->pwd, p2, p2len); |
428 | |
429 | return NSS_STATUS_SUCCESS; |
430 | } |
431 | |
432 | /* This function handle the +user entrys in /etc/passwd */ |
433 | static enum nss_status |
434 | getpwnam_plususer (const char *name, struct passwd *result, ent_t *ent, |
435 | char *buffer, size_t buflen, int *errnop) |
436 | { |
437 | if (!nss_getpwnam_r) |
438 | return NSS_STATUS_UNAVAIL; |
439 | |
440 | struct passwd pwd; |
441 | memset (&pwd, '\0', sizeof (struct passwd)); |
442 | |
443 | copy_pwd_changes (&pwd, result, NULL, 0); |
444 | |
445 | size_t plen = pwd_need_buflen (&pwd); |
446 | if (plen > buflen) |
447 | { |
448 | *errnop = ERANGE; |
449 | return NSS_STATUS_TRYAGAIN; |
450 | } |
451 | char *p = buffer + (buflen - plen); |
452 | buflen -= plen; |
453 | |
454 | enum nss_status status = nss_getpwnam_r (name, result, buffer, buflen, |
455 | errnop); |
456 | if (status != NSS_STATUS_SUCCESS) |
457 | return status; |
458 | |
459 | if (in_blacklist (result->pw_name, strlen (result->pw_name), ent)) |
460 | return NSS_STATUS_NOTFOUND; |
461 | |
462 | copy_pwd_changes (result, &pwd, p, plen); |
463 | give_pwd_free (&pwd); |
464 | /* We found the entry. */ |
465 | return NSS_STATUS_SUCCESS; |
466 | } |
467 | |
468 | static enum nss_status |
469 | getpwent_next_file (struct passwd *result, ent_t *ent, |
470 | char *buffer, size_t buflen, int *errnop) |
471 | { |
472 | struct parser_data *data = (void *) buffer; |
473 | while (1) |
474 | { |
475 | fpos_t pos; |
476 | char *p; |
477 | int parse_res; |
478 | |
479 | do |
480 | { |
481 | /* We need at least 3 characters for one line. */ |
482 | if (__glibc_unlikely (buflen < 3)) |
483 | { |
484 | erange: |
485 | *errnop = ERANGE; |
486 | return NSS_STATUS_TRYAGAIN; |
487 | } |
488 | |
489 | fgetpos (ent->stream, &pos); |
490 | buffer[buflen - 1] = '\xff'; |
491 | p = fgets_unlocked (buffer, buflen, ent->stream); |
492 | if (p == NULL && feof_unlocked (ent->stream)) |
493 | return NSS_STATUS_NOTFOUND; |
494 | |
495 | if (p == NULL || __builtin_expect (buffer[buflen - 1] != '\xff', 0)) |
496 | { |
497 | erange_reset: |
498 | fsetpos (ent->stream, &pos); |
499 | goto erange; |
500 | } |
501 | |
502 | /* Terminate the line for any case. */ |
503 | buffer[buflen - 1] = '\0'; |
504 | |
505 | /* Skip leading blanks. */ |
506 | while (isspace (*p)) |
507 | ++p; |
508 | } |
509 | while (*p == '\0' || *p == '#' || /* Ignore empty and comment lines. */ |
510 | /* Parse the line. If it is invalid, loop to |
511 | get the next line of the file to parse. */ |
512 | !(parse_res = _nss_files_parse_pwent (p, result, data, buflen, |
513 | errnop))); |
514 | |
515 | if (__glibc_unlikely (parse_res == -1)) |
516 | /* The parser ran out of space. */ |
517 | goto erange_reset; |
518 | |
519 | if (result->pw_name[0] != '+' && result->pw_name[0] != '-') |
520 | /* This is a real entry. */ |
521 | break; |
522 | |
523 | /* -@netgroup */ |
524 | if (result->pw_name[0] == '-' && result->pw_name[1] == '@' |
525 | && result->pw_name[2] != '\0') |
526 | { |
527 | /* XXX Do not use fixed length buffer. */ |
528 | char buf2[1024]; |
529 | char *user, *host, *domain; |
530 | struct __netgrent netgrdata; |
531 | |
532 | memset (&netgrdata, 0, sizeof (struct __netgrent)); |
533 | __internal_setnetgrent (&result->pw_name[2], &netgrdata); |
534 | while (__internal_getnetgrent_r (&host, &user, &domain, &netgrdata, |
535 | buf2, sizeof (buf2), errnop)) |
536 | { |
537 | if (user != NULL && user[0] != '-') |
538 | blacklist_store_name (user, ent); |
539 | } |
540 | __internal_endnetgrent (&netgrdata); |
541 | continue; |
542 | } |
543 | |
544 | /* +@netgroup */ |
545 | if (result->pw_name[0] == '+' && result->pw_name[1] == '@' |
546 | && result->pw_name[2] != '\0') |
547 | { |
548 | enum nss_status status; |
549 | |
550 | ent->netgroup = true; |
551 | ent->first = true; |
552 | copy_pwd_changes (&ent->pwd, result, NULL, 0); |
553 | |
554 | status = getpwent_next_nss_netgr (NULL, result, ent, |
555 | &result->pw_name[2], |
556 | buffer, buflen, errnop); |
557 | if (status == NSS_STATUS_RETURN) |
558 | continue; |
559 | else |
560 | return status; |
561 | } |
562 | |
563 | /* -user */ |
564 | if (result->pw_name[0] == '-' && result->pw_name[1] != '\0' |
565 | && result->pw_name[1] != '@') |
566 | { |
567 | blacklist_store_name (&result->pw_name[1], ent); |
568 | continue; |
569 | } |
570 | |
571 | /* +user */ |
572 | if (result->pw_name[0] == '+' && result->pw_name[1] != '\0' |
573 | && result->pw_name[1] != '@') |
574 | { |
575 | size_t len = strlen (result->pw_name); |
576 | char buf[len]; |
577 | enum nss_status status; |
578 | |
579 | /* Store the User in the blacklist for the "+" at the end of |
580 | /etc/passwd */ |
581 | memcpy (buf, &result->pw_name[1], len); |
582 | status = getpwnam_plususer (&result->pw_name[1], result, ent, |
583 | buffer, buflen, errnop); |
584 | blacklist_store_name (buf, ent); |
585 | |
586 | if (status == NSS_STATUS_SUCCESS) /* We found the entry. */ |
587 | break; |
588 | else if (status == NSS_STATUS_RETURN /* We couldn't parse the entry */ |
589 | || status == NSS_STATUS_NOTFOUND) /* entry doesn't exist */ |
590 | continue; |
591 | else |
592 | { |
593 | if (status == NSS_STATUS_TRYAGAIN) |
594 | { |
595 | /* The parser ran out of space */ |
596 | fsetpos (ent->stream, &pos); |
597 | *errnop = ERANGE; |
598 | } |
599 | return status; |
600 | } |
601 | } |
602 | |
603 | /* +:... */ |
604 | if (result->pw_name[0] == '+' && result->pw_name[1] == '\0') |
605 | { |
606 | ent->files = false; |
607 | ent->first = true; |
608 | copy_pwd_changes (&ent->pwd, result, NULL, 0); |
609 | |
610 | return getpwent_next_nss (result, ent, buffer, buflen, errnop); |
611 | } |
612 | } |
613 | |
614 | return NSS_STATUS_SUCCESS; |
615 | } |
616 | |
617 | |
618 | static enum nss_status |
619 | internal_getpwent_r (struct passwd *pw, ent_t *ent, char *buffer, |
620 | size_t buflen, int *errnop) |
621 | { |
622 | if (ent->netgroup) |
623 | { |
624 | enum nss_status status; |
625 | |
626 | /* We are searching members in a netgroup */ |
627 | /* Since this is not the first call, we don't need the group name */ |
628 | status = getpwent_next_nss_netgr (NULL, pw, ent, NULL, buffer, buflen, |
629 | errnop); |
630 | if (status == NSS_STATUS_RETURN) |
631 | return getpwent_next_file (pw, ent, buffer, buflen, errnop); |
632 | else |
633 | return status; |
634 | } |
635 | else if (ent->files) |
636 | return getpwent_next_file (pw, ent, buffer, buflen, errnop); |
637 | else |
638 | return getpwent_next_nss (pw, ent, buffer, buflen, errnop); |
639 | |
640 | } |
641 | |
642 | enum nss_status |
643 | _nss_compat_getpwent_r (struct passwd *pwd, char *buffer, size_t buflen, |
644 | int *errnop) |
645 | { |
646 | enum nss_status result = NSS_STATUS_SUCCESS; |
647 | |
648 | __libc_lock_lock (lock); |
649 | |
650 | /* Be prepared that the setpwent function was not called before. */ |
651 | if (ni == NULL) |
652 | init_nss_interface (); |
653 | |
654 | if (ext_ent.stream == NULL) |
655 | result = internal_setpwent (&ext_ent, 1, 1); |
656 | |
657 | if (result == NSS_STATUS_SUCCESS) |
658 | result = internal_getpwent_r (pwd, &ext_ent, buffer, buflen, errnop); |
659 | |
660 | __libc_lock_unlock (lock); |
661 | |
662 | return result; |
663 | } |
664 | |
665 | /* Searches in /etc/passwd and the NIS/NIS+ map for a special user */ |
666 | static enum nss_status |
667 | internal_getpwnam_r (const char *name, struct passwd *result, ent_t *ent, |
668 | char *buffer, size_t buflen, int *errnop) |
669 | { |
670 | struct parser_data *data = (void *) buffer; |
671 | |
672 | while (1) |
673 | { |
674 | fpos_t pos; |
675 | char *p; |
676 | int parse_res; |
677 | |
678 | do |
679 | { |
680 | /* We need at least 3 characters for one line. */ |
681 | if (__glibc_unlikely (buflen < 3)) |
682 | { |
683 | erange: |
684 | *errnop = ERANGE; |
685 | return NSS_STATUS_TRYAGAIN; |
686 | } |
687 | |
688 | fgetpos (ent->stream, &pos); |
689 | buffer[buflen - 1] = '\xff'; |
690 | p = fgets_unlocked (buffer, buflen, ent->stream); |
691 | if (p == NULL && feof_unlocked (ent->stream)) |
692 | { |
693 | return NSS_STATUS_NOTFOUND; |
694 | } |
695 | if (p == NULL || __builtin_expect (buffer[buflen - 1] != '\xff', 0)) |
696 | { |
697 | erange_reset: |
698 | fsetpos (ent->stream, &pos); |
699 | goto erange; |
700 | } |
701 | |
702 | /* Terminate the line for any case. */ |
703 | buffer[buflen - 1] = '\0'; |
704 | |
705 | /* Skip leading blanks. */ |
706 | while (isspace (*p)) |
707 | ++p; |
708 | } |
709 | while (*p == '\0' || *p == '#' || /* Ignore empty and comment lines. */ |
710 | /* Parse the line. If it is invalid, loop to |
711 | get the next line of the file to parse. */ |
712 | !(parse_res = _nss_files_parse_pwent (p, result, data, buflen, |
713 | errnop))); |
714 | |
715 | if (__glibc_unlikely (parse_res == -1)) |
716 | /* The parser ran out of space. */ |
717 | goto erange_reset; |
718 | |
719 | /* This is a real entry. */ |
720 | if (result->pw_name[0] != '+' && result->pw_name[0] != '-') |
721 | { |
722 | if (strcmp (result->pw_name, name) == 0) |
723 | return NSS_STATUS_SUCCESS; |
724 | else |
725 | continue; |
726 | } |
727 | |
728 | /* -@netgroup */ |
729 | if (result->pw_name[0] == '-' && result->pw_name[1] == '@' |
730 | && result->pw_name[2] != '\0') |
731 | { |
732 | if (innetgr (&result->pw_name[2], NULL, name, NULL)) |
733 | return NSS_STATUS_NOTFOUND; |
734 | continue; |
735 | } |
736 | |
737 | /* +@netgroup */ |
738 | if (result->pw_name[0] == '+' && result->pw_name[1] == '@' |
739 | && result->pw_name[2] != '\0') |
740 | { |
741 | enum nss_status status; |
742 | |
743 | if (innetgr (&result->pw_name[2], NULL, name, NULL)) |
744 | { |
745 | status = getpwnam_plususer (name, result, ent, buffer, |
746 | buflen, errnop); |
747 | |
748 | if (status == NSS_STATUS_RETURN) |
749 | continue; |
750 | |
751 | return status; |
752 | } |
753 | continue; |
754 | } |
755 | |
756 | /* -user */ |
757 | if (result->pw_name[0] == '-' && result->pw_name[1] != '\0' |
758 | && result->pw_name[1] != '@') |
759 | { |
760 | if (strcmp (&result->pw_name[1], name) == 0) |
761 | return NSS_STATUS_NOTFOUND; |
762 | else |
763 | continue; |
764 | } |
765 | |
766 | /* +user */ |
767 | if (result->pw_name[0] == '+' && result->pw_name[1] != '\0' |
768 | && result->pw_name[1] != '@') |
769 | { |
770 | if (strcmp (name, &result->pw_name[1]) == 0) |
771 | { |
772 | enum nss_status status; |
773 | |
774 | status = getpwnam_plususer (name, result, ent, buffer, buflen, |
775 | errnop); |
776 | if (status == NSS_STATUS_RETURN) |
777 | /* We couldn't parse the entry */ |
778 | return NSS_STATUS_NOTFOUND; |
779 | else |
780 | return status; |
781 | } |
782 | } |
783 | |
784 | /* +:... */ |
785 | if (result->pw_name[0] == '+' && result->pw_name[1] == '\0') |
786 | { |
787 | enum nss_status status; |
788 | |
789 | status = getpwnam_plususer (name, result, ent, |
790 | buffer, buflen, errnop); |
791 | if (status == NSS_STATUS_SUCCESS) /* We found the entry. */ |
792 | break; |
793 | else if (status == NSS_STATUS_RETURN) /* We couldn't parse the entry */ |
794 | return NSS_STATUS_NOTFOUND; |
795 | else |
796 | return status; |
797 | } |
798 | } |
799 | return NSS_STATUS_SUCCESS; |
800 | } |
801 | |
802 | enum nss_status |
803 | _nss_compat_getpwnam_r (const char *name, struct passwd *pwd, |
804 | char *buffer, size_t buflen, int *errnop) |
805 | { |
806 | enum nss_status result; |
807 | ent_t ent = { false, false, true, NSS_STATUS_SUCCESS, NULL, { NULL, 0, 0 }, |
808 | { NULL, NULL, 0, 0, NULL, NULL, NULL }}; |
809 | |
810 | if (name[0] == '-' || name[0] == '+') |
811 | return NSS_STATUS_NOTFOUND; |
812 | |
813 | __libc_lock_lock (lock); |
814 | |
815 | if (ni == NULL) |
816 | init_nss_interface (); |
817 | |
818 | __libc_lock_unlock (lock); |
819 | |
820 | result = internal_setpwent (&ent, 0, 0); |
821 | |
822 | if (result == NSS_STATUS_SUCCESS) |
823 | result = internal_getpwnam_r (name, pwd, &ent, buffer, buflen, errnop); |
824 | |
825 | internal_endpwent (&ent); |
826 | |
827 | return result; |
828 | } |
829 | |
830 | /* This function handle the + entry in /etc/passwd for getpwuid */ |
831 | static enum nss_status |
832 | getpwuid_plususer (uid_t uid, struct passwd *result, char *buffer, |
833 | size_t buflen, int *errnop) |
834 | { |
835 | struct passwd pwd; |
836 | char *p; |
837 | size_t plen; |
838 | |
839 | if (!nss_getpwuid_r) |
840 | return NSS_STATUS_UNAVAIL; |
841 | |
842 | memset (&pwd, '\0', sizeof (struct passwd)); |
843 | |
844 | copy_pwd_changes (&pwd, result, NULL, 0); |
845 | |
846 | plen = pwd_need_buflen (&pwd); |
847 | if (plen > buflen) |
848 | { |
849 | *errnop = ERANGE; |
850 | return NSS_STATUS_TRYAGAIN; |
851 | } |
852 | p = buffer + (buflen - plen); |
853 | buflen -= plen; |
854 | |
855 | if (nss_getpwuid_r (uid, result, buffer, buflen, errnop) == |
856 | NSS_STATUS_SUCCESS) |
857 | { |
858 | copy_pwd_changes (result, &pwd, p, plen); |
859 | give_pwd_free (&pwd); |
860 | /* We found the entry. */ |
861 | return NSS_STATUS_SUCCESS; |
862 | } |
863 | else |
864 | { |
865 | /* Give buffer the old len back */ |
866 | buflen += plen; |
867 | give_pwd_free (&pwd); |
868 | } |
869 | return NSS_STATUS_RETURN; |
870 | } |
871 | |
872 | /* Searches in /etc/passwd and the NSS subsystem for a special user id */ |
873 | static enum nss_status |
874 | internal_getpwuid_r (uid_t uid, struct passwd *result, ent_t *ent, |
875 | char *buffer, size_t buflen, int *errnop) |
876 | { |
877 | struct parser_data *data = (void *) buffer; |
878 | |
879 | while (1) |
880 | { |
881 | fpos_t pos; |
882 | char *p; |
883 | int parse_res; |
884 | |
885 | do |
886 | { |
887 | /* We need at least 3 characters for one line. */ |
888 | if (__glibc_unlikely (buflen < 3)) |
889 | { |
890 | erange: |
891 | *errnop = ERANGE; |
892 | return NSS_STATUS_TRYAGAIN; |
893 | } |
894 | |
895 | fgetpos (ent->stream, &pos); |
896 | buffer[buflen - 1] = '\xff'; |
897 | p = fgets_unlocked (buffer, buflen, ent->stream); |
898 | if (p == NULL && feof_unlocked (ent->stream)) |
899 | return NSS_STATUS_NOTFOUND; |
900 | |
901 | if (p == NULL || __builtin_expect (buffer[buflen - 1] != '\xff', 0)) |
902 | { |
903 | erange_reset: |
904 | fsetpos (ent->stream, &pos); |
905 | goto erange; |
906 | } |
907 | |
908 | /* Terminate the line for any case. */ |
909 | buffer[buflen - 1] = '\0'; |
910 | |
911 | /* Skip leading blanks. */ |
912 | while (isspace (*p)) |
913 | ++p; |
914 | } |
915 | while (*p == '\0' || *p == '#' || /* Ignore empty and comment lines. */ |
916 | /* Parse the line. If it is invalid, loop to |
917 | get the next line of the file to parse. */ |
918 | !(parse_res = _nss_files_parse_pwent (p, result, data, buflen, |
919 | errnop))); |
920 | |
921 | if (__glibc_unlikely (parse_res == -1)) |
922 | /* The parser ran out of space. */ |
923 | goto erange_reset; |
924 | |
925 | /* This is a real entry. */ |
926 | if (result->pw_name[0] != '+' && result->pw_name[0] != '-') |
927 | { |
928 | if (result->pw_uid == uid) |
929 | return NSS_STATUS_SUCCESS; |
930 | else |
931 | continue; |
932 | } |
933 | |
934 | /* -@netgroup */ |
935 | if (result->pw_name[0] == '-' && result->pw_name[1] == '@' |
936 | && result->pw_name[2] != '\0') |
937 | { |
938 | /* -1, because we remove first two character of pw_name. */ |
939 | size_t len = strlen (result->pw_name) - 1; |
940 | char buf[len]; |
941 | enum nss_status status; |
942 | |
943 | memcpy (buf, &result->pw_name[2], len); |
944 | |
945 | status = getpwuid_plususer (uid, result, buffer, buflen, errnop); |
946 | if (status == NSS_STATUS_SUCCESS && |
947 | innetgr (buf, NULL, result->pw_name, NULL)) |
948 | return NSS_STATUS_NOTFOUND; |
949 | |
950 | continue; |
951 | } |
952 | |
953 | /* +@netgroup */ |
954 | if (result->pw_name[0] == '+' && result->pw_name[1] == '@' |
955 | && result->pw_name[2] != '\0') |
956 | { |
957 | /* -1, because we remove first two characters of pw_name. */ |
958 | size_t len = strlen (result->pw_name) - 1; |
959 | char buf[len]; |
960 | enum nss_status status; |
961 | |
962 | memcpy (buf, &result->pw_name[2], len); |
963 | |
964 | status = getpwuid_plususer (uid, result, buffer, buflen, errnop); |
965 | |
966 | if (status == NSS_STATUS_RETURN) |
967 | continue; |
968 | |
969 | if (status == NSS_STATUS_SUCCESS) |
970 | { |
971 | if (innetgr (buf, NULL, result->pw_name, NULL)) |
972 | return NSS_STATUS_SUCCESS; |
973 | } |
974 | else if (status == NSS_STATUS_RETURN) /* We couldn't parse the entry */ |
975 | return NSS_STATUS_NOTFOUND; |
976 | else |
977 | return status; |
978 | |
979 | continue; |
980 | } |
981 | |
982 | /* -user */ |
983 | if (result->pw_name[0] == '-' && result->pw_name[1] != '\0' |
984 | && result->pw_name[1] != '@') |
985 | { |
986 | size_t len = strlen (result->pw_name); |
987 | char buf[len]; |
988 | enum nss_status status; |
989 | |
990 | memcpy (buf, &result->pw_name[1], len); |
991 | |
992 | status = getpwuid_plususer (uid, result, buffer, buflen, errnop); |
993 | if (status == NSS_STATUS_SUCCESS && |
994 | innetgr (buf, NULL, result->pw_name, NULL)) |
995 | return NSS_STATUS_NOTFOUND; |
996 | continue; |
997 | } |
998 | |
999 | /* +user */ |
1000 | if (result->pw_name[0] == '+' && result->pw_name[1] != '\0' |
1001 | && result->pw_name[1] != '@') |
1002 | { |
1003 | size_t len = strlen (result->pw_name); |
1004 | char buf[len]; |
1005 | enum nss_status status; |
1006 | |
1007 | memcpy (buf, &result->pw_name[1], len); |
1008 | |
1009 | status = getpwuid_plususer (uid, result, buffer, buflen, errnop); |
1010 | |
1011 | if (status == NSS_STATUS_RETURN) |
1012 | continue; |
1013 | |
1014 | if (status == NSS_STATUS_SUCCESS) |
1015 | { |
1016 | if (strcmp (buf, result->pw_name) == 0) |
1017 | return NSS_STATUS_SUCCESS; |
1018 | } |
1019 | else if (status == NSS_STATUS_RETURN) /* We couldn't parse the entry */ |
1020 | return NSS_STATUS_NOTFOUND; |
1021 | else |
1022 | return status; |
1023 | |
1024 | continue; |
1025 | } |
1026 | |
1027 | /* +:... */ |
1028 | if (result->pw_name[0] == '+' && result->pw_name[1] == '\0') |
1029 | { |
1030 | enum nss_status status; |
1031 | |
1032 | status = getpwuid_plususer (uid, result, buffer, buflen, errnop); |
1033 | if (status == NSS_STATUS_SUCCESS) /* We found the entry. */ |
1034 | break; |
1035 | else if (status == NSS_STATUS_RETURN) /* We couldn't parse the entry */ |
1036 | return NSS_STATUS_NOTFOUND; |
1037 | else |
1038 | return status; |
1039 | } |
1040 | } |
1041 | return NSS_STATUS_SUCCESS; |
1042 | } |
1043 | |
1044 | enum nss_status |
1045 | _nss_compat_getpwuid_r (uid_t uid, struct passwd *pwd, |
1046 | char *buffer, size_t buflen, int *errnop) |
1047 | { |
1048 | enum nss_status result; |
1049 | ent_t ent = { false, false, true, NSS_STATUS_SUCCESS, NULL, { NULL, 0, 0 }, |
1050 | { NULL, NULL, 0, 0, NULL, NULL, NULL }}; |
1051 | |
1052 | __libc_lock_lock (lock); |
1053 | |
1054 | if (ni == NULL) |
1055 | init_nss_interface (); |
1056 | |
1057 | __libc_lock_unlock (lock); |
1058 | |
1059 | result = internal_setpwent (&ent, 0, 0); |
1060 | |
1061 | if (result == NSS_STATUS_SUCCESS) |
1062 | result = internal_getpwuid_r (uid, pwd, &ent, buffer, buflen, errnop); |
1063 | |
1064 | internal_endpwent (&ent); |
1065 | |
1066 | return result; |
1067 | } |
1068 | |
1069 | |
1070 | /* Support routines for remembering -@netgroup and -user entries. |
1071 | The names are stored in a single string with `|' as separator. */ |
1072 | static void |
1073 | blacklist_store_name (const char *name, ent_t *ent) |
1074 | { |
1075 | int namelen = strlen (name); |
1076 | char *tmp; |
1077 | |
1078 | /* first call, setup cache */ |
1079 | if (ent->blacklist.size == 0) |
1080 | { |
1081 | ent->blacklist.size = MAX (BLACKLIST_INITIAL_SIZE, 2 * namelen); |
1082 | ent->blacklist.data = malloc (ent->blacklist.size); |
1083 | if (ent->blacklist.data == NULL) |
1084 | return; |
1085 | ent->blacklist.data[0] = '|'; |
1086 | ent->blacklist.data[1] = '\0'; |
1087 | ent->blacklist.current = 1; |
1088 | } |
1089 | else |
1090 | { |
1091 | if (in_blacklist (name, namelen, ent)) |
1092 | return; /* no duplicates */ |
1093 | |
1094 | if (ent->blacklist.current + namelen + 1 >= ent->blacklist.size) |
1095 | { |
1096 | ent->blacklist.size += MAX (BLACKLIST_INCREMENT, 2 * namelen); |
1097 | tmp = realloc (ent->blacklist.data, ent->blacklist.size); |
1098 | if (tmp == NULL) |
1099 | { |
1100 | free (ent->blacklist.data); |
1101 | ent->blacklist.size = 0; |
1102 | return; |
1103 | } |
1104 | ent->blacklist.data = tmp; |
1105 | } |
1106 | } |
1107 | |
1108 | tmp = stpcpy (ent->blacklist.data + ent->blacklist.current, name); |
1109 | *tmp++ = '|'; |
1110 | *tmp = '\0'; |
1111 | ent->blacklist.current += namelen + 1; |
1112 | |
1113 | return; |
1114 | } |
1115 | |
1116 | /* Returns whether ent->blacklist contains name. */ |
1117 | static bool |
1118 | in_blacklist (const char *name, int namelen, ent_t *ent) |
1119 | { |
1120 | char buf[namelen + 3]; |
1121 | char *cp; |
1122 | |
1123 | if (ent->blacklist.data == NULL) |
1124 | return false; |
1125 | |
1126 | buf[0] = '|'; |
1127 | cp = stpcpy (&buf[1], name); |
1128 | *cp++ = '|'; |
1129 | *cp = '\0'; |
1130 | return strstr (ent->blacklist.data, buf) != NULL; |
1131 | } |
1132 | |