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@suse.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 <grp.h> |
23 | #include <nss.h> |
24 | #include <nsswitch.h> |
25 | #include <stdio_ext.h> |
26 | #include <string.h> |
27 | #include <libc-lock.h> |
28 | #include <kernel-features.h> |
29 | |
30 | static service_user *ni; |
31 | static enum nss_status (*nss_setgrent) (int stayopen); |
32 | static enum nss_status (*nss_getgrnam_r) (const char *name, |
33 | struct group * grp, char *buffer, |
34 | size_t buflen, int *errnop); |
35 | static enum nss_status (*nss_getgrgid_r) (gid_t gid, struct group * grp, |
36 | char *buffer, size_t buflen, |
37 | int *errnop); |
38 | static enum nss_status (*nss_getgrent_r) (struct group * grp, char *buffer, |
39 | size_t buflen, int *errnop); |
40 | static enum nss_status (*nss_endgrent) (void); |
41 | |
42 | /* Get the declaration of the parser function. */ |
43 | #define ENTNAME grent |
44 | #define STRUCTURE group |
45 | #define EXTERN_PARSER |
46 | #include <nss/nss_files/files-parse.c> |
47 | |
48 | /* Structure for remembering -group members ... */ |
49 | #define BLACKLIST_INITIAL_SIZE 512 |
50 | #define BLACKLIST_INCREMENT 256 |
51 | struct blacklist_t |
52 | { |
53 | char *data; |
54 | int current; |
55 | int size; |
56 | }; |
57 | |
58 | struct ent_t |
59 | { |
60 | bool files; |
61 | enum nss_status setent_status; |
62 | FILE *stream; |
63 | struct blacklist_t blacklist; |
64 | }; |
65 | typedef struct ent_t ent_t; |
66 | |
67 | static ent_t ext_ent = { true, NSS_STATUS_SUCCESS, NULL, { NULL, 0, 0 }}; |
68 | |
69 | /* Protect global state against multiple changers. */ |
70 | __libc_lock_define_initialized (static, lock) |
71 | |
72 | /* Prototypes for local functions. */ |
73 | static void blacklist_store_name (const char *, ent_t *); |
74 | static bool in_blacklist (const char *, int, ent_t *); |
75 | |
76 | /* Initialize the NSS interface/functions. The calling function must |
77 | hold the lock. */ |
78 | static void |
79 | init_nss_interface (void) |
80 | { |
81 | if (__nss_database_lookup ("group_compat" , NULL, "nis" , &ni) >= 0) |
82 | { |
83 | nss_setgrent = __nss_lookup_function (ni, "setgrent" ); |
84 | nss_getgrnam_r = __nss_lookup_function (ni, "getgrnam_r" ); |
85 | nss_getgrgid_r = __nss_lookup_function (ni, "getgrgid_r" ); |
86 | nss_getgrent_r = __nss_lookup_function (ni, "getgrent_r" ); |
87 | nss_endgrent = __nss_lookup_function (ni, "endgrent" ); |
88 | } |
89 | } |
90 | |
91 | static enum nss_status |
92 | internal_setgrent (ent_t *ent, int stayopen, int needent) |
93 | { |
94 | enum nss_status status = NSS_STATUS_SUCCESS; |
95 | |
96 | ent->files = true; |
97 | |
98 | if (ent->blacklist.data != NULL) |
99 | { |
100 | ent->blacklist.current = 1; |
101 | ent->blacklist.data[0] = '|'; |
102 | ent->blacklist.data[1] = '\0'; |
103 | } |
104 | else |
105 | ent->blacklist.current = 0; |
106 | |
107 | if (ent->stream == NULL) |
108 | { |
109 | ent->stream = fopen ("/etc/group" , "rme" ); |
110 | |
111 | if (ent->stream == NULL) |
112 | status = errno == EAGAIN ? NSS_STATUS_TRYAGAIN : NSS_STATUS_UNAVAIL; |
113 | else |
114 | /* We take care of locking ourself. */ |
115 | __fsetlocking (ent->stream, FSETLOCKING_BYCALLER); |
116 | } |
117 | else |
118 | rewind (ent->stream); |
119 | |
120 | if (needent && status == NSS_STATUS_SUCCESS && nss_setgrent) |
121 | ent->setent_status = nss_setgrent (stayopen); |
122 | |
123 | return status; |
124 | } |
125 | |
126 | |
127 | enum nss_status |
128 | _nss_compat_setgrent (int stayopen) |
129 | { |
130 | enum nss_status result; |
131 | |
132 | __libc_lock_lock (lock); |
133 | |
134 | if (ni == NULL) |
135 | init_nss_interface (); |
136 | |
137 | result = internal_setgrent (&ext_ent, stayopen, 1); |
138 | |
139 | __libc_lock_unlock (lock); |
140 | |
141 | return result; |
142 | } |
143 | |
144 | |
145 | static enum nss_status |
146 | internal_endgrent (ent_t *ent) |
147 | { |
148 | if (ent->stream != NULL) |
149 | { |
150 | fclose (ent->stream); |
151 | ent->stream = NULL; |
152 | } |
153 | |
154 | if (ent->blacklist.data != NULL) |
155 | { |
156 | ent->blacklist.current = 1; |
157 | ent->blacklist.data[0] = '|'; |
158 | ent->blacklist.data[1] = '\0'; |
159 | } |
160 | else |
161 | ent->blacklist.current = 0; |
162 | |
163 | return NSS_STATUS_SUCCESS; |
164 | } |
165 | |
166 | enum nss_status |
167 | _nss_compat_endgrent (void) |
168 | { |
169 | enum nss_status result; |
170 | |
171 | __libc_lock_lock (lock); |
172 | |
173 | if (nss_endgrent) |
174 | nss_endgrent (); |
175 | |
176 | result = internal_endgrent (&ext_ent); |
177 | |
178 | __libc_lock_unlock (lock); |
179 | |
180 | return result; |
181 | } |
182 | |
183 | /* get the next group from NSS (+ entry) */ |
184 | static enum nss_status |
185 | getgrent_next_nss (struct group *result, ent_t *ent, char *buffer, |
186 | size_t buflen, int *errnop) |
187 | { |
188 | if (!nss_getgrent_r) |
189 | return NSS_STATUS_UNAVAIL; |
190 | |
191 | /* If the setgrent call failed, say so. */ |
192 | if (ent->setent_status != NSS_STATUS_SUCCESS) |
193 | return ent->setent_status; |
194 | |
195 | do |
196 | { |
197 | enum nss_status status; |
198 | |
199 | if ((status = nss_getgrent_r (result, buffer, buflen, errnop)) != |
200 | NSS_STATUS_SUCCESS) |
201 | return status; |
202 | } |
203 | while (in_blacklist (result->gr_name, strlen (result->gr_name), ent)); |
204 | |
205 | return NSS_STATUS_SUCCESS; |
206 | } |
207 | |
208 | /* This function handle the +group entrys in /etc/group */ |
209 | static enum nss_status |
210 | getgrnam_plusgroup (const char *name, struct group *result, ent_t *ent, |
211 | char *buffer, size_t buflen, int *errnop) |
212 | { |
213 | if (!nss_getgrnam_r) |
214 | return NSS_STATUS_UNAVAIL; |
215 | |
216 | enum nss_status status = nss_getgrnam_r (name, result, buffer, buflen, |
217 | errnop); |
218 | if (status != NSS_STATUS_SUCCESS) |
219 | return status; |
220 | |
221 | if (in_blacklist (result->gr_name, strlen (result->gr_name), ent)) |
222 | return NSS_STATUS_NOTFOUND; |
223 | |
224 | /* We found the entry. */ |
225 | return NSS_STATUS_SUCCESS; |
226 | } |
227 | |
228 | static enum nss_status |
229 | getgrent_next_file (struct group *result, ent_t *ent, |
230 | char *buffer, size_t buflen, int *errnop) |
231 | { |
232 | struct parser_data *data = (void *) buffer; |
233 | while (1) |
234 | { |
235 | fpos_t pos; |
236 | int parse_res = 0; |
237 | char *p; |
238 | |
239 | do |
240 | { |
241 | /* We need at least 3 characters for one line. */ |
242 | if (__glibc_unlikely (buflen < 3)) |
243 | { |
244 | erange: |
245 | *errnop = ERANGE; |
246 | return NSS_STATUS_TRYAGAIN; |
247 | } |
248 | |
249 | fgetpos (ent->stream, &pos); |
250 | buffer[buflen - 1] = '\xff'; |
251 | p = fgets_unlocked (buffer, buflen, ent->stream); |
252 | if (p == NULL && feof_unlocked (ent->stream)) |
253 | return NSS_STATUS_NOTFOUND; |
254 | |
255 | if (p == NULL || __builtin_expect (buffer[buflen - 1] != '\xff', 0)) |
256 | { |
257 | erange_reset: |
258 | fsetpos (ent->stream, &pos); |
259 | goto erange; |
260 | } |
261 | |
262 | /* Terminate the line for any case. */ |
263 | buffer[buflen - 1] = '\0'; |
264 | |
265 | /* Skip leading blanks. */ |
266 | while (isspace (*p)) |
267 | ++p; |
268 | } |
269 | while (*p == '\0' || *p == '#' || /* Ignore empty and comment lines. */ |
270 | /* Parse the line. If it is invalid, loop to |
271 | get the next line of the file to parse. */ |
272 | !(parse_res = _nss_files_parse_grent (p, result, data, buflen, |
273 | errnop))); |
274 | |
275 | if (__glibc_unlikely (parse_res == -1)) |
276 | /* The parser ran out of space. */ |
277 | goto erange_reset; |
278 | |
279 | if (result->gr_name[0] != '+' && result->gr_name[0] != '-') |
280 | /* This is a real entry. */ |
281 | break; |
282 | |
283 | /* -group */ |
284 | if (result->gr_name[0] == '-' && result->gr_name[1] != '\0' |
285 | && result->gr_name[1] != '@') |
286 | { |
287 | blacklist_store_name (&result->gr_name[1], ent); |
288 | continue; |
289 | } |
290 | |
291 | /* +group */ |
292 | if (result->gr_name[0] == '+' && result->gr_name[1] != '\0' |
293 | && result->gr_name[1] != '@') |
294 | { |
295 | size_t len = strlen (result->gr_name); |
296 | char buf[len]; |
297 | enum nss_status status; |
298 | |
299 | /* Store the group in the blacklist for the "+" at the end of |
300 | /etc/group */ |
301 | memcpy (buf, &result->gr_name[1], len); |
302 | status = getgrnam_plusgroup (&result->gr_name[1], result, ent, |
303 | buffer, buflen, errnop); |
304 | blacklist_store_name (buf, ent); |
305 | if (status == NSS_STATUS_SUCCESS) /* We found the entry. */ |
306 | break; |
307 | else if (status == NSS_STATUS_RETURN /* We couldn't parse the entry*/ |
308 | || status == NSS_STATUS_NOTFOUND) /* No group in NIS */ |
309 | continue; |
310 | else |
311 | { |
312 | if (status == NSS_STATUS_TRYAGAIN) |
313 | /* The parser ran out of space. */ |
314 | goto erange_reset; |
315 | |
316 | return status; |
317 | } |
318 | } |
319 | |
320 | /* +:... */ |
321 | if (result->gr_name[0] == '+' && result->gr_name[1] == '\0') |
322 | { |
323 | ent->files = false; |
324 | |
325 | return getgrent_next_nss (result, ent, buffer, buflen, errnop); |
326 | } |
327 | } |
328 | |
329 | return NSS_STATUS_SUCCESS; |
330 | } |
331 | |
332 | |
333 | enum nss_status |
334 | _nss_compat_getgrent_r (struct group *grp, char *buffer, size_t buflen, |
335 | int *errnop) |
336 | { |
337 | enum nss_status result = NSS_STATUS_SUCCESS; |
338 | |
339 | __libc_lock_lock (lock); |
340 | |
341 | /* Be prepared that the setgrent function was not called before. */ |
342 | if (ni == NULL) |
343 | init_nss_interface (); |
344 | |
345 | if (ext_ent.stream == NULL) |
346 | result = internal_setgrent (&ext_ent, 1, 1); |
347 | |
348 | if (result == NSS_STATUS_SUCCESS) |
349 | { |
350 | if (ext_ent.files) |
351 | result = getgrent_next_file (grp, &ext_ent, buffer, buflen, errnop); |
352 | else |
353 | result = getgrent_next_nss (grp, &ext_ent, buffer, buflen, errnop); |
354 | } |
355 | __libc_lock_unlock (lock); |
356 | |
357 | return result; |
358 | } |
359 | |
360 | /* Searches in /etc/group and the NIS/NIS+ map for a special group */ |
361 | static enum nss_status |
362 | internal_getgrnam_r (const char *name, struct group *result, ent_t *ent, |
363 | char *buffer, size_t buflen, int *errnop) |
364 | { |
365 | struct parser_data *data = (void *) buffer; |
366 | while (1) |
367 | { |
368 | fpos_t pos; |
369 | int parse_res = 0; |
370 | char *p; |
371 | |
372 | do |
373 | { |
374 | /* We need at least 3 characters for one line. */ |
375 | if (__glibc_unlikely (buflen < 3)) |
376 | { |
377 | erange: |
378 | *errnop = ERANGE; |
379 | return NSS_STATUS_TRYAGAIN; |
380 | } |
381 | |
382 | fgetpos (ent->stream, &pos); |
383 | buffer[buflen - 1] = '\xff'; |
384 | p = fgets_unlocked (buffer, buflen, ent->stream); |
385 | if (p == NULL && feof_unlocked (ent->stream)) |
386 | return NSS_STATUS_NOTFOUND; |
387 | |
388 | if (p == NULL || __builtin_expect (buffer[buflen - 1] != '\xff', 0)) |
389 | { |
390 | erange_reset: |
391 | fsetpos (ent->stream, &pos); |
392 | goto erange; |
393 | } |
394 | |
395 | /* Terminate the line for any case. */ |
396 | buffer[buflen - 1] = '\0'; |
397 | |
398 | /* Skip leading blanks. */ |
399 | while (isspace (*p)) |
400 | ++p; |
401 | } |
402 | while (*p == '\0' || *p == '#' || /* Ignore empty and comment lines. */ |
403 | /* Parse the line. If it is invalid, loop to |
404 | get the next line of the file to parse. */ |
405 | !(parse_res = _nss_files_parse_grent (p, result, data, buflen, |
406 | errnop))); |
407 | |
408 | if (__glibc_unlikely (parse_res == -1)) |
409 | /* The parser ran out of space. */ |
410 | goto erange_reset; |
411 | |
412 | /* This is a real entry. */ |
413 | if (result->gr_name[0] != '+' && result->gr_name[0] != '-') |
414 | { |
415 | if (strcmp (result->gr_name, name) == 0) |
416 | return NSS_STATUS_SUCCESS; |
417 | else |
418 | continue; |
419 | } |
420 | |
421 | /* -group */ |
422 | if (result->gr_name[0] == '-' && result->gr_name[1] != '\0') |
423 | { |
424 | if (strcmp (&result->gr_name[1], name) == 0) |
425 | return NSS_STATUS_NOTFOUND; |
426 | else |
427 | continue; |
428 | } |
429 | |
430 | /* +group */ |
431 | if (result->gr_name[0] == '+' && result->gr_name[1] != '\0') |
432 | { |
433 | if (strcmp (name, &result->gr_name[1]) == 0) |
434 | { |
435 | enum nss_status status; |
436 | |
437 | status = getgrnam_plusgroup (name, result, ent, |
438 | buffer, buflen, errnop); |
439 | if (status == NSS_STATUS_RETURN) |
440 | /* We couldn't parse the entry */ |
441 | continue; |
442 | else |
443 | return status; |
444 | } |
445 | } |
446 | /* +:... */ |
447 | if (result->gr_name[0] == '+' && result->gr_name[1] == '\0') |
448 | { |
449 | enum nss_status status; |
450 | |
451 | status = getgrnam_plusgroup (name, result, ent, |
452 | buffer, buflen, errnop); |
453 | if (status == NSS_STATUS_RETURN) |
454 | /* We couldn't parse the entry */ |
455 | continue; |
456 | else |
457 | return status; |
458 | } |
459 | } |
460 | |
461 | return NSS_STATUS_SUCCESS; |
462 | } |
463 | |
464 | enum nss_status |
465 | _nss_compat_getgrnam_r (const char *name, struct group *grp, |
466 | char *buffer, size_t buflen, int *errnop) |
467 | { |
468 | ent_t ent = { true, NSS_STATUS_SUCCESS, NULL, { NULL, 0, 0 }}; |
469 | enum nss_status result; |
470 | |
471 | if (name[0] == '-' || name[0] == '+') |
472 | return NSS_STATUS_NOTFOUND; |
473 | |
474 | __libc_lock_lock (lock); |
475 | |
476 | if (ni == NULL) |
477 | init_nss_interface (); |
478 | |
479 | __libc_lock_unlock (lock); |
480 | |
481 | result = internal_setgrent (&ent, 0, 0); |
482 | |
483 | if (result == NSS_STATUS_SUCCESS) |
484 | result = internal_getgrnam_r (name, grp, &ent, buffer, buflen, errnop); |
485 | |
486 | internal_endgrent (&ent); |
487 | |
488 | return result; |
489 | } |
490 | |
491 | /* Searches in /etc/group and the NIS/NIS+ map for a special group id */ |
492 | static enum nss_status |
493 | internal_getgrgid_r (gid_t gid, struct group *result, ent_t *ent, |
494 | char *buffer, size_t buflen, int *errnop) |
495 | { |
496 | struct parser_data *data = (void *) buffer; |
497 | while (1) |
498 | { |
499 | fpos_t pos; |
500 | int parse_res = 0; |
501 | char *p; |
502 | |
503 | do |
504 | { |
505 | /* We need at least 3 characters for one line. */ |
506 | if (__glibc_unlikely (buflen < 3)) |
507 | { |
508 | erange: |
509 | *errnop = ERANGE; |
510 | return NSS_STATUS_TRYAGAIN; |
511 | } |
512 | |
513 | fgetpos (ent->stream, &pos); |
514 | buffer[buflen - 1] = '\xff'; |
515 | p = fgets_unlocked (buffer, buflen, ent->stream); |
516 | if (p == NULL && feof_unlocked (ent->stream)) |
517 | return NSS_STATUS_NOTFOUND; |
518 | |
519 | if (p == NULL || __builtin_expect (buffer[buflen - 1] != '\xff', 0)) |
520 | { |
521 | erange_reset: |
522 | fsetpos (ent->stream, &pos); |
523 | goto erange; |
524 | } |
525 | |
526 | /* Terminate the line for any case. */ |
527 | buffer[buflen - 1] = '\0'; |
528 | |
529 | /* Skip leading blanks. */ |
530 | while (isspace (*p)) |
531 | ++p; |
532 | } |
533 | while (*p == '\0' || *p == '#' || /* Ignore empty and comment lines. */ |
534 | /* Parse the line. If it is invalid, loop to |
535 | get the next line of the file to parse. */ |
536 | !(parse_res = _nss_files_parse_grent (p, result, data, buflen, |
537 | errnop))); |
538 | |
539 | if (__glibc_unlikely (parse_res == -1)) |
540 | /* The parser ran out of space. */ |
541 | goto erange_reset; |
542 | |
543 | /* This is a real entry. */ |
544 | if (result->gr_name[0] != '+' && result->gr_name[0] != '-') |
545 | { |
546 | if (result->gr_gid == gid) |
547 | return NSS_STATUS_SUCCESS; |
548 | else |
549 | continue; |
550 | } |
551 | |
552 | /* -group */ |
553 | if (result->gr_name[0] == '-' && result->gr_name[1] != '\0') |
554 | { |
555 | blacklist_store_name (&result->gr_name[1], ent); |
556 | continue; |
557 | } |
558 | |
559 | /* +group */ |
560 | if (result->gr_name[0] == '+' && result->gr_name[1] != '\0') |
561 | { |
562 | /* Yes, no +1, see the memcpy call below. */ |
563 | size_t len = strlen (result->gr_name); |
564 | char buf[len]; |
565 | enum nss_status status; |
566 | |
567 | /* Store the group in the blacklist for the "+" at the end of |
568 | /etc/group */ |
569 | memcpy (buf, &result->gr_name[1], len); |
570 | status = getgrnam_plusgroup (&result->gr_name[1], result, ent, |
571 | buffer, buflen, errnop); |
572 | blacklist_store_name (buf, ent); |
573 | if (status == NSS_STATUS_SUCCESS && result->gr_gid == gid) |
574 | break; |
575 | else |
576 | continue; |
577 | } |
578 | /* +:... */ |
579 | if (result->gr_name[0] == '+' && result->gr_name[1] == '\0') |
580 | { |
581 | if (!nss_getgrgid_r) |
582 | return NSS_STATUS_UNAVAIL; |
583 | |
584 | enum nss_status status = nss_getgrgid_r (gid, result, buffer, buflen, |
585 | errnop); |
586 | if (status == NSS_STATUS_RETURN) /* We couldn't parse the entry */ |
587 | return NSS_STATUS_NOTFOUND; |
588 | else |
589 | return status; |
590 | } |
591 | } |
592 | |
593 | return NSS_STATUS_SUCCESS; |
594 | } |
595 | |
596 | enum nss_status |
597 | _nss_compat_getgrgid_r (gid_t gid, struct group *grp, |
598 | char *buffer, size_t buflen, int *errnop) |
599 | { |
600 | ent_t ent = { true, NSS_STATUS_SUCCESS, NULL, { NULL, 0, 0 }}; |
601 | enum nss_status result; |
602 | |
603 | __libc_lock_lock (lock); |
604 | |
605 | if (ni == NULL) |
606 | init_nss_interface (); |
607 | |
608 | __libc_lock_unlock (lock); |
609 | |
610 | result = internal_setgrent (&ent, 0, 0); |
611 | |
612 | if (result == NSS_STATUS_SUCCESS) |
613 | result = internal_getgrgid_r (gid, grp, &ent, buffer, buflen, errnop); |
614 | |
615 | internal_endgrent (&ent); |
616 | |
617 | return result; |
618 | } |
619 | |
620 | |
621 | /* Support routines for remembering -@netgroup and -user entries. |
622 | The names are stored in a single string with `|' as separator. */ |
623 | static void |
624 | blacklist_store_name (const char *name, ent_t *ent) |
625 | { |
626 | int namelen = strlen (name); |
627 | char *tmp; |
628 | |
629 | /* first call, setup cache */ |
630 | if (ent->blacklist.size == 0) |
631 | { |
632 | ent->blacklist.size = MAX (BLACKLIST_INITIAL_SIZE, 2 * namelen); |
633 | ent->blacklist.data = malloc (ent->blacklist.size); |
634 | if (ent->blacklist.data == NULL) |
635 | return; |
636 | ent->blacklist.data[0] = '|'; |
637 | ent->blacklist.data[1] = '\0'; |
638 | ent->blacklist.current = 1; |
639 | } |
640 | else |
641 | { |
642 | if (in_blacklist (name, namelen, ent)) |
643 | return; /* no duplicates */ |
644 | |
645 | if (ent->blacklist.current + namelen + 1 >= ent->blacklist.size) |
646 | { |
647 | ent->blacklist.size += MAX (BLACKLIST_INCREMENT, 2 * namelen); |
648 | tmp = realloc (ent->blacklist.data, ent->blacklist.size); |
649 | if (tmp == NULL) |
650 | { |
651 | free (ent->blacklist.data); |
652 | ent->blacklist.size = 0; |
653 | return; |
654 | } |
655 | ent->blacklist.data = tmp; |
656 | } |
657 | } |
658 | |
659 | tmp = stpcpy (ent->blacklist.data + ent->blacklist.current, name); |
660 | *tmp++ = '|'; |
661 | *tmp = '\0'; |
662 | ent->blacklist.current += namelen + 1; |
663 | |
664 | return; |
665 | } |
666 | |
667 | /* Return whether ent->blacklist contains name. */ |
668 | static bool |
669 | in_blacklist (const char *name, int namelen, ent_t *ent) |
670 | { |
671 | char buf[namelen + 3]; |
672 | char *cp; |
673 | |
674 | if (ent->blacklist.data == NULL) |
675 | return false; |
676 | |
677 | buf[0] = '|'; |
678 | cp = stpcpy (&buf[1], name); |
679 | *cp++ = '|'; |
680 | *cp = '\0'; |
681 | return strstr (ent->blacklist.data, buf) != NULL; |
682 | } |
683 | |