X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=libpwdgrp%2Fpwd_grp.c;h=c9bbc8bdadce5ce754407d0b3e4128f40ec62fde;hb=31c765081dc41f158786545fbea9294be4685bd2;hp=faf53d7e1cf96e27e449d27cfa92e70d6fb313c5;hpb=c1ef7bdd8d002ae0889efcf883d0e1b7faa938d4;p=oweals%2Fbusybox.git diff --git a/libpwdgrp/pwd_grp.c b/libpwdgrp/pwd_grp.c index faf53d7e1..c9bbc8bda 100644 --- a/libpwdgrp/pwd_grp.c +++ b/libpwdgrp/pwd_grp.c @@ -1,1101 +1,560 @@ -/* Copyright (C) 2003 Manuel Novoa III +/* vi: set sw=4 ts=4: */ +/* Copyright (C) 2014 Tito Ragusa * - * Licensed under GPL v2, or later. See file LICENSE in this tarball. + * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ - -/* Nov 6, 2003 Initial version. - * - * NOTE: This implementation is quite strict about requiring all - * field seperators. It also does not allow leading whitespace - * except when processing the numeric fields. glibc is more - * lenient. See the various glibc difference comments below. +/* This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY!! * - * TODO: - * Move to dynamic allocation of (currently staticly allocated) - * buffers; especially for the group-related functions since - * large group member lists will cause error returns. + * Rewrite of some parts. Main differences are: * + * 1) the buffer for getpwuid, getgrgid, getpwnam, getgrnam is dynamically + * allocated. + * If ENABLE_FEATURE_CLEAN_UP is set the buffers are freed at program + * exit using the atexit function to make valgrind happy. + * 2) the passwd/group files: + * a) must contain the expected number of fields (as per count of field + * delimiters ":") or we will complain with a error message. + * b) leading and trailing whitespace in fields is stripped. + * c) some fields are not allowed to be empty (e.g. username, uid/gid), + * and in this case NULL is returned and errno is set to EINVAL. + * This behaviour could be easily changed by modifying PW_DEF, GR_DEF, + * SP_DEF strings (uppercase makes a field mandatory). + * d) the string representing uid/gid must be convertible by strtoXX + * functions, or errno is set to EINVAL. + * e) leading and trailing whitespace in group member names is stripped. + * 3) the internal function for getgrouplist uses dynamically allocated buffer. + * 4) at the moment only the functions really used by busybox code are + * implemented, if you need a particular missing function it should be + * easy to write it by using the internal common code. */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "busybox.h" -#include "pwd_.h" -#include "grp_.h" -#include "shadow_.h" - -#ifndef _PATH_SHADOW -#define _PATH_SHADOW "/etc/shadow" -#endif -#ifndef _PATH_PASSWD -#define _PATH_PASSWD "/etc/passwd" -#endif -#ifndef _PATH_GROUP -#define _PATH_GROUP "/etc/group" -#endif - -/**********************************************************************/ -/* Sizes for staticly allocated buffers. */ - -/* If you change these values, also change _SC_GETPW_R_SIZE_MAX and - * _SC_GETGR_R_SIZE_MAX in libc/unistd/sysconf.c to match */ -#define PWD_BUFFER_SIZE 256 -#define GRP_BUFFER_SIZE 256 - -/**********************************************************************/ -/* Prototypes for internal functions. */ - -extern int __parsepwent(void *pw, char *line); -extern int __parsegrent(void *gr, char *line); -extern int __parsespent(void *sp, char *line); - -extern int __pgsreader(int (*__parserfunc)(void *d, char *line), void *data, - char *__restrict line_buff, size_t buflen, FILE *f); +#include "libbb.h" -/**********************************************************************/ -/* For the various fget??ent_r funcs, return - * - * 0: success - * ENOENT: end-of-file encountered - * ERANGE: buflen too small - * other error values possible. See __pgsreader. - * - * Also, *result == resultbuf on success and NULL on failure. - * - * NOTE: glibc difference - For the ENOENT case, glibc also sets errno. - * We do not, as it really isn't an error if we reach the end-of-file. - * Doing so is analogous to having fgetc() set errno on EOF. +struct const_passdb { + const char *filename; + char def[7 + 2*ENABLE_USE_BB_SHADOW]; + uint8_t off[7 + 2*ENABLE_USE_BB_SHADOW]; + uint8_t numfields; + uint8_t size_of; +}; +struct passdb { + const char *filename; + char def[7 + 2*ENABLE_USE_BB_SHADOW]; + uint8_t off[7 + 2*ENABLE_USE_BB_SHADOW]; + uint8_t numfields; + uint8_t size_of; + FILE *fp; + char *malloced; +}; +/* Note: for shadow db, def[] will not contain terminating NUL, + * but convert_to_struct() logic detects def[] end by "less than SP?", + * not by "is it NUL?" condition; and off[0] happens to be zero + * for every db anyway, so there _is_ in fact a terminating NUL there. */ -/**********************************************************************/ - -#ifdef L_fgetpwent_r - -int fgetpwent_r(FILE *__restrict stream, struct passwd *__restrict resultbuf, - char *__restrict buffer, size_t buflen, - struct passwd **__restrict result) -{ - int rv; - - *result = NULL; - - if (!(rv = __pgsreader(__parsepwent, resultbuf, buffer, buflen, stream))) { - *result = resultbuf; - } - - return rv; -} +/* S = string not empty, s = string maybe empty, + * I = uid,gid, l = long maybe empty, m = members, + * r = reserved + */ +#define PW_DEF "SsIIsss" +#define GR_DEF "SsIm" +#define SP_DEF "Ssllllllr" + +static const struct const_passdb const_pw_db = { + _PATH_PASSWD, PW_DEF, + { + offsetof(struct passwd, pw_name), /* 0 S */ + offsetof(struct passwd, pw_passwd), /* 1 s */ + offsetof(struct passwd, pw_uid), /* 2 I */ + offsetof(struct passwd, pw_gid), /* 3 I */ + offsetof(struct passwd, pw_gecos), /* 4 s */ + offsetof(struct passwd, pw_dir), /* 5 s */ + offsetof(struct passwd, pw_shell) /* 6 s */ + }, + sizeof(PW_DEF)-1, sizeof(struct passwd) +}; +static const struct const_passdb const_gr_db = { + _PATH_GROUP, GR_DEF, + { + offsetof(struct group, gr_name), /* 0 S */ + offsetof(struct group, gr_passwd), /* 1 s */ + offsetof(struct group, gr_gid), /* 2 I */ + offsetof(struct group, gr_mem) /* 3 m (char **) */ + }, + sizeof(GR_DEF)-1, sizeof(struct group) +}; +#if ENABLE_USE_BB_SHADOW +static const struct const_passdb const_sp_db = { + _PATH_SHADOW, SP_DEF, + { + offsetof(struct spwd, sp_namp), /* 0 S Login name */ + offsetof(struct spwd, sp_pwdp), /* 1 s Encrypted password */ + offsetof(struct spwd, sp_lstchg), /* 2 l */ + offsetof(struct spwd, sp_min), /* 3 l */ + offsetof(struct spwd, sp_max), /* 4 l */ + offsetof(struct spwd, sp_warn), /* 5 l */ + offsetof(struct spwd, sp_inact), /* 6 l */ + offsetof(struct spwd, sp_expire), /* 7 l */ + offsetof(struct spwd, sp_flag) /* 8 r Reserved */ + }, + sizeof(SP_DEF)-1, sizeof(struct spwd) +}; #endif -/**********************************************************************/ -#ifdef L_fgetgrent_r - -int fgetgrent_r(FILE *__restrict stream, struct group *__restrict resultbuf, - char *__restrict buffer, size_t buflen, - struct group **__restrict result) -{ - int rv; - *result = NULL; - - if (!(rv = __pgsreader(__parsegrent, resultbuf, buffer, buflen, stream))) { - *result = resultbuf; - } - - return rv; -} +/* We avoid having big global data. */ +struct statics { + /* We use same buffer (db[0].malloced) for getpwuid and getpwnam. + * Manpage says: + * "The return value may point to a static area, and may be overwritten + * by subsequent calls to getpwent(), getpwnam(), or getpwuid()." + */ + struct passdb db[2 + ENABLE_USE_BB_SHADOW]; + char *tokenize_end; + unsigned string_size; +}; -#endif -/**********************************************************************/ -#ifdef L_fgetspent_r +static struct statics *ptr_to_statics; +#define S (*ptr_to_statics) +#define has_S (ptr_to_statics) -int fgetspent_r(FILE *__restrict stream, struct spwd *__restrict resultbuf, - char *__restrict buffer, size_t buflen, - struct spwd **__restrict result) +#if ENABLE_FEATURE_CLEAN_UP +static void free_static(void) { - int rv; - - *result = NULL; - - if (!(rv = __pgsreader(__parsespent, resultbuf, buffer, buflen, stream))) { - *result = resultbuf; - } - - return rv; + free(S.db[0].malloced); + free(S.db[1].malloced); +# if ENABLE_USE_BB_SHADOW + free(S.db[2].malloced); +# endif + free(ptr_to_statics); } - #endif -/**********************************************************************/ -/* For the various fget??ent funcs, return NULL on failure and a - * pointer to the appropriate struct (staticly allocated) on success. - */ -/**********************************************************************/ -#ifdef L_fgetpwent -struct passwd *fgetpwent(FILE *stream) +static struct statics *get_S(void) { - static char buffer[PWD_BUFFER_SIZE]; - static struct passwd resultbuf; - struct passwd *result; - - fgetpwent_r(stream, &resultbuf, buffer, sizeof(buffer), &result); - return result; -} - + if (!ptr_to_statics) { + ptr_to_statics = xzalloc(sizeof(S)); + memcpy(&S.db[0], &const_pw_db, sizeof(const_pw_db)); + memcpy(&S.db[1], &const_gr_db, sizeof(const_gr_db)); +#if ENABLE_USE_BB_SHADOW + memcpy(&S.db[2], &const_sp_db, sizeof(const_sp_db)); #endif -/**********************************************************************/ -#ifdef L_fgetgrent - -struct group *fgetgrent(FILE *stream) -{ - static char buffer[GRP_BUFFER_SIZE]; - static struct group resultbuf; - struct group *result; - - fgetgrent_r(stream, &resultbuf, buffer, sizeof(buffer), &result); - return result; -} - +#if ENABLE_FEATURE_CLEAN_UP + atexit(free_static); #endif -/**********************************************************************/ -#ifdef L_fgetspent - -extern int fgetspent_r(FILE *__restrict stream, struct spwd *__restrict resultbuf, - char *__restrict buffer, size_t buflen, - struct spwd **__restrict result); -struct spwd *fgetspent(FILE *stream) -{ - static char buffer[PWD_BUFFER_SIZE]; - static struct spwd resultbuf; - struct spwd *result; - - fgetspent_r(stream, &resultbuf, buffer, sizeof(buffer), &result); - return result; + } + return ptr_to_statics; } -#endif -/**********************************************************************/ -#ifdef L_sgetspent_r +/* Internal functions */ -int sgetspent_r(const char *string, struct spwd *result_buf, - char *buffer, size_t buflen, struct spwd **result) +/* Divide the passwd/group/shadow record in fields + * by substituting the given delimiter + * e.g. ':' or ',' with '\0'. + * Returns the number of fields found. + * Strips leading and trailing whitespace in fields. + */ +static int tokenize(char *buffer, int ch) { - int rv = ERANGE; - - *result = NULL; - - if (buflen < PWD_BUFFER_SIZE) { - DO_ERANGE: - errno=rv; - goto DONE; - } + char *p = buffer; + char *s = p; + int num_fields = 0; - if (string != buffer) { - if (strlen(string) >= buflen) { - goto DO_ERANGE; + for (;;) { + if (isblank(*s)) { + overlapping_strcpy(s, skip_whitespace(s)); } - strcpy(buffer, string); - } - - if (!(rv = __parsespent(result_buf, buffer))) { - *result = result_buf; + if (*p == ch || *p == '\0') { + char *end = p; + while (p != s && isblank(p[-1])) + p--; + if (p != end) + overlapping_strcpy(p, end); + num_fields++; + if (*end == '\0') { + S.tokenize_end = p + 1; + return num_fields; + } + *p = '\0'; + s = p + 1; + } + p++; } - - DONE: - return rv; -} - -#endif -/**********************************************************************/ - -#ifdef GETXXKEY_R_FUNC -#error GETXXKEY_R_FUNC is already defined! -#endif - -#ifdef L_getpwnam_r -#define GETXXKEY_R_FUNC getpwnam_r -#define GETXXKEY_R_PARSER __parsepwent -#define GETXXKEY_R_ENTTYPE struct passwd -#define GETXXKEY_R_TEST(ENT) (!strcmp((ENT)->pw_name, key)) -#define DO_GETXXKEY_R_KEYTYPE const char *__restrict -#define DO_GETXXKEY_R_PATHNAME _PATH_PASSWD -#include "pwd_grp_internal.c" -#endif - -#ifdef L_getgrnam_r -#define GETXXKEY_R_FUNC getgrnam_r -#define GETXXKEY_R_PARSER __parsegrent -#define GETXXKEY_R_ENTTYPE struct group -#define GETXXKEY_R_TEST(ENT) (!strcmp((ENT)->gr_name, key)) -#define DO_GETXXKEY_R_KEYTYPE const char *__restrict -#define DO_GETXXKEY_R_PATHNAME _PATH_GROUP -#include "pwd_grp_internal.c" -#endif - -#ifdef L_getspnam_r -#define GETXXKEY_R_FUNC getspnam_r -#define GETXXKEY_R_PARSER __parsespent -#define GETXXKEY_R_ENTTYPE struct spwd -#define GETXXKEY_R_TEST(ENT) (!strcmp((ENT)->sp_namp, key)) -#define DO_GETXXKEY_R_KEYTYPE const char *__restrict -#define DO_GETXXKEY_R_PATHNAME _PATH_SHADOW -#include "pwd_grp_internal.c" -#endif - -#ifdef L_getpwuid_r -#define GETXXKEY_R_FUNC getpwuid_r -#define GETXXKEY_R_PARSER __parsepwent -#define GETXXKEY_R_ENTTYPE struct passwd -#define GETXXKEY_R_TEST(ENT) ((ENT)->pw_uid == key) -#define DO_GETXXKEY_R_KEYTYPE uid_t -#define DO_GETXXKEY_R_PATHNAME _PATH_PASSWD -#include "pwd_grp_internal.c" -#endif - -#ifdef L_getgrgid_r -#define GETXXKEY_R_FUNC getgrgid_r -#define GETXXKEY_R_PARSER __parsegrent -#define GETXXKEY_R_ENTTYPE struct group -#define GETXXKEY_R_TEST(ENT) ((ENT)->gr_gid == key) -#define DO_GETXXKEY_R_KEYTYPE gid_t -#define DO_GETXXKEY_R_PATHNAME _PATH_GROUP -#include "pwd_grp_internal.c" -#endif - -/**********************************************************************/ -#ifdef L_getpwuid - -struct passwd *getpwuid(uid_t uid) -{ - static char buffer[PWD_BUFFER_SIZE]; - static struct passwd resultbuf; - struct passwd *result; - - getpwuid_r(uid, &resultbuf, buffer, sizeof(buffer), &result); - return result; } -#endif -/**********************************************************************/ -#ifdef L_getgrgid - -struct group *getgrgid(gid_t gid) -{ - static char buffer[GRP_BUFFER_SIZE]; - static struct group resultbuf; - struct group *result; - - getgrgid_r(gid, &resultbuf, buffer, sizeof(buffer), &result); - return result; -} - -#endif -/**********************************************************************/ -#ifdef L_getspuid_r - -/* This function is non-standard and is currently not built. It seems - * to have been created as a reentrant version of the non-standard - * functions getspuid. Why getspuid was added, I do not know. */ +/* Returns !NULL on success and matching line broken up in fields by '\0' in buf. + * We require the expected number of fields to be found. + */ +static char *parse_common(FILE *fp, struct passdb *db, + const char *key, int field_pos) +{ + char *buf; + + while ((buf = xmalloc_fgetline(fp)) != NULL) { + /* Skip empty lines, comment lines */ + if (buf[0] == '\0' || buf[0] == '#') + goto free_and_next; + if (tokenize(buf, ':') != db->numfields) { + /* number of fields is wrong */ + bb_error_msg("%s: bad record", db->filename); + goto free_and_next; + } -int getspuid_r(uid_t uid, struct spwd *__restrict resultbuf, - char *__restrict buffer, size_t buflen, - struct spwd **__restrict result) -{ - int rv; - struct passwd *pp; - struct passwd password; - char pwd_buff[PWD_BUFFER_SIZE]; + if (field_pos == -1) { + /* no key specified: sequential read, return a record */ + break; + } + if (strcmp(key, nth_string(buf, field_pos)) == 0) { + /* record found */ + break; + } + free_and_next: + free(buf); + } - *result = NULL; - if (!(rv = getpwuid_r(uid, &password, pwd_buff, sizeof(pwd_buff), &pp))) { - rv = getspnam_r(password.pw_name, resultbuf, buffer, buflen, result); + S.string_size = S.tokenize_end - buf; +/* + * Ugly hack: group db requires additional buffer space + * for members[] array. If there is only one group, we need space + * for 3 pointers: alignment padding, group name, NULL. + * +1 for every additional group. + */ + if (buf && db->numfields == sizeof(GR_DEF)-1) { /* if we read group file... */ + int cnt = 3; + char *p = buf; + while (p < S.tokenize_end) + if (*p++ == ',') + cnt++; + S.string_size += cnt * sizeof(char*); +//bb_error_msg("+%d words = %u key:%s buf:'%s'", cnt, S.string_size, key, buf); + buf = xrealloc(buf, S.string_size); } - return rv; + return buf; } -#endif -/**********************************************************************/ -#ifdef L_getspuid - -/* This function is non-standard and is currently not built. - * Why it was added, I do not know. */ - -struct spwd *getspuid(uid_t uid) +static char *parse_file(struct passdb *db, + const char *key, int field_pos) { - static char buffer[PWD_BUFFER_SIZE]; - static struct spwd resultbuf; - struct spwd *result; + char *buf = NULL; + FILE *fp = fopen_for_read(db->filename); - getspuid_r(uid, &resultbuf, buffer, sizeof(buffer), &result); - return result; + if (fp) { + buf = parse_common(fp, db, key, field_pos); + fclose(fp); + } + return buf; } -#endif -/**********************************************************************/ -#ifdef L_getpwnam - -struct passwd *getpwnam(const char *name) +/* Convert passwd/group/shadow file record in buffer to a struct */ +static void *convert_to_struct(struct passdb *db, + char *buffer, void *result) { - static char buffer[PWD_BUFFER_SIZE]; - static struct passwd resultbuf; - struct passwd *result; - - getpwnam_r(name, &resultbuf, buffer, sizeof(buffer), &result); - return result; -} + const char *def = db->def; + const uint8_t *off = db->off; -#endif -/**********************************************************************/ -#ifdef L_getgrnam + /* For consistency, zero out all fields */ + memset(result, 0, db->size_of); -struct group *getgrnam(const char *name) -{ - static char buffer[GRP_BUFFER_SIZE]; - static struct group resultbuf; - struct group *result; - - getgrnam_r(name, &resultbuf, buffer, sizeof(buffer), &result); - return result; -} + for (;;) { + void *member = (char*)result + (*off++); + if ((*def | 0x20) == 's') { /* s or S */ + *(char **)member = (char*)buffer; + if (!buffer[0] && (*def == 'S')) { + errno = EINVAL; + } + } + if (*def == 'I') { + *(int *)member = bb_strtou(buffer, NULL, 10); + } +#if ENABLE_USE_BB_SHADOW + if (*def == 'l') { + long n = -1; + if (buffer[0]) + n = bb_strtol(buffer, NULL, 10); + *(long *)member = n; + } #endif -/**********************************************************************/ -#ifdef L_getspnam + if (*def == 'm') { + char **members; + int i = tokenize(buffer, ','); + + /* Store members[] after buffer's end. + * This is safe ONLY because there is a hack + * in parse_common() which allocates additional space + * at the end of malloced buffer! + */ + members = (char **) + ( ((intptr_t)S.tokenize_end + sizeof(members[0])) + & -(intptr_t)sizeof(members[0]) + ); + ((struct group *)result)->gr_mem = members; + while (--i >= 0) { + if (buffer[0]) { + *members++ = buffer; + // bb_error_msg("member[]='%s'", buffer); + } + buffer += strlen(buffer) + 1; + } + *members = NULL; + } + /* def "r" does nothing */ -struct spwd *getspnam(const char *name) -{ - static char buffer[PWD_BUFFER_SIZE]; - static struct spwd resultbuf; - struct spwd *result; + def++; + if ((unsigned char)*def <= (unsigned char)' ') + break; + buffer += strlen(buffer) + 1; + } - getspnam_r(name, &resultbuf, buffer, sizeof(buffer), &result); + if (errno) + result = NULL; return result; } -#endif -/**********************************************************************/ -#ifdef L_getpw - -int getpw(uid_t uid, char *buf) +static int massage_data_for_r_func(struct passdb *db, + char *buffer, size_t buflen, + void **result, + char *buf) { - struct passwd resultbuf; - struct passwd *result; - char buffer[PWD_BUFFER_SIZE]; - - if (!buf) { - errno=EINVAL; - } else if (!getpwuid_r(uid, &resultbuf, buffer, sizeof(buffer), &result)) { - if (sprintf(buf, "%s:%s:%lu:%lu:%s:%s:%s\n", - resultbuf.pw_name, resultbuf.pw_passwd, - (unsigned long)(resultbuf.pw_uid), - (unsigned long)(resultbuf.pw_gid), - resultbuf.pw_gecos, resultbuf.pw_dir, - resultbuf.pw_shell) >= 0 - ) { - return 0; + void *result_buf = *result; + *result = NULL; + if (buf) { + if (S.string_size > buflen) { + errno = ERANGE; + } else { + memcpy(buffer, buf, S.string_size); + *result = convert_to_struct(db, buffer, result_buf); } + free(buf); } - - return -1; + /* "The reentrant functions return zero on success. + * In case of error, an error number is returned." + * NB: not finding the record is also a "success" here: + */ + return errno; } -#endif -/**********************************************************************/ - -#if defined(L_getpwent_r) || defined(L_getgrent_r) || defined(L_getspent_r) -#if defined CONFIG_USE_BB_THREADSAFE_SHADOW && defined PTHREAD_MUTEX_INITIALIZER -static pthread_mutex_t mylock = PTHREAD_MUTEX_INITIALIZER; -# define LOCK pthread_mutex_lock(&mylock) -# define UNLOCK pthread_mutex_unlock(&mylock); -#else -# define LOCK ((void) 0) -# define UNLOCK ((void) 0) -#endif -#endif - -#ifdef L_getpwent_r -static FILE *pwf /*= NULL*/; -void setpwent(void) +static void* massage_data_for_non_r_func(struct passdb *db, char *buf) { - LOCK; - if (pwf) { - rewind(pwf); - } - UNLOCK; -} + if (!buf) + return NULL; -void endpwent(void) -{ - LOCK; - if (pwf) { - fclose(pwf); - pwf = NULL; - } - UNLOCK; + free(db->malloced); + /* We enlarge buf and move string data up, freeing space + * for struct passwd/group/spwd at the beginning. This way, + * entire result of getXXnam is in a single malloced block. + * This enables easy creation of xmalloc_getpwnam() API. + */ + db->malloced = buf = xrealloc(buf, db->size_of + S.string_size); + memmove(buf + db->size_of, buf, S.string_size); + return convert_to_struct(db, buf + db->size_of, buf); } +/****** getXXnam/id_r */ -int getpwent_r(struct passwd *__restrict resultbuf, - char *__restrict buffer, size_t buflen, - struct passwd **__restrict result) +static int FAST_FUNC getXXnam_r(const char *name, uintptr_t db_and_field_pos, + char *buffer, size_t buflen, + void *result) { - int rv; - - LOCK; - *result = NULL; /* In case of error... */ - - if (!pwf) { - if (!(pwf = fopen(_PATH_PASSWD, "r"))) { - rv = errno; - goto ERR; - } - } + char *buf; + struct passdb *db = &get_S()->db[db_and_field_pos >> 2]; - if (!(rv = __pgsreader(__parsepwent, resultbuf, - buffer, buflen, pwf))) { - *result = resultbuf; - } + buf = parse_file(db, name, 0 /*db_and_field_pos & 3*/); + /* "db_and_field_pos & 3" is commented out since so far we don't implement + * getXXXid_r() functions which would use that to pass 2 here */ - ERR: - UNLOCK; - return rv; + return massage_data_for_r_func(db, buffer, buflen, result, buf); } -#endif -/**********************************************************************/ -#ifdef L_getgrent_r - -static FILE *grf /*= NULL*/; -void setgrent(void) +int FAST_FUNC getpwnam_r(const char *name, struct passwd *struct_buf, + char *buffer, size_t buflen, + struct passwd **result) { - LOCK; - if (grf) { - rewind(grf); - } - UNLOCK; + /* Why the "store buffer address in result" trick? + * This way, getXXnam_r has the same ABI signature as getpwnam_r, + * hopefully compiler can optimize tail call better in this case. + */ + *result = struct_buf; + return getXXnam_r(name, (0 << 2) + 0, buffer, buflen, result); } - -void endgrent(void) +#if ENABLE_USE_BB_SHADOW +int FAST_FUNC getspnam_r(const char *name, struct spwd *struct_buf, char *buffer, size_t buflen, + struct spwd **result) { - LOCK; - if (grf) { - fclose(grf); - grf = NULL; - } - UNLOCK; + *result = struct_buf; + return getXXnam_r(name, (2 << 2) + 0, buffer, buflen, result); } +#endif -int getgrent_r(struct group *__restrict resultbuf, - char *__restrict buffer, size_t buflen, - struct group **__restrict result) -{ - int rv; +#ifdef UNUSED +/****** getXXent_r */ - LOCK; - *result = NULL; /* In case of error... */ +static int FAST_FUNC getXXent_r(uintptr_t db_idx, char *buffer, size_t buflen, + void *result) +{ + char *buf; + struct passdb *db = &get_S()->db[db_idx]; - if (!grf) { - if (!(grf = fopen(_PATH_GROUP, "r"))) { - rv = errno; - goto ERR; + if (!db->fp) { + db->fp = fopen_for_read(db->filename); + if (!db->fp) { + return errno; } + close_on_exec_on(fileno(db->fp)); } - if (!(rv = __pgsreader(__parsegrent, resultbuf, - buffer, buflen, grf))) { - *result = resultbuf; - } - - ERR: - UNLOCK; - return rv; + buf = parse_common(db->fp, db, /*no search key:*/ NULL, -1); + if (!buf && !errno) + errno = ENOENT; + return massage_data_for_r_func(db, buffer, buflen, result, buf); } -#endif -/**********************************************************************/ -#ifdef L_getspent_r - -static FILE *spf /*= NULL*/; -void setspent(void) +int FAST_FUNC getpwent_r(struct passwd *struct_buf, char *buffer, size_t buflen, + struct passwd **result) { - LOCK; - if (spf) { - rewind(spf); - } - UNLOCK; + *result = struct_buf; + return getXXent_r(0, buffer, buflen, result); } +#endif -void endspent(void) -{ - LOCK; - if (spf) { - fclose(spf); - spf = NULL; - } - UNLOCK; -} +/****** getXXent */ -int getspent_r(struct spwd *resultbuf, char *buffer, - size_t buflen, struct spwd **result) +static void* FAST_FUNC getXXent(uintptr_t db_idx) { - int rv; - - LOCK; - *result = NULL; /* In case of error... */ + char *buf; + struct passdb *db = &get_S()->db[db_idx]; - if (!spf) { - if (!(spf = fopen(_PATH_SHADOW, "r"))) { - rv = errno; - goto ERR; + if (!db->fp) { + db->fp = fopen_for_read(db->filename); + if (!db->fp) { + return NULL; } + close_on_exec_on(fileno(db->fp)); } - if (!(rv = __pgsreader(__parsespent, resultbuf, - buffer, buflen, spf))) { - *result = resultbuf; - } - - ERR: - UNLOCK; - return rv; + buf = parse_common(db->fp, db, /*no search key:*/ NULL, -1); + return massage_data_for_non_r_func(db, buf); } -#endif -/**********************************************************************/ -#ifdef L_getpwent - -struct passwd *getpwent(void) +struct passwd* FAST_FUNC getpwent(void) { - static char line_buff[PWD_BUFFER_SIZE]; - static struct passwd pwd; - struct passwd *result; - - getpwent_r(&pwd, line_buff, sizeof(line_buff), &result); - return result; + return getXXent(0); } -#endif -/**********************************************************************/ -#ifdef L_getgrent +/****** getXXnam/id */ -struct group *getgrent(void) +static void* FAST_FUNC getXXnam(const char *name, unsigned db_and_field_pos) { - static char line_buff[GRP_BUFFER_SIZE]; - static struct group gr; - struct group *result; + char *buf; + struct passdb *db = &get_S()->db[db_and_field_pos >> 2]; - getgrent_r(&gr, line_buff, sizeof(line_buff), &result); - return result; + buf = parse_file(db, name, db_and_field_pos & 3); + return massage_data_for_non_r_func(db, buf); } -#endif -/**********************************************************************/ -#ifdef L_getspent - -struct spwd *getspent(void) +struct passwd* FAST_FUNC getpwnam(const char *name) { - static char line_buff[PWD_BUFFER_SIZE]; - static struct spwd spwd; - struct spwd *result; - - getspent_r(&spwd, line_buff, sizeof(line_buff), &result); - return result; + return getXXnam(name, (0 << 2) + 0); } - -#endif -/**********************************************************************/ -#ifdef L_sgetspent - -struct spwd *sgetspent(const char *string) +struct group* FAST_FUNC getgrnam(const char *name) { - static char line_buff[PWD_BUFFER_SIZE]; - static struct spwd spwd; - struct spwd *result; - - sgetspent_r(string, &spwd, line_buff, sizeof(line_buff), &result); - return result; + return getXXnam(name, (1 << 2) + 0); } - -#endif -/**********************************************************************/ -#ifdef L_initgroups - -int initgroups(const char *user, gid_t gid) +struct passwd* FAST_FUNC getpwuid(uid_t id) { - FILE *grfile; - gid_t *group_list; - int num_groups, rv; - char **m; - struct group group; - char buff[PWD_BUFFER_SIZE]; - - rv = -1; - - /* We alloc space for 8 gids at a time. */ - if (((group_list = (gid_t *) malloc(8*sizeof(gid_t *))) != NULL) - && ((grfile = fopen(_PATH_GROUP, "r")) != NULL) - ) { - - *group_list = gid; - num_groups = 1; - - while (!__pgsreader(__parsegrent, &group, buff, sizeof(buff), grfile)) { - assert(group.gr_mem); /* Must have at least a NULL terminator. */ - if (group.gr_gid != gid) { - for (m=group.gr_mem ; *m ; m++) { - if (!strcmp(*m, user)) { - if (!(num_groups & 7)) { - gid_t *tmp = (gid_t *) - realloc(group_list, - (num_groups+8) * sizeof(gid_t *)); - if (!tmp) { - rv = -1; - goto DO_CLOSE; - } - group_list = tmp; - } - group_list[num_groups++] = group.gr_gid; - break; - } - } - } - } - - rv = setgroups(num_groups, group_list); - DO_CLOSE: - fclose(grfile); - } - - /* group_list will be NULL if initial malloc failed, which may trigger - * warnings from various malloc debuggers. */ - free(group_list); - return rv; + return getXXnam(utoa(id), (0 << 2) + 2); } - -#endif -/**********************************************************************/ -#ifdef L_putpwent - -int putpwent(const struct passwd *__restrict p, FILE *__restrict f) +struct group* FAST_FUNC getgrgid(gid_t id) { - int rv = -1; - - if (!p || !f) { - errno=EINVAL; - } else { - /* No extra thread locking is needed above what fprintf does. */ - if (fprintf(f, "%s:%s:%lu:%lu:%s:%s:%s\n", - p->pw_name, p->pw_passwd, - (unsigned long)(p->pw_uid), - (unsigned long)(p->pw_gid), - p->pw_gecos, p->pw_dir, p->pw_shell) >= 0 - ) { - rv = 0; - } - } - - return rv; + return getXXnam(utoa(id), (1 << 2) + 2); } -#endif -/**********************************************************************/ -#ifdef L_putgrent +/****** end/setXXend */ -int putgrent(const struct group *__restrict p, FILE *__restrict f) +void FAST_FUNC endpwent(void) { - static const char format[] = ",%s"; - char **m; - const char *fmt; - int rv = -1; - - if (!p || !f) { /* Sigh... glibc checks. */ - errno=EINVAL; - } else { - if (fprintf(f, "%s:%s:%lu:", - p->gr_name, p->gr_passwd, - (unsigned long)(p->gr_gid)) >= 0 - ) { - - fmt = format + 1; - - assert(p->gr_mem); - m = p->gr_mem; - - do { - if (!*m) { - if (fputc('\n', f) >= 0) { - rv = 0; - } - break; - } - if (fprintf(f, fmt, *m) < 0) { - break; - } - ++m; - fmt = format; - } while (1); - - } - + if (has_S && S.db[0].fp) { + fclose(S.db[0].fp); + S.db[0].fp = NULL; } - - return rv; } - -#endif -/**********************************************************************/ -#ifdef L_putspent - -static const unsigned char _sp_off[] = { - offsetof(struct spwd, sp_lstchg), /* 2 - not a char ptr */ - offsetof(struct spwd, sp_min), /* 3 - not a char ptr */ - offsetof(struct spwd, sp_max), /* 4 - not a char ptr */ - offsetof(struct spwd, sp_warn), /* 5 - not a char ptr */ - offsetof(struct spwd, sp_inact), /* 6 - not a char ptr */ - offsetof(struct spwd, sp_expire), /* 7 - not a char ptr */ -}; - -int putspent(const struct spwd *p, FILE *stream) +void FAST_FUNC setpwent(void) { - static const char ld_format[] = "%ld:"; - const char *f; - long int x; - int i; - int rv = -1; - - /* Unlike putpwent and putgrent, glibc does not check the args. */ - if (fprintf(stream, "%s:%s:", p->sp_namp, - (p->sp_pwdp ? p->sp_pwdp : "")) < 0 - ) { - goto DO_UNLOCK; - } - - for (i=0 ; i < sizeof(_sp_off) ; i++) { - f = ld_format; - if ((x = *(const long int *)(((const char *) p) + _sp_off[i])) == -1) { - f += 3; - } - if (fprintf(stream, f, x) < 0) { - goto DO_UNLOCK; - } - } - - if ((p->sp_flag != ~0UL) && (fprintf(stream, "%lu", p->sp_flag) < 0)) { - goto DO_UNLOCK; - } - - if (fputc('\n', stream) > 0) { - rv = 0; + if (has_S && S.db[0].fp) { + rewind(S.db[0].fp); } - -DO_UNLOCK: - return rv; } - -#endif -/**********************************************************************/ -/* Internal uClibc functions. */ -/**********************************************************************/ -#ifdef L___parsepwent - -static const unsigned char pw_off[] = { - offsetof(struct passwd, pw_name), /* 0 */ - offsetof(struct passwd, pw_passwd), /* 1 */ - offsetof(struct passwd, pw_uid), /* 2 - not a char ptr */ - offsetof(struct passwd, pw_gid), /* 3 - not a char ptr */ - offsetof(struct passwd, pw_gecos), /* 4 */ - offsetof(struct passwd, pw_dir), /* 5 */ - offsetof(struct passwd, pw_shell) /* 6 */ -}; - -int __parsepwent(void *data, char *line) +void FAST_FUNC endgrent(void) { - char *endptr; - char *p; - int i; - - i = 0; - do { - p = ((char *) ((struct passwd *) data)) + pw_off[i]; - - if ((i & 6) ^ 2) { /* i!=2 and i!=3 */ - *((char **) p) = line; - if (i==6) { - return 0; - } - /* NOTE: glibc difference - glibc allows omission of - * ':' seperators after the gid field if all remaining - * entries are empty. We require all separators. */ - if (!(line = strchr(line, ':'))) { - break; - } - } else { - unsigned long t = strtoul(line, &endptr, 10); - /* Make sure we had at least one digit, and that the - * failing char is the next field seperator ':'. See - * glibc difference note above. */ - /* TODO: Also check for leading whitespace? */ - if ((endptr == line) || (*endptr != ':')) { - break; - } - line = endptr; - if (i & 1) { /* i == 3 -- gid */ - *((gid_t *) p) = t; - } else { /* i == 2 -- uid */ - *((uid_t *) p) = t; - } - } - - *line++ = 0; - ++i; - } while (1); - - return -1; + if (has_S && S.db[1].fp) { + fclose(S.db[1].fp); + S.db[1].fp = NULL; + } } -#endif -/**********************************************************************/ -#ifdef L___parsegrent - -static const unsigned char gr_off[] = { - offsetof(struct group, gr_name), /* 0 */ - offsetof(struct group, gr_passwd), /* 1 */ - offsetof(struct group, gr_gid) /* 2 - not a char ptr */ -}; +/****** initgroups and getgrouplist */ -int __parsegrent(void *data, char *line) +static gid_t* FAST_FUNC getgrouplist_internal(int *ngroups_ptr, + const char *user, gid_t gid) { - char *endptr; - char *p; - int i; - char **members; - char *end_of_buf; - - end_of_buf = ((struct group *) data)->gr_name; /* Evil hack! */ - i = 0; - do { - p = ((char *) ((struct group *) data)) + gr_off[i]; - - if (i < 2) { - *((char **) p) = line; - if (!(line = strchr(line, ':'))) { - break; - } - *line++ = 0; - ++i; - } else { - *((gid_t *) p) = strtoul(line, &endptr, 10); - - /* NOTE: glibc difference - glibc allows omission of the - * trailing colon when there is no member list. We treat - * this as an error. */ - - /* Make sure we had at least one digit, and that the - * failing char is the next field seperator ':'. See - * glibc difference note above. */ - if ((endptr == line) || (*endptr != ':')) { - break; - } - - i = 1; /* Count terminating NULL ptr. */ - p = endptr; - - if (p[1]) { /* We have a member list to process. */ - /* Overwrite the last ':' with a ',' before counting. - * This allows us to test for initial ',' and adds - * one ',' so that the ',' count equals the member - * count. */ - *p = ','; - do { - /* NOTE: glibc difference - glibc allows and trims leading - * (but not trailing) space. We treat this as an error. */ - /* NOTE: glibc difference - glibc allows consecutive and - * trailing commas, and ignores "empty string" users. We - * treat this as an error. */ - if (*p == ',') { - ++i; - *p = 0; /* nul-terminate each member string. */ - if (!*++p || (*p == ',') || isspace(*p)) { - goto ERR; - } - } - } while (*++p); - } - - /* Now align (p+1), rounding up. */ - /* Assumes sizeof(char **) is a power of 2. */ - members = (char **)( (((intptr_t) p) + sizeof(char **)) - & ~((intptr_t)(sizeof(char **) - 1)) ); - - if (((char *)(members + i)) > end_of_buf) { /* No space. */ - break; - } - - ((struct group *) data)->gr_mem = members; + FILE *fp; + gid_t *group_list; + int ngroups; - if (--i) { - p = endptr; /* Pointing to char prior to first member. */ - do { - *members++ = ++p; - if (!--i) break; - while (*++p) {} - } while (1); + /* We alloc space for 8 gids at a time. */ + group_list = xzalloc(8 * sizeof(group_list[0])); + group_list[0] = gid; + ngroups = 1; + + fp = fopen_for_read(_PATH_GROUP); + if (fp) { + struct passdb *db = &get_S()->db[1]; + char *buf; + while ((buf = parse_common(fp, db, NULL, -1)) != NULL) { + char **m; + struct group group; + if (!convert_to_struct(db, buf, &group)) + goto next; + if (group.gr_gid == gid) + goto next; + for (m = group.gr_mem; *m; m++) { + if (strcmp(*m, user) != 0) + continue; + group_list = xrealloc_vector(group_list, /*8=2^3:*/ 3, ngroups); + group_list[ngroups++] = group.gr_gid; + goto next; } - *members = NULL; - - return 0; + next: + free(buf); } - } while (1); - - ERR: - return -1; + fclose(fp); + } + *ngroups_ptr = ngroups; + return group_list; } -#endif -/**********************************************************************/ -#ifdef L___parsespent - -static const unsigned char sp_off[] = { - offsetof(struct spwd, sp_namp), /* 0 */ - offsetof(struct spwd, sp_pwdp), /* 1 */ - offsetof(struct spwd, sp_lstchg), /* 2 - not a char ptr */ - offsetof(struct spwd, sp_min), /* 3 - not a char ptr */ - offsetof(struct spwd, sp_max), /* 4 - not a char ptr */ - offsetof(struct spwd, sp_warn), /* 5 - not a char ptr */ - offsetof(struct spwd, sp_inact), /* 6 - not a char ptr */ - offsetof(struct spwd, sp_expire), /* 7 - not a char ptr */ - offsetof(struct spwd, sp_flag) /* 8 - not a char ptr */ -}; - -int __parsespent(void *data, char * line) +int FAST_FUNC initgroups(const char *user, gid_t gid) { - char *endptr; - char *p; - int i; - - i = 0; - do { - p = ((char *) ((struct spwd *) data)) + sp_off[i]; - if (i < 2) { - *((char **) p) = line; - if (!(line = strchr(line, ':'))) { - break; - } - } else { -#if 0 - if (i==5) { /* Support for old format. */ - while (isspace(*line)) ++line; /* glibc eats space here. */ - if (!*line) { - ((struct spwd *) data)->sp_warn = -1; - ((struct spwd *) data)->sp_inact = -1; - ((struct spwd *) data)->sp_expire = -1; - ((struct spwd *) data)->sp_flag = ~0UL; - return 0; - } - } -#endif - - *((long *) p) = (long) strtoul(line, &endptr, 10); - - if (endptr == line) { - *((long *) p) = ((i != 8) ? -1L : ((long)(~0UL))); - } - - line = endptr; - - if (i == 8) { - if (!*endptr) { - return 0; - } - break; - } - - if (*endptr != ':') { - break; - } - - } - - *line++ = 0; - ++i; - } while (1); + int ngroups; + gid_t *group_list = getgrouplist_internal(&ngroups, user, gid); - return EINVAL; + ngroups = setgroups(ngroups, group_list); + free(group_list); + return ngroups; } -#endif -/**********************************************************************/ -#ifdef L___pgsreader - -/* Reads until if EOF, or until if finds a line which fits in the buffer - * and for which the parser function succeeds. - * - * Returns 0 on success and ENOENT for end-of-file (glibc concession). - */ - -int __pgsreader(int (*__parserfunc)(void *d, char *line), void *data, - char *__restrict line_buff, size_t buflen, FILE *f) +int FAST_FUNC getgrouplist(const char *user, gid_t gid, gid_t *groups, int *ngroups) { - int line_len; - int skip; - int rv = ERANGE; + int ngroups_old = *ngroups; + gid_t *group_list = getgrouplist_internal(ngroups, user, gid); - if (buflen < PWD_BUFFER_SIZE) { - errno=rv; + if (*ngroups <= ngroups_old) { + ngroups_old = *ngroups; + memcpy(groups, group_list, ngroups_old * sizeof(groups[0])); } else { - skip = 0; - do { - if (!fgets(line_buff, buflen, f)) { - if (feof(f)) { - rv = ENOENT; - } - break; - } - - line_len = strlen(line_buff) - 1; /* strlen() must be > 0. */ - if (line_buff[line_len] == '\n') { - line_buff[line_len] = 0; - } else if (line_len + 2 == buflen) { /* line too long */ - ++skip; - continue; - } - - if (skip) { - --skip; - continue; - } - - /* NOTE: glibc difference - glibc strips leading whitespace from - * records. We do not allow leading whitespace. */ - - /* Skip empty lines, comment lines, and lines with leading - * whitespace. */ - if (*line_buff && (*line_buff != '#') && !isspace(*line_buff)) { - if (__parserfunc == __parsegrent) { /* Do evil group hack. */ - /* The group entry parsing function needs to know where - * the end of the buffer is so that it can construct the - * group member ptr table. */ - ((struct group *) data)->gr_name = line_buff + buflen; - } - - if (!__parserfunc(data, line_buff)) { - rv = 0; - break; - } - } - } while (1); - + ngroups_old = -1; } - - return rv; + free(group_list); + return ngroups_old; } - -#endif -/**********************************************************************/