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 <shadow.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_setspent) (int stayopen); |
36 | static enum nss_status (*nss_getspnam_r) (const char *name, struct spwd * sp, |
37 | char *buffer, size_t buflen, |
38 | int *errnop); |
39 | static enum nss_status (*nss_getspent_r) (struct spwd * sp, char *buffer, |
40 | size_t buflen, int *errnop); |
41 | static enum nss_status (*nss_endspent) (void); |
42 | |
43 | /* Get the declaration of the parser function. */ |
44 | #define ENTNAME spent |
45 | #define STRUCTURE spwd |
46 | #define EXTERN_PARSER |
47 | #include <nss/nss_files/files-parse.c> |
48 | |
49 | /* Structure for remembering -@netgroup and -user members ... */ |
50 | #define BLACKLIST_INITIAL_SIZE 512 |
51 | #define BLACKLIST_INCREMENT 256 |
52 | struct blacklist_t |
53 | { |
54 | char *data; |
55 | int current; |
56 | int size; |
57 | }; |
58 | |
59 | struct ent_t |
60 | { |
61 | bool netgroup; |
62 | bool files; |
63 | bool first; |
64 | enum nss_status setent_status; |
65 | FILE *stream; |
66 | struct blacklist_t blacklist; |
67 | struct spwd pwd; |
68 | struct __netgrent netgrdata; |
69 | }; |
70 | typedef struct ent_t ent_t; |
71 | |
72 | static ent_t ext_ent = { false, true, false, NSS_STATUS_SUCCESS, NULL, |
73 | { NULL, 0, 0}, |
74 | { NULL, NULL, 0, 0, 0, 0, 0, 0, 0}}; |
75 | |
76 | /* Protect global state against multiple changers. */ |
77 | __libc_lock_define_initialized (static, lock) |
78 | |
79 | /* Prototypes for local functions. */ |
80 | static void blacklist_store_name (const char *, ent_t *); |
81 | static bool in_blacklist (const char *, int, ent_t *); |
82 | |
83 | /* Initialize the NSS interface/functions. The calling function must |
84 | hold the lock. */ |
85 | static void |
86 | init_nss_interface (void) |
87 | { |
88 | if (__nss_database_lookup ("shadow_compat" , "passwd_compat" , |
89 | "nis" , &ni) >= 0) |
90 | { |
91 | nss_setspent = __nss_lookup_function (ni, "setspent" ); |
92 | nss_getspnam_r = __nss_lookup_function (ni, "getspnam_r" ); |
93 | nss_getspent_r = __nss_lookup_function (ni, "getspent_r" ); |
94 | nss_endspent = __nss_lookup_function (ni, "endspent" ); |
95 | } |
96 | } |
97 | |
98 | static void |
99 | give_spwd_free (struct spwd *pwd) |
100 | { |
101 | free (pwd->sp_namp); |
102 | free (pwd->sp_pwdp); |
103 | |
104 | memset (pwd, '\0', sizeof (struct spwd)); |
105 | pwd->sp_warn = -1; |
106 | pwd->sp_inact = -1; |
107 | pwd->sp_expire = -1; |
108 | pwd->sp_flag = ~0ul; |
109 | } |
110 | |
111 | static int |
112 | spwd_need_buflen (struct spwd *pwd) |
113 | { |
114 | int len = 0; |
115 | |
116 | if (pwd->sp_pwdp != NULL) |
117 | len += strlen (pwd->sp_pwdp) + 1; |
118 | |
119 | return len; |
120 | } |
121 | |
122 | static void |
123 | copy_spwd_changes (struct spwd *dest, struct spwd *src, |
124 | char *buffer, size_t buflen) |
125 | { |
126 | if (src->sp_pwdp != NULL && strlen (src->sp_pwdp)) |
127 | { |
128 | if (buffer == NULL) |
129 | dest->sp_pwdp = strdup (src->sp_pwdp); |
130 | else if (dest->sp_pwdp && |
131 | strlen (dest->sp_pwdp) >= strlen (src->sp_pwdp)) |
132 | strcpy (dest->sp_pwdp, src->sp_pwdp); |
133 | else |
134 | { |
135 | dest->sp_pwdp = buffer; |
136 | strcpy (dest->sp_pwdp, src->sp_pwdp); |
137 | buffer += strlen (dest->sp_pwdp) + 1; |
138 | buflen = buflen - (strlen (dest->sp_pwdp) + 1); |
139 | } |
140 | } |
141 | if (src->sp_lstchg != 0) |
142 | dest->sp_lstchg = src->sp_lstchg; |
143 | if (src->sp_min != 0) |
144 | dest->sp_min = src->sp_min; |
145 | if (src->sp_max != 0) |
146 | dest->sp_max = src->sp_max; |
147 | if (src->sp_warn != -1) |
148 | dest->sp_warn = src->sp_warn; |
149 | if (src->sp_inact != -1) |
150 | dest->sp_inact = src->sp_inact; |
151 | if (src->sp_expire != -1) |
152 | dest->sp_expire = src->sp_expire; |
153 | if (src->sp_flag != ~0ul) |
154 | dest->sp_flag = src->sp_flag; |
155 | } |
156 | |
157 | static enum nss_status |
158 | internal_setspent (ent_t *ent, int stayopen, int needent) |
159 | { |
160 | enum nss_status status = NSS_STATUS_SUCCESS; |
161 | |
162 | ent->first = ent->netgroup = 0; |
163 | ent->files = true; |
164 | |
165 | /* If something was left over free it. */ |
166 | if (ent->netgroup) |
167 | __internal_endnetgrent (&ent->netgrdata); |
168 | |
169 | if (ent->blacklist.data != NULL) |
170 | { |
171 | ent->blacklist.current = 1; |
172 | ent->blacklist.data[0] = '|'; |
173 | ent->blacklist.data[1] = '\0'; |
174 | } |
175 | else |
176 | ent->blacklist.current = 0; |
177 | |
178 | if (ent->stream == NULL) |
179 | { |
180 | ent->stream = fopen ("/etc/shadow" , "rme" ); |
181 | |
182 | if (ent->stream == NULL) |
183 | status = errno == EAGAIN ? NSS_STATUS_TRYAGAIN : NSS_STATUS_UNAVAIL; |
184 | else |
185 | /* We take care of locking ourself. */ |
186 | __fsetlocking (ent->stream, FSETLOCKING_BYCALLER); |
187 | } |
188 | else |
189 | rewind (ent->stream); |
190 | |
191 | give_spwd_free (&ent->pwd); |
192 | |
193 | if (needent && status == NSS_STATUS_SUCCESS && nss_setspent) |
194 | ent->setent_status = nss_setspent (stayopen); |
195 | |
196 | return status; |
197 | } |
198 | |
199 | |
200 | enum nss_status |
201 | _nss_compat_setspent (int stayopen) |
202 | { |
203 | enum nss_status result; |
204 | |
205 | __libc_lock_lock (lock); |
206 | |
207 | if (ni == NULL) |
208 | init_nss_interface (); |
209 | |
210 | result = internal_setspent (&ext_ent, stayopen, 1); |
211 | |
212 | __libc_lock_unlock (lock); |
213 | |
214 | return result; |
215 | } |
216 | |
217 | |
218 | static enum nss_status |
219 | internal_endspent (ent_t *ent) |
220 | { |
221 | if (ent->stream != NULL) |
222 | { |
223 | fclose (ent->stream); |
224 | ent->stream = NULL; |
225 | } |
226 | |
227 | if (ent->netgroup) |
228 | __internal_endnetgrent (&ent->netgrdata); |
229 | |
230 | ent->first = ent->netgroup = false; |
231 | ent->files = true; |
232 | |
233 | if (ent->blacklist.data != NULL) |
234 | { |
235 | ent->blacklist.current = 1; |
236 | ent->blacklist.data[0] = '|'; |
237 | ent->blacklist.data[1] = '\0'; |
238 | } |
239 | else |
240 | ent->blacklist.current = 0; |
241 | |
242 | give_spwd_free (&ent->pwd); |
243 | |
244 | return NSS_STATUS_SUCCESS; |
245 | } |
246 | |
247 | enum nss_status |
248 | _nss_compat_endspent (void) |
249 | { |
250 | enum nss_status result; |
251 | |
252 | __libc_lock_lock (lock); |
253 | |
254 | if (nss_endspent) |
255 | nss_endspent (); |
256 | |
257 | result = internal_endspent (&ext_ent); |
258 | |
259 | __libc_lock_unlock (lock); |
260 | |
261 | return result; |
262 | } |
263 | |
264 | |
265 | static enum nss_status |
266 | getspent_next_nss_netgr (const char *name, struct spwd *result, ent_t *ent, |
267 | char *group, char *buffer, size_t buflen, |
268 | int *errnop) |
269 | { |
270 | char *curdomain = NULL, *host, *user, *domain, *p2; |
271 | size_t p2len; |
272 | |
273 | if (!nss_getspnam_r) |
274 | return NSS_STATUS_UNAVAIL; |
275 | |
276 | /* If the setpwent call failed, say so. */ |
277 | if (ent->setent_status != NSS_STATUS_SUCCESS) |
278 | return ent->setent_status; |
279 | |
280 | if (ent->first) |
281 | { |
282 | memset (&ent->netgrdata, 0, sizeof (struct __netgrent)); |
283 | __internal_setnetgrent (group, &ent->netgrdata); |
284 | ent->first = false; |
285 | } |
286 | |
287 | while (1) |
288 | { |
289 | enum nss_status status; |
290 | |
291 | status = __internal_getnetgrent_r (&host, &user, &domain, |
292 | &ent->netgrdata, buffer, buflen, |
293 | errnop); |
294 | if (status != 1) |
295 | { |
296 | __internal_endnetgrent (&ent->netgrdata); |
297 | ent->netgroup = false; |
298 | give_spwd_free (&ent->pwd); |
299 | return NSS_STATUS_RETURN; |
300 | } |
301 | |
302 | if (user == NULL || user[0] == '-') |
303 | continue; |
304 | |
305 | if (domain != NULL) |
306 | { |
307 | if (curdomain == NULL |
308 | && __nss_get_default_domain (&curdomain) != 0) |
309 | { |
310 | __internal_endnetgrent (&ent->netgrdata); |
311 | ent->netgroup = false; |
312 | give_spwd_free (&ent->pwd); |
313 | return NSS_STATUS_UNAVAIL; |
314 | } |
315 | if (strcmp (curdomain, domain) != 0) |
316 | continue; |
317 | } |
318 | |
319 | /* If name != NULL, we are called from getpwnam */ |
320 | if (name != NULL) |
321 | if (strcmp (user, name) != 0) |
322 | continue; |
323 | |
324 | p2len = spwd_need_buflen (&ent->pwd); |
325 | if (p2len > buflen) |
326 | { |
327 | *errnop = ERANGE; |
328 | return NSS_STATUS_TRYAGAIN; |
329 | } |
330 | p2 = buffer + (buflen - p2len); |
331 | buflen -= p2len; |
332 | |
333 | if (nss_getspnam_r (user, result, buffer, buflen, errnop) != |
334 | NSS_STATUS_SUCCESS) |
335 | continue; |
336 | |
337 | if (!in_blacklist (result->sp_namp, strlen (result->sp_namp), ent)) |
338 | { |
339 | /* Store the User in the blacklist for possible the "+" at the |
340 | end of /etc/passwd */ |
341 | blacklist_store_name (result->sp_namp, ent); |
342 | copy_spwd_changes (result, &ent->pwd, p2, p2len); |
343 | break; |
344 | } |
345 | } |
346 | |
347 | return NSS_STATUS_SUCCESS; |
348 | } |
349 | |
350 | |
351 | static enum nss_status |
352 | getspent_next_nss (struct spwd *result, ent_t *ent, |
353 | char *buffer, size_t buflen, int *errnop) |
354 | { |
355 | enum nss_status status; |
356 | char *p2; |
357 | size_t p2len; |
358 | |
359 | if (!nss_getspent_r) |
360 | return NSS_STATUS_UNAVAIL; |
361 | |
362 | p2len = spwd_need_buflen (&ent->pwd); |
363 | if (p2len > buflen) |
364 | { |
365 | *errnop = ERANGE; |
366 | return NSS_STATUS_TRYAGAIN; |
367 | } |
368 | p2 = buffer + (buflen - p2len); |
369 | buflen -= p2len; |
370 | do |
371 | { |
372 | if ((status = nss_getspent_r (result, buffer, buflen, errnop)) != |
373 | NSS_STATUS_SUCCESS) |
374 | return status; |
375 | } |
376 | while (in_blacklist (result->sp_namp, strlen (result->sp_namp), ent)); |
377 | |
378 | copy_spwd_changes (result, &ent->pwd, p2, p2len); |
379 | |
380 | return NSS_STATUS_SUCCESS; |
381 | } |
382 | |
383 | |
384 | /* This function handle the +user entrys in /etc/shadow */ |
385 | static enum nss_status |
386 | getspnam_plususer (const char *name, struct spwd *result, ent_t *ent, |
387 | char *buffer, size_t buflen, int *errnop) |
388 | { |
389 | if (!nss_getspnam_r) |
390 | return NSS_STATUS_UNAVAIL; |
391 | |
392 | struct spwd pwd; |
393 | memset (&pwd, '\0', sizeof (struct spwd)); |
394 | pwd.sp_warn = -1; |
395 | pwd.sp_inact = -1; |
396 | pwd.sp_expire = -1; |
397 | pwd.sp_flag = ~0ul; |
398 | |
399 | copy_spwd_changes (&pwd, result, NULL, 0); |
400 | |
401 | size_t plen = spwd_need_buflen (&pwd); |
402 | if (plen > buflen) |
403 | { |
404 | *errnop = ERANGE; |
405 | return NSS_STATUS_TRYAGAIN; |
406 | } |
407 | char *p = buffer + (buflen - plen); |
408 | buflen -= plen; |
409 | |
410 | enum nss_status status = nss_getspnam_r (name, result, buffer, buflen, |
411 | errnop); |
412 | if (status != NSS_STATUS_SUCCESS) |
413 | return status; |
414 | |
415 | if (in_blacklist (result->sp_namp, strlen (result->sp_namp), ent)) |
416 | return NSS_STATUS_NOTFOUND; |
417 | |
418 | copy_spwd_changes (result, &pwd, p, plen); |
419 | give_spwd_free (&pwd); |
420 | /* We found the entry. */ |
421 | return NSS_STATUS_SUCCESS; |
422 | } |
423 | |
424 | |
425 | static enum nss_status |
426 | getspent_next_file (struct spwd *result, ent_t *ent, |
427 | char *buffer, size_t buflen, int *errnop) |
428 | { |
429 | struct parser_data *data = (void *) buffer; |
430 | while (1) |
431 | { |
432 | fpos_t pos; |
433 | int parse_res = 0; |
434 | char *p; |
435 | |
436 | do |
437 | { |
438 | /* We need at least 3 characters for one line. */ |
439 | if (__glibc_unlikely (buflen < 3)) |
440 | { |
441 | erange: |
442 | *errnop = ERANGE; |
443 | return NSS_STATUS_TRYAGAIN; |
444 | } |
445 | |
446 | fgetpos (ent->stream, &pos); |
447 | buffer[buflen - 1] = '\xff'; |
448 | p = fgets_unlocked (buffer, buflen, ent->stream); |
449 | if (p == NULL && feof_unlocked (ent->stream)) |
450 | return NSS_STATUS_NOTFOUND; |
451 | |
452 | if (p == NULL || __builtin_expect (buffer[buflen - 1] != '\xff', 0)) |
453 | { |
454 | erange_reset: |
455 | fsetpos (ent->stream, &pos); |
456 | goto erange; |
457 | } |
458 | |
459 | /* Skip leading blanks. */ |
460 | while (isspace (*p)) |
461 | ++p; |
462 | } |
463 | while (*p == '\0' || *p == '#' /* Ignore empty and comment lines. */ |
464 | /* Parse the line. If it is invalid, loop to |
465 | get the next line of the file to parse. */ |
466 | || !(parse_res = _nss_files_parse_spent (p, result, data, |
467 | buflen, errnop))); |
468 | |
469 | if (__glibc_unlikely (parse_res == -1)) |
470 | /* The parser ran out of space. */ |
471 | goto erange_reset; |
472 | |
473 | if (result->sp_namp[0] != '+' && result->sp_namp[0] != '-') |
474 | /* This is a real entry. */ |
475 | break; |
476 | |
477 | /* -@netgroup */ |
478 | if (result->sp_namp[0] == '-' && result->sp_namp[1] == '@' |
479 | && result->sp_namp[2] != '\0') |
480 | { |
481 | /* XXX Do not use fixed length buffers. */ |
482 | char buf2[1024]; |
483 | char *user, *host, *domain; |
484 | struct __netgrent netgrdata; |
485 | |
486 | memset (&netgrdata, 0, sizeof (struct __netgrent)); |
487 | __internal_setnetgrent (&result->sp_namp[2], &netgrdata); |
488 | while (__internal_getnetgrent_r (&host, &user, &domain, |
489 | &netgrdata, buf2, sizeof (buf2), |
490 | errnop)) |
491 | { |
492 | if (user != NULL && user[0] != '-') |
493 | blacklist_store_name (user, ent); |
494 | } |
495 | __internal_endnetgrent (&netgrdata); |
496 | continue; |
497 | } |
498 | |
499 | /* +@netgroup */ |
500 | if (result->sp_namp[0] == '+' && result->sp_namp[1] == '@' |
501 | && result->sp_namp[2] != '\0') |
502 | { |
503 | int status; |
504 | |
505 | ent->netgroup = true; |
506 | ent->first = true; |
507 | copy_spwd_changes (&ent->pwd, result, NULL, 0); |
508 | |
509 | status = getspent_next_nss_netgr (NULL, result, ent, |
510 | &result->sp_namp[2], |
511 | buffer, buflen, errnop); |
512 | if (status == NSS_STATUS_RETURN) |
513 | continue; |
514 | else |
515 | return status; |
516 | } |
517 | |
518 | /* -user */ |
519 | if (result->sp_namp[0] == '-' && result->sp_namp[1] != '\0' |
520 | && result->sp_namp[1] != '@') |
521 | { |
522 | blacklist_store_name (&result->sp_namp[1], ent); |
523 | continue; |
524 | } |
525 | |
526 | /* +user */ |
527 | if (result->sp_namp[0] == '+' && result->sp_namp[1] != '\0' |
528 | && result->sp_namp[1] != '@') |
529 | { |
530 | size_t len = strlen (result->sp_namp); |
531 | char buf[len]; |
532 | enum nss_status status; |
533 | |
534 | /* Store the User in the blacklist for the "+" at the end of |
535 | /etc/passwd */ |
536 | memcpy (buf, &result->sp_namp[1], len); |
537 | status = getspnam_plususer (&result->sp_namp[1], result, ent, |
538 | buffer, buflen, errnop); |
539 | blacklist_store_name (buf, ent); |
540 | |
541 | if (status == NSS_STATUS_SUCCESS) /* We found the entry. */ |
542 | break; |
543 | /* We couldn't parse the entry */ |
544 | else if (status == NSS_STATUS_RETURN |
545 | /* entry doesn't exist */ |
546 | || status == NSS_STATUS_NOTFOUND) |
547 | continue; |
548 | else |
549 | { |
550 | if (status == NSS_STATUS_TRYAGAIN) |
551 | { |
552 | fsetpos (ent->stream, &pos); |
553 | *errnop = ERANGE; |
554 | } |
555 | return status; |
556 | } |
557 | } |
558 | |
559 | /* +:... */ |
560 | if (result->sp_namp[0] == '+' && result->sp_namp[1] == '\0') |
561 | { |
562 | ent->files = false; |
563 | ent->first = true; |
564 | copy_spwd_changes (&ent->pwd, result, NULL, 0); |
565 | |
566 | return getspent_next_nss (result, ent, buffer, buflen, errnop); |
567 | } |
568 | } |
569 | |
570 | return NSS_STATUS_SUCCESS; |
571 | } |
572 | |
573 | |
574 | static enum nss_status |
575 | internal_getspent_r (struct spwd *pw, ent_t *ent, |
576 | char *buffer, size_t buflen, int *errnop) |
577 | { |
578 | if (ent->netgroup) |
579 | { |
580 | enum nss_status status; |
581 | |
582 | /* We are searching members in a netgroup */ |
583 | /* Since this is not the first call, we don't need the group name */ |
584 | status = getspent_next_nss_netgr (NULL, pw, ent, NULL, buffer, |
585 | buflen, errnop); |
586 | |
587 | if (status == NSS_STATUS_RETURN) |
588 | return getspent_next_file (pw, ent, buffer, buflen, errnop); |
589 | else |
590 | return status; |
591 | } |
592 | else if (ent->files) |
593 | return getspent_next_file (pw, ent, buffer, buflen, errnop); |
594 | else |
595 | return getspent_next_nss (pw, ent, buffer, buflen, errnop); |
596 | } |
597 | |
598 | |
599 | enum nss_status |
600 | _nss_compat_getspent_r (struct spwd *pwd, char *buffer, size_t buflen, |
601 | int *errnop) |
602 | { |
603 | enum nss_status result = NSS_STATUS_SUCCESS; |
604 | |
605 | __libc_lock_lock (lock); |
606 | |
607 | /* Be prepared that the setpwent function was not called before. */ |
608 | if (ni == NULL) |
609 | init_nss_interface (); |
610 | |
611 | if (ext_ent.stream == NULL) |
612 | result = internal_setspent (&ext_ent, 1, 1); |
613 | |
614 | if (result == NSS_STATUS_SUCCESS) |
615 | result = internal_getspent_r (pwd, &ext_ent, buffer, buflen, errnop); |
616 | |
617 | __libc_lock_unlock (lock); |
618 | |
619 | return result; |
620 | } |
621 | |
622 | |
623 | /* Searches in /etc/passwd and the NIS/NIS+ map for a special user */ |
624 | static enum nss_status |
625 | internal_getspnam_r (const char *name, struct spwd *result, ent_t *ent, |
626 | char *buffer, size_t buflen, int *errnop) |
627 | { |
628 | struct parser_data *data = (void *) buffer; |
629 | |
630 | while (1) |
631 | { |
632 | fpos_t pos; |
633 | char *p; |
634 | int parse_res; |
635 | |
636 | do |
637 | { |
638 | /* We need at least 3 characters for one line. */ |
639 | if (__glibc_unlikely (buflen < 3)) |
640 | { |
641 | erange: |
642 | *errnop = ERANGE; |
643 | return NSS_STATUS_TRYAGAIN; |
644 | } |
645 | |
646 | fgetpos (ent->stream, &pos); |
647 | buffer[buflen - 1] = '\xff'; |
648 | p = fgets_unlocked (buffer, buflen, ent->stream); |
649 | if (p == NULL && feof_unlocked (ent->stream)) |
650 | return NSS_STATUS_NOTFOUND; |
651 | |
652 | if (p == NULL || buffer[buflen - 1] != '\xff') |
653 | { |
654 | erange_reset: |
655 | fsetpos (ent->stream, &pos); |
656 | goto erange; |
657 | } |
658 | |
659 | /* Terminate the line for any case. */ |
660 | buffer[buflen - 1] = '\0'; |
661 | |
662 | /* Skip leading blanks. */ |
663 | while (isspace (*p)) |
664 | ++p; |
665 | } |
666 | while (*p == '\0' || *p == '#' || /* Ignore empty and comment lines. */ |
667 | /* Parse the line. If it is invalid, loop to |
668 | get the next line of the file to parse. */ |
669 | !(parse_res = _nss_files_parse_spent (p, result, data, buflen, |
670 | errnop))); |
671 | |
672 | if (__glibc_unlikely (parse_res == -1)) |
673 | /* The parser ran out of space. */ |
674 | goto erange_reset; |
675 | |
676 | /* This is a real entry. */ |
677 | if (result->sp_namp[0] != '+' && result->sp_namp[0] != '-') |
678 | { |
679 | if (strcmp (result->sp_namp, name) == 0) |
680 | return NSS_STATUS_SUCCESS; |
681 | else |
682 | continue; |
683 | } |
684 | |
685 | /* -@netgroup */ |
686 | /* If the loaded NSS module does not support this service, add |
687 | all users from a +@netgroup entry to the blacklist, too. */ |
688 | if (result->sp_namp[0] == '-' && result->sp_namp[1] == '@' |
689 | && result->sp_namp[2] != '\0') |
690 | { |
691 | if (innetgr (&result->sp_namp[2], NULL, name, NULL)) |
692 | return NSS_STATUS_NOTFOUND; |
693 | continue; |
694 | } |
695 | |
696 | /* +@netgroup */ |
697 | if (result->sp_namp[0] == '+' && result->sp_namp[1] == '@' |
698 | && result->sp_namp[2] != '\0') |
699 | { |
700 | enum nss_status status; |
701 | |
702 | if (innetgr (&result->sp_namp[2], NULL, name, NULL)) |
703 | { |
704 | status = getspnam_plususer (name, result, ent, buffer, |
705 | buflen, errnop); |
706 | |
707 | if (status == NSS_STATUS_RETURN) |
708 | continue; |
709 | |
710 | return status; |
711 | } |
712 | continue; |
713 | } |
714 | |
715 | /* -user */ |
716 | if (result->sp_namp[0] == '-' && result->sp_namp[1] != '\0' |
717 | && result->sp_namp[1] != '@') |
718 | { |
719 | if (strcmp (&result->sp_namp[1], name) == 0) |
720 | return NSS_STATUS_NOTFOUND; |
721 | else |
722 | continue; |
723 | } |
724 | |
725 | /* +user */ |
726 | if (result->sp_namp[0] == '+' && result->sp_namp[1] != '\0' |
727 | && result->sp_namp[1] != '@') |
728 | { |
729 | if (strcmp (name, &result->sp_namp[1]) == 0) |
730 | { |
731 | enum nss_status status; |
732 | |
733 | status = getspnam_plususer (name, result, ent, |
734 | buffer, buflen, errnop); |
735 | |
736 | if (status == NSS_STATUS_RETURN) |
737 | /* We couldn't parse the entry */ |
738 | return NSS_STATUS_NOTFOUND; |
739 | else |
740 | return status; |
741 | } |
742 | } |
743 | |
744 | /* +:... */ |
745 | if (result->sp_namp[0] == '+' && result->sp_namp[1] == '\0') |
746 | { |
747 | enum nss_status status; |
748 | |
749 | status = getspnam_plususer (name, result, ent, |
750 | buffer, buflen, errnop); |
751 | |
752 | if (status == NSS_STATUS_SUCCESS) |
753 | /* We found the entry. */ |
754 | break; |
755 | else if (status == NSS_STATUS_RETURN) |
756 | /* We couldn't parse the entry */ |
757 | return NSS_STATUS_NOTFOUND; |
758 | else |
759 | return status; |
760 | } |
761 | } |
762 | return NSS_STATUS_SUCCESS; |
763 | } |
764 | |
765 | |
766 | enum nss_status |
767 | _nss_compat_getspnam_r (const char *name, struct spwd *pwd, |
768 | char *buffer, size_t buflen, int *errnop) |
769 | { |
770 | enum nss_status result; |
771 | ent_t ent = { false, true, false, NSS_STATUS_SUCCESS, NULL, { NULL, 0, 0}, |
772 | { NULL, NULL, 0, 0, 0, 0, 0, 0, 0}}; |
773 | |
774 | if (name[0] == '-' || name[0] == '+') |
775 | return NSS_STATUS_NOTFOUND; |
776 | |
777 | __libc_lock_lock (lock); |
778 | |
779 | if (ni == NULL) |
780 | init_nss_interface (); |
781 | |
782 | __libc_lock_unlock (lock); |
783 | |
784 | result = internal_setspent (&ent, 0, 0); |
785 | |
786 | if (result == NSS_STATUS_SUCCESS) |
787 | result = internal_getspnam_r (name, pwd, &ent, buffer, buflen, errnop); |
788 | |
789 | internal_endspent (&ent); |
790 | |
791 | return result; |
792 | } |
793 | |
794 | |
795 | /* Support routines for remembering -@netgroup and -user entries. |
796 | The names are stored in a single string with `|' as separator. */ |
797 | static void |
798 | blacklist_store_name (const char *name, ent_t *ent) |
799 | { |
800 | int namelen = strlen (name); |
801 | char *tmp; |
802 | |
803 | /* first call, setup cache */ |
804 | if (ent->blacklist.size == 0) |
805 | { |
806 | ent->blacklist.size = MAX (BLACKLIST_INITIAL_SIZE, 2 * namelen); |
807 | ent->blacklist.data = malloc (ent->blacklist.size); |
808 | if (ent->blacklist.data == NULL) |
809 | return; |
810 | ent->blacklist.data[0] = '|'; |
811 | ent->blacklist.data[1] = '\0'; |
812 | ent->blacklist.current = 1; |
813 | } |
814 | else |
815 | { |
816 | if (in_blacklist (name, namelen, ent)) |
817 | return; /* no duplicates */ |
818 | |
819 | if (ent->blacklist.current + namelen + 1 >= ent->blacklist.size) |
820 | { |
821 | ent->blacklist.size += MAX (BLACKLIST_INCREMENT, 2 * namelen); |
822 | tmp = realloc (ent->blacklist.data, ent->blacklist.size); |
823 | if (tmp == NULL) |
824 | { |
825 | free (ent->blacklist.data); |
826 | ent->blacklist.size = 0; |
827 | return; |
828 | } |
829 | ent->blacklist.data = tmp; |
830 | } |
831 | } |
832 | |
833 | tmp = stpcpy (ent->blacklist.data + ent->blacklist.current, name); |
834 | *tmp++ = '|'; |
835 | *tmp = '\0'; |
836 | ent->blacklist.current += namelen + 1; |
837 | |
838 | return; |
839 | } |
840 | |
841 | |
842 | /* Returns whether ent->blacklist contains name. */ |
843 | static bool |
844 | in_blacklist (const char *name, int namelen, ent_t *ent) |
845 | { |
846 | char buf[namelen + 3]; |
847 | char *cp; |
848 | |
849 | if (ent->blacklist.data == NULL) |
850 | return false; |
851 | |
852 | buf[0] = '|'; |
853 | cp = stpcpy (&buf[1], name); |
854 | *cp++ = '|'; |
855 | *cp = '\0'; |
856 | return strstr (ent->blacklist.data, buf) != NULL; |
857 | } |
858 | |