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