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