busybox/libpwdgrp/pwd_grp.c
<<
>>
Prefs
   1/* vi: set sw=4 ts=4: */
   2/*  Copyright (C) 2003     Manuel Novoa III
   3 *
   4 *  Licensed under GPL v2, or later.  See file LICENSE in this tarball.
   5 */
   6
   7/*  Nov 6, 2003  Initial version.
   8 *
   9 *  NOTE: This implementation is quite strict about requiring all
  10 *    field seperators.  It also does not allow leading whitespace
  11 *    except when processing the numeric fields.  glibc is more
  12 *    lenient.  See the various glibc difference comments below.
  13 *
  14 *  TODO:
  15 *    Move to dynamic allocation of (currently statically allocated)
  16 *      buffers; especially for the group-related functions since
  17 *      large group member lists will cause error returns.
  18 *
  19 */
  20
  21#include "libbb.h"
  22#include <assert.h>
  23
  24#ifndef _PATH_SHADOW
  25#define _PATH_SHADOW    "/etc/shadow"
  26#endif
  27#ifndef _PATH_PASSWD
  28#define _PATH_PASSWD    "/etc/passwd"
  29#endif
  30#ifndef _PATH_GROUP
  31#define _PATH_GROUP     "/etc/group"
  32#endif
  33
  34/**********************************************************************/
  35/* Sizes for statically allocated buffers. */
  36
  37/* If you change these values, also change _SC_GETPW_R_SIZE_MAX and
  38 * _SC_GETGR_R_SIZE_MAX in libc/unistd/sysconf.c to match */
  39#define PWD_BUFFER_SIZE 256
  40#define GRP_BUFFER_SIZE 256
  41
  42/**********************************************************************/
  43/* Prototypes for internal functions. */
  44
  45static int bb__pgsreader(
  46                int FAST_FUNC (*parserfunc)(void *d, char *line),
  47                void *data,
  48                char *__restrict line_buff,
  49                size_t buflen,
  50                FILE *f);
  51
  52static int FAST_FUNC bb__parsepwent(void *pw, char *line);
  53static int FAST_FUNC bb__parsegrent(void *gr, char *line);
  54#if ENABLE_USE_BB_SHADOW
  55static int FAST_FUNC bb__parsespent(void *sp, char *line);
  56#endif
  57
  58/**********************************************************************/
  59/* We avoid having big global data. */
  60
  61struct statics {
  62        /* Smaller things first */
  63        struct passwd getpwuid_resultbuf;
  64        struct group getgrgid_resultbuf;
  65        struct passwd getpwnam_resultbuf;
  66        struct group getgrnam_resultbuf;
  67
  68        char getpwuid_buffer[PWD_BUFFER_SIZE];
  69        char getgrgid_buffer[GRP_BUFFER_SIZE];
  70        char getpwnam_buffer[PWD_BUFFER_SIZE];
  71        char getgrnam_buffer[GRP_BUFFER_SIZE];
  72#if 0
  73        struct passwd fgetpwent_resultbuf;
  74        struct group fgetgrent_resultbuf;
  75        struct spwd fgetspent_resultbuf;
  76        char fgetpwent_buffer[PWD_BUFFER_SIZE];
  77        char fgetgrent_buffer[GRP_BUFFER_SIZE];
  78        char fgetspent_buffer[PWD_BUFFER_SIZE];
  79#endif
  80#if 0 //ENABLE_USE_BB_SHADOW
  81        struct spwd getspuid_resultbuf;
  82        struct spwd getspnam_resultbuf;
  83        char getspuid_buffer[PWD_BUFFER_SIZE];
  84        char getspnam_buffer[PWD_BUFFER_SIZE];
  85#endif
  86// Not converted - too small to bother
  87//pthread_mutex_t mylock = PTHREAD_MUTEX_INITIALIZER;
  88//FILE *pwf /*= NULL*/;
  89//FILE *grf /*= NULL*/;
  90//FILE *spf /*= NULL*/;
  91#if 0
  92        struct passwd getpwent_pwd;
  93        struct group getgrent_gr;
  94        char getpwent_line_buff[PWD_BUFFER_SIZE];
  95        char getgrent_line_buff[GRP_BUFFER_SIZE];
  96#endif
  97#if 0 //ENABLE_USE_BB_SHADOW
  98        struct spwd getspent_spwd;
  99        struct spwd sgetspent_spwd;
 100        char getspent_line_buff[PWD_BUFFER_SIZE];
 101        char sgetspent_line_buff[PWD_BUFFER_SIZE];
 102#endif
 103};
 104
 105static struct statics *ptr_to_statics;
 106
 107static struct statics *get_S(void)
 108{
 109        if (!ptr_to_statics)
 110                ptr_to_statics = xzalloc(sizeof(*ptr_to_statics));
 111        return ptr_to_statics;
 112}
 113
 114/* Always use in this order, get_S() must be called first */
 115#define RESULTBUF(name) &((S = get_S())->name##_resultbuf)
 116#define BUFFER(name)    (S->name##_buffer)
 117
 118/**********************************************************************/
 119/* For the various fget??ent_r funcs, return
 120 *
 121 *  0: success
 122 *  ENOENT: end-of-file encountered
 123 *  ERANGE: buflen too small
 124 *  other error values possible. See bb__pgsreader.
 125 *
 126 * Also, *result == resultbuf on success and NULL on failure.
 127 *
 128 * NOTE: glibc difference - For the ENOENT case, glibc also sets errno.
 129 *   We do not, as it really isn't an error if we reach the end-of-file.
 130 *   Doing so is analogous to having fgetc() set errno on EOF.
 131 */
 132/**********************************************************************/
 133
 134int fgetpwent_r(FILE *__restrict stream, struct passwd *__restrict resultbuf,
 135                                char *__restrict buffer, size_t buflen,
 136                                struct passwd **__restrict result)
 137{
 138        int rv;
 139
 140        *result = NULL;
 141
 142        rv = bb__pgsreader(bb__parsepwent, resultbuf, buffer, buflen, stream);
 143        if (!rv) {
 144                *result = resultbuf;
 145        }
 146
 147        return rv;
 148}
 149
 150int fgetgrent_r(FILE *__restrict stream, struct group *__restrict resultbuf,
 151                                char *__restrict buffer, size_t buflen,
 152                                struct group **__restrict result)
 153{
 154        int rv;
 155
 156        *result = NULL;
 157
 158        rv = bb__pgsreader(bb__parsegrent, resultbuf, buffer, buflen, stream);
 159        if (!rv) {
 160                *result = resultbuf;
 161        }
 162
 163        return rv;
 164}
 165
 166#if ENABLE_USE_BB_SHADOW
 167#ifdef UNUSED_FOR_NOW
 168int fgetspent_r(FILE *__restrict stream, struct spwd *__restrict resultbuf,
 169                                char *__restrict buffer, size_t buflen,
 170                                struct spwd **__restrict result)
 171{
 172        int rv;
 173
 174        *result = NULL;
 175
 176        rv = bb__pgsreader(bb__parsespent, resultbuf, buffer, buflen, stream);
 177        if (!rv) {
 178                *result = resultbuf;
 179        }
 180
 181        return rv;
 182}
 183#endif
 184#endif
 185
 186/**********************************************************************/
 187/* For the various fget??ent funcs, return NULL on failure and a
 188 * pointer to the appropriate struct (statically allocated) on success.
 189 * TODO: audit & stop using these in bbox, they pull in static buffers */
 190/**********************************************************************/
 191
 192#ifdef UNUSED_SINCE_WE_AVOID_STATIC_BUFS
 193struct passwd *fgetpwent(FILE *stream)
 194{
 195        struct statics *S;
 196        struct passwd *resultbuf = RESULTBUF(fgetpwent);
 197        char *buffer = BUFFER(fgetpwent);
 198        struct passwd *result;
 199
 200        fgetpwent_r(stream, resultbuf, buffer, sizeof(BUFFER(fgetpwent)), &result);
 201        return result;
 202}
 203
 204struct group *fgetgrent(FILE *stream)
 205{
 206        struct statics *S;
 207        struct group *resultbuf = RESULTBUF(fgetgrent);
 208        char *buffer = BUFFER(fgetgrent);
 209        struct group *result;
 210
 211        fgetgrent_r(stream, resultbuf, buffer, sizeof(BUFFER(fgetgrent)), &result);
 212        return result;
 213}
 214#endif
 215
 216#if ENABLE_USE_BB_SHADOW
 217#ifdef UNUSED_SINCE_WE_AVOID_STATIC_BUFS
 218struct spwd *fgetspent(FILE *stream)
 219{
 220        struct statics *S;
 221        struct spwd *resultbuf = RESULTBUF(fgetspent);
 222        char *buffer = BUFFER(fgetspent);
 223        struct spwd *result;
 224
 225        fgetspent_r(stream, resultbuf, buffer, sizeof(BUFFER(fgetspent)), &result);
 226        return result;
 227}
 228#endif
 229
 230#ifdef UNUSED_FOR_NOW
 231int sgetspent_r(const char *string, struct spwd *result_buf,
 232                                char *buffer, size_t buflen, struct spwd **result)
 233{
 234        int rv = ERANGE;
 235
 236        *result = NULL;
 237
 238        if (buflen < PWD_BUFFER_SIZE) {
 239 DO_ERANGE:
 240                errno = rv;
 241                goto DONE;
 242        }
 243
 244        if (string != buffer) {
 245                if (strlen(string) >= buflen) {
 246                        goto DO_ERANGE;
 247                }
 248                strcpy(buffer, string);
 249        }
 250
 251        rv = bb__parsespent(result_buf, buffer);
 252        if (!rv) {
 253                *result = result_buf;
 254        }
 255
 256 DONE:
 257        return rv;
 258}
 259#endif
 260#endif /* ENABLE_USE_BB_SHADOW */
 261
 262/**********************************************************************/
 263
 264#define GETXXKEY_R_FUNC         getpwnam_r
 265#define GETXXKEY_R_PARSER       bb__parsepwent
 266#define GETXXKEY_R_ENTTYPE      struct passwd
 267#define GETXXKEY_R_TEST(ENT)    (!strcmp((ENT)->pw_name, key))
 268#define GETXXKEY_R_KEYTYPE      const char *__restrict
 269#define GETXXKEY_R_PATHNAME     _PATH_PASSWD
 270#include "pwd_grp_internal.c"
 271
 272#define GETXXKEY_R_FUNC         getgrnam_r
 273#define GETXXKEY_R_PARSER       bb__parsegrent
 274#define GETXXKEY_R_ENTTYPE      struct group
 275#define GETXXKEY_R_TEST(ENT)    (!strcmp((ENT)->gr_name, key))
 276#define GETXXKEY_R_KEYTYPE      const char *__restrict
 277#define GETXXKEY_R_PATHNAME     _PATH_GROUP
 278#include "pwd_grp_internal.c"
 279
 280#if ENABLE_USE_BB_SHADOW
 281#define GETXXKEY_R_FUNC         getspnam_r
 282#define GETXXKEY_R_PARSER       bb__parsespent
 283#define GETXXKEY_R_ENTTYPE      struct spwd
 284#define GETXXKEY_R_TEST(ENT)    (!strcmp((ENT)->sp_namp, key))
 285#define GETXXKEY_R_KEYTYPE      const char *__restrict
 286#define GETXXKEY_R_PATHNAME     _PATH_SHADOW
 287#include "pwd_grp_internal.c"
 288#endif
 289
 290#define GETXXKEY_R_FUNC         getpwuid_r
 291#define GETXXKEY_R_PARSER       bb__parsepwent
 292#define GETXXKEY_R_ENTTYPE      struct passwd
 293#define GETXXKEY_R_TEST(ENT)    ((ENT)->pw_uid == key)
 294#define GETXXKEY_R_KEYTYPE      uid_t
 295#define GETXXKEY_R_PATHNAME     _PATH_PASSWD
 296#include "pwd_grp_internal.c"
 297
 298#define GETXXKEY_R_FUNC         getgrgid_r
 299#define GETXXKEY_R_PARSER       bb__parsegrent
 300#define GETXXKEY_R_ENTTYPE      struct group
 301#define GETXXKEY_R_TEST(ENT)    ((ENT)->gr_gid == key)
 302#define GETXXKEY_R_KEYTYPE      gid_t
 303#define GETXXKEY_R_PATHNAME     _PATH_GROUP
 304#include "pwd_grp_internal.c"
 305
 306/**********************************************************************/
 307/* TODO: audit & stop using these in bbox, they pull in static buffers */
 308
 309/* This one has many users */
 310struct passwd *getpwuid(uid_t uid)
 311{
 312        struct statics *S;
 313        struct passwd *resultbuf = RESULTBUF(getpwuid);
 314        char *buffer = BUFFER(getpwuid);
 315        struct passwd *result;
 316
 317        getpwuid_r(uid, resultbuf, buffer, sizeof(BUFFER(getpwuid)), &result);
 318        return result;
 319}
 320
 321/* This one has many users */
 322struct group *getgrgid(gid_t gid)
 323{
 324        struct statics *S;
 325        struct group *resultbuf = RESULTBUF(getgrgid);
 326        char *buffer = BUFFER(getgrgid);
 327        struct group *result;
 328
 329        getgrgid_r(gid, resultbuf, buffer, sizeof(BUFFER(getgrgid)), &result);
 330        return result;
 331}
 332
 333#if 0 //ENABLE_USE_BB_SHADOW
 334/* This function is non-standard and is currently not built.  It seems
 335 * to have been created as a reentrant version of the non-standard
 336 * functions getspuid.  Why getspuid was added, I do not know. */
 337int getspuid_r(uid_t uid, struct spwd *__restrict resultbuf,
 338                       char *__restrict buffer, size_t buflen,
 339                       struct spwd **__restrict result)
 340{
 341        int rv;
 342        struct passwd *pp;
 343        struct passwd password;
 344        char pwd_buff[PWD_BUFFER_SIZE];
 345
 346        *result = NULL;
 347        rv = getpwuid_r(uid, &password, pwd_buff, sizeof(pwd_buff), &pp);
 348        if (!rv) {
 349                rv = getspnam_r(password.pw_name, resultbuf, buffer, buflen, result);
 350        }
 351
 352        return rv;
 353}
 354
 355/* This function is non-standard and is currently not built.
 356 * Why it was added, I do not know. */
 357struct spwd *getspuid(uid_t uid)
 358{
 359        struct statics *S;
 360        struct spwd *resultbuf = RESULTBUF(getspuid);
 361        char *buffer = BUFFER(getspuid);
 362        struct spwd *result;
 363
 364        getspuid_r(uid, resultbuf, buffer, sizeof(BUFFER(getspuid)), &result);
 365        return result;
 366}
 367#endif
 368
 369/* This one has many users */
 370struct passwd *getpwnam(const char *name)
 371{
 372        struct statics *S;
 373        struct passwd *resultbuf = RESULTBUF(getpwnam);
 374        char *buffer = BUFFER(getpwnam);
 375        struct passwd *result;
 376
 377        getpwnam_r(name, resultbuf, buffer, sizeof(BUFFER(getpwnam)), &result);
 378        return result;
 379}
 380
 381/* This one has many users */
 382struct group *getgrnam(const char *name)
 383{
 384        struct statics *S;
 385        struct group *resultbuf = RESULTBUF(getgrnam);
 386        char *buffer = BUFFER(getgrnam);
 387        struct group *result;
 388
 389        getgrnam_r(name, resultbuf, buffer, sizeof(BUFFER(getgrnam)), &result);
 390        return result;
 391}
 392
 393#if 0 //ENABLE_USE_BB_SHADOW
 394struct spwd *getspnam(const char *name)
 395{
 396        struct statics *S;
 397        struct spwd *resultbuf = RESULTBUF(getspnam);
 398        char *buffer = BUFFER(getspnam);
 399        struct spwd *result;
 400
 401        getspnam_r(name, resultbuf, buffer, sizeof(BUFFER(getspnam)), &result);
 402        return result;
 403}
 404#endif
 405
 406/**********************************************************************/
 407
 408/* FIXME: we don't have such CONFIG_xx - ?! */
 409
 410#if defined CONFIG_USE_BB_THREADSAFE_SHADOW && defined PTHREAD_MUTEX_INITIALIZER
 411static pthread_mutex_t mylock = PTHREAD_MUTEX_INITIALIZER;
 412# define LOCK           pthread_mutex_lock(&mylock)
 413# define UNLOCK         pthread_mutex_unlock(&mylock);
 414#else
 415# define LOCK           ((void) 0)
 416# define UNLOCK         ((void) 0)
 417#endif
 418
 419static FILE *pwf /*= NULL*/;
 420void setpwent(void)
 421{
 422        LOCK;
 423        if (pwf) {
 424                rewind(pwf);
 425        }
 426        UNLOCK;
 427}
 428
 429void endpwent(void)
 430{
 431        LOCK;
 432        if (pwf) {
 433                fclose(pwf);
 434                pwf = NULL;
 435        }
 436        UNLOCK;
 437}
 438
 439
 440int getpwent_r(struct passwd *__restrict resultbuf,
 441                           char *__restrict buffer, size_t buflen,
 442                           struct passwd **__restrict result)
 443{
 444        int rv;
 445
 446        LOCK;
 447        *result = NULL;                         /* In case of error... */
 448
 449        if (!pwf) {
 450                pwf = fopen_for_read(_PATH_PASSWD);
 451                if (!pwf) {
 452                        rv = errno;
 453                        goto ERR;
 454                }
 455        }
 456
 457        rv = bb__pgsreader(bb__parsepwent, resultbuf, buffer, buflen, pwf);
 458        if (!rv) {
 459                *result = resultbuf;
 460        }
 461
 462 ERR:
 463        UNLOCK;
 464        return rv;
 465}
 466
 467static FILE *grf /*= NULL*/;
 468void setgrent(void)
 469{
 470        LOCK;
 471        if (grf) {
 472                rewind(grf);
 473        }
 474        UNLOCK;
 475}
 476
 477void endgrent(void)
 478{
 479        LOCK;
 480        if (grf) {
 481                fclose(grf);
 482                grf = NULL;
 483        }
 484        UNLOCK;
 485}
 486
 487int getgrent_r(struct group *__restrict resultbuf,
 488                           char *__restrict buffer, size_t buflen,
 489                           struct group **__restrict result)
 490{
 491        int rv;
 492
 493        LOCK;
 494        *result = NULL;                         /* In case of error... */
 495
 496        if (!grf) {
 497                grf = fopen_for_read(_PATH_GROUP);
 498                if (!grf) {
 499                        rv = errno;
 500                        goto ERR;
 501                }
 502        }
 503
 504        rv = bb__pgsreader(bb__parsegrent, resultbuf, buffer, buflen, grf);
 505        if (!rv) {
 506                *result = resultbuf;
 507        }
 508
 509 ERR:
 510        UNLOCK;
 511        return rv;
 512}
 513
 514#ifdef UNUSED_FOR_NOW
 515#if ENABLE_USE_BB_SHADOW
 516static FILE *spf /*= NULL*/;
 517void setspent(void)
 518{
 519        LOCK;
 520        if (spf) {
 521                rewind(spf);
 522        }
 523        UNLOCK;
 524}
 525
 526void endspent(void)
 527{
 528        LOCK;
 529        if (spf) {
 530                fclose(spf);
 531                spf = NULL;
 532        }
 533        UNLOCK;
 534}
 535
 536int getspent_r(struct spwd *resultbuf, char *buffer,
 537                           size_t buflen, struct spwd **result)
 538{
 539        int rv;
 540
 541        LOCK;
 542        *result = NULL;                         /* In case of error... */
 543
 544        if (!spf) {
 545                spf = fopen_for_read(_PATH_SHADOW);
 546                if (!spf) {
 547                        rv = errno;
 548                        goto ERR;
 549                }
 550        }
 551
 552        rv = bb__pgsreader(bb__parsespent, resultbuf, buffer, buflen, spf);
 553        if (!rv) {
 554                *result = resultbuf;
 555        }
 556
 557 ERR:
 558        UNLOCK;
 559        return rv;
 560}
 561#endif
 562#endif /* UNUSED_FOR_NOW */
 563
 564#ifdef UNUSED_SINCE_WE_AVOID_STATIC_BUFS
 565struct passwd *getpwent(void)
 566{
 567        static char line_buff[PWD_BUFFER_SIZE];
 568        static struct passwd pwd;
 569        struct passwd *result;
 570
 571        getpwent_r(&pwd, line_buff, sizeof(line_buff), &result);
 572        return result;
 573}
 574
 575struct group *getgrent(void)
 576{
 577        static char line_buff[GRP_BUFFER_SIZE];
 578        static struct group gr;
 579        struct group *result;
 580
 581        getgrent_r(&gr, line_buff, sizeof(line_buff), &result);
 582        return result;
 583}
 584
 585#if ENABLE_USE_BB_SHADOW
 586struct spwd *getspent(void)
 587{
 588        static char line_buff[PWD_BUFFER_SIZE];
 589        static struct spwd spwd;
 590        struct spwd *result;
 591
 592        getspent_r(&spwd, line_buff, sizeof(line_buff), &result);
 593        return result;
 594}
 595
 596struct spwd *sgetspent(const char *string)
 597{
 598        static char line_buff[PWD_BUFFER_SIZE];
 599        static struct spwd spwd;
 600        struct spwd *result;
 601
 602        sgetspent_r(string, &spwd, line_buff, sizeof(line_buff), &result);
 603        return result;
 604}
 605#endif
 606#endif /* UNUSED_SINCE_WE_AVOID_STATIC_BUFS */
 607
 608static gid_t *getgrouplist_internal(int *ngroups_ptr, const char *user, gid_t gid)
 609{
 610        FILE *grfile;
 611        gid_t *group_list;
 612        int ngroups;
 613        struct group group;
 614        char buff[PWD_BUFFER_SIZE];
 615
 616        /* We alloc space for 8 gids at a time. */
 617        group_list = xmalloc(8 * sizeof(group_list[0]));
 618        group_list[0] = gid;
 619        ngroups = 1;
 620
 621        grfile = fopen_for_read(_PATH_GROUP);
 622        if (grfile) {
 623                while (!bb__pgsreader(bb__parsegrent, &group, buff, sizeof(buff), grfile)) {
 624                        char **m;
 625                        assert(group.gr_mem); /* Must have at least a NULL terminator. */
 626                        if (group.gr_gid == gid)
 627                                continue;
 628                        for (m = group.gr_mem; *m; m++) {
 629                                if (strcmp(*m, user) != 0)
 630                                        continue;
 631                                group_list = xrealloc_vector(group_list, /*8=2^3:*/ 3, ngroups);
 632                                group_list[ngroups++] = group.gr_gid;
 633                                break;
 634                        }
 635                }
 636                fclose(grfile);
 637        }
 638        *ngroups_ptr = ngroups;
 639        return group_list;
 640}
 641
 642int initgroups(const char *user, gid_t gid)
 643{
 644        int ngroups;
 645        gid_t *group_list = getgrouplist_internal(&ngroups, user, gid);
 646
 647        ngroups = setgroups(ngroups, group_list);
 648        free(group_list);
 649        return ngroups;
 650}
 651
 652int getgrouplist(const char *user, gid_t gid, gid_t *groups, int *ngroups)
 653{
 654        int ngroups_old = *ngroups;
 655        gid_t *group_list = getgrouplist_internal(ngroups, user, gid);
 656
 657        if (*ngroups <= ngroups_old) {
 658                ngroups_old = *ngroups;
 659                memcpy(groups, group_list, ngroups_old * sizeof(groups[0]));
 660        } else {
 661                ngroups_old = -1;
 662        }
 663        free(group_list);
 664        return ngroups_old;
 665}
 666
 667#ifdef UNUSED_SINCE_WE_AVOID_STATIC_BUFS
 668int putpwent(const struct passwd *__restrict p, FILE *__restrict f)
 669{
 670        int rv = -1;
 671
 672#if 0
 673        /* glibc does this check */
 674        if (!p || !f) {
 675                errno = EINVAL;
 676                return rv;
 677        }
 678#endif
 679
 680        /* No extra thread locking is needed above what fprintf does. */
 681        if (fprintf(f, "%s:%s:%lu:%lu:%s:%s:%s\n",
 682                                p->pw_name, p->pw_passwd,
 683                                (unsigned long)(p->pw_uid),
 684                                (unsigned long)(p->pw_gid),
 685                                p->pw_gecos, p->pw_dir, p->pw_shell) >= 0
 686                ) {
 687                rv = 0;
 688        }
 689
 690        return rv;
 691}
 692
 693int putgrent(const struct group *__restrict p, FILE *__restrict f)
 694{
 695        int rv = -1;
 696
 697#if 0
 698        /* glibc does this check */
 699        if (!p || !f) {
 700                errno = EINVAL;
 701                return rv;
 702        }
 703#endif
 704
 705        if (fprintf(f, "%s:%s:%lu:",
 706                                p->gr_name, p->gr_passwd,
 707                                (unsigned long)(p->gr_gid)) >= 0
 708        ) {
 709                static const char format[] ALIGN1 = ",%s";
 710
 711                char **m;
 712                const char *fmt;
 713
 714                fmt = format + 1;
 715
 716                assert(p->gr_mem);
 717                m = p->gr_mem;
 718
 719                while (1) {
 720                        if (!*m) {
 721                                if (fputc('\n', f) >= 0) {
 722                                        rv = 0;
 723                                }
 724                                break;
 725                        }
 726                        if (fprintf(f, fmt, *m) < 0) {
 727                                break;
 728                        }
 729                        m++;
 730                        fmt = format;
 731                }
 732        }
 733
 734        return rv;
 735}
 736#endif
 737
 738#if ENABLE_USE_BB_SHADOW
 739#ifdef UNUSED_FOR_NOW
 740static const unsigned char put_sp_off[] ALIGN1 = {
 741        offsetof(struct spwd, sp_lstchg),       /* 2 - not a char ptr */
 742        offsetof(struct spwd, sp_min),          /* 3 - not a char ptr */
 743        offsetof(struct spwd, sp_max),          /* 4 - not a char ptr */
 744        offsetof(struct spwd, sp_warn),         /* 5 - not a char ptr */
 745        offsetof(struct spwd, sp_inact),        /* 6 - not a char ptr */
 746        offsetof(struct spwd, sp_expire)        /* 7 - not a char ptr */
 747};
 748
 749int putspent(const struct spwd *p, FILE *stream)
 750{
 751        const char *fmt;
 752        long x;
 753        int i;
 754        int rv = -1;
 755
 756        /* Unlike putpwent and putgrent, glibc does not check the args. */
 757        if (fprintf(stream, "%s:%s:", p->sp_namp,
 758                                (p->sp_pwdp ? p->sp_pwdp : "")) < 0
 759        ) {
 760                goto DO_UNLOCK;
 761        }
 762
 763        for (i = 0; i < sizeof(put_sp_off); i++) {
 764                fmt = "%ld:";
 765                x = *(long *)((char *)p + put_sp_off[i]);
 766                if (x == -1) {
 767                        fmt += 3;
 768                }
 769                if (fprintf(stream, fmt, x) < 0) {
 770                        goto DO_UNLOCK;
 771                }
 772        }
 773
 774        if ((p->sp_flag != ~0UL) && (fprintf(stream, "%lu", p->sp_flag) < 0)) {
 775                goto DO_UNLOCK;
 776        }
 777
 778        if (fputc('\n', stream) > 0) {
 779                rv = 0;
 780        }
 781
 782 DO_UNLOCK:
 783        return rv;
 784}
 785#endif
 786#endif /* USE_BB_SHADOW */
 787
 788/**********************************************************************/
 789/* Internal functions                                                 */
 790/**********************************************************************/
 791
 792static const unsigned char pw_off[] ALIGN1 = {
 793        offsetof(struct passwd, pw_name),       /* 0 */
 794        offsetof(struct passwd, pw_passwd),     /* 1 */
 795        offsetof(struct passwd, pw_uid),        /* 2 - not a char ptr */
 796        offsetof(struct passwd, pw_gid),        /* 3 - not a char ptr */
 797        offsetof(struct passwd, pw_gecos),      /* 4 */
 798        offsetof(struct passwd, pw_dir),        /* 5 */
 799        offsetof(struct passwd, pw_shell)       /* 6 */
 800};
 801
 802static int FAST_FUNC bb__parsepwent(void *data, char *line)
 803{
 804        char *endptr;
 805        char *p;
 806        int i;
 807
 808        i = 0;
 809        while (1) {
 810                p = (char *) data + pw_off[i];
 811
 812                if (i < 2 || i > 3) {
 813                        *((char **) p) = line;
 814                        if (i == 6) {
 815                                return 0;
 816                        }
 817                        /* NOTE: glibc difference - glibc allows omission of
 818                         * ':' seperators after the gid field if all remaining
 819                         * entries are empty.  We require all separators. */
 820                        line = strchr(line, ':');
 821                        if (!line) {
 822                                break;
 823                        }
 824                } else {
 825                        unsigned long t = strtoul(line, &endptr, 10);
 826                        /* Make sure we had at least one digit, and that the
 827                         * failing char is the next field seperator ':'.  See
 828                         * glibc difference note above. */
 829                        /* TODO: Also check for leading whitespace? */
 830                        if ((endptr == line) || (*endptr != ':')) {
 831                                break;
 832                        }
 833                        line = endptr;
 834                        if (i & 1) {            /* i == 3 -- gid */
 835                                *((gid_t *) p) = t;
 836                        } else {                        /* i == 2 -- uid */
 837                                *((uid_t *) p) = t;
 838                        }
 839                }
 840
 841                *line++ = '\0';
 842                i++;
 843        } /* while (1) */
 844
 845        return -1;
 846}
 847
 848/**********************************************************************/
 849
 850static const unsigned char gr_off[] ALIGN1 = {
 851        offsetof(struct group, gr_name),        /* 0 */
 852        offsetof(struct group, gr_passwd),      /* 1 */
 853        offsetof(struct group, gr_gid)          /* 2 - not a char ptr */
 854};
 855
 856static int FAST_FUNC bb__parsegrent(void *data, char *line)
 857{
 858        char *endptr;
 859        char *p;
 860        int i;
 861        char **members;
 862        char *end_of_buf;
 863
 864        end_of_buf = ((struct group *) data)->gr_name; /* Evil hack! */
 865        i = 0;
 866        while (1) {
 867                p = (char *) data + gr_off[i];
 868
 869                if (i < 2) {
 870                        *((char **) p) = line;
 871                        line = strchr(line, ':');
 872                        if (!line) {
 873                                break;
 874                        }
 875                        *line++ = '\0';
 876                        i++;
 877                } else {
 878                        *((gid_t *) p) = strtoul(line, &endptr, 10);
 879
 880                        /* NOTE: glibc difference - glibc allows omission of the
 881                         * trailing colon when there is no member list.  We treat
 882                         * this as an error. */
 883
 884                        /* Make sure we had at least one digit, and that the
 885                         * failing char is the next field seperator ':'.  See
 886                         * glibc difference note above. */
 887                        if ((endptr == line) || (*endptr != ':')) {
 888                                break;
 889                        }
 890
 891                        i = 1;                          /* Count terminating NULL ptr. */
 892                        p = endptr;
 893
 894                        if (p[1]) { /* We have a member list to process. */
 895                                /* Overwrite the last ':' with a ',' before counting.
 896                                 * This allows us to (1) test for initial ','
 897                                 * and (2) adds one ',' so that the number of commas
 898                                 * equals the member count. */
 899                                *p = ',';
 900                                do {
 901                                        /* NOTE: glibc difference - glibc allows and trims leading
 902                                         * (but not trailing) space.  We treat this as an error. */
 903                                        /* NOTE: glibc difference - glibc allows consecutive and
 904                                         * trailing commas, and ignores "empty string" users.  We
 905                                         * treat this as an error. */
 906                                        if (*p == ',') {
 907                                                ++i;
 908                                                *p = 0; /* nul-terminate each member string. */
 909                                                if (!*++p || (*p == ',') || isspace(*p)) {
 910                                                        goto ERR;
 911                                                }
 912                                        }
 913                                } while (*++p);
 914                        }
 915
 916                        /* Now align (p+1), rounding up. */
 917                        /* Assumes sizeof(char **) is a power of 2. */
 918                        members = (char **)( (((intptr_t) p) + sizeof(char **))
 919                                                                 & ~((intptr_t)(sizeof(char **) - 1)) );
 920
 921                        if (((char *)(members + i)) > end_of_buf) {     /* No space. */
 922                                break;
 923                        }
 924
 925                        ((struct group *) data)->gr_mem = members;
 926
 927                        if (--i) {
 928                                p = endptr;     /* Pointing to char prior to first member. */
 929                                while (1) {
 930                                        *members++ = ++p;
 931                                        if (!--i)
 932                                                break;
 933                                        while (*++p)
 934                                                continue;
 935                                }
 936                        }
 937                        *members = NULL;
 938
 939                        return 0;
 940                }
 941        } /* while (1) */
 942
 943 ERR:
 944        return -1;
 945}
 946
 947/**********************************************************************/
 948
 949#if ENABLE_USE_BB_SHADOW
 950static const unsigned char sp_off[] ALIGN1 = {
 951        offsetof(struct spwd, sp_namp),         /* 0: char* */
 952        offsetof(struct spwd, sp_pwdp),         /* 1: char* */
 953        offsetof(struct spwd, sp_lstchg),       /* 2: long */
 954        offsetof(struct spwd, sp_min),          /* 3: long */
 955        offsetof(struct spwd, sp_max),          /* 4: long */
 956        offsetof(struct spwd, sp_warn),         /* 5: long */
 957        offsetof(struct spwd, sp_inact),        /* 6: long */
 958        offsetof(struct spwd, sp_expire),       /* 7: long */
 959        offsetof(struct spwd, sp_flag)          /* 8: unsigned long */
 960};
 961
 962static int FAST_FUNC bb__parsespent(void *data, char *line)
 963{
 964        char *endptr;
 965        char *p;
 966        int i;
 967
 968        i = 0;
 969        while (1) {
 970                p = (char *) data + sp_off[i];
 971                if (i < 2) {
 972                        *((char **) p) = line;
 973                        line = strchr(line, ':');
 974                        if (!line) {
 975                                break; /* error */
 976                        }
 977                } else {
 978                        *((long *) p) = strtoul(line, &endptr, 10);
 979                        if (endptr == line) {
 980                                *((long *) p) = -1L;
 981                        }
 982                        line = endptr;
 983                        if (i == 8) {
 984                                if (*line != '\0') {
 985                                        break; /* error */
 986                                }
 987                                return 0; /* all ok */
 988                        }
 989                        if (*line != ':') {
 990                                break; /* error */
 991                        }
 992                }
 993                *line++ = '\0';
 994                i++;
 995        }
 996
 997        return EINVAL;
 998}
 999#endif
1000
1001/**********************************************************************/
1002
1003/* Reads until EOF, or until it finds a line which fits in the buffer
1004 * and for which the parser function succeeds.
1005 *
1006 * Returns 0 on success and ENOENT for end-of-file (glibc convention).
1007 */
1008static int bb__pgsreader(
1009                int FAST_FUNC (*parserfunc)(void *d, char *line),
1010                void *data,
1011                char *__restrict line_buff,
1012                size_t buflen,
1013                FILE *f)
1014{
1015        int skip;
1016        int rv = ERANGE;
1017
1018        if (buflen < PWD_BUFFER_SIZE) {
1019                errno = rv;
1020                return rv;
1021        }
1022
1023        skip = 0;
1024        while (1) {
1025                if (!fgets(line_buff, buflen, f)) {
1026                        if (feof(f)) {
1027                                rv = ENOENT;
1028                        }
1029                        break;
1030                }
1031
1032                {
1033                        int line_len = strlen(line_buff) - 1;
1034                        if (line_len >= 0 && line_buff[line_len] == '\n') {
1035                                line_buff[line_len] = '\0';
1036                        } else
1037                        if (line_len + 2 == buflen) {
1038                                /* A start (or continuation) of overlong line */
1039                                skip = 1;
1040                                continue;
1041                        } /* else: a last line in the file, and it has no '\n' */
1042                }
1043
1044                if (skip) {
1045                        /* This "line" is a remainder of overlong line, ignore */
1046                        skip = 0;
1047                        continue;
1048                }
1049
1050                /* NOTE: glibc difference - glibc strips leading whitespace from
1051                 * records.  We do not allow leading whitespace. */
1052
1053                /* Skip empty lines, comment lines, and lines with leading
1054                 * whitespace. */
1055                if (line_buff[0] != '\0' && line_buff[0] != '#' && !isspace(line_buff[0])) {
1056                        if (parserfunc == bb__parsegrent) {
1057                                /* Do evil group hack:
1058                                 * The group entry parsing function needs to know where
1059                                 * the end of the buffer is so that it can construct the
1060                                 * group member ptr table. */
1061                                ((struct group *) data)->gr_name = line_buff + buflen;
1062                        }
1063                        if (parserfunc(data, line_buff) == 0) {
1064                                rv = 0;
1065                                break;
1066                        }
1067                }
1068        } /* while (1) */
1069
1070        return rv;
1071}
1072