aboutsummaryrefslogtreecommitdiff
path: root/libpwdgrp/pwd_grp.c
diff options
context:
space:
mode:
Diffstat (limited to 'libpwdgrp/pwd_grp.c')
-rw-r--r--libpwdgrp/pwd_grp.c1238
1 files changed, 358 insertions, 880 deletions
diff --git a/libpwdgrp/pwd_grp.c b/libpwdgrp/pwd_grp.c
index 2060d7811..ed8370124 100644
--- a/libpwdgrp/pwd_grp.c
+++ b/libpwdgrp/pwd_grp.c
@@ -1,598 +1,480 @@
1/* vi: set sw=4 ts=4: */ 1/* vi: set sw=4 ts=4: */
2/* Copyright (C) 2003 Manuel Novoa III 2/* Copyright (C) 2014 Tito Ragusa <farmatito@tiscali.it>
3 * 3 *
4 * Licensed under GPLv2 or later, see file LICENSE in this source tree. 4 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
5 */ 5 */
6 6/* This program is distributed in the hope that it will be useful,
7/* Nov 6, 2003 Initial version. 7 * but WITHOUT ANY WARRANTY!!
8 * 8 *
9 * NOTE: This implementation is quite strict about requiring all 9 * Rewrite of some parts. Main differences are:
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 * 10 *
14 * TODO: 11 * 1) the buffer for getpwuid, getgrgid, getpwnam, getgrnam is dynamically
15 * Move to dynamic allocation of (currently statically allocated) 12 * allocated and reused by later calls. if ERANGE error pops up it is
16 * buffers; especially for the group-related functions since 13 * reallocated to the size of the longest line found so far in the
17 * large group member lists will cause error returns. 14 * passwd/group files and reused for later calls.
15 * If ENABLE_FEATURE_CLEAN_UP is set the buffers are freed at program
16 * exit using the atexit function to make valgrind happy.
17 * 2) the passwd/group files:
18 * a) must contain the expected number of fields (as per count of field
19 * delimeters ":") or we will complain with a error message.
20 * b) leading or trailing whitespace in fields is allowed and handled.
21 * c) some fields are not allowed to be empty (e.g. username, uid/gid,
22 * homedir, shell) and in this case NULL is returned and errno is
23 * set to EINVAL. This behaviour could be easily changed by
24 * modifying PW_DEF, GR_DEF, SP_DEF strings (uppercase
25 * makes a field mandatory).
26 * d) the string representing uid/gid must be convertible by strtoXX
27 * functions or NULL is returned and errno is set to EINVAL.
28 * e) leading or trailing whitespaces in member names and empty members
29 * are allowed and handled.
30 * 3) the internal function for getgrouplist uses a dynamically allocated
31 * buffer and retries with a bigger one in case it is too small;
32 * 4) the _r functions use the user supplied buffers that are never reallocated
33 * but use mostly the same common code as the other functions.
34 * 5) at the moment only the functions really used by busybox code are
35 * implemented, if you need a particular missing function it should be
36 * easy to write it by using the internal common code.
18 */ 37 */
19 38
20#include "libbb.h" 39#include "libbb.h"
21#include <assert.h>
22
23/**********************************************************************/
24/* Sizes for statically allocated buffers. */
25 40
26#define PWD_BUFFER_SIZE 256 41/* S = string not empty, s = string maybe empty, */
27#define GRP_BUFFER_SIZE 256 42/* I = uid,gid, l = long maybe empty, m = members,*/
28 43/* r = reserved */
29/**********************************************************************/ 44#define PW_DEF "SsIIsSS"
30/* Prototypes for internal functions. */ 45#define GR_DEF "SsIm"
46#define SP_DEF "Ssllllllr"
47
48static const uint8_t pw_off[] ALIGN1 = {
49 offsetof(struct passwd, pw_name), /* 0 S */
50 offsetof(struct passwd, pw_passwd), /* 1 s */
51 offsetof(struct passwd, pw_uid), /* 2 I */
52 offsetof(struct passwd, pw_gid), /* 3 I */
53 offsetof(struct passwd, pw_gecos), /* 4 s */
54 offsetof(struct passwd, pw_dir), /* 5 S */
55 offsetof(struct passwd, pw_shell) /* 6 S */
56};
57static const uint8_t gr_off[] ALIGN1 = {
58 offsetof(struct group, gr_name), /* 0 S */
59 offsetof(struct group, gr_passwd), /* 1 s */
60 offsetof(struct group, gr_gid), /* 2 I */
61 offsetof(struct group, gr_mem) /* 3 m (char **) */
62};
63#if ENABLE_USE_BB_SHADOW
64static const uint8_t sp_off[] ALIGN1 = {
65 offsetof(struct spwd, sp_namp), /* 0 S Login name */
66 offsetof(struct spwd, sp_pwdp), /* 1 s Encrypted password */
67 offsetof(struct spwd, sp_lstchg), /* 2 l */
68 offsetof(struct spwd, sp_min), /* 3 l */
69 offsetof(struct spwd, sp_max), /* 4 l */
70 offsetof(struct spwd, sp_warn), /* 5 l */
71 offsetof(struct spwd, sp_inact), /* 6 l */
72 offsetof(struct spwd, sp_expire), /* 7 l */
73 offsetof(struct spwd, sp_flag) /* 8 r Reserved */
74};
75#endif
31 76
32static int bb__pgsreader( 77struct const_passdb {
33 int FAST_FUNC (*parserfunc)(void *d, char *line), 78 const char *filename;
34 void *data, 79 const uint8_t *off;
35 char *__restrict line_buff, 80 const char def[10];
36 size_t buflen, 81 uint8_t numfields;
37 FILE *f); 82 uint8_t size_of;
83};
84struct passdb {
85 const char *filename;
86 const uint8_t *off;
87 const char def[10];
88 uint8_t numfields;
89 uint8_t size_of;
90 FILE *fp;
91 void *malloced;
92};
38 93
39static int FAST_FUNC bb__parsepwent(void *pw, char *line); 94static const struct const_passdb const_pw_db = { _PATH_PASSWD, pw_off, PW_DEF, sizeof(PW_DEF)-1, sizeof(struct passwd) };
40static int FAST_FUNC bb__parsegrent(void *gr, char *line); 95static const struct const_passdb const_gr_db = { _PATH_GROUP , gr_off, GR_DEF, sizeof(GR_DEF)-1, sizeof(struct group) };
41#if ENABLE_USE_BB_SHADOW 96#if ENABLE_USE_BB_SHADOW
42static int FAST_FUNC bb__parsespent(void *sp, char *line); 97static const struct const_passdb const_sp_db = { _PATH_SHADOW, sp_off, SP_DEF, sizeof(SP_DEF)-1, sizeof(struct spwd) };
43#endif 98#endif
44 99
45/**********************************************************************/
46/* We avoid having big global data. */ 100/* We avoid having big global data. */
47
48struct statics { 101struct statics {
49 /* Smaller things first */
50 /* It's ok to use one buffer for getpwuid and getpwnam. Manpage says: 102 /* 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 103 * "The return value may point to a static area, and may be overwritten
52 * by subsequent calls to getpwent(), getpwnam(), or getpwuid()." 104 * by subsequent calls to getpwent(), getpwnam(), or getpwuid()."
53 */ 105 */
54 struct passwd getpw_resultbuf; 106 struct passdb db[2 + ENABLE_USE_BB_SHADOW];
55 struct group getgr_resultbuf; 107 char *tokenize_end;
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}; 108};
69 109
70static struct statics *ptr_to_statics; 110static struct statics *ptr_to_statics;
111#define S (*ptr_to_statics)
112#define has_S (ptr_to_statics)
71 113
72static struct statics *get_S(void) 114static struct statics *get_S(void)
73{ 115{
74 if (!ptr_to_statics) 116 if (!ptr_to_statics) {
75 ptr_to_statics = xzalloc(sizeof(*ptr_to_statics)); 117 ptr_to_statics = xzalloc(sizeof(S));
118 memcpy(&S.db[0], &const_pw_db, sizeof(const_pw_db));
119 memcpy(&S.db[1], &const_gr_db, sizeof(const_gr_db));
120#if ENABLE_USE_BB_SHADOW
121 memcpy(&S.db[2], &const_sp_db, sizeof(const_sp_db));
122#endif
123 }
76 return ptr_to_statics; 124 return ptr_to_statics;
77} 125}
78 126
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/**********************************************************************/ 127/**********************************************************************/
84/* For the various fget??ent_r funcs, return 128/* Internal functions */
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/**********************************************************************/ 129/**********************************************************************/
98 130
99int fgetpwent_r(FILE *__restrict stream, struct passwd *__restrict resultbuf, 131/* Divide the passwd/group/shadow record in fields
100 char *__restrict buffer, size_t buflen, 132 * by substituting the given delimeter
101 struct passwd **__restrict result) 133 * e.g. ':' or ',' with '\0'.
134 * Returns the number of fields found.
135 * Strips leading and trailing whitespace in fields.
136 */
137static int tokenize(char *buffer, int ch)
102{ 138{
103 int rv; 139 char *p = buffer;
140 char *s = p;
141 int num_fields = 0;
104 142
105 *result = NULL; 143 for (;;) {
106 144 if (isblank(*s)) {
107 rv = bb__pgsreader(bb__parsepwent, resultbuf, buffer, buflen, stream); 145 overlapping_strcpy(s, skip_whitespace(s));
108 if (!rv) { 146 }
109 *result = resultbuf; 147 if (*p == ch || *p == '\0') {
148 char *end = p;
149 while (p != s && isblank(p[-1]))
150 p--;
151 if (p != end)
152 overlapping_strcpy(p, end);
153 num_fields++;
154 if (*end == '\0') {
155 S.tokenize_end = p + 1;
156 return num_fields;
157 }
158 *p = '\0';
159 s = p + 1;
160 }
161 p++;
110 } 162 }
111
112 return rv;
113} 163}
114 164
115int fgetgrent_r(FILE *__restrict stream, struct group *__restrict resultbuf, 165/* Returns !NULL on success and matching line broken up in fields by '\0' in buf.
116 char *__restrict buffer, size_t buflen, 166 * We require the expected number of fields to be found.
117 struct group **__restrict result) 167 */
118{ 168static char *parse_common(FILE *fp, const char *filename,
119 int rv; 169 int n_fields,
170 const char *key, int field_pos)
171{
172 int count = 0;
173 char *buf;
174
175 while ((buf = xmalloc_fgetline(fp)) != NULL) {
176 count++;
177 /* Skip empty lines, comment lines */
178 if (buf[0] == '\0' || buf[0] == '#')
179 goto free_and_next;
180 if (tokenize(buf, ':') != n_fields) {
181 /* number of fields is wrong */
182 bb_error_msg("bad record at %s:%u", filename, count);
183 goto free_and_next;
184 }
120 185
121 *result = NULL; 186/* Ugly hack: group db requires aqdditional buffer space
187 * for members[] array. If there is only one group, we need space
188 * for 3 pointers: alignment padding, group name, NULL.
189 * +1 for every additional group.
190 */
191 if (n_fields == sizeof(GR_DEF)-1) { /* if we read group file */
192 int resize = 3;
193 char *p = buf;
194 while (*p)
195 if (*p++ == ',')
196 resize++;
197 resize *= sizeof(char**);
198 resize += S.tokenize_end - buf;
199 buf = xrealloc(buf, resize);
200 }
122 201
123 rv = bb__pgsreader(bb__parsegrent, resultbuf, buffer, buflen, stream); 202 if (!key) {
124 if (!rv) { 203 /* no key specified: sequential read, return a record */
125 *result = resultbuf; 204 break;
205 }
206 if (strcmp(key, nth_string(buf, field_pos)) == 0) {
207 /* record found */
208 break;
209 }
210 free_and_next:
211 free(buf);
126 } 212 }
127 213
128 return rv; 214 return buf;
129} 215}
130 216
131#if ENABLE_USE_BB_SHADOW 217static char *parse_file(const char *filename,
132#ifdef UNUSED_FOR_NOW 218 int n_fields,
133int fgetspent_r(FILE *__restrict stream, struct spwd *__restrict resultbuf, 219 const char *key, int field_pos)
134 char *__restrict buffer, size_t buflen,
135 struct spwd **__restrict result)
136{ 220{
137 int rv; 221 char *buf = NULL;
222 FILE *fp = fopen_for_read(filename);
138 223
139 *result = NULL; 224 if (fp) {
140 225 buf = parse_common(fp, filename, n_fields, key, field_pos);
141 rv = bb__pgsreader(bb__parsespent, resultbuf, buffer, buflen, stream); 226 fclose(fp);
142 if (!rv) {
143 *result = resultbuf;
144 } 227 }
145 228 return buf;
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
158struct 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} 229}
168 230
169struct group *fgetgrent(FILE *stream) 231/* Convert passwd/group/shadow file record in buffer to a struct */
232static void *convert_to_struct(const char *def, const unsigned char *off,
233 char *buffer, void *result)
170{ 234{
171 struct statics *S; 235 for (;;) {
172 struct group *resultbuf = RESULTBUF(getgr); 236 void *member = (char*)result + (*off++);
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 237
238 if ((*def | 0x20) == 's') { /* s or S */
239 *(char **)member = (char*)buffer;
240 if (!buffer[0] && (*def == 'S')) {
241 errno = EINVAL;
242 }
243 }
244 if (*def == 'I') {
245 *(int *)member = bb_strtou(buffer, NULL, 10);
246 }
181#if ENABLE_USE_BB_SHADOW 247#if ENABLE_USE_BB_SHADOW
182#ifdef UNUSED_SINCE_WE_AVOID_STATIC_BUFS 248 if (*def == 'l') {
183struct spwd *fgetspent(FILE *stream) 249 long n = -1;
184{ 250 if (buffer[0])
185 struct statics *S; 251 n = bb_strtol(buffer, NULL, 10);
186 struct spwd *resultbuf = RESULTBUF(getsp); 252 *(long *)member = n;
187 char *buffer = BUFFER(getsp); 253 }
188 struct spwd *result;
189
190 fgetspent_r(stream, resultbuf, buffer, sizeof(BUFFER(getsp)), &result);
191 return result;
192}
193#endif 254#endif
194 255 if (*def == 'm') {
195#ifdef UNUSED_FOR_NOW 256 char **members;
196int sgetspent_r(const char *string, struct spwd *result_buf, 257 int i = tokenize(buffer, ',');
197 char *buffer, size_t buflen, struct spwd **result) 258
198{ 259 /* Store members[] after buffer's end.
199 int rv = ERANGE; 260 * This is safe ONLY because there is a hack
200 261 * in parse_common() which allocates additional space
201 *result = NULL; 262 * at the end of malloced buffer!
202 263 */
203 if (buflen < PWD_BUFFER_SIZE) { 264 members = (char **)
204 DO_ERANGE: 265 ( ((intptr_t)S.tokenize_end + sizeof(char**))
205 errno = rv; 266 & -(intptr_t)sizeof(char**)
206 goto DONE; 267 );
207 } 268
208 269 ((struct group *)result)->gr_mem = members;
209 if (string != buffer) { 270 while (--i >= 0) {
210 if (strlen(string) >= buflen) { 271 *members++ = buffer;
211 goto DO_ERANGE; 272 buffer += strlen(buffer) + 1;
273 }
274 *members = NULL;
212 } 275 }
213 strcpy(buffer, string); 276 /* def "r" does nothing */
214 }
215 277
216 rv = bb__parsespent(result_buf, buffer); 278 def++;
217 if (!rv) { 279 if (*def == '\0')
218 *result = result_buf; 280 break;
281 buffer += strlen(buffer) + 1;
219 } 282 }
220 283
221 DONE: 284 if (errno)
222 return rv; 285 result = NULL;
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 */
275struct 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; 286 return result;
284} 287}
285 288
286/* This one has many users */ 289/****** getXXnam/id_r */
287struct 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 290
294 getgrgid_r(gid, resultbuf, buffer, sizeof(BUFFER(getgr)), &result); 291static int getXXnam_r(const char *name, uintptr_t db_and_field_pos, char *buffer, size_t buflen,
295 return result; 292 void *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. */
302int getspuid_r(uid_t uid, struct spwd *__restrict resultbuf,
303 char *__restrict buffer, size_t buflen,
304 struct spwd **__restrict result)
305{ 293{
306 int rv; 294 void *struct_buf = *(void**)result;
307 struct passwd *pp; 295 char *buf;
308 struct passwd password; 296 struct passdb *db;
309 char pwd_buff[PWD_BUFFER_SIZE]; 297 get_S();
298 db = &S.db[db_and_field_pos >> 2];
310 299
311 *result = NULL; 300 *(void**)result = NULL;
312 rv = getpwuid_r(uid, &password, pwd_buff, sizeof(pwd_buff), &pp); 301 buf = parse_file(db->filename, db->numfields, name, db_and_field_pos & 3);
313 if (!rv) { 302 if (buf) {
314 rv = getspnam_r(password.pw_name, resultbuf, buffer, buflen, result); 303 size_t size = S.tokenize_end - buf;
304 if (size > buflen) {
305 errno = ERANGE;
306 } else {
307 memcpy(buffer, buf, size);
308 *(void**)result = convert_to_struct(db->def, db->off, buffer, struct_buf);
309 }
310 free(buf);
315 } 311 }
316 312 /* "The reentrant functions return zero on success.
317 return rv; 313 * In case of error, an error number is returned."
318} 314 * NB: not finding the record is also a "success" here:
319 315 */
320/* This function is non-standard and is currently not built. 316 return errno;
321 * Why it was added, I do not know. */
322struct 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 */
335struct 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} 317}
345 318
346/* This one has many users */ 319int getpwnam_r(const char *name, struct passwd *struct_buf, char *buffer, size_t buflen,
347struct group *getgrnam(const char *name) 320 struct passwd **result)
348{ 321{
349 struct statics *S; 322 /* Why the "store buffer address in result" trick?
350 struct group *resultbuf = RESULTBUF(getgr); 323 * This way, getXXnam_r has the same ABI signature as getpwnam_r,
351 char *buffer = BUFFER(getgr); 324 * hopefully compiler can optimize tall call better in this case.
352 struct group *result; 325 */
353 326 *result = struct_buf;
354 getgrnam_r(name, resultbuf, buffer, sizeof(BUFFER(getgr)), &result); 327 return getXXnam_r(name, (0 << 2) + 0, buffer, buflen, result);
355 return result;
356} 328}
357 329#if ENABLE_USE_BB_SHADOW
358#if 0 //ENABLE_USE_BB_SHADOW 330int getspnam_r(const char *name, struct spwd *struct_buf, char *buffer, size_t buflen,
359struct spwd *getspnam(const char *name) 331 struct spwd **result)
360{ 332{
361 struct statics *S; 333 *result = struct_buf;
362 struct spwd *resultbuf = RESULTBUF(getsp); 334 return getXXnam_r(name, (2 << 2) + 0, buffer, buflen, result);
363 char *buffer = BUFFER(getsp);
364 struct spwd *result;
365
366 getspnam_r(name, resultbuf, buffer, sizeof(BUFFER(getsp)), &result);
367 return result;
368} 335}
369#endif 336#endif
370 337
371/**********************************************************************/ 338/****** getXXent_r */
372
373/* FIXME: we don't have such CONFIG_xx - ?! */
374
375#if defined CONFIG_USE_BB_THREADSAFE_SHADOW && defined PTHREAD_MUTEX_INITIALIZER
376static 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
384static FILE *pwf /*= NULL*/;
385void setpwent(void)
386{
387 LOCK;
388 if (pwf) {
389 rewind(pwf);
390 }
391 UNLOCK;
392}
393
394void endpwent(void)
395{
396 LOCK;
397 if (pwf) {
398 fclose(pwf);
399 pwf = NULL;
400 }
401 UNLOCK;
402}
403
404 339
405int getpwent_r(struct passwd *__restrict resultbuf, 340static int getXXent_r(void *struct_buf, char *buffer, size_t buflen,
406 char *__restrict buffer, size_t buflen, 341 void *result,
407 struct passwd **__restrict result) 342 unsigned db_idx)
408{ 343{
409 int rv; 344 char *buf;
345 struct passdb *db;
346 get_S();
347 db = &S.db[db_idx];
410 348
411 LOCK; 349 *(void**)result = NULL;
412 *result = NULL; /* In case of error... */
413 350
414 if (!pwf) { 351 if (!db->fp) {
415 pwf = fopen_for_read(_PATH_PASSWD); 352 db->fp = fopen_for_read(db->filename);
416 if (!pwf) { 353 if (!db->fp) {
417 rv = errno; 354 return errno;
418 goto ERR;
419 } 355 }
420 close_on_exec_on(fileno(pwf)); 356 close_on_exec_on(fileno(db->fp));
421 } 357 }
422 358
423 rv = bb__pgsreader(bb__parsepwent, resultbuf, buffer, buflen, pwf); 359 buf = parse_common(db->fp, db->filename, db->numfields, /*no search key:*/ NULL, 0);
424 if (!rv) { 360 if (buf) {
425 *result = resultbuf; 361 size_t size = S.tokenize_end - buf;
362 if (size > buflen) {
363 errno = ERANGE;
364 } else {
365 memcpy(buffer, buf, size);
366 *(void**)result = convert_to_struct(db->def, db->off, buffer, struct_buf);
367 }
368 free(buf);
426 } 369 }
427 370 /* "The reentrant functions return zero on success.
428 ERR: 371 * In case of error, an error number is returned."
429 UNLOCK; 372 * NB: not finding the record is also a "success" here:
430 return rv; 373 */
374 return errno;
431} 375}
432 376
433static FILE *grf /*= NULL*/; 377int getpwent_r(struct passwd *struct_buf, char *buffer, size_t buflen, struct passwd **result)
434void setgrent(void)
435{ 378{
436 LOCK; 379 return getXXent_r(struct_buf, buffer, buflen, result, 0);
437 if (grf) {
438 rewind(grf);
439 }
440 UNLOCK;
441} 380}
442 381
443void endgrent(void) 382/****** getXXnam/id */
444{
445 LOCK;
446 if (grf) {
447 fclose(grf);
448 grf = NULL;
449 }
450 UNLOCK;
451}
452 383
453int getgrent_r(struct group *__restrict resultbuf, 384static void *getXXnam(const char *name, unsigned db_and_field_pos)
454 char *__restrict buffer, size_t buflen,
455 struct group **__restrict result)
456{ 385{
457 int rv; 386 char *buf;
387 void *result;
388 struct passdb *db;
389 get_S();
390 db = &S.db[db_and_field_pos >> 2];
458 391
459 LOCK; 392 result = NULL;
460 *result = NULL; /* In case of error... */
461 393
462 if (!grf) { 394 if (!db->fp) {
463 grf = fopen_for_read(_PATH_GROUP); 395 db->fp = fopen_for_read(db->filename);
464 if (!grf) { 396 if (!db->fp) {
465 rv = errno; 397 return NULL;
466 goto ERR;
467 } 398 }
468 close_on_exec_on(fileno(grf)); 399 close_on_exec_on(fileno(db->fp));
469 } 400 }
470 401
471 rv = bb__pgsreader(bb__parsegrent, resultbuf, buffer, buflen, grf); 402 free(db->malloced);
472 if (!rv) { 403 db->malloced = NULL;
473 *result = resultbuf; 404 buf = parse_common(db->fp, db->filename, db->numfields, name, db_and_field_pos & 3);
405 if (buf) {
406 db->malloced = xzalloc(db->size_of);
407 result = convert_to_struct(db->def, db->off, buf, db->malloced);
474 } 408 }
475 409 return result;
476 ERR:
477 UNLOCK;
478 return rv;
479} 410}
480 411
481#ifdef UNUSED_FOR_NOW 412struct passwd *getpwnam(const char *name)
482#if ENABLE_USE_BB_SHADOW
483static FILE *spf /*= NULL*/;
484void setspent(void)
485{ 413{
486 LOCK; 414 return getXXnam(name, (0 << 2) + 0);
487 if (spf) {
488 rewind(spf);
489 }
490 UNLOCK;
491} 415}
492 416struct group *getgrnam(const char *name)
493void endspent(void)
494{ 417{
495 LOCK; 418 return getXXnam(name, (1 << 2) + 0);
496 if (spf) {
497 fclose(spf);
498 spf = NULL;
499 }
500 UNLOCK;
501} 419}
502 420struct passwd *getpwuid(uid_t id)
503int getspent_r(struct spwd *resultbuf, char *buffer,
504 size_t buflen, struct spwd **result)
505{ 421{
506 int rv; 422 return getXXnam(utoa(id), (0 << 2) + 2);
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} 423}
529#endif 424struct group *getgrgid(gid_t id)
530#endif /* UNUSED_FOR_NOW */
531
532#ifdef UNUSED_SINCE_WE_AVOID_STATIC_BUFS
533struct passwd *getpwent(void)
534{ 425{
535 static char line_buff[PWD_BUFFER_SIZE]; 426 return getXXnam(utoa(id), (1 << 2) + 2);
536 static struct passwd pwd;
537 struct passwd *result;
538
539 getpwent_r(&pwd, line_buff, sizeof(line_buff), &result);
540 return result;
541} 427}
542 428
543struct group *getgrent(void) 429/****** end/setXXend */
544{
545 static char line_buff[GRP_BUFFER_SIZE];
546 static struct group gr;
547 struct group *result;
548 430
549 getgrent_r(&gr, line_buff, sizeof(line_buff), &result); 431void endpwent(void)
550 return result; 432{
433 if (has_S && S.db[0].fp) {
434 fclose(S.db[0].fp);
435 S.db[0].fp = NULL;
436 }
551} 437}
552 438void setpwent(void)
553#if ENABLE_USE_BB_SHADOW
554struct spwd *getspent(void)
555{ 439{
556 static char line_buff[PWD_BUFFER_SIZE]; 440 if (has_S && S.db[0].fp) {
557 static struct spwd spwd; 441 rewind(S.db[0].fp);
558 struct spwd *result; 442 }
559
560 getspent_r(&spwd, line_buff, sizeof(line_buff), &result);
561 return result;
562} 443}
563 444void endgrent(void)
564struct spwd *sgetspent(const char *string)
565{ 445{
566 static char line_buff[PWD_BUFFER_SIZE]; 446 if (has_S && S.db[1].fp) {
567 static struct spwd spwd; 447 fclose(S.db[1].fp);
568 struct spwd *result; 448 S.db[1].fp = NULL;
569 449 }
570 sgetspent_r(string, &spwd, line_buff, sizeof(line_buff), &result);
571 return result;
572} 450}
573#endif
574#endif /* UNUSED_SINCE_WE_AVOID_STATIC_BUFS */
575 451
576static gid_t *getgrouplist_internal(int *ngroups_ptr, const char *user, gid_t gid) 452/****** initgroups and getgrouplist */
453
454static gid_t* FAST_FUNC getgrouplist_internal(int *ngroups_ptr,
455 const char *user, gid_t gid)
577{ 456{
578 FILE *grfile; 457 FILE *fp;
579 gid_t *group_list; 458 gid_t *group_list;
580 int ngroups; 459 int ngroups;
581 struct group group; 460
582 char buff[PWD_BUFFER_SIZE]; 461 get_S();
583 462
584 /* We alloc space for 8 gids at a time. */ 463 /* We alloc space for 8 gids at a time. */
585 group_list = xmalloc(8 * sizeof(group_list[0])); 464 group_list = xmalloc(8 * sizeof(group_list[0]));
586 group_list[0] = gid; 465 group_list[0] = gid;
587 ngroups = 1; 466 ngroups = 1;
588 467
589 grfile = fopen_for_read(_PATH_GROUP); 468 fp = fopen_for_read(_PATH_GROUP);
590 if (grfile) { 469 if (fp) {
591 while (!bb__pgsreader(bb__parsegrent, &group, buff, sizeof(buff), grfile)) { 470 char *buf;
471 while ((buf = parse_common(fp, _PATH_GROUP, sizeof(GR_DEF)-1, NULL, 0)) != NULL) {
592 char **m; 472 char **m;
593 assert(group.gr_mem); /* Must have at least a NULL terminator. */ 473 struct group group;
474 if (!convert_to_struct(GR_DEF, gr_off, buf, &group))
475 goto next;
594 if (group.gr_gid == gid) 476 if (group.gr_gid == gid)
595 continue; 477 goto next;
596 for (m = group.gr_mem; *m; m++) { 478 for (m = group.gr_mem; *m; m++) {
597 if (strcmp(*m, user) != 0) 479 if (strcmp(*m, user) != 0)
598 continue; 480 continue;
@@ -600,8 +482,10 @@ static gid_t *getgrouplist_internal(int *ngroups_ptr, const char *user, gid_t gi
600 group_list[ngroups++] = group.gr_gid; 482 group_list[ngroups++] = group.gr_gid;
601 break; 483 break;
602 } 484 }
485 next:
486 free(buf);
603 } 487 }
604 fclose(grfile); 488 fclose(fp);
605 } 489 }
606 *ngroups_ptr = ngroups; 490 *ngroups_ptr = ngroups;
607 return group_list; 491 return group_list;
@@ -631,409 +515,3 @@ int getgrouplist(const char *user, gid_t gid, gid_t *groups, int *ngroups)
631 free(group_list); 515 free(group_list);
632 return ngroups_old; 516 return ngroups_old;
633} 517}
634
635#ifdef UNUSED_SINCE_WE_AVOID_STATIC_BUFS
636int 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
661int 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
708static 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
717int 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
760static 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
770static 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
818static 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
824static 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
918static 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
930static 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 */
976static 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}