]> git.ipfire.org Git - thirdparty/shadow.git/commitdiff
Create a new libsubid 250/head
authorSerge Hallyn <serge@hallyn.com>
Sat, 18 Apr 2020 23:03:54 +0000 (18:03 -0500)
committerSerge Hallyn <serge@hallyn.com>
Sun, 7 Jun 2020 17:11:58 +0000 (12:11 -0500)
Closes #154

Currently this has three functions: one which returns the
list of subuid ranges for a user, one returning the subgids,
and one which frees the ranges lists.

I might be mistaken about what -disable-man means;  some of
the code suggests it means just don't re-generate them, but
not totally ignore them.  But that doesn't seem to really work,
so let's just ignore man/ when -disable-man.

Remove --disable-shared.  I'm not sure why it was there, but it stems
from long, long ago, and I suspect it comes from some ancient
toolchain bug.

Create a tests/run_some, a shorter version of run_all.  I'll
slowly add tests to this as I verify they work, then I can
work on fixing the once which don't.

Also, don't touch man/ if not -enable-man.

Changelog:
Apr 22: change the subid list api as recomended by Dan Walsh.
Apr 23: implement get_subid_owner
Apr 24: implement range add/release
Apr 25: finish tests and rebase
May 10: make @owner const

Signed-off-by: Serge Hallyn <serge@hallyn.com>
31 files changed:
.travis.yml
Makefile.am
autogen.sh
configure.ac
lib/subordinateio.c
lib/subordinateio.h
libsubid/Makefile.am [new file with mode: 0644]
libsubid/api.c [new file with mode: 0644]
libsubid/api.h [new file with mode: 0644]
libsubid/subid.h [new file with mode: 0644]
src/.gitignore
src/Makefile.am
src/free_subid_range.c [new file with mode: 0644]
src/get_subid_owners.c [new file with mode: 0644]
src/list_subid_ranges.c [new file with mode: 0644]
src/new_subid_range.c [new file with mode: 0644]
tests/libsubid/01_list_ranges/config.txt [new file with mode: 0644]
tests/libsubid/01_list_ranges/config/etc/subgid [new file with mode: 0644]
tests/libsubid/01_list_ranges/config/etc/subuid [new file with mode: 0644]
tests/libsubid/01_list_ranges/list_ranges.test [new file with mode: 0755]
tests/libsubid/02_get_subid_owners/config.txt [new file with mode: 0644]
tests/libsubid/02_get_subid_owners/config/etc/passwd [new file with mode: 0644]
tests/libsubid/02_get_subid_owners/config/etc/subgid [new file with mode: 0644]
tests/libsubid/02_get_subid_owners/config/etc/subuid [new file with mode: 0644]
tests/libsubid/02_get_subid_owners/get_subid_owners.test [new file with mode: 0755]
tests/libsubid/03_add_remove/add_remove_subids.test [new file with mode: 0755]
tests/libsubid/03_add_remove/config.txt [new file with mode: 0644]
tests/libsubid/03_add_remove/config/etc/passwd [new file with mode: 0644]
tests/libsubid/03_add_remove/config/etc/subgid [new file with mode: 0644]
tests/libsubid/03_add_remove/config/etc/subuid [new file with mode: 0644]
tests/run_some [new file with mode: 0755]

index ba73e7e2e879d3552f5b51264d99ddba1394ffca..7bc8340b8295e5bc2874e010ca3ad2209d8a669a 100644 (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
index 8851f5d6aadeb75e8730474cd4278f5f7a05f6c0..b6456cf9c6b1a3793891d48aa971f46951e2da79 100644 (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
index 336463c9c75d10000c4c977aefa9a71e40fc69bc..b90fefb37aefb2642cbbc9f84e3d6135293fcdf8 100755 (executable)
@@ -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 \
        "$@"
index b8bc2b7aafb18ed585911ff51f39662c5a4d88b1..b766eff0fa313c7ff656534554228dc1987eb878 100644 (file)
@@ -22,8 +22,8 @@ test "$prefix" = "/usr" && exec_prefix=""
 
 AC_GNU_SOURCE
 
-AM_DISABLE_SHARED
 AM_ENABLE_STATIC
+AM_ENABLE_SHARED
 
 AM_MAINTAINER_MODE
 
@@ -725,6 +725,7 @@ AC_CONFIG_FILES([
        man/zh_TW/Makefile
        libmisc/Makefile
        lib/Makefile
+       libsubid/Makefile
        src/Makefile
        contrib/Makefile
        etc/Makefile
index 0d89a64ef7bb0e6dcc954198ec71c39022fed395..67202c98ec694d96631c0d47dc20a04edfee87db 100644 (file)
 #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 */
index a21d72b8e8fb4124535026a94b8fc4ed2e903b60..13a213417a0bd440165268dbf51d1a010f461288 100644 (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);
diff --git a/libsubid/Makefile.am b/libsubid/Makefile.am
new file mode 100644 (file)
index 0000000..bdbc52c
--- /dev/null
@@ -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\"
diff --git a/libsubid/api.c b/libsubid/api.c
new file mode 100644 (file)
index 0000000..91d73be
--- /dev/null
@@ -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);
+}
diff --git a/libsubid/api.h b/libsubid/api.h
new file mode 100644 (file)
index 0000000..f65e3ec
--- /dev/null
@@ -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);
diff --git a/libsubid/subid.h b/libsubid/subid.h
new file mode 100644 (file)
index 0000000..ba9a2f6
--- /dev/null
@@ -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
index d5716b9896b7b1f039d35ce6b8773f3e067561e0..8c14e6f45b30f5cbf43dff2b68848fa6b307b683 100644 (file)
@@ -33,3 +33,7 @@
 /userdel
 /usermod
 /vipw
+/get_subid_owners
+/list_subid_ranges
+/new_subid_range
+/free_subid_range
index f175928a60ccecd9d121f1bd1269d6bb353b2e17..8499ce08f84bd708cc8ec9bd6069b28c0cbd675e 100644 (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
diff --git a/src/free_subid_range.c b/src/free_subid_range.c
new file mode 100644 (file)
index 0000000..3685887
--- /dev/null
@@ -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;
+}
diff --git a/src/get_subid_owners.c b/src/get_subid_owners.c
new file mode 100644 (file)
index 0000000..a438554
--- /dev/null
@@ -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;
+}
diff --git a/src/list_subid_ranges.c b/src/list_subid_ranges.c
new file mode 100644 (file)
index 0000000..cdba610
--- /dev/null
@@ -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;
+}
diff --git a/src/new_subid_range.c b/src/new_subid_range.c
new file mode 100644 (file)
index 0000000..6d7b033
--- /dev/null
@@ -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;
+}
diff --git a/tests/libsubid/01_list_ranges/config.txt b/tests/libsubid/01_list_ranges/config.txt
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/tests/libsubid/01_list_ranges/config/etc/subgid b/tests/libsubid/01_list_ranges/config/etc/subgid
new file mode 100644 (file)
index 0000000..b9495cf
--- /dev/null
@@ -0,0 +1,2 @@
+foo:200000:10000
+root:500000:1000
diff --git a/tests/libsubid/01_list_ranges/config/etc/subuid b/tests/libsubid/01_list_ranges/config/etc/subuid
new file mode 100644 (file)
index 0000000..e5c537b
--- /dev/null
@@ -0,0 +1,3 @@
+foo:300000:10000
+foo:400000:10000
+root:500000:1000
diff --git a/tests/libsubid/01_list_ranges/list_ranges.test b/tests/libsubid/01_list_ranges/list_ranges.test
new file mode 100755 (executable)
index 0000000..b131303
--- /dev/null
@@ -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
+
diff --git a/tests/libsubid/02_get_subid_owners/config.txt b/tests/libsubid/02_get_subid_owners/config.txt
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/tests/libsubid/02_get_subid_owners/config/etc/passwd b/tests/libsubid/02_get_subid_owners/config/etc/passwd
new file mode 100644 (file)
index 0000000..bf52df0
--- /dev/null
@@ -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
diff --git a/tests/libsubid/02_get_subid_owners/config/etc/subgid b/tests/libsubid/02_get_subid_owners/config/etc/subgid
new file mode 100644 (file)
index 0000000..b9495cf
--- /dev/null
@@ -0,0 +1,2 @@
+foo:200000:10000
+root:500000:1000
diff --git a/tests/libsubid/02_get_subid_owners/config/etc/subuid b/tests/libsubid/02_get_subid_owners/config/etc/subuid
new file mode 100644 (file)
index 0000000..dd11875
--- /dev/null
@@ -0,0 +1,4 @@
+foo:300000:10000
+foo:400000:10000
+foo:500000:10000
+root:500000:1000
diff --git a/tests/libsubid/02_get_subid_owners/get_subid_owners.test b/tests/libsubid/02_get_subid_owners/get_subid_owners.test
new file mode 100755 (executable)
index 0000000..145477c
--- /dev/null
@@ -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
+
diff --git a/tests/libsubid/03_add_remove/add_remove_subids.test b/tests/libsubid/03_add_remove/add_remove_subids.test
new file mode 100755 (executable)
index 0000000..a24a975
--- /dev/null
@@ -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
+
diff --git a/tests/libsubid/03_add_remove/config.txt b/tests/libsubid/03_add_remove/config.txt
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/tests/libsubid/03_add_remove/config/etc/passwd b/tests/libsubid/03_add_remove/config/etc/passwd
new file mode 100644 (file)
index 0000000..bf52df0
--- /dev/null
@@ -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
diff --git a/tests/libsubid/03_add_remove/config/etc/subgid b/tests/libsubid/03_add_remove/config/etc/subgid
new file mode 100644 (file)
index 0000000..b9495cf
--- /dev/null
@@ -0,0 +1,2 @@
+foo:200000:10000
+root:500000:1000
diff --git a/tests/libsubid/03_add_remove/config/etc/subuid b/tests/libsubid/03_add_remove/config/etc/subuid
new file mode 100644 (file)
index 0000000..cf80c2f
--- /dev/null
@@ -0,0 +1 @@
+foo:300000:10000
diff --git a/tests/run_some b/tests/run_some
new file mode 100755 (executable)
index 0000000..2d085d5
--- /dev/null
@@ -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
+