Merge pull request #250 from hallyn/libsubid

[strawman] Implement libsubid
This commit is contained in:
Serge Hallyn 2020-06-08 00:10:16 -05:00 committed by GitHub
commit b01bd3b139
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
33 changed files with 1120 additions and 24 deletions

View File

@ -15,7 +15,7 @@ arch:
before_install:
- sudo apt-get update -qq
- sudo apt-get -y install -qq automake autopoint xsltproc libselinux1-dev gettext
- sudo apt-get -y install -qq automake autopoint xsltproc libselinux1-dev gettext expect
script:
- ./autogen.sh --without-selinux --disable-man
- grep ENABLE_ config.status
@ -38,4 +38,14 @@ addons:
build_command: "make -j4"
branch_pattern: master
script:
- cat /proc/self/uid_map
- cat /proc/self/status
- systemd-detect-virt
- ./autogen.sh --without-selinux --disable-man
- grep ENABLE_ config.status
- make
- sudo make install
- (cd tests; sudo ./run_some; cat testsuite.log)
# vim:et:ts=2:sw=2

View File

@ -2,5 +2,14 @@
EXTRA_DIST = NEWS README TODO shadow.spec.in
SUBDIRS = po man libmisc lib src \
contrib doc etc
SUBDIRS = libmisc lib
if ENABLE_SUBIDS
SUBDIRS += libsubid
endif
SUBDIRS += src po contrib doc etc
if ENABLE_REGENERATE_MAN
SUBDIRS += man
endif

View File

@ -6,7 +6,7 @@ autoreconf -v -f --install || exit 1
CFLAGS="-O2 -Wall" \
--enable-man \
--enable-maintainer-mode \
--disable-shared \
--enable-shared \
--without-libpam \
--with-selinux \
"$@"

View File

@ -1,19 +1,29 @@
dnl Process this file with autoconf to produce a configure script.
AC_PREREQ([2.64])
AC_PREREQ([2.69])
m4_define([libsubid_abi_major], 1)
m4_define([libsubid_abi_minor], 0)
m4_define([libsubid_abi_micro], 0)
m4_define([libsubid_abi], [libsubid_abi_major.libsubid_abi_minor.libsubid_abi_micro])
AC_INIT([shadow], [4.8.1], [pkg-shadow-devel@lists.alioth.debian.org], [],
[https://github.com/shadow-maint/shadow])
AM_INIT_AUTOMAKE([1.11 foreign dist-xz])
AC_CONFIG_MACRO_DIRS([m4])
AM_SILENT_RULES([yes])
AC_CONFIG_HEADERS([config.h])
AC_SUBST([LIBSUBID_ABI_MAJOR], [libsubid_abi_major])
AC_SUBST([LIBSUBID_ABI_MINOR], [libsubid_abi_minor])
AC_SUBST([LIBSUBID_ABI_MICRO], [libsubid_abi_micro])
AC_SUBST([LIBSUBID_ABI], [libsubid_abi])
dnl Some hacks...
test "$prefix" = "NONE" && prefix="/usr"
test "$prefix" = "/usr" && exec_prefix=""
AC_GNU_SOURCE
AM_DISABLE_SHARED
AM_ENABLE_STATIC
AM_ENABLE_SHARED
AM_MAINTAINER_MODE
@ -715,6 +725,7 @@ AC_CONFIG_FILES([
man/zh_TW/Makefile
libmisc/Makefile
lib/Makefile
libsubid/Makefile
src/Makefile
contrib/Makefile
etc/Makefile

View File

@ -5,7 +5,6 @@ DEFS =
noinst_LTLIBRARIES = libshadow.la
libshadow_la_LDFLAGS = -version-info 0:0:0
libshadow_la_CPPFLAGS = $(ECONF_CPPFLAGS)
if HAVE_VENDORDIR
libshadow_la_CPPFLAGS += -DVENDORDIR=\"$(VENDORDIR)\"

View File

@ -13,14 +13,7 @@
#include "subordinateio.h"
#include <sys/types.h>
#include <pwd.h>
struct subordinate_range {
const char *owner;
unsigned long start;
unsigned long count;
};
#define NFIELDS 3
#include <ctype.h>
/*
* subordinate_dup: create a duplicate range
@ -78,7 +71,7 @@ static void *subordinate_parse (const char *line)
static char rangebuf[1024];
int i;
char *cp;
char *fields[NFIELDS];
char *fields[SUBID_NFIELDS];
/*
* Copy the string to a temporary buffer so the substrings can
@ -93,7 +86,7 @@ static void *subordinate_parse (const char *line)
* field. The fields are converted into NUL terminated strings.
*/
for (cp = rangebuf, i = 0; (i < NFIELDS) && (NULL != cp); i++) {
for (cp = rangebuf, i = 0; (i < SUBID_NFIELDS) && (NULL != cp); i++) {
fields[i] = cp;
while (('\0' != *cp) && (':' != *cp)) {
cp++;
@ -108,10 +101,10 @@ static void *subordinate_parse (const char *line)
}
/*
* There must be exactly NFIELDS colon separated fields or
* There must be exactly SUBID_NFIELDS colon separated fields or
* the entry is invalid. Also, fields must be non-blank.
*/
if (i != NFIELDS || *fields[0] == '\0' || *fields[1] == '\0' || *fields[2] == '\0')
if (i != SUBID_NFIELDS || *fields[0] == '\0' || *fields[1] == '\0' || *fields[2] == '\0')
return NULL;
range.owner = fields[0];
if (getulong (fields[1], &range.start) == 0)
@ -314,6 +307,39 @@ static bool have_range(struct commonio_db *db,
return false;
}
static bool append_range(struct subordinate_range ***ranges, const struct subordinate_range *new, int n)
{
struct subordinate_range *tmp;
if (!*ranges) {
*ranges = malloc(2 * sizeof(struct subordinate_range **));
if (!*ranges)
return false;
} else {
struct subordinate_range **new;
new = realloc(*ranges, (n + 2) * (sizeof(struct subordinate_range **)));
if (!new)
return false;
*ranges = new;
}
(*ranges)[n] = (*ranges)[n+1] = NULL;
tmp = subordinate_dup(new);
if (!tmp)
return false;
(*ranges)[n] = tmp;
return true;
}
void free_subordinate_ranges(struct subordinate_range **ranges)
{
int i;
if (!ranges)
return;
for (i = 0; ranges[i]; i++)
subordinate_free(ranges[i]);
free(ranges);
}
/*
* subordinate_range_cmp: compare uid ranges
*
@ -692,6 +718,160 @@ gid_t sub_gid_find_free_range(gid_t min, gid_t max, unsigned long count)
start = find_free_range (&subordinate_gid_db, min, max, count);
return start == ULONG_MAX ? (gid_t) -1 : start;
}
/*
struct subordinate_range **list_owner_ranges(const char *owner, enum subid_type id_type)
*
* @owner: username
* @id_type: UID or GUID
*
* Returns the subuid or subgid ranges which are owned by the specified
* user. Username may be a username or a string representation of a
* UID number. If id_type is UID, then subuids are returned, else
* subgids are returned. If there is an error, < 0 is returned.
*
* The caller must free the subordinate range list.
*/
struct subordinate_range **list_owner_ranges(const char *owner, enum subid_type id_type)
{
// TODO - need to handle owner being either uid or username
const struct subordinate_range *range;
struct subordinate_range **ranges = NULL;
struct commonio_db *db;
int size = 0;
if (id_type == ID_TYPE_UID)
db = &subordinate_uid_db;
else
db = &subordinate_gid_db;
commonio_rewind(db);
while ((range = commonio_next(db)) != NULL) {
if (0 == strcmp(range->owner, owner)) {
if (!append_range(&ranges, range, size++)) {
free_subordinate_ranges(ranges);
return NULL;
}
}
}
return ranges;
}
static bool all_digits(const char *str)
{
int i;
for (i = 0; str[i] != '\0'; i++)
if (!isdigit(str[i]))
return false;
return true;
}
static int append_uids(uid_t **uids, const char *owner, int n)
{
uid_t owner_uid;
uid_t *ret;
int i;
if (all_digits(owner)) {
i = sscanf(owner, "%d", &owner_uid);
if (i != 1) {
// should not happen
free(*uids);
*uids = NULL;
return -1;
}
} else {
struct passwd *pwd = getpwnam(owner);
if (NULL == pwd) {
/* Username not defined in /etc/passwd, or error occured during lookup */
free(*uids);
*uids = NULL;
return -1;
}
owner_uid = pwd->pw_uid;
}
for (i = 0; i < n; i++) {
if (owner_uid == (*uids)[i])
return n;
}
ret = realloc(*uids, (n + 1) * sizeof(uid_t));
if (!ret) {
free(*uids);
return -1;
}
ret[n] = owner_uid;
*uids = ret;
return n+1;
}
int find_subid_owners(unsigned long id, uid_t **uids, enum subid_type id_type)
{
const struct subordinate_range *range;
struct commonio_db *db;
int n = 0;
*uids = NULL;
if (id_type == ID_TYPE_UID)
db = &subordinate_uid_db;
else
db = &subordinate_gid_db;
commonio_rewind(db);
while ((range = commonio_next(db)) != NULL) {
if (id >= range->start && id < range->start + range-> count) {
n = append_uids(uids, range->owner, n);
if (n < 0)
break;
}
}
return n;
}
bool new_subid_range(struct subordinate_range *range, enum subid_type id_type, bool reuse)
{
struct commonio_db *db;
const struct subordinate_range *r;
if (id_type == ID_TYPE_UID)
db = &subordinate_uid_db;
else
db = &subordinate_gid_db;
commonio_rewind(db);
if (reuse) {
while ((r = commonio_next(db)) != NULL) {
// TODO account for username vs uid_t
if (0 != strcmp(r->owner, range->owner))
continue;
if (r->count >= range->count) {
range->count = r->count;
range->start = r->start;
return true;
}
}
}
range->start = find_free_range(db, range->start, ULONG_MAX, range->count);
if (range->start == ULONG_MAX)
return false;
return add_range(db, range->owner, range->start, range->count) == 1;
}
bool release_subid_range(struct subordinate_range *range, enum subid_type id_type)
{
struct commonio_db *db;
if (id_type == ID_TYPE_UID)
db = &subordinate_uid_db;
else
db = &subordinate_gid_db;
return remove_range(db, range->owner, range->start, range->count) == 1;
}
#else /* !ENABLE_SUBIDS */
extern int errno; /* warning: ANSI C forbids an empty source file */
#endif /* !ENABLE_SUBIDS */

View File

@ -11,6 +11,8 @@
#include <sys/types.h>
#include "../libsubid/subid.h"
extern int sub_uid_close(void);
extern bool have_sub_uids(const char *owner, uid_t start, unsigned long count);
extern bool sub_uid_file_present (void);
@ -23,6 +25,11 @@ extern int sub_uid_unlock (void);
extern int sub_uid_add (const char *owner, uid_t start, unsigned long count);
extern int sub_uid_remove (const char *owner, uid_t start, unsigned long count);
extern uid_t sub_uid_find_free_range(uid_t min, uid_t max, unsigned long count);
extern struct subordinate_range **list_owner_ranges(const char *owner, enum subid_type id_type);
extern bool new_subid_range(struct subordinate_range *range, enum subid_type id_type, bool reuse);
extern bool release_subid_range(struct subordinate_range *range, enum subid_type id_type);
extern int find_subid_owners(unsigned long id, uid_t **uids, enum subid_type id_type);
extern void free_subordinate_ranges(struct subordinate_range **ranges);
extern int sub_gid_close(void);
extern bool have_sub_gids(const char *owner, gid_t start, unsigned long count);

25
libsubid/Makefile.am Normal file
View File

@ -0,0 +1,25 @@
lib_LTLIBRARIES = libsubid.la
libsubid_la_LDFLAGS = -Wl,-soname,libsubid.so.@LIBSUBID_ABI@ \
-shared -version-info @LIBSUBID_ABI_MAJOR@
libsubid_la_SOURCES = api.c
MISCLIBS = \
$(LIBAUDIT) \
$(LIBSELINUX) \
$(LIBSEMANAGE) \
$(LIBCRYPT_NOPAM) \
$(LIBSKEY) \
$(LIBMD) \
$(LIBECONF) \
$(LIBCRYPT) \
$(LIBTCB)
libsubid_la_LIBADD = \
$(top_srcdir)/lib/libshadow.la \
$(top_srcdir)/libmisc/libmisc.a \
$(MISCLIBS)
AM_CPPFLAGS = \
-I${top_srcdir}/lib \
-I${top_srcdir}/libmisc \
-DLOCALEDIR=\"$(datadir)/locale\"

231
libsubid/api.c Normal file
View File

@ -0,0 +1,231 @@
/*
* Copyright (c) 2020 Serge Hallyn
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the copyright holders or contributors may not be used to
* endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <config.h>
#include <fcntl.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <pwd.h>
#include <stdbool.h>
#include "subordinateio.h"
#include "idmapping.h"
#include "api.h"
static struct subordinate_range **get_subid_ranges(const char *owner, enum subid_type id_type)
{
struct subordinate_range **ranges = NULL;
switch (id_type) {
case ID_TYPE_UID:
if (!sub_uid_open(O_RDONLY)) {
return NULL;
}
break;
case ID_TYPE_GID:
if (!sub_gid_open(O_RDONLY)) {
return NULL;
}
break;
default:
return NULL;
}
ranges = list_owner_ranges(owner, id_type);
if (id_type == ID_TYPE_UID)
sub_uid_close();
else
sub_gid_close();
return ranges;
}
struct subordinate_range **get_subuid_ranges(const char *owner)
{
return get_subid_ranges(owner, ID_TYPE_UID);
}
struct subordinate_range **get_subgid_ranges(const char *owner)
{
return get_subid_ranges(owner, ID_TYPE_GID);
}
void subid_free_ranges(struct subordinate_range **ranges)
{
return free_subordinate_ranges(ranges);
}
int get_subid_owner(unsigned long id, uid_t **owner, enum subid_type id_type)
{
int ret = -1;
switch (id_type) {
case ID_TYPE_UID:
if (!sub_uid_open(O_RDONLY)) {
return -1;
}
break;
case ID_TYPE_GID:
if (!sub_gid_open(O_RDONLY)) {
return -1;
}
break;
default:
return -1;
}
ret = find_subid_owners(id, owner, id_type);
if (id_type == ID_TYPE_UID)
sub_uid_close();
else
sub_gid_close();
return ret;
}
int get_subuid_owners(uid_t uid, uid_t **owner)
{
return get_subid_owner((unsigned long)uid, owner, ID_TYPE_UID);
}
int get_subgid_owners(gid_t gid, uid_t **owner)
{
return get_subid_owner((unsigned long)gid, owner, ID_TYPE_GID);
}
bool grant_subid_range(struct subordinate_range *range, bool reuse,
enum subid_type id_type)
{
bool ret;
switch (id_type) {
case ID_TYPE_UID:
if (!sub_uid_lock()) {
printf("Failed loging subuids (errno %d)\n", errno);
return false;
}
if (!sub_uid_open(O_CREAT | O_RDWR)) {
printf("Failed opening subuids (errno %d)\n", errno);
sub_uid_unlock();
return false;
}
break;
case ID_TYPE_GID:
if (!sub_gid_lock()) {
printf("Failed loging subgids (errno %d)\n", errno);
return false;
}
if (!sub_gid_open(O_CREAT | O_RDWR)) {
printf("Failed opening subgids (errno %d)\n", errno);
sub_gid_unlock();
return false;
}
break;
default:
return false;
}
ret = new_subid_range(range, id_type, reuse);
if (id_type == ID_TYPE_UID) {
sub_uid_close();
sub_uid_unlock();
} else {
sub_gid_close();
sub_gid_unlock();
}
return ret;
}
bool grant_subuid_range(struct subordinate_range *range, bool reuse)
{
return grant_subid_range(range, reuse, ID_TYPE_UID);
}
bool grant_subgid_range(struct subordinate_range *range, bool reuse)
{
return grant_subid_range(range, reuse, ID_TYPE_GID);
}
bool free_subid_range(struct subordinate_range *range, enum subid_type id_type)
{
bool ret;
switch (id_type) {
case ID_TYPE_UID:
if (!sub_uid_lock()) {
printf("Failed loging subuids (errno %d)\n", errno);
return false;
}
if (!sub_uid_open(O_CREAT | O_RDWR)) {
printf("Failed opening subuids (errno %d)\n", errno);
sub_uid_unlock();
return false;
}
break;
case ID_TYPE_GID:
if (!sub_gid_lock()) {
printf("Failed loging subgids (errno %d)\n", errno);
return false;
}
if (!sub_gid_open(O_CREAT | O_RDWR)) {
printf("Failed opening subgids (errno %d)\n", errno);
sub_gid_unlock();
return false;
}
break;
default:
return false;
}
ret = release_subid_range(range, id_type);
if (id_type == ID_TYPE_UID) {
sub_uid_close();
sub_uid_unlock();
} else {
sub_gid_close();
sub_gid_unlock();
}
return ret;
}
bool free_subuid_range(struct subordinate_range *range)
{
return free_subid_range(range, ID_TYPE_UID);
}
bool free_subgid_range(struct subordinate_range *range)
{
return free_subid_range(range, ID_TYPE_GID);
}

17
libsubid/api.h Normal file
View File

@ -0,0 +1,17 @@
#include "subid.h"
#include <stdbool.h>
struct subordinate_range **get_subuid_ranges(const char *owner);
struct subordinate_range **get_subgid_ranges(const char *owner);
void subid_free_ranges(struct subordinate_range **ranges);
int get_subuid_owners(uid_t uid, uid_t **owner);
int get_subgid_owners(uid_t uid, uid_t **owner);
/* range should be pre-allocated with owner and count filled in, start is
* ignored, can be 0 */
bool grant_subuid_range(struct subordinate_range *range, bool reuse);
bool grant_subgid_range(struct subordinate_range *range, bool reuse);
bool free_subuid_range(struct subordinate_range *range);
bool free_subgid_range(struct subordinate_range *range);

17
libsubid/subid.h Normal file
View File

@ -0,0 +1,17 @@
#include <sys/types.h>
#ifndef SUBID_RANGE_DEFINED
#define SUBID_RANGE_DEFINED 1
struct subordinate_range {
const char *owner;
unsigned long start;
unsigned long count;
};
enum subid_type {
ID_TYPE_UID = 1,
ID_TYPE_GID = 2
};
#define SUBID_NFIELDS 3
#endif

4
src/.gitignore vendored
View File

@ -33,3 +33,7 @@
/userdel
/usermod
/vipw
/get_subid_owners
/list_subid_ranges
/new_subid_range
/free_subid_range

View File

@ -156,4 +156,64 @@ if FCAPS
setcap cap_setuid+ep $(DESTDIR)$(ubindir)/newuidmap
setcap cap_setgid+ep $(DESTDIR)$(ubindir)/newgidmap
endif
noinst_PROGRAMS += list_subid_ranges \
get_subid_owners \
new_subid_range \
free_subid_range
MISCLIBS = \
$(LIBAUDIT) \
$(LIBSELINUX) \
$(LIBSEMANAGE) \
$(LIBCRYPT_NOPAM) \
$(LIBSKEY) \
$(LIBMD) \
$(LIBECONF) \
$(LIBCRYPT) \
$(LIBTCB)
list_subid_ranges_LDADD = \
$(top_builddir)/lib/libshadow.la \
$(top_builddir)/libmisc/libmisc.a \
$(top_builddir)/libsubid/libsubid.la \
$(MISCLIBS)
list_subid_ranges_CPPFLAGS = \
-I$(top_srcdir)/lib \
-I$(top_srcdir)/libmisc \
-I$(top_srcdir)/libsubid
get_subid_owners_LDADD = \
$(top_builddir)/lib/libshadow.la \
$(top_builddir)/libmisc/libmisc.a \
$(top_builddir)/libsubid/libsubid.la \
$(MISCLIBS)
get_subid_owners_CPPFLAGS = \
-I$(top_srcdir)/lib \
-I$(top_srcdir)/libmisc \
-I$(top_srcdir)/libsubid
new_subid_range_CPPFLAGS = \
-I$(top_srcdir)/lib \
-I$(top_srcdir)/libmisc \
-I$(top_srcdir)/libsubid
new_subid_range_LDADD = \
$(top_builddir)/lib/libshadow.la \
$(top_builddir)/libmisc/libmisc.a \
$(top_builddir)/libsubid/libsubid.la \
$(MISCLIBS)
free_subid_range_CPPFLAGS = \
-I$(top_srcdir)/lib \
-I$(top_srcdir)/libmisc \
-I$(top_srcdir)/libsubid
free_subid_range_LDADD = \
$(top_builddir)/lib/libshadow.la \
$(top_builddir)/libmisc/libmisc.a \
$(top_builddir)/libsubid/libsubid.la \
$(MISCLIBS)
endif

50
src/free_subid_range.c Normal file
View File

@ -0,0 +1,50 @@
#include <stdio.h>
#include <unistd.h>
#include "api.h"
#include "stdlib.h"
#include "prototypes.h"
/* Test program for the subid freeing routine */
const char *Prog;
void usage(void)
{
fprintf(stderr, "Usage: %s [-g] user start count\n", Prog);
fprintf(stderr, " Release a user's subuid (or with -g, subgid) range\n");
exit(EXIT_FAILURE);
}
int main(int argc, char *argv[])
{
int c;
bool ok;
struct subordinate_range range;
bool group = false; // get subuids by default
Prog = Basename (argv[0]);
while ((c = getopt(argc, argv, "g")) != EOF) {
switch(c) {
case 'g': group = true; break;
default: usage();
}
}
argv = &argv[optind];
argc = argc - optind;
if (argc < 3)
usage();
range.owner = argv[0];
range.start = atoi(argv[1]);
range.count = atoi(argv[2]);
if (group)
ok = free_subgid_range(&range);
else
ok = free_subuid_range(&range);
if (!ok) {
fprintf(stderr, "Failed freeing id range\n");
exit(EXIT_FAILURE);
}
return 0;
}

40
src/get_subid_owners.c Normal file
View File

@ -0,0 +1,40 @@
#include <stdio.h>
#include "api.h"
#include "stdlib.h"
#include "prototypes.h"
const char *Prog;
void usage(void)
{
fprintf(stderr, "Usage: [-g] %s subuid\n", Prog);
fprintf(stderr, " list uids who own the given subuid\n");
fprintf(stderr, " pass -g to query a subgid\n");
exit(EXIT_FAILURE);
}
int main(int argc, char *argv[])
{
int i, n;
uid_t *uids;
Prog = Basename (argv[0]);
if (argc < 2) {
usage();
}
if (argc == 3 && strcmp(argv[1], "-g") == 0)
n = get_subgid_owners(atoi(argv[2]), &uids);
else if (argc == 2 && strcmp(argv[1], "-h") == 0)
usage();
else
n = get_subuid_owners(atoi(argv[1]), &uids);
if (n < 0) {
fprintf(stderr, "No owners found\n");
exit(1);
}
for (i = 0; i < n; i++) {
printf("%d\n", uids[i]);
}
free(uids);
return 0;
}

41
src/list_subid_ranges.c Normal file
View File

@ -0,0 +1,41 @@
#include <stdio.h>
#include "api.h"
#include "stdlib.h"
#include "prototypes.h"
const char *Prog;
void usage(void)
{
fprintf(stderr, "Usage: %s [-g] user\n", Prog);
fprintf(stderr, " list subuid ranges for user\n");
fprintf(stderr, " pass -g to list subgid ranges\n");
exit(EXIT_FAILURE);
}
int main(int argc, char *argv[])
{
int i;
struct subordinate_range **ranges;
Prog = Basename (argv[0]);
if (argc < 2) {
usage();
}
if (argc == 3 && strcmp(argv[1], "-g") == 0)
ranges = get_subgid_ranges(argv[2]);
else if (argc == 2 && strcmp(argv[1], "-h") == 0)
usage();
else
ranges = get_subuid_ranges(argv[1]);
if (!ranges) {
fprintf(stderr, "Error fetching ranges\n");
exit(1);
}
for (i = 0; ranges[i]; i++) {
printf("%d: %s %lu %lu\n", i, ranges[i]->owner,
ranges[i]->start, ranges[i]->count);
}
subid_free_ranges(ranges);
return 0;
}

57
src/new_subid_range.c Normal file
View File

@ -0,0 +1,57 @@
#include <stdio.h>
#include <unistd.h>
#include "api.h"
#include "stdlib.h"
#include "prototypes.h"
/* Test program for the subid creation routine */
const char *Prog;
void usage(void)
{
fprintf(stderr, "Usage: %s [-g] [-n] user count\n", Prog);
fprintf(stderr, " Find a subuid (or with -g, subgid) range for user\n");
fprintf(stderr, " If -n is given, a new range will be created even if one exists\n");
fprintf(stderr, " count defaults to 65536\n");
exit(EXIT_FAILURE);
}
int main(int argc, char *argv[])
{
int c;
struct subordinate_range range;
bool makenew = false; // reuse existing by default
bool group = false; // get subuids by default
bool ok;
Prog = Basename (argv[0]);
while ((c = getopt(argc, argv, "gn")) != EOF) {
switch(c) {
case 'n': makenew = true; break;
case 'g': group = true; break;
default: usage();
}
}
argv = &argv[optind];
argc = argc - optind;
if (argc == 0)
usage();
range.owner = argv[0];
range.start = 0;
range.count = 65536;
if (argc > 1)
range.count = atoi(argv[1]);
if (group)
ok = grant_subgid_range(&range, !makenew);
else
ok = grant_subuid_range(&range, !makenew);
if (!ok) {
fprintf(stderr, "Failed creating new id range\n");
exit(EXIT_FAILURE);
}
printf("Subuid range %lu:%lu\n", range.start, range.count);
return 0;
}

View File

@ -2,13 +2,13 @@
set -e
build_path=/root/build/shadow-4.1.5/
build_path=$(git rev-parse --show-toplevel)
# Save the configuration files in tmp.
save_config ()
{
[ ! -d tmp ] && mkdir tmp
find config -depth -path "*/.svn/*" -prune -o -type f -print | sed -e 's/config\///' |
find config -depth -type f -print | sed -e 's/config\///' |
while read file
do
mkdir -p "tmp/$(dirname "$file")"
@ -19,7 +19,7 @@ save_config ()
# Copy the config files from config to the system
change_config ()
{
find config -depth -path "*/.svn/*" -prune -o -type f -print | sed -e 's/config\///' |
find config -depth -type f -print | sed -e 's/config\///' |
while read file
do
cp -f "config/$file" "/$file"
@ -30,7 +30,7 @@ change_config ()
# The config files must be saved before with save_config ().
restore_config ()
{
find config -depth -path "*/.svn/*" -prune -o -type f -print | sed -e 's/config\///' |
find config -depth -type f -print | sed -e 's/config\///' |
while read file
do
if [ -f "tmp/$file" ]; then
@ -54,7 +54,6 @@ prepare_chroot ()
{
mkdir tmp/root
cp -rfdp config_chroot/* tmp/root/
find tmp/root/ -name .svn -type d -print0 | xargs -0 rm -rf
lists=/root/tests/common/config_chroot.list
[ -f config_chroot.list ] && lists="$lists config_chroot.list"

View File

View File

@ -0,0 +1,2 @@
foo:200000:10000
root:500000:1000

View File

@ -0,0 +1,3 @@
foo:300000:10000
foo:400000:10000
root:500000:1000

View File

@ -0,0 +1,38 @@
#!/bin/sh
set -e
cd $(dirname $0)
. ../../common/config.sh
. ../../common/log.sh
log_start "$0" "list_ranges shows subid ranges"
save_config
# restore the files on exit
trap 'log_status "$0" "FAILURE"; restore_config' 0
change_config
echo -n "list foo's ranges..."
${build_path}/src/list_subid_ranges foo > /tmp/subuidlistout
${build_path}/src/list_subid_ranges -g foo > /tmp/subgidlistout
echo "OK"
echo -n "Check the subuid ranges..."
[ $(wc -l /tmp/subuidlistout | awk '{ print $1 }') -eq 2 ]
grep "0: foo 300000 10000" /tmp/subuidlistout
grep "1: foo 400000 10000" /tmp/subuidlistout
echo "OK"
echo -n "Check the subgid ranges..."
[ $(wc -l /tmp/subgidlistout | awk '{ print $1 }') -eq 1 ]
grep "0: foo 200000 10000" /tmp/subgidlistout
echo "OK"
log_status "$0" "SUCCESS"
restore_config
trap '' 0

View File

@ -0,0 +1,20 @@
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/bin/sh
bin:x:2:2:bin:/bin:/bin/sh
sys:x:3:3:sys:/dev:/bin/sh
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/bin/sh
man:x:6:12:man:/var/cache/man:/bin/sh
lp:x:7:7:lp:/var/spool/lpd:/bin/sh
mail:x:8:8:mail:/var/mail:/bin/sh
news:x:9:9:news:/var/spool/news:/bin/sh
uucp:x:10:10:uucp:/var/spool/uucp:/bin/sh
proxy:x:13:13:proxy:/bin:/bin/sh
www-data:x:33:33:www-data:/var/www:/bin/sh
backup:x:34:34:backup:/var/backups:/bin/sh
list:x:38:38:Mailing List Manager:/var/list:/bin/sh
irc:x:39:39:ircd:/var/run/ircd:/bin/sh
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/bin/sh
nobody:x:65534:65534:nobody:/nonexistent:/bin/sh
Debian-exim:x:102:102::/var/spool/exim4:/bin/false
foo:x:1000:1000::/home/foo:/bin/false

View File

@ -0,0 +1,2 @@
foo:200000:10000
root:500000:1000

View File

@ -0,0 +1,4 @@
foo:300000:10000
foo:400000:10000
foo:500000:10000
root:500000:1000

View File

@ -0,0 +1,52 @@
#!/bin/sh
set -e
cd $(dirname $0)
. ../../common/config.sh
. ../../common/log.sh
log_start "$0" "get subid owners"
save_config
# restore the files on exit
trap 'log_status "$0" "FAILURE"; restore_config' 0
change_config
echo -n "Noone owns 0 as a subid..."
[ -z "$(${build_path}/src/get_subid_owners 0)" ]
echo "OK"
echo -n "foo owns subuid 300000..."
[ "$(${build_path}/src/get_subid_owners 300000)" = "1000" ]
echo "OK"
echo -n "foo owns subgid 200000..."
[ "$(${build_path}/src/get_subid_owners -g 200000)" = "1000" ]
echo "OK"
echo -n "Noone owns subuid 200000..."
[ -z "$(${build_path}/src/get_subid_owners -g 300000)" ]
echo "OK"
echo -n "Noone owns subgid 300000..."
[ -z "$(${build_path}/src/get_subid_owners -g 300000)" ]
echo "OK"
echo -n "Both foo and root own subuid 500000..."
cat > /tmp/expected << EOF
1000
0
EOF
${build_path}/src/get_subid_owners 500000 > /tmp/actual
diff /tmp/expected /tmp/actual
echo "OK"
log_status "$0" "SUCCESS"
restore_config
trap '' 0

View File

@ -0,0 +1,59 @@
#!/bin/sh
set -e
cd $(dirname $0)
. ../../common/config.sh
. ../../common/log.sh
log_start "$0" "add and remove subid ranges"
save_config
# restore the files on exit
trap 'log_status "$0" "FAILURE"; restore_config' 0
change_config
echo -n "Existing ranges returned when possible..."
res=$(${build_path}/src/new_subid_range foo 500)
echo "debug"
echo "res is $res"
echo "wanted Subuid range 300000:10000"
echo "end debug"
[ "$res" = "Subuid range 300000:10000" ]
[ $(grep -c foo /etc/subuid) -eq 1 ]
echo "OK"
echo -n "New range returned if requested..."
res=$(${build_path}/src/new_subid_range foo 500 -n)
[ "$res" = "Subuid range 310000:500" ]
[ $(grep -c foo /etc/subuid) -eq 2 ]
echo "OK"
echo -n "Free works..."
res=$(${build_path}/src/free_subid_range foo 310000 500)
[ $(grep -c foo /etc/subuid) -eq 1 ]
echo "OK"
echo -n "Subgids work too..."
res=$(${build_path}/src/new_subid_range -g foo 100000)
echo "DEBUG: res is ${res}"
[ "$res" = "Subuid range 501000:100000" ]
echo "DEBUG: subgid is:"
cat /etc/subgid
[ $(grep -c foo /etc/subgid) -eq 2 ]
echo -n "Subgid free works..."
res=$(${build_path}/src/free_subid_range -g foo 501000 100000)
echo "DEBUG: res is ${res}"
echo "DEBUG: subgid is:"
cat /etc/subgid
[ $(grep -c foo /etc/subgid) -eq 1 ]
echo "OK"
log_status "$0" "SUCCESS"
restore_config
trap '' 0

View File

View File

@ -0,0 +1,20 @@
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/bin/sh
bin:x:2:2:bin:/bin:/bin/sh
sys:x:3:3:sys:/dev:/bin/sh
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/bin/sh
man:x:6:12:man:/var/cache/man:/bin/sh
lp:x:7:7:lp:/var/spool/lpd:/bin/sh
mail:x:8:8:mail:/var/mail:/bin/sh
news:x:9:9:news:/var/spool/news:/bin/sh
uucp:x:10:10:uucp:/var/spool/uucp:/bin/sh
proxy:x:13:13:proxy:/bin:/bin/sh
www-data:x:33:33:www-data:/var/www:/bin/sh
backup:x:34:34:backup:/var/backups:/bin/sh
list:x:38:38:Mailing List Manager:/var/list:/bin/sh
irc:x:39:39:ircd:/var/run/ircd:/bin/sh
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/bin/sh
nobody:x:65534:65534:nobody:/nonexistent:/bin/sh
Debian-exim:x:102:102::/var/spool/exim4:/bin/false
foo:x:1000:1000::/home/foo:/bin/false

View File

@ -0,0 +1,2 @@
foo:200000:10000
root:500000:1000

View File

@ -0,0 +1 @@
foo:300000:10000

136
tests/run_some Executable file
View File

@ -0,0 +1,136 @@
#!/bin/sh
set -e
export LC_ALL=C
unset LANG
unset LANGUAGE
. common/config.sh
USE_PAM="yes"
FAILURE_TESTS="yes"
succeeded=0
failed=0
failed_tests=""
run_test()
{
[ -f RUN_TEST.STOP ] && exit 1
if $1 > $1.log
then
succeeded=$((succeeded+1))
echo -n "+"
else
failed=$((failed+1))
failed_tests="$failed_tests $1"
echo -n "-"
fi
cat $1.log >> testsuite.log
[ -f /etc/passwd.lock ] && echo $1 /etc/passwd.lock || true
[ -f /etc/group.lock ] && echo $1 /etc/group.lock || true
[ -f /etc/shadow.lock ] && echo $1 /etc/shadow.lock || true
[ -f /etc/gshadow.lock ] && echo $1 /etc/gshadow.lock || true
if [ "$(stat -c"%G" /etc/shadow)" != "shadow" ]
then
echo $1
ls -l /etc/shadow
chgrp shadow /etc/shadow
fi
if [ -d /nonexistent ]
then
echo $1 /nonexistent
rmdir /nonexistent
fi
}
echo "+: test passed"
echo "-: test failed"
# Empty the complete log.
> testsuite.log
find ${build_path} -name "*.gcda" -delete
run_test ./su/01/su_root.test
run_test ./su/01/su_user.test
find ${build_path} -name "*.gcda" -exec chmod a+rw {} \;
run_test ./su/02/env_FOO-options_--login
run_test ./su/02/env_FOO-options_--login_bash
run_test ./su/02/env_FOO-options_--preserve-environment
run_test ./su/02/env_FOO-options_--preserve-environment_bash
run_test ./su/02/env_FOO-options_-
run_test ./su/02/env_FOO-options_-_bash
run_test ./su/02/env_FOO-options_-l-m
run_test ./su/02/env_FOO-options_-l-m_bash
run_test ./su/02/env_FOO-options_-l
run_test ./su/02/env_FOO-options_-l_bash
run_test ./su/02/env_FOO-options_-m_bash
run_test ./su/02/env_FOO-options_-m
run_test ./su/02/env_FOO-options_-p
run_test ./su/02/env_FOO-options_-p_bash
run_test ./su/02/env_FOO-options__bash
run_test ./su/02/env_FOO-options_
run_test ./su/02/env_FOO-options_-p-
run_test ./su/02/env_FOO-options_-p-_bash
run_test ./su/02/env_special-options_-l-p
run_test ./su/02/env_special-options_-l
run_test ./su/02/env_special-options_-l-p_bash
run_test ./su/02/env_special-options_-l_bash
run_test ./su/02/env_special-options_-p
run_test ./su/02/env_special-options_-p_bash
run_test ./su/02/env_special-options_
run_test ./su/02/env_special-options__bash
run_test ./su/02/env_special_root-options_-l-p
run_test ./su/02/env_special_root-options_-l-p_bash
run_test ./su/02/env_special_root-options_-l
run_test ./su/02/env_special_root-options_-l_bash
run_test ./su/02/env_special_root-options_-p
run_test ./su/02/env_special_root-options_-p_bash
run_test ./su/02/env_special_root-options_
run_test ./su/02/env_special_root-options__bash
run_test ./su/03/su_run_command01.test
run_test ./su/03/su_run_command02.test
run_test ./su/03/su_run_command03.test
run_test ./su/03/su_run_command04.test
run_test ./su/03/su_run_command05.test
run_test ./su/03/su_run_command06.test
run_test ./su/03/su_run_command07.test
run_test ./su/03/su_run_command08.test
run_test ./su/03/su_run_command09.test
run_test ./su/03/su_run_command10.test
run_test ./su/03/su_run_command11.test
run_test ./su/03/su_run_command12.test
run_test ./su/03/su_run_command13.test
run_test ./su/03/su_run_command14.test
run_test ./su/03/su_run_command15.test
run_test ./su/03/su_run_command16.test
run_test ./su/03/su_run_command17.test
run_test ./su/04/su_wrong_user.test
run_test ./su/04/su_user_wrong_passwd.test
run_test ./su/04/su_user_wrong_passwd_syslog.test
run_test ./su/05/su_user_wrong_passwd_syslog.test
run_test ./su/06/su_user_syslog.test
run_test ./su/07/su_user_syslog.test
run_test ./su/08/env_special-options_
run_test ./su/08/env_special_root-options_
run_test ./su/09/env_special-options_
run_test ./su/09/env_special_root-options_
run_test ./su/10_su_sulog_success/su.test
run_test ./su/11_su_sulog_failure/su.test
run_test ./su/12_su_child_failure/su.test
run_test ./su/13_su_child_success/su.test
run_test ./libsubid/01_list_ranges/list_ranges.test
run_test ./libsubid/02_get_subid_owners/get_subid_owners.test
run_test ./libsubid/03_add_remove/add_remove_subids.test
echo
echo "$succeeded test(s) passed"
echo "$failed test(s) failed"
echo "log written in 'testsuite.log'"
if [ "$failed" != "0" ]
then
echo "the following tests failed:"
echo $failed_tests
fi