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