subids: support nsswitch
Closes #154 When starting any operation to do with subuid delegation, check nsswitch for a module to use. If none is specified, then use the traditional /etc/subuid and /etc/subgid files. Currently only one module is supported, and there is no fallback to the files on errors. Several possibilities could be considered: 1. in case of connection error, fall back to files 2. in case of unknown user, also fall back to files etc... When non-files nss module is used, functions to edit the range are not supported. It may make sense to support it, but it also may make sense to require another tool to be used. libsubordinateio also uses the nss_ helpers. This is how for instance lxc could easily be converted to supporting nsswitch. Add a set of test cases, including a dummy libsubid_zzz module. This hardcodes values such that: 'ubuntu' gets 200000 - 300000 'user1' gets 100000 - 165536 'error' emulates an nss module error 'unknown' emulates a user unknown to the nss module 'conn' emulates a connection error ot the nss module Changes to libsubid: Change the list_owner_ranges api: return a count instead of making the array null terminated. This is a breaking change, so bump the libsubid abi major number. Rename free_subuid_range and free_subgid_range to ungrant_subuid_range, because otherwise it's confusing with free_subid_ranges which frees memory. Run libsubid tests in jenkins Switch argument order in find_subid_owners Move the db locking into subordinateio.c Signed-off-by: Serge Hallyn <serge@hallyn.com>
This commit is contained in:
12
tests/libsubid/04_nss/Makefile
Normal file
12
tests/libsubid/04_nss/Makefile
Normal file
@@ -0,0 +1,12 @@
|
||||
all: test_nss libsubid_zzz.so
|
||||
|
||||
test_nss: test_nss.c ../../../lib/nss.c
|
||||
gcc -c -I../../../lib/ -I../../.. -o test_nss.o test_nss.c
|
||||
gcc -o test_nss test_nss.o ../../../libmisc/.libs/libmisc.a ../../../lib/.libs/libshadow.a -ldl
|
||||
|
||||
libsubid_zzz.so: libsubid_zzz.c
|
||||
gcc -c -I../../../lib/ -I../../.. -I../../../libmisc -I../../../libsubid libsubid_zzz.c
|
||||
gcc -L../../../libsubid -shared -o libsubid_zzz.so libsubid_zzz.o ../../../lib/.libs/libshadow.a -ldl
|
||||
|
||||
clean:
|
||||
rm -f *.o *.so test_nss
|
0
tests/libsubid/04_nss/empty
Normal file
0
tests/libsubid/04_nss/empty
Normal file
146
tests/libsubid/04_nss/libsubid_zzz.c
Normal file
146
tests/libsubid/04_nss/libsubid_zzz.c
Normal file
@@ -0,0 +1,146 @@
|
||||
#include <sys/types.h>
|
||||
#include <pwd.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <subid.h>
|
||||
#include <string.h>
|
||||
|
||||
enum subid_status shadow_subid_has_any_range(const char *owner, enum subid_type t, bool *result)
|
||||
{
|
||||
if (strcmp(owner, "ubuntu") == 0) {
|
||||
*result = true;
|
||||
return SUBID_STATUS_SUCCESS;
|
||||
}
|
||||
if (strcmp(owner, "error") == 0) {
|
||||
*result = false;
|
||||
return SUBID_STATUS_ERROR;
|
||||
}
|
||||
if (strcmp(owner, "unknown") == 0) {
|
||||
*result = false;
|
||||
return SUBID_STATUS_UNKNOWN_USER;
|
||||
}
|
||||
if (strcmp(owner, "conn") == 0) {
|
||||
*result = false;
|
||||
return SUBID_STATUS_ERROR_CONN;
|
||||
}
|
||||
if (t == ID_TYPE_UID) {
|
||||
*result = strcmp(owner, "user1") == 0;
|
||||
return SUBID_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
*result = strcmp(owner, "group1") == 0;
|
||||
return SUBID_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
enum subid_status shadow_subid_has_range(const char *owner, unsigned long start, unsigned long count, enum subid_type t, bool *result)
|
||||
{
|
||||
if (strcmp(owner, "ubuntu") == 0 &&
|
||||
start >= 200000 &&
|
||||
count <= 100000) {
|
||||
*result = true;
|
||||
return SUBID_STATUS_SUCCESS;
|
||||
}
|
||||
*result = false;
|
||||
if (strcmp(owner, "error") == 0)
|
||||
return SUBID_STATUS_ERROR;
|
||||
if (strcmp(owner, "unknown") == 0)
|
||||
return SUBID_STATUS_UNKNOWN_USER;
|
||||
if (strcmp(owner, "conn") == 0)
|
||||
return SUBID_STATUS_ERROR_CONN;
|
||||
|
||||
if (t == ID_TYPE_UID && strcmp(owner, "user1") != 0)
|
||||
return SUBID_STATUS_SUCCESS;
|
||||
if (t == ID_TYPE_GID && strcmp(owner, "group1") != 0)
|
||||
return SUBID_STATUS_SUCCESS;
|
||||
|
||||
if (start < 100000)
|
||||
return SUBID_STATUS_SUCCESS;
|
||||
if (count >= 65536)
|
||||
return SUBID_STATUS_SUCCESS;
|
||||
*result = true;
|
||||
return SUBID_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// So if 'user1' or 'ubuntu' is defined in passwd, we'll return those values,
|
||||
// to ease manual testing. For automated testing, if you return those values,
|
||||
// we'll return 1000 for ubuntu and 1001 otherwise.
|
||||
static uid_t getnamuid(const char *name) {
|
||||
struct passwd *pw;
|
||||
|
||||
pw = getpwnam(name);
|
||||
if (pw)
|
||||
return pw->pw_uid;
|
||||
|
||||
// For testing purposes
|
||||
return strcmp(name, "ubuntu") == 0 ? (uid_t)1000 : (uid_t)1001;
|
||||
}
|
||||
|
||||
static int alloc_uid(uid_t **uids, uid_t id) {
|
||||
*uids = malloc(sizeof(uid_t));
|
||||
if (!*uids)
|
||||
return -1;
|
||||
*uids[0] = id;
|
||||
return 1;
|
||||
}
|
||||
|
||||
enum subid_status shadow_subid_find_subid_owners(unsigned long id, enum subid_type id_type, uid_t **uids, int *count)
|
||||
{
|
||||
if (id >= 100000 && id < 165536) {
|
||||
*count = alloc_uid(uids, getnamuid("user1"));
|
||||
if (*count == 1)
|
||||
return SUBID_STATUS_SUCCESS;
|
||||
return SUBID_STATUS_ERROR; // out of memory
|
||||
}
|
||||
if (id >= 200000 && id < 300000) {
|
||||
*count = alloc_uid(uids, getnamuid("ubuntu"));
|
||||
if (*count == 1)
|
||||
return SUBID_STATUS_SUCCESS;
|
||||
return SUBID_STATUS_ERROR; // out of memory
|
||||
}
|
||||
*count = 0; // nothing found
|
||||
return SUBID_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
enum subid_status shadow_subid_list_owner_ranges(const char *owner, enum subid_type id_type, struct subordinate_range ***in_ranges, int *count)
|
||||
{
|
||||
struct subordinate_range **ranges;
|
||||
|
||||
*count = 0;
|
||||
if (strcmp(owner, "error") == 0)
|
||||
return SUBID_STATUS_ERROR;
|
||||
if (strcmp(owner, "unknown") == 0)
|
||||
return SUBID_STATUS_UNKNOWN_USER;
|
||||
if (strcmp(owner, "conn") == 0)
|
||||
return SUBID_STATUS_ERROR_CONN;
|
||||
|
||||
*ranges = NULL;
|
||||
if (strcmp(owner, "user1") != 0 && strcmp(owner, "ubuntu") != 0 &&
|
||||
strcmp(owner, "group1") != 0)
|
||||
return SUBID_STATUS_SUCCESS;
|
||||
if (id_type == ID_TYPE_GID && strcmp(owner, "user1") == 0)
|
||||
return SUBID_STATUS_SUCCESS;
|
||||
if (id_type == ID_TYPE_UID && strcmp(owner, "group1") == 0)
|
||||
return SUBID_STATUS_SUCCESS;
|
||||
ranges = (struct subordinate_range **)malloc(sizeof(struct subordinate_range *));
|
||||
if (!*ranges)
|
||||
return SUBID_STATUS_ERROR;
|
||||
ranges[0] = (struct subordinate_range *)malloc(sizeof(struct subordinate_range));
|
||||
if (!ranges[0]) {
|
||||
free(*ranges);
|
||||
*ranges = NULL;
|
||||
return SUBID_STATUS_ERROR;
|
||||
}
|
||||
ranges[0]->owner = strdup(owner);
|
||||
if (strcmp(owner, "user1") == 0 || strcmp(owner, "group1") == 0) {
|
||||
ranges[0]->start = 100000;
|
||||
ranges[0]->count = 65536;
|
||||
} else {
|
||||
ranges[0]->start = 200000;
|
||||
ranges[0]->count = 100000;
|
||||
}
|
||||
|
||||
*count = 1;
|
||||
*in_ranges = ranges;
|
||||
|
||||
return SUBID_STATUS_SUCCESS;
|
||||
}
|
20
tests/libsubid/04_nss/nsswitch1.conf
Normal file
20
tests/libsubid/04_nss/nsswitch1.conf
Normal file
@@ -0,0 +1,20 @@
|
||||
# /etc/nsswitch.conf
|
||||
#
|
||||
# Example configuration of GNU Name Service Switch functionality.
|
||||
# If you have the `glibc-doc-reference' and `info' packages installed, try:
|
||||
# `info libc "Name Service Switch"' for information about this file.
|
||||
|
||||
passwd: files systemd
|
||||
group: files systemd
|
||||
shadow: files
|
||||
gshadow: files
|
||||
|
||||
hosts: files mdns4_minimal [NOTFOUND=return] dns
|
||||
networks: files
|
||||
|
||||
protocols: db files
|
||||
services: db files
|
||||
ethers: db files
|
||||
rpc: db files
|
||||
|
||||
netgroup: nis
|
22
tests/libsubid/04_nss/nsswitch2.conf
Normal file
22
tests/libsubid/04_nss/nsswitch2.conf
Normal file
@@ -0,0 +1,22 @@
|
||||
# /etc/nsswitch.conf
|
||||
#
|
||||
# Example configuration of GNU Name Service Switch functionality.
|
||||
# If you have the `glibc-doc-reference' and `info' packages installed, try:
|
||||
# `info libc "Name Service Switch"' for information about this file.
|
||||
|
||||
passwd: files systemd
|
||||
group: files systemd
|
||||
shadow: files
|
||||
gshadow: files
|
||||
|
||||
hosts: files mdns4_minimal [NOTFOUND=return] dns
|
||||
networks: files
|
||||
|
||||
protocols: db files
|
||||
services: db files
|
||||
ethers: db files
|
||||
rpc: db files
|
||||
|
||||
netgroup: nis
|
||||
|
||||
subid: files
|
22
tests/libsubid/04_nss/nsswitch3.conf
Normal file
22
tests/libsubid/04_nss/nsswitch3.conf
Normal file
@@ -0,0 +1,22 @@
|
||||
# /etc/nsswitch.conf
|
||||
#
|
||||
# Example configuration of GNU Name Service Switch functionality.
|
||||
# If you have the `glibc-doc-reference' and `info' packages installed, try:
|
||||
# `info libc "Name Service Switch"' for information about this file.
|
||||
|
||||
passwd: files systemd
|
||||
group: files systemd
|
||||
shadow: files
|
||||
gshadow: files
|
||||
|
||||
hosts: files mdns4_minimal [NOTFOUND=return] dns
|
||||
networks: files
|
||||
|
||||
protocols: db files
|
||||
services: db files
|
||||
ethers: db files
|
||||
rpc: db files
|
||||
|
||||
netgroup: nis
|
||||
|
||||
subid: zzz
|
22
tests/libsubid/04_nss/subidnss.test
Executable file
22
tests/libsubid/04_nss/subidnss.test
Executable file
@@ -0,0 +1,22 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
cd $(dirname $0)
|
||||
|
||||
. ../../common/config.sh
|
||||
. ../../common/log.sh
|
||||
|
||||
make
|
||||
|
||||
export LD_LIBRARY_PATH=.:../../../lib/.libs:$LD_LIBRARY_PATH
|
||||
|
||||
./test_nss 1
|
||||
./test_nss 2
|
||||
./test_nss 3
|
||||
|
||||
unshare -Urm ./test_range
|
||||
|
||||
log_status "$0" "SUCCESS"
|
||||
|
||||
trap '' 0
|
72
tests/libsubid/04_nss/test_nss.c
Normal file
72
tests/libsubid/04_nss/test_nss.c
Normal file
@@ -0,0 +1,72 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <prototypes.h>
|
||||
#include <stdbool.h>
|
||||
#include <dlfcn.h>
|
||||
|
||||
extern bool nss_is_initialized();
|
||||
extern struct subid_nss_ops *get_subid_nss_handle();
|
||||
|
||||
void test1() {
|
||||
// nsswitch1 has no subid: entry
|
||||
setenv("LD_LIBRARY_PATH", ".", 1);
|
||||
printf("Test with no subid entry\n");
|
||||
nss_init("./nsswitch1.conf");
|
||||
if (!nss_is_initialized() || get_subid_nss_handle())
|
||||
exit(1);
|
||||
// second run should change nothing
|
||||
printf("Test with no subid entry, second run\n");
|
||||
nss_init("./nsswitch1.conf");
|
||||
if (!nss_is_initialized() || get_subid_nss_handle())
|
||||
exit(1);
|
||||
}
|
||||
|
||||
void test2() {
|
||||
// nsswitch2 has a subid: files entry
|
||||
printf("test with 'files' subid entry\n");
|
||||
nss_init("./nsswitch2.conf");
|
||||
if (!nss_is_initialized() || get_subid_nss_handle())
|
||||
exit(1);
|
||||
// second run should change nothing
|
||||
printf("test with 'files' subid entry, second run\n");
|
||||
nss_init("./nsswitch2.conf");
|
||||
if (!nss_is_initialized() || get_subid_nss_handle())
|
||||
exit(1);
|
||||
}
|
||||
|
||||
void test3() {
|
||||
// nsswitch3 has a subid: testnss entry
|
||||
printf("test with 'test' subid entry\n");
|
||||
nss_init("./nsswitch3.conf");
|
||||
if (!nss_is_initialized() || !get_subid_nss_handle())
|
||||
exit(1);
|
||||
// second run should change nothing
|
||||
printf("test with 'test' subid entry, second run\n");
|
||||
nss_init("./nsswitch3.conf");
|
||||
if (!nss_is_initialized() || !get_subid_nss_handle())
|
||||
exit(1);
|
||||
}
|
||||
|
||||
const char *Prog;
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int which;
|
||||
|
||||
Prog = Basename(argv[0]);
|
||||
|
||||
if (argc < 1)
|
||||
exit(1);
|
||||
|
||||
which = atoi(argv[1]);
|
||||
switch(which) {
|
||||
case 1: test1(); break;
|
||||
case 2: test2(); break;
|
||||
case 3: test3(); break;
|
||||
default: exit(1);
|
||||
}
|
||||
|
||||
printf("nss parsing tests done\n");
|
||||
exit(0);
|
||||
}
|
50
tests/libsubid/04_nss/test_range
Executable file
50
tests/libsubid/04_nss/test_range
Executable file
@@ -0,0 +1,50 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -x
|
||||
|
||||
echo "starting check_range tests"
|
||||
|
||||
export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH
|
||||
mount --bind ./nsswitch3.conf /etc/nsswitch.conf
|
||||
cleanup1() {
|
||||
umount /etc/nsswitch.conf
|
||||
}
|
||||
trap cleanup1 EXIT HUP INT TERM
|
||||
../../../src/check_subid_range user1 u 100000 65535
|
||||
if [ $? -ne 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
../../../src/check_subid_range user2 u 100000 65535
|
||||
if [ $? -eq 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
../../../src/check_subid_range unknown u 100000 65535
|
||||
if [ $? -eq 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
../../../src/check_subid_range error u 100000 65535
|
||||
if [ $? -eq 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
../../../src/check_subid_range user1 u 1000 65535
|
||||
if [ $? -eq 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
umount /etc/nsswitch.conf
|
||||
|
||||
mount --bind ./nsswitch1.conf /etc/nsswitch.conf
|
||||
mount --bind ./empty /etc/subuid
|
||||
|
||||
cleanup2() {
|
||||
umount /etc/subuid
|
||||
umount /etc/nsswitch.conf
|
||||
}
|
||||
trap cleanup2 EXIT HUP INT TERM
|
||||
../../../src/check_subid_range user1 u 100000 65535
|
||||
if [ $? -eq 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "check_range tests complete"
|
||||
exit 0
|
@@ -127,6 +127,7 @@ run_test ./newuidmap/01_newuidmap/newuidmap.test
|
||||
run_test ./newuidmap/02_newuidmap_relaxed_gid_check/newuidmap.test
|
||||
run_test ./newgidmap/01_newgidmap/newgidmap.test
|
||||
run_test ./newgidmap/02_newgidmap_relaxed_gid_check/newgidmap.test
|
||||
run_test ./libsubid/04_nss/subidnss.test
|
||||
|
||||
echo
|
||||
echo "$succeeded test(s) passed"
|
||||
|
Reference in New Issue
Block a user