extdom: handle ERANGE return code for getXXYYY_r() calls

The getXXYYY_r() calls require a buffer to store the variable data of
the passwd and group structs. If the provided buffer is too small ERANGE
is returned and the caller can try with a larger buffer again.

Cmocka/cwrap based unit-tests for get*_r_wrapper() are added.

Resolves https://fedorahosted.org/freeipa/ticket/4908

Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
This commit is contained in:
Sumit Bose 2015-02-24 15:33:39 +01:00 committed by Martin Kosek
parent cc6fc3728c
commit 5bd4b7a09d
7 changed files with 498 additions and 84 deletions

View File

@ -35,9 +35,20 @@ libipa_extdom_extop_la_LIBADD = \
$(SSSNSSIDMAP_LIBS) \
$(NULL)
TESTS =
check_PROGRAMS =
if HAVE_CHECK
TESTS = extdom_tests
check_PROGRAMS = extdom_tests
TESTS += extdom_tests
check_PROGRAMS += extdom_tests
endif
if HAVE_CMOCKA
if HAVE_NSS_WRAPPER
TESTS_ENVIRONMENT = . ./test_data/test_setup.sh;
TESTS += extdom_cmocka_tests
check_PROGRAMS += extdom_cmocka_tests
endif
endif
extdom_tests_SOURCES = \
@ -55,6 +66,22 @@ extdom_tests_LDADD = \
$(SSSNSSIDMAP_LIBS) \
$(NULL)
extdom_cmocka_tests_SOURCES = \
ipa_extdom_cmocka_tests.c \
ipa_extdom_common.c \
$(NULL)
extdom_cmocka_tests_CFLAGS = $(CMOCKA_CFLAGS)
extdom_cmocka_tests_LDFLAGS = \
-rpath $(shell pkg-config --libs-only-L dirsrv | sed -e 's/-L//') \
$(NULL)
extdom_cmocka_tests_LDADD = \
$(CMOCKA_LIBS) \
$(LDAP_LIBS) \
$(DIRSRV_LIBS) \
$(SSSNSSIDMAP_LIBS) \
$(NULL)
appdir = $(IPA_DATA_DIR)
app_DATA = \
ipa-extdom-extop-conf.ldif \

View File

@ -174,4 +174,13 @@ int check_request(struct extdom_req *req, enum extdom_version version);
int handle_request(struct ipa_extdom_ctx *ctx, struct extdom_req *req,
struct berval **berval);
int pack_response(struct extdom_res *res, struct berval **ret_val);
int get_buffer(size_t *_buf_len, char **_buf);
int getpwnam_r_wrapper(size_t buf_max, const char *name,
struct passwd *pwd, char **_buf, size_t *_buf_len);
int getpwuid_r_wrapper(size_t buf_max, uid_t uid,
struct passwd *pwd, char **_buf, size_t *_buf_len);
int getgrnam_r_wrapper(size_t buf_max, const char *name,
struct group *grp, char **_buf, size_t *_buf_len);
int getgrgid_r_wrapper(size_t buf_max, gid_t gid,
struct group *grp, char **_buf, size_t *_buf_len);
#endif /* _IPA_EXTDOM_H_ */

View File

@ -0,0 +1,226 @@
/*
Authors:
Sumit Bose <sbose@redhat.com>
Copyright (C) 2015 Red Hat
Extdom tests
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <errno.h>
#include <stdarg.h>
#include <stddef.h>
#include <setjmp.h>
#include <cmocka.h>
#include <sys/types.h>
#include <pwd.h>
#include "ipa_extdom.h"
#define MAX_BUF (1024*1024*1024)
void test_getpwnam_r_wrapper(void **state)
{
int ret;
struct passwd pwd;
char *buf;
size_t buf_len;
ret = get_buffer(&buf_len, &buf);
assert_int_equal(ret, 0);
ret = getpwnam_r_wrapper(MAX_BUF, "non_exisiting_user", &pwd, &buf,
&buf_len);
assert_int_equal(ret, ENOENT);
ret = getpwnam_r_wrapper(MAX_BUF, "user", &pwd, &buf, &buf_len);
assert_int_equal(ret, 0);
assert_string_equal(pwd.pw_name, "user");
assert_string_equal(pwd.pw_passwd, "x");
assert_int_equal(pwd.pw_uid, 12345);
assert_int_equal(pwd.pw_gid, 23456);
assert_string_equal(pwd.pw_gecos, "gecos");
assert_string_equal(pwd.pw_dir, "/home/user");
assert_string_equal(pwd.pw_shell, "/bin/shell");
free(buf);
ret = get_buffer(&buf_len, &buf);
assert_int_equal(ret, 0);
ret = getpwnam_r_wrapper(MAX_BUF, "user_big", &pwd, &buf, &buf_len);
assert_int_equal(ret, 0);
assert_string_equal(pwd.pw_name, "user_big");
assert_string_equal(pwd.pw_passwd, "x");
assert_int_equal(pwd.pw_uid, 12346);
assert_int_equal(pwd.pw_gid, 23457);
assert_int_equal(strlen(pwd.pw_gecos), 4000 * strlen("gecos"));
assert_string_equal(pwd.pw_dir, "/home/user_big");
assert_string_equal(pwd.pw_shell, "/bin/shell");
free(buf);
ret = get_buffer(&buf_len, &buf);
assert_int_equal(ret, 0);
ret = getpwnam_r_wrapper(1024, "user_big", &pwd, &buf, &buf_len);
assert_int_equal(ret, ERANGE);
free(buf);
}
void test_getpwuid_r_wrapper(void **state)
{
int ret;
struct passwd pwd;
char *buf;
size_t buf_len;
ret = get_buffer(&buf_len, &buf);
assert_int_equal(ret, 0);
ret = getpwuid_r_wrapper(MAX_BUF, 99999, &pwd, &buf, &buf_len);
assert_int_equal(ret, ENOENT);
ret = getpwuid_r_wrapper(MAX_BUF, 12345, &pwd, &buf, &buf_len);
assert_int_equal(ret, 0);
assert_string_equal(pwd.pw_name, "user");
assert_string_equal(pwd.pw_passwd, "x");
assert_int_equal(pwd.pw_uid, 12345);
assert_int_equal(pwd.pw_gid, 23456);
assert_string_equal(pwd.pw_gecos, "gecos");
assert_string_equal(pwd.pw_dir, "/home/user");
assert_string_equal(pwd.pw_shell, "/bin/shell");
free(buf);
ret = get_buffer(&buf_len, &buf);
assert_int_equal(ret, 0);
ret = getpwuid_r_wrapper(MAX_BUF, 12346, &pwd, &buf, &buf_len);
assert_int_equal(ret, 0);
assert_string_equal(pwd.pw_name, "user_big");
assert_string_equal(pwd.pw_passwd, "x");
assert_int_equal(pwd.pw_uid, 12346);
assert_int_equal(pwd.pw_gid, 23457);
assert_int_equal(strlen(pwd.pw_gecos), 4000 * strlen("gecos"));
assert_string_equal(pwd.pw_dir, "/home/user_big");
assert_string_equal(pwd.pw_shell, "/bin/shell");
free(buf);
ret = get_buffer(&buf_len, &buf);
assert_int_equal(ret, 0);
ret = getpwuid_r_wrapper(1024, 12346, &pwd, &buf, &buf_len);
assert_int_equal(ret, ERANGE);
free(buf);
}
void test_getgrnam_r_wrapper(void **state)
{
int ret;
struct group grp;
char *buf;
size_t buf_len;
ret = get_buffer(&buf_len, &buf);
assert_int_equal(ret, 0);
ret = getgrnam_r_wrapper(MAX_BUF, "non_exisiting_group", &grp, &buf, &buf_len);
assert_int_equal(ret, ENOENT);
ret = getgrnam_r_wrapper(MAX_BUF, "group", &grp, &buf, &buf_len);
assert_int_equal(ret, 0);
assert_string_equal(grp.gr_name, "group");
assert_string_equal(grp.gr_passwd, "x");
assert_int_equal(grp.gr_gid, 11111);
assert_string_equal(grp.gr_mem[0], "member0001");
assert_string_equal(grp.gr_mem[1], "member0002");
assert_null(grp.gr_mem[2]);
free(buf);
ret = get_buffer(&buf_len, &buf);
assert_int_equal(ret, 0);
ret = getgrnam_r_wrapper(MAX_BUF, "group_big", &grp, &buf, &buf_len);
assert_int_equal(ret, 0);
assert_string_equal(grp.gr_name, "group_big");
assert_string_equal(grp.gr_passwd, "x");
assert_int_equal(grp.gr_gid, 22222);
assert_string_equal(grp.gr_mem[0], "member0001");
assert_string_equal(grp.gr_mem[1], "member0002");
free(buf);
ret = get_buffer(&buf_len, &buf);
assert_int_equal(ret, 0);
ret = getgrnam_r_wrapper(1024, "group_big", &grp, &buf, &buf_len);
assert_int_equal(ret, ERANGE);
free(buf);
}
void test_getgrgid_r_wrapper(void **state)
{
int ret;
struct group grp;
char *buf;
size_t buf_len;
ret = get_buffer(&buf_len, &buf);
assert_int_equal(ret, 0);
ret = getgrgid_r_wrapper(MAX_BUF, 99999, &grp, &buf, &buf_len);
assert_int_equal(ret, ENOENT);
ret = getgrgid_r_wrapper(MAX_BUF, 11111, &grp, &buf, &buf_len);
assert_int_equal(ret, 0);
assert_string_equal(grp.gr_name, "group");
assert_string_equal(grp.gr_passwd, "x");
assert_int_equal(grp.gr_gid, 11111);
assert_string_equal(grp.gr_mem[0], "member0001");
assert_string_equal(grp.gr_mem[1], "member0002");
assert_null(grp.gr_mem[2]);
free(buf);
ret = get_buffer(&buf_len, &buf);
assert_int_equal(ret, 0);
ret = getgrgid_r_wrapper(MAX_BUF, 22222, &grp, &buf, &buf_len);
assert_int_equal(ret, 0);
assert_string_equal(grp.gr_name, "group_big");
assert_string_equal(grp.gr_passwd, "x");
assert_int_equal(grp.gr_gid, 22222);
assert_string_equal(grp.gr_mem[0], "member0001");
assert_string_equal(grp.gr_mem[1], "member0002");
free(buf);
ret = get_buffer(&buf_len, &buf);
assert_int_equal(ret, 0);
ret = getgrgid_r_wrapper(1024, 22222, &grp, &buf, &buf_len);
assert_int_equal(ret, ERANGE);
free(buf);
}
int main(int argc, const char *argv[])
{
const UnitTest tests[] = {
unit_test(test_getpwnam_r_wrapper),
unit_test(test_getpwuid_r_wrapper),
unit_test(test_getgrnam_r_wrapper),
unit_test(test_getgrgid_r_wrapper),
};
return run_tests(tests);
}

View File

@ -49,6 +49,188 @@
#define MAX(a,b) (((a)>(b))?(a):(b))
#define SSSD_DOMAIN_SEPARATOR '@'
#define MAX_BUF (1024*1024*1024)
int get_buffer(size_t *_buf_len, char **_buf)
{
long pw_max;
long gr_max;
size_t buf_len;
char *buf;
pw_max = sysconf(_SC_GETPW_R_SIZE_MAX);
gr_max = sysconf(_SC_GETGR_R_SIZE_MAX);
buf_len = MAX(16384, MAX(pw_max, gr_max));
buf = malloc(sizeof(char) * buf_len);
if (buf == NULL) {
return LDAP_OPERATIONS_ERROR;
}
*_buf_len = buf_len;
*_buf = buf;
return LDAP_SUCCESS;
}
static int inc_buffer(size_t buf_max, size_t *_buf_len, char **_buf)
{
size_t tmp_len;
char *tmp_buf;
tmp_buf = *_buf;
tmp_len = *_buf_len;
tmp_len *= 2;
if (tmp_len > buf_max) {
return ERANGE;
}
tmp_buf = realloc(tmp_buf, tmp_len);
if (tmp_buf == NULL) {
return ENOMEM;
}
*_buf_len = tmp_len;
*_buf = tmp_buf;
return 0;
}
int getpwnam_r_wrapper(size_t buf_max, const char *name,
struct passwd *pwd, char **_buf, size_t *_buf_len)
{
char *buf = NULL;
size_t buf_len = 0;
int ret;
struct passwd *result = NULL;
buf = *_buf;
buf_len = *_buf_len;
while (buf != NULL
&& (ret = getpwnam_r(name, pwd, buf, buf_len, &result)) == ERANGE) {
ret = inc_buffer(buf_max, &buf_len, &buf);
if (ret != 0) {
if (ret == ERANGE) {
LOG("Buffer too small, increase ipaExtdomMaxNssBufSize.\n");
}
goto done;
}
}
if (ret == 0 && result == NULL) {
ret = ENOENT;
}
done:
*_buf = buf;
*_buf_len = buf_len;
return ret;
}
int getpwuid_r_wrapper(size_t buf_max, uid_t uid,
struct passwd *pwd, char **_buf, size_t *_buf_len)
{
char *buf = NULL;
size_t buf_len = 0;
int ret;
struct passwd *result = NULL;
buf = *_buf;
buf_len = *_buf_len;
while (buf != NULL
&& (ret = getpwuid_r(uid, pwd, buf, buf_len, &result)) == ERANGE) {
ret = inc_buffer(buf_max, &buf_len, &buf);
if (ret != 0) {
if (ret == ERANGE) {
LOG("Buffer too small, increase ipaExtdomMaxNssBufSize.\n");
}
goto done;
}
}
if (ret == 0 && result == NULL) {
ret = ENOENT;
}
done:
*_buf = buf;
*_buf_len = buf_len;
return ret;
}
int getgrnam_r_wrapper(size_t buf_max, const char *name,
struct group *grp, char **_buf, size_t *_buf_len)
{
char *buf = NULL;
size_t buf_len = 0;
int ret;
struct group *result = NULL;
buf = *_buf;
buf_len = *_buf_len;
while (buf != NULL
&& (ret = getgrnam_r(name, grp, buf, buf_len, &result)) == ERANGE) {
ret = inc_buffer(buf_max, &buf_len, &buf);
if (ret != 0) {
if (ret == ERANGE) {
LOG("Buffer too small, increase ipaExtdomMaxNssBufSize.\n");
}
goto done;
}
}
if (ret == 0 && result == NULL) {
ret = ENOENT;
}
done:
*_buf = buf;
*_buf_len = buf_len;
return ret;
}
int getgrgid_r_wrapper(size_t buf_max, gid_t gid,
struct group *grp, char **_buf, size_t *_buf_len)
{
char *buf = NULL;
size_t buf_len = 0;
int ret;
struct group *result = NULL;
buf = *_buf;
buf_len = *_buf_len;
while (buf != NULL
&& (ret = getgrgid_r(gid, grp, buf, buf_len, &result)) == ERANGE) {
ret = inc_buffer(buf_max, &buf_len, &buf);
if (ret != 0) {
if (ret == ERANGE) {
LOG("Buffer too small, increase ipaExtdomMaxNssBufSize.\n");
}
goto done;
}
}
if (ret == 0 && result == NULL) {
ret = ENOENT;
}
done:
*_buf = buf;
*_buf_len = buf_len;
return ret;
}
int parse_request_data(struct berval *req_val, struct extdom_req **_req)
{
@ -191,33 +373,6 @@ int check_request(struct extdom_req *req, enum extdom_version version)
return LDAP_SUCCESS;
}
static int get_buffer(size_t *_buf_len, char **_buf)
{
long pw_max;
long gr_max;
size_t buf_len;
char *buf;
pw_max = sysconf(_SC_GETPW_R_SIZE_MAX);
gr_max = sysconf(_SC_GETGR_R_SIZE_MAX);
if (pw_max == -1 && gr_max == -1) {
buf_len = 16384;
} else {
buf_len = MAX(pw_max, gr_max);
}
buf = malloc(sizeof(char) * buf_len);
if (buf == NULL) {
return LDAP_OPERATIONS_ERROR;
}
*_buf_len = buf_len;
*_buf = buf;
return LDAP_SUCCESS;
}
static int get_user_grouplist(const char *name, gid_t gid,
size_t *_ngroups, gid_t **_groups )
{
@ -323,7 +478,6 @@ static int pack_ber_user(enum response_types response_type,
size_t buf_len;
char *buf = NULL;
struct group grp;
struct group *grp_result;
size_t c;
char *locat;
char *short_user_name = NULL;
@ -375,13 +529,13 @@ static int pack_ber_user(enum response_types response_type,
}
for (c = 0; c < ngroups; c++) {
ret = getgrgid_r(groups[c], &grp, buf, buf_len, &grp_result);
ret = getgrgid_r_wrapper(MAX_BUF, groups[c], &grp, &buf, &buf_len);
if (ret != 0) {
if (ret == ENOMEM || ret == ERANGE) {
ret = LDAP_OPERATIONS_ERROR;
} else {
ret = LDAP_NO_SUCH_OBJECT;
goto done;
}
if (grp_result == NULL) {
ret = LDAP_NO_SUCH_OBJECT;
goto done;
}
@ -542,7 +696,6 @@ static int handle_uid_request(enum request_types request_type, uid_t uid,
{
int ret;
struct passwd pwd;
struct passwd *pwd_result = NULL;
char *sid_str = NULL;
enum sss_id_type id_type;
size_t buf_len;
@ -568,13 +721,13 @@ static int handle_uid_request(enum request_types request_type, uid_t uid,
ret = pack_ber_sid(sid_str, berval);
} else {
ret = getpwuid_r(uid, &pwd, buf, buf_len, &pwd_result);
ret = getpwuid_r_wrapper(MAX_BUF, uid, &pwd, &buf, &buf_len);
if (ret != 0) {
if (ret == ENOMEM || ret == ERANGE) {
ret = LDAP_OPERATIONS_ERROR;
} else {
ret = LDAP_NO_SUCH_OBJECT;
goto done;
}
if (pwd_result == NULL) {
ret = LDAP_NO_SUCH_OBJECT;
goto done;
}
@ -610,7 +763,6 @@ static int handle_gid_request(enum request_types request_type, gid_t gid,
{
int ret;
struct group grp;
struct group *grp_result = NULL;
char *sid_str = NULL;
enum sss_id_type id_type;
size_t buf_len;
@ -635,13 +787,13 @@ static int handle_gid_request(enum request_types request_type, gid_t gid,
ret = pack_ber_sid(sid_str, berval);
} else {
ret = getgrgid_r(gid, &grp, buf, buf_len, &grp_result);
ret = getgrgid_r_wrapper(MAX_BUF, gid, &grp, &buf, &buf_len);
if (ret != 0) {
if (ret == ENOMEM || ret == ERANGE) {
ret = LDAP_OPERATIONS_ERROR;
} else {
ret = LDAP_NO_SUCH_OBJECT;
goto done;
}
if (grp_result == NULL) {
ret = LDAP_NO_SUCH_OBJECT;
goto done;
}
@ -676,9 +828,7 @@ static int handle_sid_request(enum request_types request_type, const char *sid,
{
int ret;
struct passwd pwd;
struct passwd *pwd_result = NULL;
struct group grp;
struct group *grp_result = NULL;
char *domain_name = NULL;
char *fq_name = NULL;
char *object_name = NULL;
@ -724,14 +874,13 @@ static int handle_sid_request(enum request_types request_type, const char *sid,
switch(id_type) {
case SSS_ID_TYPE_UID:
case SSS_ID_TYPE_BOTH:
ret = getpwnam_r(fq_name, &pwd, buf, buf_len, &pwd_result);
ret = getpwnam_r_wrapper(MAX_BUF, fq_name, &pwd, &buf, &buf_len);
if (ret != 0) {
if (ret == ENOMEM || ret == ERANGE) {
ret = LDAP_OPERATIONS_ERROR;
} else {
ret = LDAP_NO_SUCH_OBJECT;
goto done;
}
if (pwd_result == NULL) {
ret = LDAP_NO_SUCH_OBJECT;
goto done;
}
@ -755,14 +904,13 @@ static int handle_sid_request(enum request_types request_type, const char *sid,
pwd.pw_shell, kv_list, berval);
break;
case SSS_ID_TYPE_GID:
ret = getgrnam_r(fq_name, &grp, buf, buf_len, &grp_result);
ret = getgrnam_r_wrapper(MAX_BUF, fq_name, &grp, &buf, &buf_len);
if (ret != 0) {
if (ret == ENOMEM || ret == ERANGE) {
ret = LDAP_OPERATIONS_ERROR;
} else {
ret = LDAP_NO_SUCH_OBJECT;
goto done;
}
if (grp_result == NULL) {
ret = LDAP_NO_SUCH_OBJECT;
goto done;
}
@ -806,9 +954,7 @@ static int handle_name_request(enum request_types request_type,
int ret;
char *fq_name = NULL;
struct passwd pwd;
struct passwd *pwd_result = NULL;
struct group grp;
struct group *grp_result = NULL;
char *sid_str = NULL;
enum sss_id_type id_type;
size_t buf_len;
@ -842,15 +988,8 @@ static int handle_name_request(enum request_types request_type,
goto done;
}
ret = getpwnam_r(fq_name, &pwd, buf, buf_len, &pwd_result);
if (ret != 0) {
/* according to the man page there are a couple of error codes
* which can indicate that the user was not found. To be on the
* safe side we fail back to the group lookup on all errors. */
pwd_result = NULL;
}
if (pwd_result != NULL) {
ret = getpwnam_r_wrapper(MAX_BUF, fq_name, &pwd, &buf, &buf_len);
if (ret == 0) {
if (request_type == REQ_FULL_WITH_GROUPS) {
ret = sss_nss_getorigbyname(pwd.pw_name, &kv_list, &id_type);
if (ret != 0 || !(id_type == SSS_ID_TYPE_UID
@ -868,15 +1007,21 @@ static int handle_name_request(enum request_types request_type,
domain_name, pwd.pw_name, pwd.pw_uid,
pwd.pw_gid, pwd.pw_gecos, pwd.pw_dir,
pwd.pw_shell, kv_list, berval);
} else { /* no user entry found */
ret = getgrnam_r(fq_name, &grp, buf, buf_len, &grp_result);
if (ret != 0) {
ret = LDAP_NO_SUCH_OBJECT;
} else if (ret == ENOMEM || ret == ERANGE) {
ret = LDAP_OPERATIONS_ERROR;
goto done;
}
if (grp_result == NULL) {
} else { /* no user entry found */
/* according to the getpwnam() man page there are a couple of
* error codes which can indicate that the user was not found. To
* be on the safe side we fail back to the group lookup on all
* errors. */
ret = getgrnam_r_wrapper(MAX_BUF, fq_name, &grp, &buf, &buf_len);
if (ret != 0) {
if (ret == ENOMEM || ret == ERANGE) {
ret = LDAP_OPERATIONS_ERROR;
} else {
ret = LDAP_NO_SUCH_OBJECT;
}
goto done;
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,3 @@
export LD_PRELOAD=$(pkg-config --libs nss_wrapper)
export NSS_WRAPPER_PASSWD=./test_data/passwd
export NSS_WRAPPER_GROUP=./test_data/group