libbb.h: add check for bad off_t size detection
[oweals/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         }
445
446         rv = bb__pgsreader(bb__parsepwent, resultbuf, buffer, buflen, pwf);
447         if (!rv) {
448                 *result = resultbuf;
449         }
450
451  ERR:
452         UNLOCK;
453         return rv;
454 }
455
456 static FILE *grf /*= NULL*/;
457 void setgrent(void)
458 {
459         LOCK;
460         if (grf) {
461                 rewind(grf);
462         }
463         UNLOCK;
464 }
465
466 void endgrent(void)
467 {
468         LOCK;
469         if (grf) {
470                 fclose(grf);
471                 grf = NULL;
472         }
473         UNLOCK;
474 }
475
476 int getgrent_r(struct group *__restrict resultbuf,
477                            char *__restrict buffer, size_t buflen,
478                            struct group **__restrict result)
479 {
480         int rv;
481
482         LOCK;
483         *result = NULL;                         /* In case of error... */
484
485         if (!grf) {
486                 grf = fopen_for_read(_PATH_GROUP);
487                 if (!grf) {
488                         rv = errno;
489                         goto ERR;
490                 }
491         }
492
493         rv = bb__pgsreader(bb__parsegrent, resultbuf, buffer, buflen, grf);
494         if (!rv) {
495                 *result = resultbuf;
496         }
497
498  ERR:
499         UNLOCK;
500         return rv;
501 }
502
503 #ifdef UNUSED_FOR_NOW
504 #if ENABLE_USE_BB_SHADOW
505 static FILE *spf /*= NULL*/;
506 void setspent(void)
507 {
508         LOCK;
509         if (spf) {
510                 rewind(spf);
511         }
512         UNLOCK;
513 }
514
515 void endspent(void)
516 {
517         LOCK;
518         if (spf) {
519                 fclose(spf);
520                 spf = NULL;
521         }
522         UNLOCK;
523 }
524
525 int getspent_r(struct spwd *resultbuf, char *buffer,
526                            size_t buflen, struct spwd **result)
527 {
528         int rv;
529
530         LOCK;
531         *result = NULL;                         /* In case of error... */
532
533         if (!spf) {
534                 spf = fopen_for_read(_PATH_SHADOW);
535                 if (!spf) {
536                         rv = errno;
537                         goto ERR;
538                 }
539         }
540
541         rv = bb__pgsreader(bb__parsespent, resultbuf, buffer, buflen, spf);
542         if (!rv) {
543                 *result = resultbuf;
544         }
545
546  ERR:
547         UNLOCK;
548         return rv;
549 }
550 #endif
551 #endif /* UNUSED_FOR_NOW */
552
553 #ifdef UNUSED_SINCE_WE_AVOID_STATIC_BUFS
554 struct passwd *getpwent(void)
555 {
556         static char line_buff[PWD_BUFFER_SIZE];
557         static struct passwd pwd;
558         struct passwd *result;
559
560         getpwent_r(&pwd, line_buff, sizeof(line_buff), &result);
561         return result;
562 }
563
564 struct group *getgrent(void)
565 {
566         static char line_buff[GRP_BUFFER_SIZE];
567         static struct group gr;
568         struct group *result;
569
570         getgrent_r(&gr, line_buff, sizeof(line_buff), &result);
571         return result;
572 }
573
574 #if ENABLE_USE_BB_SHADOW
575 struct spwd *getspent(void)
576 {
577         static char line_buff[PWD_BUFFER_SIZE];
578         static struct spwd spwd;
579         struct spwd *result;
580
581         getspent_r(&spwd, line_buff, sizeof(line_buff), &result);
582         return result;
583 }
584
585 struct spwd *sgetspent(const char *string)
586 {
587         static char line_buff[PWD_BUFFER_SIZE];
588         static struct spwd spwd;
589         struct spwd *result;
590
591         sgetspent_r(string, &spwd, line_buff, sizeof(line_buff), &result);
592         return result;
593 }
594 #endif
595 #endif /* UNUSED_SINCE_WE_AVOID_STATIC_BUFS */
596
597 static gid_t *getgrouplist_internal(int *ngroups_ptr, const char *user, gid_t gid)
598 {
599         FILE *grfile;
600         gid_t *group_list;
601         int ngroups;
602         struct group group;
603         char buff[PWD_BUFFER_SIZE];
604
605         /* We alloc space for 8 gids at a time. */
606         group_list = xmalloc(8 * sizeof(group_list[0]));
607         group_list[0] = gid;
608         ngroups = 1;
609
610         grfile = fopen_for_read(_PATH_GROUP);
611         if (grfile) {
612                 while (!bb__pgsreader(bb__parsegrent, &group, buff, sizeof(buff), grfile)) {
613                         char **m;
614                         assert(group.gr_mem); /* Must have at least a NULL terminator. */
615                         if (group.gr_gid == gid)
616                                 continue;
617                         for (m = group.gr_mem; *m; m++) {
618                                 if (strcmp(*m, user) != 0)
619                                         continue;
620                                 group_list = xrealloc_vector(group_list, /*8=2^3:*/ 3, ngroups);
621                                 group_list[ngroups++] = group.gr_gid;
622                                 break;
623                         }
624                 }
625                 fclose(grfile);
626         }
627         *ngroups_ptr = ngroups;
628         return group_list;
629 }
630
631 int initgroups(const char *user, gid_t gid)
632 {
633         int ngroups;
634         gid_t *group_list = getgrouplist_internal(&ngroups, user, gid);
635
636         ngroups = setgroups(ngroups, group_list);
637         free(group_list);
638         return ngroups;
639 }
640
641 int getgrouplist(const char *user, gid_t gid, gid_t *groups, int *ngroups)
642 {
643         int ngroups_old = *ngroups;
644         gid_t *group_list = getgrouplist_internal(ngroups, user, gid);
645
646         if (*ngroups <= ngroups_old) {
647                 ngroups_old = *ngroups;
648                 memcpy(groups, group_list, ngroups_old * sizeof(groups[0]));
649         } else {
650                 ngroups_old = -1;
651         }
652         free(group_list);
653         return ngroups_old;
654 }
655
656 #ifdef UNUSED_SINCE_WE_AVOID_STATIC_BUFS
657 int putpwent(const struct passwd *__restrict p, FILE *__restrict f)
658 {
659         int rv = -1;
660
661 #if 0
662         /* glibc does this check */
663         if (!p || !f) {
664                 errno = EINVAL;
665                 return rv;
666         }
667 #endif
668
669         /* No extra thread locking is needed above what fprintf does. */
670         if (fprintf(f, "%s:%s:%lu:%lu:%s:%s:%s\n",
671                                 p->pw_name, p->pw_passwd,
672                                 (unsigned long)(p->pw_uid),
673                                 (unsigned long)(p->pw_gid),
674                                 p->pw_gecos, p->pw_dir, p->pw_shell) >= 0
675                 ) {
676                 rv = 0;
677         }
678
679         return rv;
680 }
681
682 int putgrent(const struct group *__restrict p, FILE *__restrict f)
683 {
684         int rv = -1;
685
686 #if 0
687         /* glibc does this check */
688         if (!p || !f) {
689                 errno = EINVAL;
690                 return rv;
691         }
692 #endif
693
694         if (fprintf(f, "%s:%s:%lu:",
695                                 p->gr_name, p->gr_passwd,
696                                 (unsigned long)(p->gr_gid)) >= 0
697         ) {
698                 static const char format[] ALIGN1 = ",%s";
699
700                 char **m;
701                 const char *fmt;
702
703                 fmt = format + 1;
704
705                 assert(p->gr_mem);
706                 m = p->gr_mem;
707
708                 while (1) {
709                         if (!*m) {
710                                 if (fputc('\n', f) >= 0) {
711                                         rv = 0;
712                                 }
713                                 break;
714                         }
715                         if (fprintf(f, fmt, *m) < 0) {
716                                 break;
717                         }
718                         m++;
719                         fmt = format;
720                 }
721         }
722
723         return rv;
724 }
725 #endif
726
727 #if ENABLE_USE_BB_SHADOW
728 #ifdef UNUSED_FOR_NOW
729 static const unsigned char put_sp_off[] ALIGN1 = {
730         offsetof(struct spwd, sp_lstchg),       /* 2 - not a char ptr */
731         offsetof(struct spwd, sp_min),          /* 3 - not a char ptr */
732         offsetof(struct spwd, sp_max),          /* 4 - not a char ptr */
733         offsetof(struct spwd, sp_warn),         /* 5 - not a char ptr */
734         offsetof(struct spwd, sp_inact),        /* 6 - not a char ptr */
735         offsetof(struct spwd, sp_expire)        /* 7 - not a char ptr */
736 };
737
738 int putspent(const struct spwd *p, FILE *stream)
739 {
740         const char *fmt;
741         long x;
742         int i;
743         int rv = -1;
744
745         /* Unlike putpwent and putgrent, glibc does not check the args. */
746         if (fprintf(stream, "%s:%s:", p->sp_namp,
747                                 (p->sp_pwdp ? p->sp_pwdp : "")) < 0
748         ) {
749                 goto DO_UNLOCK;
750         }
751
752         for (i = 0; i < sizeof(put_sp_off); i++) {
753                 fmt = "%ld:";
754                 x = *(long *)((char *)p + put_sp_off[i]);
755                 if (x == -1) {
756                         fmt += 3;
757                 }
758                 if (fprintf(stream, fmt, x) < 0) {
759                         goto DO_UNLOCK;
760                 }
761         }
762
763         if ((p->sp_flag != ~0UL) && (fprintf(stream, "%lu", p->sp_flag) < 0)) {
764                 goto DO_UNLOCK;
765         }
766
767         if (fputc('\n', stream) > 0) {
768                 rv = 0;
769         }
770
771  DO_UNLOCK:
772         return rv;
773 }
774 #endif
775 #endif /* USE_BB_SHADOW */
776
777 /**********************************************************************/
778 /* Internal functions                                                 */
779 /**********************************************************************/
780
781 static const unsigned char pw_off[] ALIGN1 = {
782         offsetof(struct passwd, pw_name),       /* 0 */
783         offsetof(struct passwd, pw_passwd),     /* 1 */
784         offsetof(struct passwd, pw_uid),        /* 2 - not a char ptr */
785         offsetof(struct passwd, pw_gid),        /* 3 - not a char ptr */
786         offsetof(struct passwd, pw_gecos),      /* 4 */
787         offsetof(struct passwd, pw_dir),        /* 5 */
788         offsetof(struct passwd, pw_shell)       /* 6 */
789 };
790
791 static int FAST_FUNC bb__parsepwent(void *data, char *line)
792 {
793         char *endptr;
794         char *p;
795         int i;
796
797         i = 0;
798         while (1) {
799                 p = (char *) data + pw_off[i];
800
801                 if (i < 2 || i > 3) {
802                         *((char **) p) = line;
803                         if (i == 6) {
804                                 return 0;
805                         }
806                         /* NOTE: glibc difference - glibc allows omission of
807                          * ':' seperators after the gid field if all remaining
808                          * entries are empty.  We require all separators. */
809                         line = strchr(line, ':');
810                         if (!line) {
811                                 break;
812                         }
813                 } else {
814                         unsigned long t = strtoul(line, &endptr, 10);
815                         /* Make sure we had at least one digit, and that the
816                          * failing char is the next field seperator ':'.  See
817                          * glibc difference note above. */
818                         /* TODO: Also check for leading whitespace? */
819                         if ((endptr == line) || (*endptr != ':')) {
820                                 break;
821                         }
822                         line = endptr;
823                         if (i & 1) {            /* i == 3 -- gid */
824                                 *((gid_t *) p) = t;
825                         } else {                        /* i == 2 -- uid */
826                                 *((uid_t *) p) = t;
827                         }
828                 }
829
830                 *line++ = '\0';
831                 i++;
832         } /* while (1) */
833
834         return -1;
835 }
836
837 /**********************************************************************/
838
839 static const unsigned char gr_off[] ALIGN1 = {
840         offsetof(struct group, gr_name),        /* 0 */
841         offsetof(struct group, gr_passwd),      /* 1 */
842         offsetof(struct group, gr_gid)          /* 2 - not a char ptr */
843 };
844
845 static int FAST_FUNC bb__parsegrent(void *data, char *line)
846 {
847         char *endptr;
848         char *p;
849         int i;
850         char **members;
851         char *end_of_buf;
852
853         end_of_buf = ((struct group *) data)->gr_name; /* Evil hack! */
854         i = 0;
855         while (1) {
856                 p = (char *) data + gr_off[i];
857
858                 if (i < 2) {
859                         *((char **) p) = line;
860                         line = strchr(line, ':');
861                         if (!line) {
862                                 break;
863                         }
864                         *line++ = '\0';
865                         i++;
866                 } else {
867                         *((gid_t *) p) = strtoul(line, &endptr, 10);
868
869                         /* NOTE: glibc difference - glibc allows omission of the
870                          * trailing colon when there is no member list.  We treat
871                          * this as an error. */
872
873                         /* Make sure we had at least one digit, and that the
874                          * failing char is the next field seperator ':'.  See
875                          * glibc difference note above. */
876                         if ((endptr == line) || (*endptr != ':')) {
877                                 break;
878                         }
879
880                         i = 1;                          /* Count terminating NULL ptr. */
881                         p = endptr;
882
883                         if (p[1]) { /* We have a member list to process. */
884                                 /* Overwrite the last ':' with a ',' before counting.
885                                  * This allows us to (1) test for initial ','
886                                  * and (2) adds one ',' so that the number of commas
887                                  * equals the member count. */
888                                 *p = ',';
889                                 do {
890                                         /* NOTE: glibc difference - glibc allows and trims leading
891                                          * (but not trailing) space.  We treat this as an error. */
892                                         /* NOTE: glibc difference - glibc allows consecutive and
893                                          * trailing commas, and ignores "empty string" users.  We
894                                          * treat this as an error. */
895                                         if (*p == ',') {
896                                                 ++i;
897                                                 *p = 0; /* nul-terminate each member string. */
898                                                 if (!*++p || (*p == ',') || isspace(*p)) {
899                                                         goto ERR;
900                                                 }
901                                         }
902                                 } while (*++p);
903                         }
904
905                         /* Now align (p+1), rounding up. */
906                         /* Assumes sizeof(char **) is a power of 2. */
907                         members = (char **)( (((intptr_t) p) + sizeof(char **))
908                                                                  & ~((intptr_t)(sizeof(char **) - 1)) );
909
910                         if (((char *)(members + i)) > end_of_buf) {     /* No space. */
911                                 break;
912                         }
913
914                         ((struct group *) data)->gr_mem = members;
915
916                         if (--i) {
917                                 p = endptr;     /* Pointing to char prior to first member. */
918                                 while (1) {
919                                         *members++ = ++p;
920                                         if (!--i)
921                                                 break;
922                                         while (*++p)
923                                                 continue;
924                                 }
925                         }
926                         *members = NULL;
927
928                         return 0;
929                 }
930         } /* while (1) */
931
932  ERR:
933         return -1;
934 }
935
936 /**********************************************************************/
937
938 #if ENABLE_USE_BB_SHADOW
939 static const unsigned char sp_off[] ALIGN1 = {
940         offsetof(struct spwd, sp_namp),         /* 0: char* */
941         offsetof(struct spwd, sp_pwdp),         /* 1: char* */
942         offsetof(struct spwd, sp_lstchg),       /* 2: long */
943         offsetof(struct spwd, sp_min),          /* 3: long */
944         offsetof(struct spwd, sp_max),          /* 4: long */
945         offsetof(struct spwd, sp_warn),         /* 5: long */
946         offsetof(struct spwd, sp_inact),        /* 6: long */
947         offsetof(struct spwd, sp_expire),       /* 7: long */
948         offsetof(struct spwd, sp_flag)          /* 8: unsigned long */
949 };
950
951 static int FAST_FUNC bb__parsespent(void *data, char *line)
952 {
953         char *endptr;
954         char *p;
955         int i;
956
957         i = 0;
958         while (1) {
959                 p = (char *) data + sp_off[i];
960                 if (i < 2) {
961                         *((char **) p) = line;
962                         line = strchr(line, ':');
963                         if (!line) {
964                                 break; /* error */
965                         }
966                 } else {
967                         *((long *) p) = strtoul(line, &endptr, 10);
968                         if (endptr == line) {
969                                 *((long *) p) = -1L;
970                         }
971                         line = endptr;
972                         if (i == 8) {
973                                 if (*line != '\0') {
974                                         break; /* error */
975                                 }
976                                 return 0; /* all ok */
977                         }
978                         if (*line != ':') {
979                                 break; /* error */
980                         }
981                 }
982                 *line++ = '\0';
983                 i++;
984         }
985
986         return EINVAL;
987 }
988 #endif
989
990 /**********************************************************************/
991
992 /* Reads until EOF, or until it finds a line which fits in the buffer
993  * and for which the parser function succeeds.
994  *
995  * Returns 0 on success and ENOENT for end-of-file (glibc convention).
996  */
997 static int bb__pgsreader(
998                 int FAST_FUNC (*parserfunc)(void *d, char *line),
999                 void *data,
1000                 char *__restrict line_buff,
1001                 size_t buflen,
1002                 FILE *f)
1003 {
1004         int skip;
1005         int rv = ERANGE;
1006
1007         if (buflen < PWD_BUFFER_SIZE) {
1008                 errno = rv;
1009                 return rv;
1010         }
1011
1012         skip = 0;
1013         while (1) {
1014                 if (!fgets(line_buff, buflen, f)) {
1015                         if (feof(f)) {
1016                                 rv = ENOENT;
1017                         }
1018                         break;
1019                 }
1020
1021                 {
1022                         int line_len = strlen(line_buff) - 1;
1023                         if (line_len >= 0 && line_buff[line_len] == '\n') {
1024                                 line_buff[line_len] = '\0';
1025                         } else
1026                         if (line_len + 2 == buflen) {
1027                                 /* A start (or continuation) of overlong line */
1028                                 skip = 1;
1029                                 continue;
1030                         } /* else: a last line in the file, and it has no '\n' */
1031                 }
1032
1033                 if (skip) {
1034                         /* This "line" is a remainder of overlong line, ignore */
1035                         skip = 0;
1036                         continue;
1037                 }
1038
1039                 /* NOTE: glibc difference - glibc strips leading whitespace from
1040                  * records.  We do not allow leading whitespace. */
1041
1042                 /* Skip empty lines, comment lines, and lines with leading
1043                  * whitespace. */
1044                 if (line_buff[0] != '\0' && line_buff[0] != '#' && !isspace(line_buff[0])) {
1045                         if (parserfunc == bb__parsegrent) {
1046                                 /* Do evil group hack:
1047                                  * The group entry parsing function needs to know where
1048                                  * the end of the buffer is so that it can construct the
1049                                  * group member ptr table. */
1050                                 ((struct group *) data)->gr_name = line_buff + buflen;
1051                         }
1052                         if (parserfunc(data, line_buff) == 0) {
1053                                 rv = 0;
1054                                 break;
1055                         }
1056                 }
1057         } /* while (1) */
1058
1059         return rv;
1060 }