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