1 /* 2 * Copyright (C) 2008 The Android Open Source Project 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * * Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * * Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in 12 * the documentation and/or other materials provided with the 13 * distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 16 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 17 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 18 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 19 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 22 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 25 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 #include <ctype.h> 30 #include <errno.h> 31 #include <grp.h> 32 #include <mntent.h> 33 #include <pthread.h> 34 #include <pwd.h> 35 #include <stdio.h> 36 #include <stdlib.h> 37 #include <string.h> 38 #include <unistd.h> 39 40 #include "private/android_filesystem_config.h" 41 #include "private/bionic_macros.h" 42 #include "private/grp_pwd.h" 43 #include "private/ErrnoRestorer.h" 44 #include "private/libc_logging.h" 45 46 // Generated android_ids array 47 #include "generated_android_ids.h" 48 49 // POSIX seems to envisage an implementation where the <pwd.h> functions are 50 // implemented by brute-force searching with getpwent(3), and the <grp.h> 51 // functions are implemented similarly with getgrent(3). This means that it's 52 // okay for all the <grp.h> functions to share state, and all the <passwd.h> 53 // functions to share state, but <grp.h> functions can't clobber <passwd.h> 54 // functions' state and vice versa. 55 #include "bionic/pthread_internal.h" 56 static group_state_t* get_group_tls_buffer() { 57 return &__get_bionic_tls().group; 58 } 59 60 static passwd_state_t* get_passwd_tls_buffer() { 61 return &__get_bionic_tls().passwd; 62 } 63 64 static void init_group_state(group_state_t* state) { 65 memset(state, 0, sizeof(group_state_t) - sizeof(state->getgrent_idx)); 66 state->group_.gr_mem = state->group_members_; 67 } 68 69 static group_state_t* __group_state() { 70 group_state_t* result = get_group_tls_buffer(); 71 if (result != nullptr) { 72 init_group_state(result); 73 } 74 return result; 75 } 76 77 static int do_getpw_r(int by_name, const char* name, uid_t uid, 78 passwd* dst, char* buf, size_t byte_count, 79 passwd** result) { 80 // getpwnam_r and getpwuid_r don't modify errno, but library calls we 81 // make might. 82 ErrnoRestorer errno_restorer; 83 *result = NULL; 84 85 // Our implementation of getpwnam(3) and getpwuid(3) use thread-local 86 // storage, so we can call them as long as we copy everything out 87 // before returning. 88 const passwd* src = by_name ? getpwnam(name) : getpwuid(uid); // NOLINT: see above. 89 90 // POSIX allows failure to find a match to be considered a non-error. 91 // Reporting success (0) but with *result NULL is glibc's behavior. 92 if (src == NULL) { 93 return (errno == ENOENT) ? 0 : errno; 94 } 95 96 // Work out where our strings will go in 'buf', and whether we've got 97 // enough space. 98 size_t required_byte_count = 0; 99 dst->pw_name = buf; 100 required_byte_count += strlen(src->pw_name) + 1; 101 dst->pw_dir = buf + required_byte_count; 102 required_byte_count += strlen(src->pw_dir) + 1; 103 dst->pw_shell = buf + required_byte_count; 104 required_byte_count += strlen(src->pw_shell) + 1; 105 if (byte_count < required_byte_count) { 106 return ERANGE; 107 } 108 109 // Copy the strings. 110 snprintf(buf, byte_count, "%s%c%s%c%s", src->pw_name, 0, src->pw_dir, 0, src->pw_shell); 111 112 // pw_passwd and pw_gecos are non-POSIX and unused (always NULL) in bionic. 113 // Note: On LP32, we define pw_gecos to pw_passwd since they're both NULL. 114 dst->pw_passwd = NULL; 115 #if defined(__LP64__) 116 dst->pw_gecos = NULL; 117 #endif 118 119 // Copy the integral fields. 120 dst->pw_gid = src->pw_gid; 121 dst->pw_uid = src->pw_uid; 122 123 *result = dst; 124 return 0; 125 } 126 127 int getpwnam_r(const char* name, passwd* pwd, 128 char* buf, size_t byte_count, passwd** result) { 129 return do_getpw_r(1, name, -1, pwd, buf, byte_count, result); 130 } 131 132 int getpwuid_r(uid_t uid, passwd* pwd, 133 char* buf, size_t byte_count, passwd** result) { 134 return do_getpw_r(0, NULL, uid, pwd, buf, byte_count, result); 135 } 136 137 static passwd* android_iinfo_to_passwd(passwd_state_t* state, 138 const android_id_info* iinfo) { 139 snprintf(state->name_buffer_, sizeof(state->name_buffer_), "%s", iinfo->name); 140 snprintf(state->dir_buffer_, sizeof(state->dir_buffer_), "/"); 141 snprintf(state->sh_buffer_, sizeof(state->sh_buffer_), "/system/bin/sh"); 142 143 passwd* pw = &state->passwd_; 144 pw->pw_name = state->name_buffer_; 145 pw->pw_uid = iinfo->aid; 146 pw->pw_gid = iinfo->aid; 147 pw->pw_dir = state->dir_buffer_; 148 pw->pw_shell = state->sh_buffer_; 149 return pw; 150 } 151 152 static group* android_iinfo_to_group(group_state_t* state, 153 const android_id_info* iinfo) { 154 snprintf(state->group_name_buffer_, sizeof(state->group_name_buffer_), "%s", iinfo->name); 155 156 group* gr = &state->group_; 157 gr->gr_name = state->group_name_buffer_; 158 gr->gr_gid = iinfo->aid; 159 gr->gr_mem[0] = gr->gr_name; 160 return gr; 161 } 162 163 static passwd* android_id_to_passwd(passwd_state_t* state, unsigned id) { 164 for (size_t n = 0; n < android_id_count; ++n) { 165 if (android_ids[n].aid == id) { 166 return android_iinfo_to_passwd(state, android_ids + n); 167 } 168 } 169 return NULL; 170 } 171 172 static passwd* android_name_to_passwd(passwd_state_t* state, const char* name) { 173 for (size_t n = 0; n < android_id_count; ++n) { 174 if (!strcmp(android_ids[n].name, name)) { 175 return android_iinfo_to_passwd(state, android_ids + n); 176 } 177 } 178 return NULL; 179 } 180 181 static group* android_id_to_group(group_state_t* state, unsigned id) { 182 for (size_t n = 0; n < android_id_count; ++n) { 183 if (android_ids[n].aid == id) { 184 return android_iinfo_to_group(state, android_ids + n); 185 } 186 } 187 return NULL; 188 } 189 190 static group* android_name_to_group(group_state_t* state, const char* name) { 191 for (size_t n = 0; n < android_id_count; ++n) { 192 if (!strcmp(android_ids[n].name, name)) { 193 return android_iinfo_to_group(state, android_ids + n); 194 } 195 } 196 return NULL; 197 } 198 199 // Translate a user/group name to the corresponding user/group id. 200 // all_a1234 -> 0 * AID_USER_OFFSET + AID_SHARED_GID_START + 1234 (group name only) 201 // u0_a1234_cache -> 0 * AID_USER_OFFSET + AID_CACHE_GID_START + 1234 (group name only) 202 // u0_a1234 -> 0 * AID_USER_OFFSET + AID_APP_START + 1234 203 // u2_i1000 -> 2 * AID_USER_OFFSET + AID_ISOLATED_START + 1000 204 // u1_system -> 1 * AID_USER_OFFSET + android_ids['system'] 205 // returns 0 and sets errno to ENOENT in case of error. 206 static id_t app_id_from_name(const char* name, bool is_group) { 207 char* end; 208 unsigned long userid; 209 bool is_shared_gid = false; 210 211 if (is_group && name[0] == 'a' && name[1] == 'l' && name[2] == 'l') { 212 end = const_cast<char*>(name+3); 213 userid = 0; 214 is_shared_gid = true; 215 } else if (name[0] == 'u' && isdigit(name[1])) { 216 userid = strtoul(name+1, &end, 10); 217 } else { 218 errno = ENOENT; 219 return 0; 220 } 221 222 if (end[0] != '_' || end[1] == 0) { 223 errno = ENOENT; 224 return 0; 225 } 226 227 unsigned long appid = 0; 228 if (end[1] == 'a' && isdigit(end[2])) { 229 if (is_shared_gid) { 230 // end will point to \0 if the strtoul below succeeds. 231 appid = strtoul(end+2, &end, 10) + AID_SHARED_GID_START; 232 if (appid > AID_SHARED_GID_END) { 233 errno = ENOENT; 234 return 0; 235 } 236 } else { 237 // end will point to \0 if the strtoul below succeeds. 238 appid = strtoul(end+2, &end, 10); 239 if (is_group && !strcmp(end, "_cache")) { 240 end += 6; 241 appid += AID_CACHE_GID_START; 242 } else { 243 appid += AID_APP_START; 244 } 245 } 246 } else if (end[1] == 'i' && isdigit(end[2])) { 247 // end will point to \0 if the strtoul below succeeds. 248 appid = strtoul(end+2, &end, 10) + AID_ISOLATED_START; 249 } else { 250 for (size_t n = 0; n < android_id_count; n++) { 251 if (!strcmp(android_ids[n].name, end + 1)) { 252 appid = android_ids[n].aid; 253 // Move the end pointer to the null terminator. 254 end += strlen(android_ids[n].name) + 1; 255 break; 256 } 257 } 258 } 259 260 // Check that the entire string was consumed by one of the 3 cases above. 261 if (end[0] != 0) { 262 errno = ENOENT; 263 return 0; 264 } 265 266 // Check that user id won't overflow. 267 if (userid > 1000) { 268 errno = ENOENT; 269 return 0; 270 } 271 272 // Check that app id is within range. 273 if (appid >= AID_USER_OFFSET) { 274 errno = ENOENT; 275 return 0; 276 } 277 278 return (appid + userid*AID_USER_OFFSET); 279 } 280 281 static void print_app_name_from_uid(const uid_t uid, char* buffer, const int bufferlen) { 282 const uid_t appid = uid % AID_USER_OFFSET; 283 const uid_t userid = uid / AID_USER_OFFSET; 284 if (appid >= AID_ISOLATED_START) { 285 snprintf(buffer, bufferlen, "u%u_i%u", userid, appid - AID_ISOLATED_START); 286 } else if (appid < AID_APP_START) { 287 for (size_t n = 0; n < android_id_count; n++) { 288 if (android_ids[n].aid == appid) { 289 snprintf(buffer, bufferlen, "u%u_%s", userid, android_ids[n].name); 290 return; 291 } 292 } 293 } else { 294 snprintf(buffer, bufferlen, "u%u_a%u", userid, appid - AID_APP_START); 295 } 296 } 297 298 static void print_app_name_from_gid(const gid_t gid, char* buffer, const int bufferlen) { 299 const uid_t appid = gid % AID_USER_OFFSET; 300 const uid_t userid = gid / AID_USER_OFFSET; 301 if (appid >= AID_ISOLATED_START) { 302 snprintf(buffer, bufferlen, "u%u_i%u", userid, appid - AID_ISOLATED_START); 303 } else if (userid == 0 && appid >= AID_SHARED_GID_START && appid <= AID_SHARED_GID_END) { 304 snprintf(buffer, bufferlen, "all_a%u", appid - AID_SHARED_GID_START); 305 } else if (appid >= AID_CACHE_GID_START && appid <= AID_CACHE_GID_END) { 306 snprintf(buffer, bufferlen, "u%u_a%u_cache", userid, appid - AID_CACHE_GID_START); 307 } else if (appid < AID_APP_START) { 308 for (size_t n = 0; n < android_id_count; n++) { 309 if (android_ids[n].aid == appid) { 310 snprintf(buffer, bufferlen, "u%u_%s", userid, android_ids[n].name); 311 return; 312 } 313 } 314 } else { 315 snprintf(buffer, bufferlen, "u%u_a%u", userid, appid - AID_APP_START); 316 } 317 } 318 319 // oem_XXXX -> uid 320 // Supported ranges: 321 // AID_OEM_RESERVED_START to AID_OEM_RESERVED_END (2900-2999) 322 // AID_OEM_RESERVED_2_START to AID_OEM_RESERVED_2_END (5000-5999) 323 // Check OEM id is within range. 324 static bool is_oem_id(id_t id) { 325 return (((id >= AID_OEM_RESERVED_START) && (id <= AID_OEM_RESERVED_END)) || 326 ((id >= AID_OEM_RESERVED_2_START) && (id <= AID_OEM_RESERVED_2_END))); 327 } 328 329 // Translate an OEM name to the corresponding user/group id. 330 static id_t oem_id_from_name(const char* name) { 331 unsigned int id; 332 if (sscanf(name, "oem_%u", &id) != 1) { 333 return 0; 334 } 335 if (!is_oem_id(id)) { 336 return 0; 337 } 338 return static_cast<id_t>(id); 339 } 340 341 static passwd* oem_id_to_passwd(uid_t uid, passwd_state_t* state) { 342 if (!is_oem_id(uid)) { 343 return NULL; 344 } 345 346 snprintf(state->name_buffer_, sizeof(state->name_buffer_), "oem_%u", uid); 347 snprintf(state->dir_buffer_, sizeof(state->dir_buffer_), "/"); 348 snprintf(state->sh_buffer_, sizeof(state->sh_buffer_), "/system/bin/sh"); 349 350 passwd* pw = &state->passwd_; 351 pw->pw_name = state->name_buffer_; 352 pw->pw_dir = state->dir_buffer_; 353 pw->pw_shell = state->sh_buffer_; 354 pw->pw_uid = uid; 355 pw->pw_gid = uid; 356 return pw; 357 } 358 359 static group* oem_id_to_group(gid_t gid, group_state_t* state) { 360 if (!is_oem_id(gid)) { 361 return NULL; 362 } 363 364 snprintf(state->group_name_buffer_, sizeof(state->group_name_buffer_), 365 "oem_%u", gid); 366 367 group* gr = &state->group_; 368 gr->gr_name = state->group_name_buffer_; 369 gr->gr_gid = gid; 370 gr->gr_mem[0] = gr->gr_name; 371 return gr; 372 } 373 374 // Translate a uid into the corresponding name. 375 // 0 to AID_APP_START-1 -> "system", "radio", etc. 376 // AID_APP_START to AID_ISOLATED_START-1 -> u0_a1234 377 // AID_ISOLATED_START to AID_USER_OFFSET-1 -> u0_i1234 378 // AID_USER_OFFSET+ -> u1_radio, u1_a1234, u2_i1234, etc. 379 // returns a passwd structure (sets errno to ENOENT on failure). 380 static passwd* app_id_to_passwd(uid_t uid, passwd_state_t* state) { 381 if (uid < AID_APP_START) { 382 errno = ENOENT; 383 return NULL; 384 } 385 386 print_app_name_from_uid(uid, state->name_buffer_, sizeof(state->name_buffer_)); 387 388 const uid_t appid = uid % AID_USER_OFFSET; 389 if (appid < AID_APP_START) { 390 snprintf(state->dir_buffer_, sizeof(state->dir_buffer_), "/"); 391 } else { 392 snprintf(state->dir_buffer_, sizeof(state->dir_buffer_), "/data"); 393 } 394 395 snprintf(state->sh_buffer_, sizeof(state->sh_buffer_), "/system/bin/sh"); 396 397 passwd* pw = &state->passwd_; 398 pw->pw_name = state->name_buffer_; 399 pw->pw_dir = state->dir_buffer_; 400 pw->pw_shell = state->sh_buffer_; 401 pw->pw_uid = uid; 402 pw->pw_gid = uid; 403 return pw; 404 } 405 406 // Translate a gid into the corresponding app_<gid> 407 // group structure (sets errno to ENOENT on failure). 408 static group* app_id_to_group(gid_t gid, group_state_t* state) { 409 if (gid < AID_APP_START) { 410 errno = ENOENT; 411 return NULL; 412 } 413 414 print_app_name_from_gid(gid, state->group_name_buffer_, sizeof(state->group_name_buffer_)); 415 416 group* gr = &state->group_; 417 gr->gr_name = state->group_name_buffer_; 418 gr->gr_gid = gid; 419 gr->gr_mem[0] = gr->gr_name; 420 return gr; 421 } 422 423 passwd* getpwuid(uid_t uid) { // NOLINT: implementing bad function. 424 passwd_state_t* state = get_passwd_tls_buffer(); 425 if (state == NULL) { 426 return NULL; 427 } 428 429 passwd* pw = android_id_to_passwd(state, uid); 430 if (pw != NULL) { 431 return pw; 432 } 433 // Handle OEM range. 434 pw = oem_id_to_passwd(uid, state); 435 if (pw != NULL) { 436 return pw; 437 } 438 return app_id_to_passwd(uid, state); 439 } 440 441 passwd* getpwnam(const char* login) { // NOLINT: implementing bad function. 442 passwd_state_t* state = get_passwd_tls_buffer(); 443 if (state == NULL) { 444 return NULL; 445 } 446 447 passwd* pw = android_name_to_passwd(state, login); 448 if (pw != NULL) { 449 return pw; 450 } 451 // Handle OEM range. 452 pw = oem_id_to_passwd(oem_id_from_name(login), state); 453 if (pw != NULL) { 454 return pw; 455 } 456 return app_id_to_passwd(app_id_from_name(login, false), state); 457 } 458 459 // All users are in just one group, the one passed in. 460 int getgrouplist(const char* /*user*/, gid_t group, gid_t* groups, int* ngroups) { 461 if (*ngroups < 1) { 462 *ngroups = 1; 463 return -1; 464 } 465 groups[0] = group; 466 return (*ngroups = 1); 467 } 468 469 char* getlogin() { // NOLINT: implementing bad function. 470 passwd *pw = getpwuid(getuid()); // NOLINT: implementing bad function in terms of bad function. 471 return (pw != NULL) ? pw->pw_name : NULL; 472 } 473 474 void setpwent() { 475 passwd_state_t* state = get_passwd_tls_buffer(); 476 if (state) { 477 state->getpwent_idx = 0; 478 } 479 } 480 481 void endpwent() { 482 setpwent(); 483 } 484 485 passwd* getpwent() { 486 passwd_state_t* state = get_passwd_tls_buffer(); 487 if (state == NULL) { 488 return NULL; 489 } 490 if (state->getpwent_idx < 0) { 491 return NULL; 492 } 493 494 size_t start = 0; 495 ssize_t end = android_id_count; 496 if (state->getpwent_idx < end) { 497 return android_iinfo_to_passwd(state, android_ids + state->getpwent_idx++); 498 } 499 500 start = end; 501 end += AID_OEM_RESERVED_END - AID_OEM_RESERVED_START + 1; 502 503 if (state->getpwent_idx < end) { 504 return oem_id_to_passwd( 505 state->getpwent_idx++ - start + AID_OEM_RESERVED_START, state); 506 } 507 508 start = end; 509 end += AID_OEM_RESERVED_2_END - AID_OEM_RESERVED_2_START + 1; 510 511 if (state->getpwent_idx < end) { 512 return oem_id_to_passwd( 513 state->getpwent_idx++ - start + AID_OEM_RESERVED_2_START, state); 514 } 515 516 start = end; 517 end += AID_USER_OFFSET - AID_APP_START; // Do not expose higher users 518 519 if (state->getpwent_idx < end) { 520 return app_id_to_passwd(state->getpwent_idx++ - start + AID_APP_START, state); 521 } 522 523 // We are not reporting u1_a* and higher or we will be here forever 524 state->getpwent_idx = -1; 525 return NULL; 526 } 527 528 static group* getgrgid_internal(gid_t gid, group_state_t* state) { 529 group* grp = android_id_to_group(state, gid); 530 if (grp != NULL) { 531 return grp; 532 } 533 // Handle OEM range. 534 grp = oem_id_to_group(gid, state); 535 if (grp != NULL) { 536 return grp; 537 } 538 return app_id_to_group(gid, state); 539 } 540 541 group* getgrgid(gid_t gid) { // NOLINT: implementing bad function. 542 group_state_t* state = __group_state(); 543 if (state == NULL) { 544 return NULL; 545 } 546 return getgrgid_internal(gid, state); 547 } 548 549 static group* getgrnam_internal(const char* name, group_state_t* state) { 550 group* grp = android_name_to_group(state, name); 551 if (grp != NULL) { 552 return grp; 553 } 554 // Handle OEM range. 555 grp = oem_id_to_group(oem_id_from_name(name), state); 556 if (grp != NULL) { 557 return grp; 558 } 559 return app_id_to_group(app_id_from_name(name, true), state); 560 } 561 562 group* getgrnam(const char* name) { // NOLINT: implementing bad function. 563 group_state_t* state = __group_state(); 564 if (state == NULL) { 565 return NULL; 566 } 567 return getgrnam_internal(name, state); 568 } 569 570 static int getgroup_r(bool by_name, const char* name, gid_t gid, struct group* grp, char* buf, 571 size_t buflen, struct group** result) { 572 ErrnoRestorer errno_restorer; 573 *result = NULL; 574 char* p = reinterpret_cast<char*>( 575 BIONIC_ALIGN(reinterpret_cast<uintptr_t>(buf), sizeof(uintptr_t))); 576 if (p + sizeof(group_state_t) > buf + buflen) { 577 return ERANGE; 578 } 579 group_state_t* state = reinterpret_cast<group_state_t*>(p); 580 init_group_state(state); 581 group* retval = (by_name ? getgrnam_internal(name, state) : getgrgid_internal(gid, state)); 582 if (retval != NULL) { 583 *grp = *retval; 584 *result = grp; 585 return 0; 586 } 587 return errno; 588 } 589 590 int getgrgid_r(gid_t gid, struct group* grp, char* buf, size_t buflen, struct group** result) { 591 return getgroup_r(false, NULL, gid, grp, buf, buflen, result); 592 } 593 594 int getgrnam_r(const char* name, struct group* grp, char* buf, size_t buflen, 595 struct group **result) { 596 return getgroup_r(true, name, 0, grp, buf, buflen, result); 597 } 598 599 void setgrent() { 600 group_state_t* state = get_group_tls_buffer(); 601 if (state) { 602 state->getgrent_idx = 0; 603 } 604 } 605 606 void endgrent() { 607 setgrent(); 608 } 609 610 group* getgrent() { 611 group_state_t* state = get_group_tls_buffer(); 612 if (state == NULL) { 613 return NULL; 614 } 615 if (state->getgrent_idx < 0) { 616 return NULL; 617 } 618 619 size_t start = 0; 620 ssize_t end = android_id_count; 621 if (state->getgrent_idx < end) { 622 init_group_state(state); 623 return android_iinfo_to_group(state, android_ids + state->getgrent_idx++); 624 } 625 626 start = end; 627 end += AID_OEM_RESERVED_END - AID_OEM_RESERVED_START + 1; 628 629 if (state->getgrent_idx < end) { 630 init_group_state(state); 631 return oem_id_to_group( 632 state->getgrent_idx++ - start + AID_OEM_RESERVED_START, state); 633 } 634 635 start = end; 636 end += AID_OEM_RESERVED_2_END - AID_OEM_RESERVED_2_START + 1; 637 638 if (state->getgrent_idx < end) { 639 init_group_state(state); 640 return oem_id_to_group( 641 state->getgrent_idx++ - start + AID_OEM_RESERVED_2_START, state); 642 } 643 644 start = end; 645 end += AID_USER_OFFSET - AID_APP_START; // Do not expose higher groups 646 647 if (state->getgrent_idx < end) { 648 init_group_state(state); 649 return app_id_to_group(state->getgrent_idx++ - start + AID_APP_START, state); 650 } 651 652 // We are not reporting u1_a* and higher or we will be here forever 653 state->getgrent_idx = -1; 654 return NULL; 655 } 656