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