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