On Wednesday 24 September 2014 23:02:55 tito wrote: > On Saturday 20 September 2014 16:32:01 tito wrote: > > Hi, > > One more fix of a return value. > > > > Ciao, > > Tito > > > > Hi, > more return value and errno fixes. > > Ciao, > Tito > Hi, make the tokenize function more robust.
Ciao, Tito
/* vi: set sw=4 ts=4: */ /* Copyright (C) 2003 Manuel Novoa III * * 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. * * TODO: * Move to dynamic allocation of (currently statically allocated) * buffers; especially for the group-related functions since * large group member lists will cause error returns. */ /* Copyright (C) 2014 Tito Ragusa <farmat...@tiscali.it> * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ /* Rewrite of some parts. Main differences are: * * 1) the buffer for getpwuid, getgrgid, getpwnam, getgrnam is dynamically * allocated and reused by later calls. if ERANGE error pops up it is * reallocated to the size of the longest line found in the passwd/group * files and reused for later calls. * * 2) the passwd/group files: * a) must contain the expected number of fields; * b) some fields are not allowed to be empty (e.g. username, homedir, shell); * c) leading or trailing spaces in fields are not allowed; * d) the string representing uid/gid must be convertible by strtoXX functions; * if the above explained conditions are not met a error message about bad * record in file is printed so that the user knows about it. * 3) the internal function for getgrouplist uses a dynamically allocated * buffer and retries with a bigger one in case it is to small; * 4) the _r functions use the user supplied buffers that are never reallocated * but use mostly the same common code as the other functions. * 5) 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 "libbb.h" /* for the fields in passwd/group/shadow files */ #define NOT_EMPTY 0 #define MAYBE_EMPTY 1 /* Fields in passwd file starting from pos 1 */ #define _PASSWD_FIELDS 7 /* S = string not empty, s = string maybe empty, */ /* I = uid,gid, l = long maybe empty, m = members */ static const char *pw_def = "SsIIsSS"; /* Fields in group file starting from pos 1 */ #define _GROUP_FIELDS 4 static const char *gr_def = "SsIm"; /* Fields in shadow file starting from pos 1 */ #define _SHADOW_FIELDS 9 #if ENABLE_USE_BB_SHADOW /* But we parse only 8 as last is reserved*/ static const char *sp_def = "Ssllllll"; #endif /* Initial buffer size */ #define PWD_GRP_BUFSIZE 256 /* for getpw_common_malloc */ static char *pwd_buffer = NULL; static size_t pwd_buffer_size = PWD_GRP_BUFSIZE; struct passwd *my_pw; struct passwd my_pwd; /* for getgr_common_malloc */ static char *grp_buffer = NULL; static size_t grp_buffer_size = PWD_GRP_BUFSIZE; struct group *my_gr; struct group my_grp; /* for setpwent, getpwent_r, endpwent */ static FILE *pwf = NULL; /* for setgrent, getgrent_r, endgrent */ static FILE *grf = NULL; /* TODO: We do no file locking, but we do * in libbb/update_passwd.c, so maybe the same code * could be reused here */ #define LOCK ((void) 0) #define UNLOCK ((void) 0) /**********************************************************************/ /* Internal functions */ /**********************************************************************/ /* Divide the passwd/group/shadow record in fields * by substituting the given delimeter * e.g. ':' or ',' with '\0'. * Returns the number of fields found. * Checks for leading or trailing spaces in fields. */ static int FAST_FUNC tokenize(char *buffer, int ch, int *error) { char *p = buffer; char *s = p; int num_fields = 1; while (*p) { if (*p == ch) { *p = '\0'; num_fields++; s = p + 1; } if (*s == ' ' || last_char_is(s, ' ') != NULL) { *error = EINVAL; } p++; } return num_fields; } /* Fill the buffer linebuf up to len with * a line of a passwd/group file. * Returns 0 on success, ENOENT on EOF and * the length of the passwd/group record * as negative integer if bigger than len. * CAVEAT: if a OS has negative errno * this will break!!! */ static int FAST_FUNC get_record_line(FILE *file, char *linebuf, int len) { int ch; int idx = 0; while ((ch = getc(file)) != EOF) { if (idx < len) { linebuf[idx] = (char) ch; } idx++; if (ch == '\n' || ch == '\0') break; } if (idx > len) { /* return -size of buffer needed to hold the */ /* full line instead of ERANGE.*/ return -idx; } /* terminate string and remove trailing '\n' if any.*/ linebuf[idx - 1] = '\0'; /* return ENONT only on EOF and no data */ return (idx > 0) ? 0 : ENOENT; } static void FAST_FUNC warn_if_error(int error, const char *filename) { if (error > 0) { errno = error; bb_error_msg("'%s': bad record", filename); } } /* Returns 0 on success and matching line broken up in fields by '\0' in buf or error. * We require all separators ':' to be there and warn and skip the line if they are not. * int n_fields is the expected number of fields of the passwd/group file starting from 1. * size_t len is used to check that the buffer provided is big enough, if not we use the * return value to pass the needed lenght as negative integer to the caller. */ static int FAST_FUNC parse_common(FILE *f, const char *key, int field, char *buf, size_t len, const char *filename, int n_fields) { int error = 0; const char *s; while (1) { if ((error = get_record_line(f, buf, len)) != 0) { /* We use error to pass the needed length to the */ /* caller as negative value instead of ERANGE */ return error; } /* Skip empty lines, comment lines */ if (buf[0] != '\0' && buf[0] != '#') { /* Check that there are the expected fields */ if (tokenize(buf, ':', &error) == n_fields) { if (key) { s = nth_string(buf, field); /* Empty fields not allowed */ if (!*s) { error = EINVAL; break; } if (strcmp(key, s) == 0) { /* Record found */ break; } } else { /* Sequential read - return record */ break; } } else { error = EINVAL; } } } warn_if_error(error, filename); return error; } static int FAST_FUNC parse_file(const char *filename, int n_fields, const char *key, int field, char *buf, size_t len) { int ret; FILE *f = fopen_for_read(filename); ret = errno; if (f) { ret = parse_common(f, key, field, buf, len, filename, n_fields); fclose(f); } return ret; } /* Convert passwd/group/shadow file record in buffer to a struct */ static void * FAST_FUNC convert_to_struct(const char *def, const unsigned char *off, char *buffer, void *result, int *error, const char *filename) { int idx; char **members = NULL; char *m; for (idx = 0; def[idx] != '\0'; idx++) { if (def[idx] == 'S' || def[idx] == 's') { m = (char *) nth_string(buffer, idx); *(char **)((char *)result + off[idx]) = m; if (!*m && (def[idx] == 'S')) { *error = EINVAL; } } if (def[idx] == 'I') { *(int *)((int) result + off[idx]) = bb_strtou(nth_string(buffer, idx), NULL, 10); if (errno) { *error = EINVAL; } } #if ENABLE_USE_BB_SHADOW if (def[idx] == 'l') { char *endptr; long ret; /* Reuse char *m */ m = (char *) nth_string(buffer, idx); ret = bb_strtol(m, &endptr, 10); if (endptr == m) { ret = -1L; } else if (errno) { *error = EINVAL; } *(long *)((long) result + off[idx]) = ret; } #endif if (def[idx] == 'm') { int i = 0; int num_members = 0; char *s = (char *) nth_string(buffer, idx); /* members with leading or trailing spaces set error to EINVAL */ num_members = tokenize(s, ',', error); members = xmalloc(sizeof (char *) * (num_members + 1)); if (*s) { /* Leaks a little memory */ while (num_members--) { /* Reuse char *m */ m = (char *) nth_string(s, i); if (!*m) { *error = EINVAL; } members[i++] = m; } } members[i] = NULL; *(char ***)((char *)result + off[idx]) = members; } } warn_if_error(*error, filename); if (*error) { free(members); } return (*error) ? NULL : result; } static int FAST_FUNC fix_error_code(int error) { if (error < 0) error = errno = ERANGE; return error; } #if ENABLE_USE_BB_SHADOW static const unsigned char sp_off[] ALIGN1 = { offsetof(struct spwd, sp_namp), /* 1 - Login name */ offsetof(struct spwd, sp_pwdp), /* 2 - Encrypted password */ 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 - Reserved */ }; static struct spwd * FAST_FUNC convert_to_shadow(char *buffer, struct spwd *result, int *error) { return convert_to_struct(sp_def, sp_off, buffer, result, error, _PATH_SHADOW); } int getspnam_r(const char *name, struct spwd *spbuf, char *buf, size_t buflen, struct spwd **spbufp) { int error; *spbufp = NULL; error = parse_file(_PATH_SHADOW, _SHADOW_FIELDS, name, 0, buf, buflen); if (error == 0) { *spbufp = convert_to_shadow(buf, spbuf, &error); } return fix_error_code(error); } #endif /* ENABLE_USE_BB_SHADOW */ static const unsigned char pw_off[] ALIGN1 = { 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 */ }; static struct passwd * FAST_FUNC convert_to_pwd(char *buffer, struct passwd *result, int *error) { return convert_to_struct(pw_def, pw_off, buffer, result, error, _PATH_PASSWD); } static const unsigned char gr_off[] ALIGN1 = { offsetof(struct group, gr_name), /* 0 */ offsetof(struct group, gr_passwd), /* 1 */ offsetof(struct group, gr_gid), /* 2 - not a char ptr */ offsetof(struct group, gr_mem) /* 3 - char ** ptr */ }; static struct group * FAST_FUNC convert_to_grp(char *buffer, struct group *result, int *error) { return convert_to_struct(gr_def, gr_off, buffer, result, error, _PATH_GROUP); } /* Common code for getpwxxx_r functions. */ static int FAST_FUNC getpw_common_r(const char *name, int field, struct passwd *pwd, char *buf, size_t buflen, struct passwd **result) { int error; *result = NULL; error = parse_file(_PATH_PASSWD, _PASSWD_FIELDS, name, field, buf, buflen); if (error == 0) { *result = convert_to_pwd(buf, pwd, &error); } return (error == ENOENT) ? 0 : error; } /* Common code for getgrxxx_r functions. */ static int FAST_FUNC getgr_common_r(const char *name, int field, struct group *grp, char *buf, size_t buflen, struct group **result) { int error; *result = NULL; error = parse_file(_PATH_GROUP, _GROUP_FIELDS, name, field, buf, buflen); if (error == 0) { *result = convert_to_grp(buf, grp, &error); } return (error == ENOENT) ? 0 : error; } /* Common code for getpwxxx functions. */ static struct passwd * FAST_FUNC getpw_common_malloc(const char *name, int field) { int error = 0; retry: pwd_buffer = xrealloc(pwd_buffer, pwd_buffer_size * sizeof( char)); error = getpw_common_r(name, field, &my_pwd, pwd_buffer, pwd_buffer_size, &my_pw); if (error < 0) { pwd_buffer_size = abs(error); goto retry; } return my_pw; } /* Common code for getgrxxx functions. */ static struct group * FAST_FUNC getgr_common_malloc(const char *name, int field) { int error = 0; retry: grp_buffer = xrealloc(grp_buffer, grp_buffer_size * sizeof( char)); error = getgr_common_r(name, field, &my_grp, grp_buffer, grp_buffer_size, &my_gr); if (error < 0) { grp_buffer_size = abs(error); goto retry; } return my_gr; } /**********************************************************************/ int getpwent_r(struct passwd *pwbuf, char *buf, size_t buflen, struct passwd **pwbufp) { int error; LOCK; *pwbufp = NULL; if (!pwf) { pwf = fopen_for_read(_PATH_PASSWD); if (!pwf) { return errno; } close_on_exec_on(fileno(pwf)); } error = parse_common(pwf, NULL, -1, buf, buflen, _PATH_PASSWD, _PASSWD_FIELDS); if (error == 0) { *pwbufp = convert_to_pwd(buf, pwbuf, &error); } UNLOCK; return fix_error_code(error); } int getgrent_r(struct group *gbuf, char *buf, size_t buflen, struct group **gbufp) { int error; LOCK; *gbufp = NULL; if (!grf) { grf = fopen_for_read(_PATH_GROUP); if (!grf) { return errno; } close_on_exec_on(fileno(grf)); } error = parse_common(grf, NULL, -1, buf, buflen, _PATH_GROUP, _GROUP_FIELDS); if (error == 0) { *gbufp = convert_to_grp(buf, gbuf, &error); } UNLOCK; return fix_error_code(error); } int getpwnam_r(const char *name, struct passwd *pwd, char *buf, size_t buflen, struct passwd **result) { return fix_error_code(getpw_common_r(name, 0, pwd, buf, buflen, result)); } int getgrnam_r(const char *name, struct group *grp, char *buf, size_t buflen, struct group **result) { return fix_error_code(getgr_common_r(name, 0, grp, buf, buflen, result)); } int getpwuid_r(uid_t uid, struct passwd *pwd, char *buf, size_t buflen, struct passwd **result) { return fix_error_code(getpw_common_r(utoa(uid), 2, pwd, buf, buflen, result)); } int getgrgid_r(gid_t gid, struct group *grp, char *buf, size_t buflen, struct group **result) { return fix_error_code(getgr_common_r(utoa(gid), 2, grp, buf, buflen, result)); } struct passwd *getpwnam(const char *name) { return getpw_common_malloc(name, 0); } struct group *getgrnam(const char *name) { return getgr_common_malloc(name, 0); } struct passwd *getpwuid(uid_t uid) { return getpw_common_malloc(utoa(uid), 2); } struct group *getgrgid(gid_t gid) { return getgr_common_malloc(utoa(gid), 2); } void endpwent(void) { LOCK; if (pwf) { fclose(pwf); pwf = NULL; } UNLOCK; } void setpwent(void) { LOCK; if (pwf) { rewind(pwf); } UNLOCK; } void endgrent(void) { LOCK; if (grf) { fclose(grf); grf = NULL; } UNLOCK; } void setgrent(void) { LOCK; if (grf) { rewind(grf); } UNLOCK; } static gid_t * FAST_FUNC getgrouplist_internal(int *ngroups_ptr, const char *user, gid_t gid) { FILE *f; struct group *gr; struct group grp; gid_t *group_list; int ngroups; char * buffer = NULL; size_t buflen = PWD_GRP_BUFSIZE; int ret = 0; retry: group_list = NULL; ngroups = 1; f = fopen_for_read(_PATH_GROUP); if (f) { group_list = xrealloc(group_list, 1 * sizeof(gid_t)); *group_list = gid; buffer = xrealloc(buffer, buflen * sizeof( char)); while ((ret = parse_common(f, NULL, -1, buffer, buflen, _PATH_GROUP, _GROUP_FIELDS)) == 0) { /* convert_to_grp warns on error so ret is unused */ gr = convert_to_grp(buffer, &grp, &ret); if (gr != NULL) { if (gr->gr_gid != gid) { /* reuse int ret */ for (/*ret = 0*/; gr->gr_mem[ret] != NULL; ret++) { if (strcmp(gr->gr_mem[ret], user) == 0) { group_list = xrealloc(group_list, (ngroups + 1) * sizeof(gid_t)); group_list[ngroups++] = gr->gr_gid; } } } /* our gr->gr_mem is malloced, free it to reduce memory leaks */ free(gr->gr_mem); } } fclose(f); } if (ret < 0) { /* buffer was to small, retry */ buflen = abs(ret); free(group_list); goto retry; } free(buffer); *ngroups_ptr = ngroups; return group_list; } int initgroups(const char *user, gid_t gid) { int ngroups; gid_t *group_list = getgrouplist_internal(&ngroups, user, gid); ngroups = setgroups(ngroups, group_list); free(group_list); return ngroups; } int getgrouplist(const char *user, gid_t gid, gid_t *groups, int *ngroups) { int ngroups_old = *ngroups; gid_t *group_list = getgrouplist_internal(ngroups, user, gid); if (*ngroups <= ngroups_old) { ngroups_old = *ngroups; memcpy(groups, group_list, ngroups_old * sizeof(groups[0])); } else { ngroups_old = -1; } free(group_list); return ngroups_old; }
_______________________________________________ busybox mailing list busybox@busybox.net http://lists.busybox.net/mailman/listinfo/busybox