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