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