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