diff --git a/NEWS b/NEWS index 65d4292fce..d3ab63ae1c 100644 --- a/NEWS +++ b/NEWS @@ -4,6 +4,7 @@ Noteworthy changes in release ?.?? (????-??-??) * Improvements * Updated decoding of IFLA_BRPORT_* netlink attributes to match Linux 5.12. * Updated lists of DEVCONF_*, IORING_*, KVM_*, MPOL_*, and ST_* constants. + * Implemented displaying of SELinux contexts (--secontext[=full] option). * Bug fixes * Fixed build using bionic libc. diff --git a/bootstrap b/bootstrap index 1378400102..13247644a0 100755 --- a/bootstrap +++ b/bootstrap @@ -24,11 +24,17 @@ for m in m32 mx32; do s/^MPERS_NAME$s=.*/& $m/; s/^\\(CC$s=\\).*/\\1 @CC_FOR_${m_upper}@/; s/^MPERS_CC_FLAGS$s=.*/& @CFLAGS_FOR_${m_upper}@ @cc_flags_$m@/; - s/^ARCH_MFLAGS$s=.*/& -DMPERS_IS_\$(MPERS_NAME) \$(MPERS_CC_FLAGS)/" \ + s/^ARCH_MFLAGS$s=.*/& -DMPERS_IS_\$(MPERS_NAME) \$(MPERS_CC_FLAGS)/; \ + s/HAVE_SELINUX_RUNTIME/HAVE_${m_upper}_SELINUX_RUNTIME/" \ tests/Makefile.am > $tests/Makefile.am for f in tests/*; do - case "${f##*/}" in + fname="${f##*/}" + case "$fname" in Makefile*) continue;; + *--secontext.c) + sed "s/HAVE_SELINUX_RUNTIME/HAVE_${m_upper}_SELINUX_RUNTIME/" \ + "$f" > $tests/"$fname" + continue;; esac ln -s ../"$f" $tests/ done diff --git a/configure.ac b/configure.ac index f801dc6771..7e31942166 100644 --- a/configure.ac +++ b/configure.ac @@ -619,6 +619,8 @@ AC_CHECK_TOOL([READELF], [readelf]) st_STACKTRACE +st_SELINUX + if test "$arch" = mips && test "$no_create" != yes; then mkdir -p src/linux/mips if $srcdir/src/linux/mips/genstub.sh \ diff --git a/doc/strace.1.in b/doc/strace.1.in index 05c32d9022..15d8e25315 100644 --- a/doc/strace.1.in +++ b/doc/strace.1.in @@ -53,6 +53,7 @@ strace \- trace system calls and signals .OM \-P path .OM \-p pid .OP \-\-seccomp\-bpf +.if '@USE_SELINUX_FALSE@'#' .OP \-\-secontext\fR[=full] .BR "" { .OR \-p pid .BR "" | @@ -1084,6 +1085,14 @@ and PIDs associated with pidfd file descriptors. .B \-\-pidns\-translation If strace and tracee are in different PID namespaces, print PIDs in strace's namespace, too. +.if '@USE_SELINUX_FALSE@'#' .TP +.if '@USE_SELINUX_FALSE@'#' .BR \-\-secontext "[=full]" +.if '@USE_SELINUX_FALSE@'#' Print SELinux contexts of processes and files in +.if '@USE_SELINUX_FALSE@'#' square brackets when SELinux is available and not +.if '@USE_SELINUX_FALSE@'#' disabled. When +.if '@USE_SELINUX_FALSE@'#' .B full +.if '@USE_SELINUX_FALSE@'#' is specified, print the complete context (user, +.if '@USE_SELINUX_FALSE@'#' role, type and category) instead of just the type. .SS Statistics .TP 12 .B \-c diff --git a/m4/mpers.m4 b/m4/mpers.m4 index 510aabe847..15a4b2afe6 100644 --- a/m4/mpers.m4 +++ b/m4/mpers.m4 @@ -63,9 +63,11 @@ pushdef([mpers_name], [$1]) pushdef([MPERS_NAME], translit([$1], [a-z], [A-Z])) pushdef([HAVE_MPERS], [HAVE_]MPERS_NAME[_MPERS]) pushdef([HAVE_RUNTIME], [HAVE_]MPERS_NAME[_RUNTIME]) +pushdef([HAVE_SELINUX_RUNTIME], [HAVE_]MPERS_NAME[_SELINUX_RUNTIME]) pushdef([MPERS_CFLAGS], [$cc_flags_$1]) pushdef([st_cv_cc], [st_cv_$1_cc]) pushdef([st_cv_runtime], [st_cv_$1_runtime]) +pushdef([st_cv_selinux_runtime], [st_cv_$1_selinux_runtime]) pushdef([st_cv_mpers], [st_cv_$1_mpers]) pushdef([EXEEXT], MPERS_NAME[_EXEEXT])dnl @@ -126,6 +128,25 @@ case "$arch" in else st_cv_mpers=no fi]) + AS_IF([test "x$with_secontexts$st_cv_mpers$st_cv_runtime" = xyesyesyes], + AC_CACHE_CHECK([whether selinux runtime works with mpers_name personality], + [st_cv_selinux_runtime], + [saved_CPPFLAGS="$CPPFLAGS" + saved_LDFLAGS="$LDFLAGS" + saved_LIBS="$LIBS" + CPPFLAGS="$CPPFLAGS $libselinux_CPPFLAGS" + LDFLAGS="$LDFLAGS $libselinux_LDFLAGS" + LIBS="$LIBS $libselinux_LIBS" + AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include ]], + [[return 0]])], + [st_cv_selinux_runtime=yes], + [st_cv_selinux_runtime=no], + [st_cv_selinux_runtime=no]) + LIBS="$saved_LIBS" + LDFLAGS="$saved_LDFLAGS" + CPPFLAGS="$saved_CPPFLAGS" + ]), + [st_cv_selinux_runtime=no]) if test $st_cv_mpers = yes; then AC_DEFINE(HAVE_MPERS, [1], [Define to 1 if you have mpers_name mpers support]) @@ -165,6 +186,7 @@ case "$arch" in *) # case "$enable_mpers" st_cv_runtime=no st_cv_mpers=no + st_cv_selinux_runtime=no ;; esac @@ -187,6 +209,7 @@ case "$arch" in esac AM_CONDITIONAL(HAVE_RUNTIME, [test "$st_cv_mpers$st_cv_runtime" = yesyes]) +AM_CONDITIONAL(HAVE_SELINUX_RUNTIME, [test "$st_cv_mpers$st_cv_selinux_runtime" = yesyes]) AM_CONDITIONAL(HAVE_MPERS, [test "$st_cv_mpers" = yes]) st_RESTORE_VAR([CC]) @@ -201,9 +224,11 @@ popdef([EXEEXT])dnl popdef([st_cv_mpers]) popdef([st_cv_runtime]) +popdef([st_cv_selinux_runtime]) popdef([st_cv_cc]) popdef([MPERS_CFLAGS]) popdef([HAVE_RUNTIME]) +popdef([HAVE_SELINUX_RUNTIME]) popdef([HAVE_MPERS]) popdef([MPERS_NAME]) popdef([mpers_name]) diff --git a/m4/st_selinux.m4 b/m4/st_selinux.m4 new file mode 100644 index 0000000000..e9f1eda84c --- /dev/null +++ b/m4/st_selinux.m4 @@ -0,0 +1,80 @@ +#!/usr/bin/m4 +# +# Copyright (c) 2020 The strace developers. +# All rights reserved. +# +# SPDX-License-Identifier: LGPL-2.1-or-later + +AC_DEFUN([st_SELINUX], [dnl + +libselinux_CPPFLAGS= +libselinux_LDFLAGS= +libselinux_LIBS= +with_secontexts=no + +AC_ARG_WITH([libselinux], + [AS_HELP_STRING([--with-libselinux], + [use libselinux to collect security contexts])], + [case "${withval}" in + yes|no|check) ;; + *) with_libselinux=yes + libselinux_CPPFLAGS="-I${withval}/include" + libselinux_LDFLAGS="-L${withval}/lib" ;; + esac], + [with_libselinux=check] +) + +AS_IF([test "x$with_libselinux" != xno], + [saved_CPPFLAGS="$CPPFLAGS" + CPPFLAGS="$CPPFLAGS $libselinux_CPPFLAGS" + found_selinux_h=no + AC_CHECK_HEADERS([selinux/selinux.h], + [found_selinux_h=yes]) + CPPFLAGS="$saved_CPPFLAGS" + AS_IF([test "x$found_selinux_h" = xyes], + [saved_LDFLAGS="$LDFLAGS" + LDFLAGS="$LDFLAGS $libselinux_LDFLAGS" + AC_CHECK_LIB([selinux],[getpidcon], + [libselinux_LIBS="-lselinux" + with_secontexts=yes + ], + [if test "x$with_libselinux" != xcheck; then + AC_MSG_FAILURE([failed to find getpidcon in libselinux]) + fi + ] + ) + AC_CHECK_LIB([selinux],[getfilecon], + [libselinux_LIBS="-lselinux" + with_secontexts=yes + ], + [if test "x$with_libselinux" != xcheck; then + AC_MSG_FAILURE([failed to find getfilecon in libselinux]) + fi + ] + ) + LDFLAGS="$saved_LDFLAGS" + ], + [if test "x$with_libselinux" != xcheck; then + AC_MSG_FAILURE([failed to find selinux.h]) + fi + ] + ) + ] +) + +AC_MSG_CHECKING([whether to enable security contexts support]) +AS_IF([test "x$with_secontexts" = xyes], + [AC_DEFINE([USE_SELINUX], [1], + [Define to enable SELinux security contexts support]) + AC_DEFINE([HAVE_SELINUX_RUNTIME], [1], + [Define to enable SELinux security contexts testing]) + AC_SUBST(libselinux_LIBS) + AC_SUBST(libselinux_LDFLAGS) + AC_SUBST(libselinux_CPPFLAGS) + AC_MSG_RESULT([yes])], + [AC_MSG_RESULT([no])]) + +AM_CONDITIONAL([USE_SELINUX], [test "x$with_secontexts" = xyes]) +AM_CONDITIONAL([HAVE_SELINUX_RUNTIME], [test "x$with_secontexts" = xyes]) + +]) diff --git a/src/Makefile.am b/src/Makefile.am index ee9415ea55..814d85a7ab 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -406,6 +406,15 @@ strace_LDADD += $(libiberty_LIBS) endif endif +if USE_SELINUX +libstrace_a_SOURCES += \ + selinux.c \ + selinux.h +strace_CPPFLAGS += $(libselinux_CPPFLAGS) +strace_LDFLAGS += $(libselinux_LDFLAGS) +strace_LDADD += $(libselinux_LIBS) +endif + strace_CPPFLAGS += $(CODE_COVERAGE_CPPFLAGS) strace_CFLAGS += $(CODE_COVERAGE_CFLAGS) strace_LDADD += $(CODE_COVERAGE_LIBS) diff --git a/src/defs.h b/src/defs.h index 08a293130a..864252f08e 100644 --- a/src/defs.h +++ b/src/defs.h @@ -293,6 +293,10 @@ struct tcb { */ unsigned int pid_ns; +#ifdef USE_SELINUX + int last_dirfd; /* Use AT_FDCWD for 'not set' */ +#endif + struct mmap_cache_t *mmap_cache; /* @@ -1164,6 +1168,11 @@ extern void print_ax25_addr(const void /* ax25_address */ *addr); extern void print_x25_addr(const void /* struct x25_address */ *addr); extern const char *get_sockaddr_by_inode(struct tcb *, int fd, unsigned long inode); extern bool print_sockaddr_by_inode(struct tcb *, int fd, unsigned long inode); + +/** + * Prints dirfd file descriptor and saves it in tcp->last_dirfd, the latter is + * used when printing SELinux contexts. + */ extern void print_dirfd(struct tcb *, int); extern int diff --git a/src/dirent.c b/src/dirent.c index 3c2c09e9bf..f56a06018c 100644 --- a/src/dirent.c +++ b/src/dirent.c @@ -106,6 +106,9 @@ SYS_FUNC(readdir) if (entering(tcp)) { /* fd */ printfd(tcp, tcp->u_arg[0]); +#ifdef USE_SELINUX + tcp->last_dirfd = (int) tcp->u_arg[0]; +#endif tprint_arg_next(); } else { /* dirp */ diff --git a/src/open.c b/src/open.c index b48fc087a9..a364c56a57 100644 --- a/src/open.c +++ b/src/open.c @@ -33,6 +33,9 @@ print_dirfd(struct tcb *tcp, int fd) print_xlat_d(AT_FDCWD); else printfd(tcp, fd); +#ifdef USE_SELINUX + tcp->last_dirfd = fd; +#endif } /* diff --git a/src/selinux.c b/src/selinux.c new file mode 100644 index 0000000000..cfc93c9a1e --- /dev/null +++ b/src/selinux.c @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2020-2021 The strace developers. + * All rights reserved. + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include "defs.h" + +#include +#include +#include +#include + +#include "selinux.h" +#include "xstring.h" + +bool selinux_context = false; +bool selinux_context_full = false; + +static int +getcontext(int rc, char **secontext, char **result) +{ + if (rc < 0) + return rc; + + *result = NULL; + if (!selinux_context_full) { + char *saveptr = NULL; + char *secontext_copy = xstrdup(*secontext); + const char *token; + unsigned int i; + + /* + * We only want to keep the type (3rd field, ':' separator). + */ + for (token = strtok_r(secontext_copy, ":", &saveptr), i = 0; + token; token = strtok_r(NULL, ":", &saveptr), i++) { + if (i == 2) { + *result = xstrdup(token); + break; + } + } + free(secontext_copy); + } + + if (*result == NULL) { + /* + * On the CI at least, the context may have a trailing \n, + * let's remove it just in case + */ + size_t len = strlen(*secontext); + for (; len > 0; --len) { + if ((*secontext)[len - 1] != '\n') + break; + } + *result = xstrndup(*secontext, len); + } + freecon(*secontext); + return 0; +} +/* + * Retrieves the SELinux context of the given PID (extracted from the tcb). + * Memory must be freed. + * Returns 0 on success, -1 on failure. + */ +int +selinux_getpidcon(struct tcb *tcp, char **result) +{ + if (!selinux_context) + return -1; + + int proc_pid = 0; + translate_pid(NULL, tcp->pid, PT_TID, &proc_pid); + if (!proc_pid) + return -1; + + char *secontext; + return getcontext(getpidcon(proc_pid, &secontext), &secontext, result); +} + +/* + * Retrieves the SELinux context of the given pid and descriptor. + * Memory must be freed. + * Returns 0 on success, -1 on failure. + */ +int +selinux_getfdcon(pid_t pid, int fd, char **result) +{ + if (!selinux_context || pid <= 0 || fd < 0) + return -1; + + int proc_pid = 0; + translate_pid(NULL, pid, PT_TID, &proc_pid); + if (!proc_pid) + return -1; + + char linkpath[sizeof("/proc/%u/fd/%u") + 2 * sizeof(int)*3]; + xsprintf(linkpath, "/proc/%u/fd/%u", proc_pid, fd); + + char *secontext; + return getcontext(getfilecon(linkpath, &secontext), &secontext, result); +} + +/* + * Retrieves the SELinux context of the given path. + * Memory must be freed. + * Returns 0 on success, -1 on failure. + */ +int +selinux_getfilecon(struct tcb *tcp, const char *path, char **result) +{ + if (!selinux_context) + return -1; + + int proc_pid = 0; + translate_pid(NULL, tcp->pid, PT_TID, &proc_pid); + if (!proc_pid) + return -1; + + int ret = -1; + char fname[PATH_MAX]; + + if (path[0] == '/') + ret = snprintf(fname, sizeof(fname), "/proc/%u/root%s", + proc_pid, path); + else if (tcp->last_dirfd == AT_FDCWD) + ret = snprintf(fname, sizeof(fname), "/proc/%u/cwd/%s", + proc_pid, path); + else if (tcp->last_dirfd >= 0 ) + ret = snprintf(fname, sizeof(fname), "/proc/%u/fd/%u/%s", + proc_pid, tcp->last_dirfd, path); + + if ((unsigned int) ret >= sizeof(fname)) + return -1; + + char *secontext; + return getcontext(getfilecon(fname, &secontext), &secontext, result); +} diff --git a/src/selinux.h b/src/selinux.h new file mode 100644 index 0000000000..009e410d36 --- /dev/null +++ b/src/selinux.h @@ -0,0 +1,21 @@ +/* + * SELinux interface. + * + * Copyright (c) 2020-2021 The strace developers. + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#ifndef STRACE_SELINUX_H +#define STRACE_SELINUX_H + +#include "defs.h" + +extern bool selinux_context; +extern bool selinux_context_full; + +int selinux_getfdcon(pid_t pid, int fd, char **result); +int selinux_getfilecon(struct tcb *tcp, const char *path, char **context); +int selinux_getpidcon(struct tcb *tcp, char **context); + +#endif /* !STRACE_SELINUX_H */ diff --git a/src/strace.c b/src/strace.c index 950db7af10..bbabbbc236 100644 --- a/src/strace.c +++ b/src/strace.c @@ -40,6 +40,7 @@ #include "xstring.h" #include "delay.h" #include "wait.h" +#include "selinux.h" /* In some libc, these aren't declared. Do it ourself: */ extern char **environ; @@ -240,6 +241,9 @@ print_version(void) " no-mx32-mpers" # endif #endif /* SUPPORTED_PERSONALITIES > 2 */ +#ifdef USE_SELINUX + " secontext" +#endif ""; printf("%s -- version %s\n" @@ -258,12 +262,18 @@ usage(void) # define K_OPT "k" #else # define K_OPT "" +#endif +#ifdef USE_SELINUX +# define SELINUX_OPT "[--secontext[=full]]\n" +#else +# define SELINUX_OPT "" #endif printf("\ Usage: strace [-ACdffhi" K_OPT "qqrtttTvVwxxyyzZ] [-I N] [-b execve] [-e EXPR]...\n\ [-a COLUMN] [-o FILE] [-s STRSIZE] [-X FORMAT] [-O OVERHEAD]\n\ - [-S SORTBY] [-P PATH]... [-p PID]... [-U COLUMNS] [--seccomp-bpf]\n\ + [-S SORTBY] [-P PATH]... [-p PID]... [-U COLUMNS] [--seccomp-bpf]\n"\ + SELINUX_OPT "\ { -p PID | [-DDD] [-E VAR=VAL]... [-u USERNAME] PROG [ARGS] }\n\ or: strace -c[dfwzZ] [-I N] [-b execve] [-e EXPR]... [-O OVERHEAD]\n\ [-S SORTBY] [-P PATH]... [-p PID]... [-U COLUMNS] [--seccomp-bpf]\n\ @@ -404,6 +414,14 @@ Output format:\n\ -yy, --decode-fds=all\n\ print all available information associated with file\n\ descriptors in addition to paths\n\ +" +#ifdef USE_SELINUX +"\ + --secontext[=full]\n\ + print SELinux contexts (type only unless 'full' is specified)\n\ +" +#endif +"\ \n\ Statistics:\n\ -c, --summary-only\n\ @@ -783,6 +801,14 @@ printleader(struct tcb *tcp) else if (nprocs > 1 && !outfname) tprintf("[pid %5u] ", tcp->pid); +#ifdef USE_SELINUX + char *context; + if (!selinux_getpidcon(tcp, &context)) { + tprintf("[%s] ", context); + free(context); + } +#endif + if (tflag_format) { struct timespec ts; clock_gettime(CLOCK_REALTIME, &ts); @@ -896,6 +922,9 @@ alloctcb(int pid) tcp->pid = pid; #if SUPPORTED_PERSONALITIES > 1 tcp->currpers = current_personality; +#endif +#ifdef USE_SELINUX + tcp->last_dirfd = AT_FDCWD; #endif nprocs++; debug_msg("new tcb for pid %d, active tcbs:%d", @@ -2037,6 +2066,9 @@ init(int argc, char *argv[]) GETOPT_OUTPUT_SEPARATELY, GETOPT_TS, GETOPT_PIDNS_TRANSLATION, +#ifdef USE_SELINUX + GETOPT_SELINUX_CONTEXT, +#endif GETOPT_QUAL_TRACE, GETOPT_QUAL_ABBREV, @@ -2093,6 +2125,9 @@ init(int argc, char *argv[]) { "failed-only", no_argument, 0, 'Z' }, { "failing-only", no_argument, 0, 'Z' }, { "seccomp-bpf", no_argument, 0, GETOPT_SECCOMP }, +#ifdef USE_SELINUX + { "secontext", optional_argument, 0, GETOPT_SELINUX_CONTEXT }, +#endif { "trace", required_argument, 0, GETOPT_QUAL_TRACE }, { "abbrev", required_argument, 0, GETOPT_QUAL_ABBREV }, @@ -2321,6 +2356,17 @@ init(int argc, char *argv[]) case GETOPT_SECCOMP: seccomp_filtering = true; break; +#ifdef USE_SELINUX + case GETOPT_SELINUX_CONTEXT: + selinux_context = true; + if (optarg) { + if (!strcmp(optarg, "full")) + selinux_context_full = true; + else + error_opt_arg(c, lopt, optarg); + } + break; +#endif case GETOPT_QUAL_TRACE: qualify_trace(optarg); break; @@ -2503,6 +2549,11 @@ init(int argc, char *argv[]) if (!number_set_array_is_empty(decode_fd_set, 0)) error_msg("-y/--decode-fds has no effect " "with -c/--summary-only"); +#ifdef USE_SELINUX + if (selinux_context) + error_msg("--secontext has no effect with " + "-c/--summary-only"); +#endif } if (!outfname) { diff --git a/src/syscall.c b/src/syscall.c index d143f257ba..245cc74199 100644 --- a/src/syscall.c +++ b/src/syscall.c @@ -24,6 +24,7 @@ #include "poke.h" #include "retval.h" #include +#include /* for struct iovec */ #include @@ -1019,6 +1020,10 @@ syscall_exiting_finish(struct tcb *tcp) tcp->sys_func_rval = 0; free_tcb_priv_data(tcp); +#ifdef USE_SELINUX + tcp->last_dirfd = AT_FDCWD; +#endif + if (cflag) tcp->ltime = tcp->stime; } diff --git a/src/util.c b/src/util.c index 9cb555ecb7..b681297034 100644 --- a/src/util.c +++ b/src/util.c @@ -26,6 +26,7 @@ #include "largefile_wrappers.h" #include "number_set.h" #include "print_utils.h" +#include "selinux.h" #include "static_assert.h" #include "string_to_uint.h" #include "xlat.h" @@ -667,6 +668,13 @@ printfd_pid(struct tcb *tcp, pid_t pid, int fd) } else { tprintf("%d", fd); } +#ifdef USE_SELINUX + char *context; + if (!selinux_getfdcon(pid, fd, &context)) { + tprintf(" [%s]", context); + free(context); + } +#endif } void @@ -959,6 +967,14 @@ printpathn(struct tcb *const tcp, const kernel_ulong_t addr, unsigned int n) else { path[n++] = !nul_seen; print_quoted_cstring(path, n); + +#ifdef USE_SELINUX + char *context; + if (nul_seen && !selinux_getfilecon(tcp, path, &context)) { + tprintf(" [%s]", context); + free(context); + } +#endif } return nul_seen; diff --git a/src/xgetdents.c b/src/xgetdents.c index 7985f409d1..0ed9351f02 100644 --- a/src/xgetdents.c +++ b/src/xgetdents.c @@ -123,6 +123,9 @@ xgetdents(struct tcb *const tcp, const unsigned int header_size, if (entering(tcp)) { /* fd */ printfd(tcp, tcp->u_arg[0]); +#ifdef USE_SELINUX + tcp->last_dirfd = (int) tcp->u_arg[0]; +#endif tprint_arg_next(); return 0; } diff --git a/strace.spec.in b/strace.spec.in index c8f281f18f..107715f462 100644 --- a/strace.spec.in +++ b/strace.spec.in @@ -29,11 +29,14 @@ BuildRequires: pkgconfig(bluez) # Install binutils-devel to enable symbol demangling. %if 0%{?fedora} >= 20 || 0%{?centos} >= 6 || 0%{?rhel} >= 6 %define buildrequires_stacktrace BuildRequires: elfutils-devel binutils-devel +%define buildrequires_selinux BuildRequires: libselinux-devel %endif %if 0%{?suse_version} >= 1100 %define buildrequires_stacktrace BuildRequires: libdw-devel binutils-devel +%define buildrequires_selinux BuildRequires: libselinux-devel %endif %{?buildrequires_stacktrace} +%{?buildrequires_selinux} # OBS compatibility %{?!buildroot:BuildRoot: %_tmppath/buildroot-%name-%version-%release} diff --git a/tests/.gitignore b/tests/.gitignore index a2f3419b04..f2d911c62e 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -3,6 +3,8 @@ *.log *.o *.trs +*--secontext +*--secontext_full _newselect _newselect-P accept @@ -121,6 +123,7 @@ fanotify_mark-Xraw fanotify_mark-Xverbose fchdir fchmod +fchmod-y fchmodat fchown fchown32 diff --git a/tests/Makefile.am b/tests/Makefile.am index b8efce8249..300196fc00 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -34,6 +34,7 @@ AM_LDFLAGS = $(ARCH_MFLAGS) libtests_a_SOURCES = \ create_nl_socket.c \ create_tmpfile.c \ + dirfd.c \ errno2name.c \ error_msg.c \ fill_memory.c \ @@ -77,7 +78,38 @@ LDADD = libtests.a include pure_executables.am +secontext_EXECUTABLES = \ + access--secontext \ + access--secontext_full \ + chmod--secontext \ + chmod--secontext_full \ + execve--secontext \ + execve--secontext_full \ + execveat--secontext \ + execveat--secontext_full \ + faccessat--secontext \ + faccessat--secontext_full \ + faccessat-y--secontext \ + fanotify_mark--secontext \ + fanotify_mark--secontext_full \ + fchmod--secontext \ + fchmod--secontext_full \ + fchmod-y--secontext \ + fchmodat--secontext \ + fchmodat--secontext_full \ + fchownat--secontext \ + fchownat--secontext_full \ + file_handle--secontext \ + file_handle--secontext_full \ + linkat--secontext \ + linkat--secontext_full \ + open--secontext \ + open--secontext_full \ + openat--secontext \ + openat--secontext_full + check_PROGRAMS = $(PURE_EXECUTABLES) \ + $(secontext_EXECUTABLES) \ _newselect-P \ answer \ attach-f-p \ @@ -292,6 +324,41 @@ check_PROGRAMS = $(PURE_EXECUTABLES) \ zeroargc \ # end of check_PROGRAMS +if HAVE_SELINUX_RUNTIME +libselinux_LDADD = $(libselinux_LIBS) +else +libselinux_LDADD = +endif + +access__secontext_LDADD = $(libselinux_LDADD) $(LDADD) +access__secontext_full_LDADD = $(libselinux_LDADD) $(LDADD) +chmod__secontext_LDADD = $(libselinux_LDADD) $(LDADD) +chmod__secontext_full_LDADD = $(libselinux_LDADD) $(LDADD) +execve__secontext_LDADD = $(libselinux_LDADD) $(LDADD) +execve__secontext_full_LDADD = $(libselinux_LDADD) $(LDADD) +execveat__secontext_LDADD = $(libselinux_LDADD) $(LDADD) +execveat__secontext_full_LDADD = $(libselinux_LDADD) $(LDADD) +faccessat__secontext_LDADD = $(libselinux_LDADD) $(LDADD) +faccessat__secontext_full_LDADD = $(libselinux_LDADD) $(LDADD) +faccessat_y__secontext_LDADD = $(libselinux_LDADD) $(LDADD) +fanotify_mark__secontext_LDADD = $(libselinux_LDADD) $(LDADD) +fanotify_mark__secontext_full_LDADD = $(libselinux_LDADD) $(LDADD) +fchmod__secontext_LDADD = $(libselinux_LDADD) $(LDADD) +fchmod__secontext_full_LDADD = $(libselinux_LDADD) $(LDADD) +fchmod_y__secontext_LDADD = $(libselinux_LDADD) $(LDADD) +fchmodat__secontext_LDADD = $(libselinux_LDADD) $(LDADD) +fchmodat__secontext_full_LDADD = $(libselinux_LDADD) $(LDADD) +fchownat__secontext_LDADD = $(libselinux_LDADD) $(LDADD) +fchownat__secontext_full_LDADD = $(libselinux_LDADD) $(LDADD) +file_handle__secontext_LDADD = $(libselinux_LDADD) $(LDADD) +file_handle__secontext_full_LDADD = $(libselinux_LDADD) $(LDADD) +linkat__secontext_LDADD = $(libselinux_LDADD) $(LDADD) +linkat__secontext_full_LDADD = $(libselinux_LDADD) $(LDADD) +open__secontext_LDADD = $(libselinux_LDADD) $(LDADD) +open__secontext_full_LDADD = $(libselinux_LDADD) $(LDADD) +openat__secontext_LDADD = $(libselinux_LDADD) $(LDADD) +openat__secontext_full_LDADD = $(libselinux_LDADD) $(LDADD) + attach_f_p_LDADD = -lpthread $(LDADD) count_f_LDADD = -lpthread $(LDADD) delay_LDADD = $(clock_LIBS) $(LDADD) @@ -596,6 +663,7 @@ EXTRA_DIST = \ run.sh \ sched.in \ scno_tampering.sh \ + selinux.c \ semop-common.c \ semtimedop-common.c \ semtimedop-syscall.c \ diff --git a/tests/access--secontext.c b/tests/access--secontext.c new file mode 100644 index 0000000000..95dca3ba8a --- /dev/null +++ b/tests/access--secontext.c @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2021 The strace developers. + * All rights reserved. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "tests.h" + +#ifdef HAVE_SELINUX_RUNTIME + +# define TEST_SECONTEXT +# include "access.c" + +#else + +SKIP_MAIN_UNDEFINED("HAVE_SELINUX_RUNTIME") + +#endif diff --git a/tests/access--secontext_full.c b/tests/access--secontext_full.c new file mode 100644 index 0000000000..f175a7a8e3 --- /dev/null +++ b/tests/access--secontext_full.c @@ -0,0 +1,10 @@ +/* + * Copyright (c) 2021 The strace developers. + * All rights reserved. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#define PRINT_SECONTEXT_FULL + +#include "access--secontext.c" diff --git a/tests/access.c b/tests/access.c index 8bdbb62657..034beafc05 100644 --- a/tests/access.c +++ b/tests/access.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2019 The strace developers. + * Copyright (c) 2016-2021 The strace developers. * All rights reserved. * * SPDX-License-Identifier: GPL-2.0-or-later @@ -10,21 +10,36 @@ #ifdef __NR_access +# include # include # include +# include "selinux.c" + int main(void) { static const char sample[] = "access_sample"; + char *my_secontext = SELINUX_MYCONTEXT(); + + unlink(sample); + if (open(sample, O_CREAT|O_RDONLY, 0400) == -1) + perror_msg_and_fail("open"); long rc = syscall(__NR_access, sample, F_OK); - printf("access(\"%s\", F_OK) = %ld %s (%m)\n", - sample, rc, errno2name()); + printf("%saccess(\"%s\"%s, F_OK) = %s\n", + my_secontext, + sample, SELINUX_FILECONTEXT(sample), + sprintrc(rc)); + + if (unlink(sample) == -1) + perror_msg_and_fail("unlink"); rc = syscall(__NR_access, sample, R_OK|W_OK|X_OK); - printf("access(\"%s\", R_OK|W_OK|X_OK) = %ld %s (%m)\n", - sample, rc, errno2name()); + printf("%saccess(\"%s\", R_OK|W_OK|X_OK) = %s\n", + my_secontext, + sample, + sprintrc(rc)); puts("+++ exited with 0 +++"); return 0; diff --git a/tests/chmod--secontext.c b/tests/chmod--secontext.c new file mode 100644 index 0000000000..dd3b31523d --- /dev/null +++ b/tests/chmod--secontext.c @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2021 The strace developers. + * All rights reserved. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "tests.h" + +#ifdef HAVE_SELINUX_RUNTIME + +# define TEST_SECONTEXT +# include "chmod.c" + +#else + +SKIP_MAIN_UNDEFINED("HAVE_SELINUX_RUNTIME") + +#endif diff --git a/tests/chmod--secontext_full.c b/tests/chmod--secontext_full.c new file mode 100644 index 0000000000..fb7d3601ff --- /dev/null +++ b/tests/chmod--secontext_full.c @@ -0,0 +1,10 @@ +/* + * Copyright (c) 2021 The strace developers. + * All rights reserved. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#define PRINT_SECONTEXT_FULL + +#include "chmod--secontext.c" diff --git a/tests/chmod.c b/tests/chmod.c index 25601fb54c..abba77d44f 100644 --- a/tests/chmod.c +++ b/tests/chmod.c @@ -17,25 +17,38 @@ # include # include +# include "selinux.c" + int main(void) { static const char fname[] = "chmod_test_file"; + char *my_secontext = SELINUX_MYCONTEXT(); + unlink(fname); if (open(fname, O_CREAT|O_RDONLY, 0400) < 0) perror_msg_and_fail("open"); long rc = syscall(__NR_chmod, fname, 0600); - printf("chmod(\"%s\", 0600) = %s\n", fname, sprintrc(rc)); + printf("%schmod(\"%s\"%s, 0600) = %s\n", + my_secontext, + fname, SELINUX_FILECONTEXT(fname), + sprintrc(rc)); if (unlink(fname)) perror_msg_and_fail("unlink"); rc = syscall(__NR_chmod, fname, 051); - printf("chmod(\"%s\", 051) = %s\n", fname, sprintrc(rc)); + printf("%schmod(\"%s\", 051) = %s\n", + my_secontext, + fname, + sprintrc(rc)); rc = syscall(__NR_chmod, fname, 004); - printf("chmod(\"%s\", 004) = %s\n", fname, sprintrc(rc)); + printf("%schmod(\"%s\", 004) = %s\n", + my_secontext, + fname, + sprintrc(rc)); puts("+++ exited with 0 +++"); return 0; diff --git a/tests/dirfd.c b/tests/dirfd.c new file mode 100644 index 0000000000..40ed00ef35 --- /dev/null +++ b/tests/dirfd.c @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2021 The strace developers. + * All rights reserved. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "tests.h" + +#include +#include +#include + +#include "xmalloc.h" + +int +get_dir_fd(const char *dir_path) +{ + DIR *dir = opendir(dir_path); + if (dir == NULL) + perror_msg_and_fail("opendir(%s)", dir_path); + int dfd = dirfd(dir); + if (dfd == -1) + perror_msg_and_fail("dirfd"); + return dfd; +} + +int +get_curdir_fd(char **curdir) +{ + int res = get_dir_fd("."); + + if (curdir != NULL) { + char *buf = xmalloc(PATH_MAX); + ssize_t n = readlink("/proc/self/cwd", buf, PATH_MAX); + if (n == -1) + perror_msg_and_skip("readlink: %s", "/proc/self/cwd"); + if (n >= PATH_MAX) + error_msg_and_fail("readlink: %s: %s", + "/proc/self/cwd", + "symlink value is too long"); + buf[n] = '\0'; + *curdir = buf; + } + + return res; +} diff --git a/tests/execve--secontext.c b/tests/execve--secontext.c new file mode 100644 index 0000000000..b5fdc2b87f --- /dev/null +++ b/tests/execve--secontext.c @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2021 The strace developers. + * All rights reserved. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "tests.h" + +#ifdef HAVE_SELINUX_RUNTIME + +# define TEST_SECONTEXT +# include "execve.c" + +#else + +SKIP_MAIN_UNDEFINED("HAVE_SELINUX_RUNTIME") + +#endif diff --git a/tests/execve--secontext_full.c b/tests/execve--secontext_full.c new file mode 100644 index 0000000000..7e2f99e322 --- /dev/null +++ b/tests/execve--secontext_full.c @@ -0,0 +1,10 @@ +/* + * Copyright (c) 2021 The strace developers. + * All rights reserved. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#define PRINT_SECONTEXT_FULL + +#include "execve--secontext.c" diff --git a/tests/execve.c b/tests/execve.c index de20438762..506ffca642 100644 --- a/tests/execve.c +++ b/tests/execve.c @@ -11,6 +11,11 @@ #include "tests.h" #include #include +#include +#include +#include + +#include "selinux.c" static const char *errstr; @@ -46,9 +51,16 @@ main(void) { char ** const tail_argv = tail_memdup(argv, sizeof(argv)); char ** const tail_envp = tail_memdup(envp, sizeof(envp)); + char *my_secontext = SELINUX_MYCONTEXT(); + + unlink(FILENAME); + if (open(FILENAME, O_RDONLY | O_CREAT, 0400) < 0) + perror_msg_and_fail("open"); + + char *FILENAME_secontext = SELINUX_FILECONTEXT(FILENAME); call_execve(FILENAME, tail_argv, tail_envp); - printf("execve(\"%s\"" + printf("%sexecve(\"%s\"%s" ", [\"%s\", \"%s\", \"%s\", %p, %p, %p, ... /* %p */]" #if VERBOSE ", [\"%s\", \"%s\", %p, %p, %p, ... /* %p */]" @@ -56,7 +68,9 @@ main(void) ", %p /* 5 vars, unterminated */" #endif ") = %s\n", - Q_FILENAME, q_argv[0], q_argv[1], q_argv[2], + my_secontext, + Q_FILENAME, FILENAME_secontext, + q_argv[0], q_argv[1], q_argv[2], argv[3], argv[4], argv[5], (char *) tail_argv + sizeof(argv) #if VERBOSE , q_envp[0], q_envp[1], envp[2], envp[3], envp[4], @@ -71,14 +85,16 @@ main(void) (void) q_envp; /* workaround for clang bug #33068 */ call_execve(FILENAME, tail_argv, tail_envp); - printf("execve(\"%s\", [\"%s\", \"%s\", \"%s\"]" + printf("%sexecve(\"%s\"%s, [\"%s\", \"%s\", \"%s\"]" #if VERBOSE ", [\"%s\", \"%s\"]" #else ", %p /* 2 vars */" #endif ") = %s\n", - Q_FILENAME, q_argv[0], q_argv[1], q_argv[2] + my_secontext, + Q_FILENAME, FILENAME_secontext, + q_argv[0], q_argv[1], q_argv[2] #if VERBOSE , q_envp[0], q_envp[1] #else @@ -87,14 +103,16 @@ main(void) , errstr); call_execve(FILENAME, tail_argv + 2, tail_envp + 1); - printf("execve(\"%s\", [\"%s\"]" + printf("%sexecve(\"%s\"%s, [\"%s\"]" #if VERBOSE ", [\"%s\"]" #else ", %p /* 1 var */" #endif ") = %s\n", - Q_FILENAME, q_argv[2] + my_secontext, + Q_FILENAME, FILENAME_secontext, + q_argv[2] #if VERBOSE , q_envp[1] #else @@ -107,13 +125,15 @@ main(void) *empty = NULL; call_execve(FILENAME, empty, empty); - printf("execve(\"%s\", []" + printf("%sexecve(\"%s\"%s, []" #if VERBOSE ", []" #else ", %p /* 0 vars */" #endif - ") = %s\n", Q_FILENAME + ") = %s\n", + my_secontext, + Q_FILENAME, FILENAME_secontext #if !VERBOSE , empty #endif @@ -137,7 +157,10 @@ main(void) a[i] = b[i] = NULL; call_execve(FILENAME, a, b); - printf("execve(\"%s\", [\"%.*s\"...", Q_FILENAME, DEFAULT_STRLEN, a[0]); + printf("%sexecve(\"%s\"%s, [\"%.*s\"...", + my_secontext, + Q_FILENAME, FILENAME_secontext, + DEFAULT_STRLEN, a[0]); for (i = 1; i < DEFAULT_STRLEN; ++i) printf(", \"%s\"", a[i]); #if VERBOSE @@ -156,7 +179,10 @@ main(void) printf(") = %s\n", errstr); call_execve(FILENAME, a + 1, b + 1); - printf("execve(\"%s\", [\"%s\"", Q_FILENAME, a[1]); + printf("%sexecve(\"%s\"%s, [\"%s\"", + my_secontext, + Q_FILENAME, FILENAME_secontext, + a[1]); for (i = 2; i <= DEFAULT_STRLEN; ++i) printf(", \"%s\"", a[i]); #if VERBOSE @@ -169,12 +195,17 @@ main(void) #endif printf(") = %s\n", errstr); + if (unlink(FILENAME)) + perror_msg_and_fail("unlink"); + call_execve(FILENAME, (char **) tail_argv[ARRAY_SIZE(q_argv)], efault); - printf("execve(\"%s\", NULL, %p) = %s\n", + printf("%sexecve(\"%s\", NULL, %p) = %s\n", + my_secontext, Q_FILENAME, efault, errstr); call_execve(FILENAME, efault, NULL); - printf("execve(\"%s\", %p, NULL) = %s\n", + printf("%sexecve(\"%s\", %p, NULL) = %s\n", + my_secontext, Q_FILENAME, efault, errstr); return 0; diff --git a/tests/execve.test b/tests/execve.test index 0824bab48a..3275226ae4 100755 --- a/tests/execve.test +++ b/tests/execve.test @@ -11,7 +11,7 @@ check_prog grep run_prog > /dev/null -run_strace -eexecve $args > "$EXP" +run_strace -eexecve "$@" $args > "$EXP" # Filter out execve() call made by strace. grep -F test.execve < "$LOG" > "$OUT" diff --git a/tests/execveat--secontext.c b/tests/execveat--secontext.c new file mode 100644 index 0000000000..75a7501ea8 --- /dev/null +++ b/tests/execveat--secontext.c @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2021 The strace developers. + * All rights reserved. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "tests.h" + +#ifdef HAVE_SELINUX_RUNTIME + +# define TEST_SECONTEXT +# include "execveat.c" + +#else + +SKIP_MAIN_UNDEFINED("HAVE_SELINUX_RUNTIME") + +#endif diff --git a/tests/execveat--secontext_full.c b/tests/execveat--secontext_full.c new file mode 100644 index 0000000000..ca6271af90 --- /dev/null +++ b/tests/execveat--secontext_full.c @@ -0,0 +1,10 @@ +/* + * Copyright (c) 2021 The strace developers. + * All rights reserved. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#define PRINT_SECONTEXT_FULL + +#include "execveat--secontext.c" diff --git a/tests/execveat.c b/tests/execveat.c index 43b53edb10..96c5dbe976 100644 --- a/tests/execveat.c +++ b/tests/execveat.c @@ -13,8 +13,91 @@ #ifdef __NR_execveat +# include +# include # include +# include # include +# include + +# include "selinux.c" + +void +tests_with_existing_file(void) +{ + static const char sample[] = "execveat_sample"; + static const char *argv[] = { sample, NULL }; + char *my_secontext = SELINUX_MYCONTEXT(); + + unlink(sample); + if (open(sample, O_RDONLY | O_CREAT, 0400) < 0) + perror_msg_and_fail("open"); + + char *sample_secontext = SELINUX_FILECONTEXT(sample); + + long rc = syscall(__NR_execveat, -100, sample, argv, NULL, 0); + printf("%sexecveat(AT_FDCWD, \"%s\"%s, [\"%s\"], NULL, 0) = %s\n", + my_secontext, + sample, sample_secontext, + argv[0], + sprintrc(rc)); + + if (unlink(sample)) + perror_msg_and_fail("unlink"); + + rc = syscall(__NR_execveat, -100, sample, argv, NULL, 0); + printf("%sexecveat(AT_FDCWD, \"%s\", [\"%s\"], NULL, 0) = %s\n", + my_secontext, + sample, + argv[0], + sprintrc(rc)); + + /* + * Tests with dirfd + */ + + char *cwd = NULL; + int cwd_fd = get_curdir_fd(&cwd); + char *cwd_secontext = SELINUX_FILECONTEXT("."); + char *sample_realpath = xasprintf("%s/%s", cwd, sample); + + /* no file */ + rc = syscall(__NR_execveat, cwd_fd, sample, argv, NULL, 0); + printf("%sexecveat(%d%s, \"%s\", [\"%s\"], NULL, 0) = %s\n", + my_secontext, + cwd_fd, cwd_secontext, + sample, + argv[0], + sprintrc(rc)); + + if (open(sample, O_RDONLY | O_CREAT, 0400) < 0) + perror_msg_and_fail("open"); + + rc = syscall(__NR_execveat, cwd_fd, sample, argv, NULL, 0); + printf("%sexecveat(%d%s, \"%s\"%s, [\"%s\"], NULL, 0) = %s\n", + my_secontext, + cwd_fd, cwd_secontext, + sample, sample_secontext, + argv[0], + sprintrc(rc)); + + /* cwd_fd ignored when path is absolute */ + if (chdir("..") == -1) + perror_msg_and_fail("chdir"); + + rc = syscall(__NR_execveat, cwd_fd, sample_realpath, argv, NULL, 0); + printf("%sexecveat(%d%s, \"%s\"%s, [\"%s\"], NULL, 0) = %s\n", + my_secontext, + cwd_fd, cwd_secontext, + sample_realpath, sample_secontext, + argv[0], + sprintrc(rc)); + + if (fchdir(cwd_fd)) + perror_msg_and_fail("fchdir"); + + unlink(sample); +} # define FILENAME "test.execveat\nfilename" # define Q_FILENAME "test.execveat\\nfilename" @@ -40,9 +123,10 @@ main(void) { const char ** const tail_argv = tail_memdup(argv, sizeof(argv)); const char ** const tail_envp = tail_memdup(envp, sizeof(envp)); + char *my_secontext = SELINUX_MYCONTEXT(); syscall(__NR_execveat, -100, FILENAME, tail_argv, tail_envp, 0x1100); - printf("execveat(AT_FDCWD, \"%s\"" + printf("%sexecveat(AT_FDCWD, \"%s\"" ", [\"%s\", \"%s\", \"%s\", %p, %p, %p, ... /* %p */]" # if VERBOSE ", [\"%s\", \"%s\", %p, %p, %p, ... /* %p */]" @@ -50,6 +134,7 @@ main(void) ", %p /* 5 vars, unterminated */" # endif ", AT_SYMLINK_NOFOLLOW|AT_EMPTY_PATH) = -1 %s (%m)\n", + my_secontext, Q_FILENAME, q_argv[0], q_argv[1], q_argv[2], argv[3], argv[4], argv[5], (char *) tail_argv + sizeof(argv), # if VERBOSE @@ -65,13 +150,14 @@ main(void) (void) q_envp; /* workaround for clang bug #33068 */ syscall(__NR_execveat, -100, FILENAME, tail_argv, tail_envp, 0x1100); - printf("execveat(AT_FDCWD, \"%s\", [\"%s\", \"%s\", \"%s\"]" + printf("%sexecveat(AT_FDCWD, \"%s\", [\"%s\", \"%s\", \"%s\"]" # if VERBOSE ", [\"%s\", \"%s\"]" # else ", %p /* 2 vars */" # endif ", AT_SYMLINK_NOFOLLOW|AT_EMPTY_PATH) = -1 %s (%m)\n", + my_secontext, Q_FILENAME, q_argv[0], q_argv[1], q_argv[2], # if VERBOSE q_envp[0], q_envp[1], @@ -81,13 +167,14 @@ main(void) errno2name()); syscall(__NR_execveat, -100, FILENAME, tail_argv + 2, tail_envp + 1, 0x1100); - printf("execveat(AT_FDCWD, \"%s\", [\"%s\"]" + printf("%sexecveat(AT_FDCWD, \"%s\", [\"%s\"]" # if VERBOSE ", [\"%s\"]" # else ", %p /* 1 var */" # endif ", AT_SYMLINK_NOFOLLOW|AT_EMPTY_PATH) = -1 %s (%m)\n", + my_secontext, Q_FILENAME, q_argv[2], # if VERBOSE q_envp[1], @@ -101,13 +188,14 @@ main(void) *empty = NULL; syscall(__NR_execveat, -100, FILENAME, empty, empty, 0x1100); - printf("execveat(AT_FDCWD, \"%s\", []" + printf("%sexecveat(AT_FDCWD, \"%s\", []" # if VERBOSE ", []" # else ", %p /* 0 vars */" # endif ", AT_SYMLINK_NOFOLLOW|AT_EMPTY_PATH) = -1 %s (%m)\n", + my_secontext, Q_FILENAME, # if !VERBOSE empty, @@ -132,7 +220,9 @@ main(void) a[i] = b[i] = NULL; syscall(__NR_execveat, -100, FILENAME, a, b, 0x1100); - printf("execveat(AT_FDCWD, \"%s\", [\"%.*s\"...", Q_FILENAME, DEFAULT_STRLEN, a[0]); + printf("%sexecveat(AT_FDCWD, \"%s\", [\"%.*s\"...", + my_secontext, + Q_FILENAME, DEFAULT_STRLEN, a[0]); for (i = 1; i < DEFAULT_STRLEN; ++i) printf(", \"%s\"", a[i]); # if VERBOSE @@ -152,7 +242,9 @@ main(void) errno2name()); syscall(__NR_execveat, -100, FILENAME, a + 1, b + 1, 0x1100); - printf("execveat(AT_FDCWD, \"%s\", [\"%s\"", Q_FILENAME, a[1]); + printf("%sexecveat(AT_FDCWD, \"%s\", [\"%s\"", + my_secontext, + Q_FILENAME, a[1]); for (i = 2; i <= DEFAULT_STRLEN; ++i) printf(", \"%s\"", a[i]); # if VERBOSE @@ -167,15 +259,19 @@ main(void) errno2name()); syscall(__NR_execveat, -100, FILENAME, NULL, efault, 0x1100); - printf("execveat(AT_FDCWD, \"%s\", NULL, %p" + printf("%sexecveat(AT_FDCWD, \"%s\", NULL, %p" ", AT_SYMLINK_NOFOLLOW|AT_EMPTY_PATH) = -1 %s (%m)\n", + my_secontext, Q_FILENAME, efault, errno2name()); syscall(__NR_execveat, -100, FILENAME, efault, NULL, 0x1100); - printf("execveat(AT_FDCWD, \"%s\", %p, NULL" + printf("%sexecveat(AT_FDCWD, \"%s\", %p, NULL" ", AT_SYMLINK_NOFOLLOW|AT_EMPTY_PATH) = -1 %s (%m)\n", + my_secontext, Q_FILENAME, efault, errno2name()); + tests_with_existing_file(); + puts("+++ exited with 0 +++"); return 0; } diff --git a/tests/faccessat--secontext.c b/tests/faccessat--secontext.c new file mode 100644 index 0000000000..1111ab4f6c --- /dev/null +++ b/tests/faccessat--secontext.c @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2021 The strace developers. + * All rights reserved. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "tests.h" + +#ifdef HAVE_SELINUX_RUNTIME + +# define TEST_SECONTEXT +# include "faccessat.c" + +#else + +SKIP_MAIN_UNDEFINED("HAVE_SELINUX_RUNTIME") + +#endif diff --git a/tests/faccessat--secontext_full.c b/tests/faccessat--secontext_full.c new file mode 100644 index 0000000000..5f51433f62 --- /dev/null +++ b/tests/faccessat--secontext_full.c @@ -0,0 +1,2 @@ +#define PRINT_SECONTEXT_FULL +#include "faccessat--secontext.c" diff --git a/tests/faccessat-y--secontext.c b/tests/faccessat-y--secontext.c new file mode 100644 index 0000000000..6c6d563cc4 --- /dev/null +++ b/tests/faccessat-y--secontext.c @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2021 The strace developers. + * All rights reserved. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "tests.h" + +#ifdef HAVE_SELINUX_RUNTIME + +# define TEST_SECONTEXT +# include "faccessat-y.c" + +#else + +SKIP_MAIN_UNDEFINED("HAVE_SELINUX_RUNTIME") + +#endif diff --git a/tests/faccessat-y.c b/tests/faccessat-y.c index 61f67d730a..317c167728 100644 --- a/tests/faccessat-y.c +++ b/tests/faccessat-y.c @@ -1,4 +1,6 @@ #define FD_PATH "" #define SKIP_IF_PROC_IS_UNAVAILABLE skip_if_unavailable("/proc/self/fd/") +#define YFLAG + #include "faccessat.c" diff --git a/tests/faccessat-yy.c b/tests/faccessat-yy.c index b516240ae3..bb46887387 100644 --- a/tests/faccessat-yy.c +++ b/tests/faccessat-yy.c @@ -1,4 +1,6 @@ #define FD_PATH ">" #define SKIP_IF_PROC_IS_UNAVAILABLE skip_if_unavailable("/proc/self/fd/") +#define YFLAG + #include "faccessat.c" diff --git a/tests/faccessat.c b/tests/faccessat.c index 670e9b21fa..93c6107baa 100644 --- a/tests/faccessat.c +++ b/tests/faccessat.c @@ -12,10 +12,14 @@ #ifdef __NR_faccessat -# include "xmalloc.h" # include # include +# include # include +# include + +# include "selinux.c" +# include "xmalloc.h" # ifndef FD_PATH # define FD_PATH "" @@ -24,6 +28,7 @@ # define SKIP_IF_PROC_IS_UNAVAILABLE # endif +# ifndef TEST_SECONTEXT static const char *errstr; static long @@ -42,12 +47,115 @@ k_faccessat(const unsigned int dirfd, errstr = sprintrc(rc); return rc; } +# endif + +# ifndef PATH_TRACING +void +tests_with_existing_file(void) +{ + static const char sample[] = "access_sample"; + char *my_secontext = SELINUX_MYCONTEXT(); + + unlink(sample); + int fd = open(sample, O_CREAT|O_RDONLY, 0400); + if (fd == -1) + perror_msg_and_fail("open"); + close(fd); + char *sample_secontext = SELINUX_FILECONTEXT(sample); + + long rc = syscall(__NR_faccessat, -100, sample, F_OK); + printf("%sfaccessat(AT_FDCWD, \"%s\"%s, F_OK) = %s\n", + my_secontext, + sample, sample_secontext, + sprintrc(rc)); + + if (unlink(sample) == -1) + perror_msg_and_fail("unlink"); + + rc = syscall(__NR_faccessat, -100, sample, F_OK); + printf("%sfaccessat(AT_FDCWD, \"%s\", F_OK) = %s\n", + my_secontext, + sample, + sprintrc(rc)); + + /* + * Tests with dirfd + */ + + char *cwd = NULL; + int cwd_fd = get_curdir_fd(&cwd); + char *cwd_secontext = SELINUX_FILECONTEXT("."); + char *sample_realpath = xasprintf("%s/%s", cwd, sample); + + /* no file */ + rc = syscall(__NR_faccessat, cwd_fd, sample, F_OK); +# ifdef YFLAG + printf("%sfaccessat(%d<%s>%s, \"%s\", F_OK) = %s\n", +# else + printf("%sfaccessat(%d%s, \"%s\", F_OK) = %s\n", +# endif + my_secontext, + cwd_fd, +# ifdef YFLAG + cwd, +# endif + cwd_secontext, + sample, + sprintrc(rc)); + + fd = open(sample, O_CREAT|O_RDONLY, 0400); + if (fd == -1) + perror_msg_and_fail("open"); + close(fd); + + rc = syscall(__NR_faccessat, cwd_fd, sample, F_OK); +# ifdef YFLAG + printf("%sfaccessat(%d<%s>%s, \"%s\"%s, F_OK) = %s\n", +# else + printf("%sfaccessat(%d%s, \"%s\"%s, F_OK) = %s\n", +# endif + my_secontext, + cwd_fd, +# ifdef YFLAG + cwd, +# endif + cwd_secontext, + sample, sample_secontext, + sprintrc(rc)); + + /* cwd_fd ignored when path is absolute */ + if (chdir("..") == -1) + perror_msg_and_fail("chdir"); + + rc = syscall(__NR_faccessat, cwd_fd, sample_realpath, F_OK); +# ifdef YFLAG + printf("%sfaccessat(%d<%s>%s, \"%s\"%s, F_OK) = %s\n", +# else + printf("%sfaccessat(%d%s, \"%s\"%s, F_OK) = %s\n", +# endif + my_secontext, + cwd_fd, +# ifdef YFLAG + cwd, +# endif + cwd_secontext, + sample_realpath, sample_secontext, + sprintrc(rc)); + + if (fchdir(cwd_fd) == -1) + perror_msg_and_fail("fchdir"); + + unlink(sample); +} +# endif int main(void) { SKIP_IF_PROC_IS_UNAVAILABLE; +# ifndef TEST_SECONTEXT + TAIL_ALLOC_OBJECT_CONST_PTR(const char, unterminated); char *unterminated_str = xasprintf("%p", unterminated); const void *const efault = unterminated + 1; @@ -120,10 +228,10 @@ main(void) k_faccessat(dirfds[dirfd_i].val, paths[path_i].val, modes[mode_i].val); -# ifdef PATH_TRACING +# ifdef PATH_TRACING if (dirfds[dirfd_i].val == fd || paths[path_i].val == fd_path) -# endif +# endif printf("faccessat(%s, %s, %s) = %s\n", dirfds[dirfd_i].str, paths[path_i].str, @@ -133,6 +241,12 @@ main(void) } } +# endif + +# ifndef PATH_TRACING + tests_with_existing_file(); +# endif + puts("+++ exited with 0 +++"); return 0; } diff --git a/tests/faccessat.test b/tests/faccessat.test index 83f94ba401..2685f89a82 100755 --- a/tests/faccessat.test +++ b/tests/faccessat.test @@ -15,5 +15,5 @@ run_prog > /dev/null run_strace -a23 --trace=faccessat "$@" $args > "$EXP" # Filter out faccessat() calls made by ld.so and libc. -sed -n '/^faccessat(-1, NULL,/,$p' < "$LOG" > "$OUT" -match_diff "$OUT" "$EXP" +sed -n '/faccessat(-1, NULL,/,$p' < "$LOG" > "$OUT" +match_diff "$LOG" "$EXP" diff --git a/tests/fanotify_mark--secontext.c b/tests/fanotify_mark--secontext.c new file mode 100644 index 0000000000..2c734feea4 --- /dev/null +++ b/tests/fanotify_mark--secontext.c @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2021 The strace developers. + * All rights reserved. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "tests.h" + +#ifdef HAVE_SELINUX_RUNTIME + +# define TEST_SECONTEXT +# include "fanotify_mark.c" + +#else + +SKIP_MAIN_UNDEFINED("HAVE_SELINUX_RUNTIME") + +#endif diff --git a/tests/fanotify_mark--secontext_full.c b/tests/fanotify_mark--secontext_full.c new file mode 100644 index 0000000000..5121b5f937 --- /dev/null +++ b/tests/fanotify_mark--secontext_full.c @@ -0,0 +1,10 @@ +/* + * Copyright (c) 2021 The strace developers. + * All rights reserved. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#define PRINT_SECONTEXT_FULL + +#include "fanotify_mark--secontext.c" diff --git a/tests/fanotify_mark.c b/tests/fanotify_mark.c index bd14f47904..03e13357c4 100644 --- a/tests/fanotify_mark.c +++ b/tests/fanotify_mark.c @@ -21,6 +21,8 @@ # include # include +# include "selinux.c" + # if XLAT_RAW # define str_fan_mark_add "0x1" # define str_fan_modify_ondir "0x40000002" @@ -35,6 +37,7 @@ # define str_at_fdcwd "AT_FDCWD" # endif +# ifndef TEST_SECONTEXT /* Performs fanotify_mark call via the syscall interface. */ static void do_call(kernel_ulong_t fd, kernel_ulong_t flags, const char *flags_str, @@ -44,18 +47,18 @@ do_call(kernel_ulong_t fd, kernel_ulong_t flags, const char *flags_str, long rc; rc = syscall(__NR_fanotify_mark, fd, flags, -# if (LONG_MAX > INT_MAX) \ - || (defined __x86_64__ && defined __ILP32__) \ - || defined LINUX_MIPSN32 +# if (LONG_MAX > INT_MAX) \ + || (defined __x86_64__ && defined __ILP32__) \ + || defined LINUX_MIPSN32 mask, -# else +# else /* arch/parisc/kernel/sys_parisc32.c, commit ab8a261b */ -# ifdef HPPA +# ifdef HPPA LL_VAL_TO_PAIR((mask << 32) | (mask >> 32)), -# else +# else LL_VAL_TO_PAIR(mask), +# endif # endif -# endif dirfd, path); printf("fanotify_mark(%d, %s, %s, %s, %s) = %s\n", @@ -68,12 +71,14 @@ struct strval { const char *str; }; -# define STR16 "0123456789abcdef" -# define STR64 STR16 STR16 STR16 STR16 +# define STR16 "0123456789abcdef" +# define STR64 STR16 STR16 STR16 STR16 +# endif int main(void) { +# ifndef TEST_SECONTEXT enum { PATH1_SIZE = 64, }; @@ -87,47 +92,47 @@ main(void) { F8ILL_KULONG_MASK, "0" }, { (kernel_ulong_t) 0xdec0deddefacec00ULL, "0xefacec00" -# if !XLAT_RAW +# if !XLAT_RAW " /* FAN_MARK_??? */" -# endif +# endif }, { (kernel_ulong_t) 0xda7a105700000040ULL, -# if XLAT_RAW +# if XLAT_RAW "0x40" -# elif XLAT_VERBOSE +# elif XLAT_VERBOSE "0x40 /* FAN_MARK_IGNORED_SURV_MODIFY */" -# else +# else "FAN_MARK_IGNORED_SURV_MODIFY" -# endif +# endif }, { (kernel_ulong_t) 0xbadc0deddeadffffULL, -# if XLAT_RAW || XLAT_VERBOSE +# if XLAT_RAW || XLAT_VERBOSE "0xdeadffff" -# endif -# if XLAT_VERBOSE +# endif +# if XLAT_VERBOSE " /* " -# endif -# if !XLAT_RAW +# endif +# if !XLAT_RAW "FAN_MARK_ADD|FAN_MARK_REMOVE|FAN_MARK_DONT_FOLLOW|" "FAN_MARK_ONLYDIR|FAN_MARK_MOUNT|FAN_MARK_IGNORED_MASK|" "FAN_MARK_IGNORED_SURV_MODIFY|FAN_MARK_FLUSH|" "FAN_MARK_FILESYSTEM|0xdeadfe00" -# endif -# if XLAT_VERBOSE +# endif +# if XLAT_VERBOSE " */" -# endif +# endif }, }; static const struct strval64 masks[] = { { ARG_ULL_STR(0) }, { 0xdeadfeedffffffffULL, -# if XLAT_RAW || XLAT_VERBOSE +# if XLAT_RAW || XLAT_VERBOSE "0xdeadfeedffffffff" -# endif -# if XLAT_VERBOSE +# endif +# if XLAT_VERBOSE " /* " -# endif -# if !XLAT_RAW +# endif +# if !XLAT_RAW "FAN_ACCESS|" "FAN_MODIFY|" "FAN_ATTRIB|" @@ -149,27 +154,27 @@ main(void) "FAN_ONDIR|" "FAN_EVENT_ON_CHILD|" "0xdeadfeedb7f0a000" -# endif -# if XLAT_VERBOSE +# endif +# if XLAT_VERBOSE " */" -# endif +# endif }, { ARG_ULL_STR(0xffffffffb7f0a000) -# if !XLAT_RAW +# if !XLAT_RAW " /* FAN_??? */" -# endif +# endif }, }; static const struct strval dirfds[] = { { (kernel_ulong_t) 0xfacefeed00000001ULL, "1" }, { (kernel_ulong_t) 0xdec0ded0ffffffffULL, -# if XLAT_RAW +# if XLAT_RAW "-1" -# elif XLAT_VERBOSE +# elif XLAT_VERBOSE "-1 /* FAN_NOFD */" -# else +# else "FAN_NOFD" -# endif +# endif }, { (kernel_ulong_t) 0xbadfacedffffff9cULL, str_at_fdcwd }, { (kernel_ulong_t) 0xdefaced1beeff00dULL, "-1091571699" }, @@ -202,12 +207,6 @@ main(void) snprintf(bogus_path1_after_addr, sizeof(bogus_path1_after_addr), "%p", bogus_path1 + PATH1_SIZE); - rc = fanotify_mark(-1, FAN_MARK_ADD, FAN_MODIFY | FAN_ONDIR, - -100, "."); - printf("fanotify_mark(-1, %s, %s, %s, \".\") = %s\n", - str_fan_mark_add, str_fan_modify_ondir, str_at_fdcwd, - sprintrc(rc)); - for (i = 0; i < ARRAY_SIZE(fds); i++) { for (j = 0; j < ARRAY_SIZE(flags); j++) { for (k = 0; k < ARRAY_SIZE(masks); k++) { @@ -226,7 +225,36 @@ main(void) } } } +# else + int rc; +# endif + char *my_secontext = SELINUX_MYCONTEXT(); + char path[] = "."; + char *path_secontext = SELINUX_FILECONTEXT(path); + rc = fanotify_mark(-1, FAN_MARK_ADD, FAN_MODIFY | FAN_ONDIR, + -100, path); + printf("%sfanotify_mark(-1, %s, %s, %s, \"%s\"%s) = %s\n", + my_secontext, + str_fan_mark_add, str_fan_modify_ondir, str_at_fdcwd, + path, path_secontext, + sprintrc(rc)); + + /* + * Test with dirfd + */ + + int cwd_fd = get_curdir_fd(NULL); + char *cwd_secontext = SELINUX_FILECONTEXT("."); + + rc = fanotify_mark(-1, FAN_MARK_ADD, FAN_MODIFY | FAN_ONDIR, + cwd_fd, path); + printf("%sfanotify_mark(-1, %s, %s, %d%s, \"%s\"%s) = %s\n", + my_secontext, + str_fan_mark_add, str_fan_modify_ondir, + cwd_fd, cwd_secontext, + path, path_secontext, + sprintrc(rc)); puts("+++ exited with 0 +++"); return 0; } diff --git a/tests/fchmod--secontext.c b/tests/fchmod--secontext.c new file mode 100644 index 0000000000..c5a05226db --- /dev/null +++ b/tests/fchmod--secontext.c @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2021 The strace developers. + * All rights reserved. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "tests.h" + +#ifdef HAVE_SELINUX_RUNTIME + +# define TEST_SECONTEXT +# include "fchmod.c" + +#else + +SKIP_MAIN_UNDEFINED("HAVE_SELINUX_RUNTIME") + +#endif diff --git a/tests/fchmod--secontext_full.c b/tests/fchmod--secontext_full.c new file mode 100644 index 0000000000..ca260862a0 --- /dev/null +++ b/tests/fchmod--secontext_full.c @@ -0,0 +1,2 @@ +#define PRINT_SECONTEXT_FULL +#include "fchmod--secontext.c" diff --git a/tests/fchmod-y--secontext.c b/tests/fchmod-y--secontext.c new file mode 100644 index 0000000000..3f60f913a9 --- /dev/null +++ b/tests/fchmod-y--secontext.c @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2021 The strace developers. + * All rights reserved. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "tests.h" + +#ifdef HAVE_SELINUX_RUNTIME + +# define TEST_SECONTEXT +# include "fchmod-y.c" + +#else + +SKIP_MAIN_UNDEFINED("HAVE_SELINUX_RUNTIME") + +#endif diff --git a/tests/fchmod-y.c b/tests/fchmod-y.c new file mode 100644 index 0000000000..be0a828552 --- /dev/null +++ b/tests/fchmod-y.c @@ -0,0 +1,10 @@ +/* + * Copyright (c) 2021 The strace developers. + * All rights reserved. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#define YFLAG + +#include "fchmod.c" diff --git a/tests/fchmod.c b/tests/fchmod.c index 8a65b8bcbc..3a94243c6d 100644 --- a/tests/fchmod.c +++ b/tests/fchmod.c @@ -17,23 +17,74 @@ # include # include # include +# include +# include # include +# include "selinux.c" + int main(void) { - int fd = create_tmpfile(O_RDWR); + static const char fname[] = "fchmod_test_file"; + char *my_secontext = SELINUX_MYCONTEXT(); + + unlink(fname); + int fd = open(fname, O_CREAT|O_RDONLY, 0400); + if (fd == -1) + perror_msg_and_fail("open"); + +# ifdef YFLAG + char *cwd = NULL; + get_curdir_fd(&cwd); + char *fname_realpath = xasprintf("%s/%s", cwd, fname); +# endif + const char *fname_context = SELINUX_FILECONTEXT(fname); long rc = syscall(__NR_fchmod, fd, 0600); - printf("fchmod(%d, 0600) = %s\n", fd, sprintrc(rc)); +# ifdef YFLAG + printf("%sfchmod(%d<%s>%s, 0600) = %s\n", +# else + printf("%sfchmod(%d%s, 0600) = %s\n", +# endif + my_secontext, + fd, +# ifdef YFLAG + fname_realpath, +# endif + fname_context, + sprintrc(rc)); - close(fd); + if (unlink(fname)) + perror_msg_and_fail("unlink"); rc = syscall(__NR_fchmod, fd, 051); - printf("fchmod(%d, 051) = %s\n", fd, sprintrc(rc)); +# ifdef YFLAG + printf("%sfchmod(%d<%s (deleted)>%s, 051) = %s\n", +# else + printf("%sfchmod(%d%s, 051) = %s\n", +# endif + my_secontext, + fd, +# ifdef YFLAG + fname_realpath, +# endif + fname_context, + sprintrc(rc)); rc = syscall(__NR_fchmod, fd, 004); - printf("fchmod(%d, 004) = %s\n", fd, sprintrc(rc)); +# ifdef YFLAG + printf("%sfchmod(%d<%s (deleted)>%s, 004) = %s\n", +# else + printf("%sfchmod(%d%s, 004) = %s\n", +# endif + my_secontext, + fd, +# ifdef YFLAG + fname_realpath, +# endif + fname_context, + sprintrc(rc)); puts("+++ exited with 0 +++"); return 0; diff --git a/tests/fchmodat--secontext.c b/tests/fchmodat--secontext.c new file mode 100644 index 0000000000..876863e3a9 --- /dev/null +++ b/tests/fchmodat--secontext.c @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2021 The strace developers. + * All rights reserved. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "tests.h" + +#ifdef HAVE_SELINUX_RUNTIME + +# define TEST_SECONTEXT +# include "fchmodat.c" + +#else + +SKIP_MAIN_UNDEFINED("HAVE_SELINUX_RUNTIME") + +#endif diff --git a/tests/fchmodat--secontext_full.c b/tests/fchmodat--secontext_full.c new file mode 100644 index 0000000000..b0dbaa0c07 --- /dev/null +++ b/tests/fchmodat--secontext_full.c @@ -0,0 +1,10 @@ +/* + * Copyright (c) 2021 The strace developers. + * All rights reserved. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#define PRINT_SECONTEXT_FULL + +#include "fchmodat--secontext.c" diff --git a/tests/fchmodat.c b/tests/fchmodat.c index f059008acd..6170655af7 100644 --- a/tests/fchmodat.c +++ b/tests/fchmodat.c @@ -16,31 +16,85 @@ # include # include # include +# include +# include # include +# include "selinux.c" + int main(void) { static const char sample[] = "fchmodat_sample"; + char *my_secontext = SELINUX_MYCONTEXT(); if (open(sample, O_RDONLY | O_CREAT, 0400) < 0) perror_msg_and_fail("open"); + char *sample_secontext = SELINUX_FILECONTEXT(sample); + long rc = syscall(__NR_fchmodat, -100, sample, 0600); - printf("fchmodat(AT_FDCWD, \"%s\", 0600) = %s\n", - sample, sprintrc(rc)); + printf("%sfchmodat(AT_FDCWD, \"%s\"%s, 0600) = %s\n", + my_secontext, + sample, sample_secontext, + sprintrc(rc)); if (unlink(sample)) perror_msg_and_fail("unlink"); rc = syscall(__NR_fchmodat, -100, sample, 051); - printf("fchmodat(AT_FDCWD, \"%s\", 051) = %s\n", + printf("%sfchmodat(AT_FDCWD, \"%s\", 051) = %s\n", + my_secontext, sample, sprintrc(rc)); rc = syscall(__NR_fchmodat, -100, sample, 004); - printf("fchmodat(AT_FDCWD, \"%s\", 004) = %s\n", + printf("%sfchmodat(AT_FDCWD, \"%s\", 004) = %s\n", + my_secontext, sample, sprintrc(rc)); + /* + * Tests with dirfd + */ + + char *cwd = NULL; + int cwd_fd = get_curdir_fd(&cwd); + char *cwd_secontext = SELINUX_FILECONTEXT("."); + char *sample_realpath = xasprintf("%s/%s", cwd, sample); + + /* no file */ + rc = syscall(__NR_fchmodat, cwd_fd, sample, 0400); + printf("%sfchmodat(%d%s, \"%s\", 0400) = %s\n", + my_secontext, + cwd_fd, cwd_secontext, + sample, + sprintrc(rc)); + + if (open(sample, O_RDONLY | O_CREAT, 0400) < 0) + perror_msg_and_fail("open"); + + rc = syscall(__NR_fchmodat, cwd_fd, sample, 0400); + printf("%sfchmodat(%d%s, \"%s\"%s, 0400) = %s\n", + my_secontext, + cwd_fd, cwd_secontext, + sample, sample_secontext, + sprintrc(rc)); + + /* cwd_fd ignored when path is absolute */ + if (chdir("..") == -1) + perror_msg_and_fail("chdir"); + + rc = syscall(__NR_fchmodat, cwd_fd, sample_realpath, 0400); + printf("%sfchmodat(%d%s, \"%s\"%s, 0400) = %s\n", + my_secontext, + cwd_fd, cwd_secontext, + sample_realpath, sample_secontext, + sprintrc(rc)); + + if (fchdir(cwd_fd) == -1) + perror_msg_and_fail("fchdir"); + + unlink(sample); + puts("+++ exited with 0 +++"); return 0; } diff --git a/tests/fchownat--secontext.c b/tests/fchownat--secontext.c new file mode 100644 index 0000000000..8810ff92f0 --- /dev/null +++ b/tests/fchownat--secontext.c @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2021 The strace developers. + * All rights reserved. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "tests.h" + +#ifdef HAVE_SELINUX_RUNTIME + +# define TEST_SECONTEXT +# include "fchownat.c" + +#else + +SKIP_MAIN_UNDEFINED("HAVE_SELINUX_RUNTIME") + +#endif diff --git a/tests/fchownat--secontext_full.c b/tests/fchownat--secontext_full.c new file mode 100644 index 0000000000..9f9e1486d3 --- /dev/null +++ b/tests/fchownat--secontext_full.c @@ -0,0 +1,2 @@ +#define PRINT_SECONTEXT_FULL +#include "fchownat--secontext.c" diff --git a/tests/fchownat.c b/tests/fchownat.c index ca1d14ad3a..7d0cd18661 100644 --- a/tests/fchownat.c +++ b/tests/fchownat.c @@ -15,30 +15,90 @@ #if defined __NR_fchownat && defined AT_FDCWD && defined AT_SYMLINK_NOFOLLOW # include +# include +# include # include +# include "selinux.c" + int main(void) { static const char sample[] = "fchownat_sample"; + char *my_secontext = SELINUX_MYCONTEXT(); uid_t uid = geteuid(); uid_t gid = getegid(); + int fd = open(sample, O_RDONLY | O_CREAT, 0400); - if (open(sample, O_RDONLY | O_CREAT, 0400) == -1) + if (fd == -1) perror_msg_and_fail("open"); + close(fd); + + char *sample_secontext = SELINUX_FILECONTEXT(sample); long rc = syscall(__NR_fchownat, AT_FDCWD, sample, uid, gid, 0); - printf("fchownat(AT_FDCWD, \"%s\", %d, %d, 0) = %s\n", - sample, uid, gid, sprintrc(rc)); + printf("%sfchownat(AT_FDCWD, \"%s\"%s, %d, %d, 0) = %s\n", + my_secontext, + sample, sample_secontext, + uid, gid, sprintrc(rc)); if (unlink(sample)) perror_msg_and_fail("unlink"); rc = syscall(__NR_fchownat, AT_FDCWD, sample, -1, -1L, AT_SYMLINK_NOFOLLOW); - printf("fchownat(AT_FDCWD, \"%s\", -1, -1, AT_SYMLINK_NOFOLLOW) = %s\n", + printf("%sfchownat(AT_FDCWD, \"%s\", -1, -1, AT_SYMLINK_NOFOLLOW) = %s\n", + my_secontext, sample, sprintrc(rc)); + /* + * Tests with dirfd + */ + + char *cwd = NULL; + int cwd_fd = get_curdir_fd(&cwd); + char *cwd_secontext = SELINUX_FILECONTEXT("."); + char *sample_realpath = xasprintf("%s/%s", cwd, sample); + + /* no file */ + rc = syscall(__NR_fchownat, cwd_fd, sample, uid, gid, 0); + printf("%sfchownat(%d%s, \"%s\", %d, %d, 0) = %s\n", + my_secontext, + cwd_fd, cwd_secontext, + sample, + uid, gid, + sprintrc(rc)); + + fd = open(sample, O_RDONLY | O_CREAT, 0400); + if (fd == -1) + perror_msg_and_fail("open"); + close(fd); + + rc = syscall(__NR_fchownat, cwd_fd, sample, uid, gid, 0); + printf("%sfchownat(%d%s, \"%s\"%s, %d, %d, 0) = %s\n", + my_secontext, + cwd_fd, cwd_secontext, + sample, sample_secontext, + uid, gid, + sprintrc(rc)); + + /* cwd_fd ignored when path is absolute */ + if (chdir("..") == -1) + perror_msg_and_fail("chdir"); + + rc = syscall(__NR_fchownat, cwd_fd, sample_realpath, uid, gid, 0); + printf("%sfchownat(%d%s, \"%s\"%s, %d, %d, 0) = %s\n", + my_secontext, + cwd_fd, cwd_secontext, + sample_realpath, sample_secontext, + uid, gid, + sprintrc(rc)); + + if (fchdir(cwd_fd) == -1) + perror_msg_and_fail("fchdir"); + + unlink(sample); + puts("+++ exited with 0 +++"); return 0; } diff --git a/tests/file_handle--secontext.c b/tests/file_handle--secontext.c new file mode 100644 index 0000000000..3f441e8de4 --- /dev/null +++ b/tests/file_handle--secontext.c @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2021 The strace developers. + * All rights reserved. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "tests.h" + +#ifdef HAVE_SELINUX_RUNTIME + +# define TEST_SECONTEXT +# include "file_handle.c" + +#else + +SKIP_MAIN_UNDEFINED("HAVE_SELINUX_RUNTIME") + +#endif diff --git a/tests/file_handle--secontext_full.c b/tests/file_handle--secontext_full.c new file mode 100644 index 0000000000..b4d06fc0ef --- /dev/null +++ b/tests/file_handle--secontext_full.c @@ -0,0 +1,2 @@ +#define PRINT_SECONTEXT_FULL +#include "file_handle--secontext.c" diff --git a/tests/file_handle.c b/tests/file_handle.c index e6a69910bf..277be112cc 100644 --- a/tests/file_handle.c +++ b/tests/file_handle.c @@ -19,8 +19,11 @@ # include # include # include +# include # include +# include "selinux.c" + enum assert_rc { ASSERT_NONE, ASSERT_SUCCESS, @@ -48,6 +51,7 @@ print_handle_data(unsigned char *bytes, unsigned int size) printf("..."); } +# ifndef TEST_SECONTEXT void do_name_to_handle_at(kernel_ulong_t dirfd, const char *dirfd_str, kernel_ulong_t pathname, const char *pathname_str, @@ -129,6 +133,7 @@ do_open_by_handle_at(kernel_ulong_t mount_fd, printf("%s\n", sprintrc(rc)); } +# endif struct strval { kernel_ulong_t val; @@ -141,12 +146,87 @@ struct strval { int main(void) { + char *my_secontext = SELINUX_MYCONTEXT(); enum { PATH1_SIZE = 64, }; static const kernel_ulong_t fdcwd = (kernel_ulong_t) 0x87654321ffffff9cULL; + + struct file_handle *handle = + tail_alloc(sizeof(struct file_handle) + MAX_HANDLE_SZ); + struct file_handle *handle_0 = + tail_alloc(sizeof(struct file_handle) + 0); + struct file_handle *handle_8 = + tail_alloc(sizeof(struct file_handle) + 8); + struct file_handle *handle_128 = + tail_alloc(sizeof(struct file_handle) + 128); + struct file_handle *handle_256 = + tail_alloc(sizeof(struct file_handle) + 256); + TAIL_ALLOC_OBJECT_CONST_PTR(int, bogus_mount_id); + + char handle_0_addr[sizeof("0x") + sizeof(void *) * 2]; + + const int flags = 0x400; + int mount_id; + + handle_0->handle_bytes = 256; + handle_8->handle_bytes = 0; + handle_128->handle_bytes = 128; + handle_256->handle_bytes = 256; + + fill_memory((char *) handle_128 + sizeof(struct file_handle), 128); + fill_memory((char *) handle_256 + sizeof(struct file_handle), 256); + + snprintf(handle_0_addr, sizeof(handle_0_addr), "%p", + handle_0 + sizeof(struct file_handle)); + + handle->handle_bytes = 0; + + char path[] = "."; + char *path_secontext = SELINUX_FILECONTEXT(path); + + assert(syscall(__NR_name_to_handle_at, fdcwd, path, handle, &mount_id, + flags | 1) == -1); + if (EINVAL != errno) + perror_msg_and_skip("name_to_handle_at"); + printf("%sname_to_handle_at(AT_FDCWD, \"%s\"%s, {handle_bytes=0}, %p" + ", AT_SYMLINK_FOLLOW|0x1) = -1 EINVAL (%m)\n", + my_secontext, + path, path_secontext, + &mount_id); + + assert(syscall(__NR_name_to_handle_at, fdcwd, path, handle, &mount_id, + flags) == -1); + if (EOVERFLOW != errno) + perror_msg_and_skip("name_to_handle_at"); + printf("%sname_to_handle_at(AT_FDCWD, \"%s\"%s, {handle_bytes=0 => %u}" + ", %p, AT_SYMLINK_FOLLOW) = -1 EOVERFLOW (%m)\n", + my_secontext, + path, path_secontext, + handle->handle_bytes, &mount_id); + + assert(syscall(__NR_name_to_handle_at, fdcwd, path, handle, &mount_id, + flags) == 0); + printf("%sname_to_handle_at(AT_FDCWD, \"%s\"%s, {handle_bytes=%u" + ", handle_type=%d, f_handle=", + my_secontext, + path, path_secontext, + handle->handle_bytes, handle->handle_type); + print_handle_data(handle->f_handle, handle->handle_bytes); + printf("}, [%d], AT_SYMLINK_FOLLOW) = 0\n", mount_id); + + printf("%sopen_by_handle_at(-1, {handle_bytes=%u, handle_type=%d" + ", f_handle=", + my_secontext, + handle->handle_bytes, handle->handle_type); + print_handle_data(handle->f_handle, handle->handle_bytes); + int rc = syscall(__NR_open_by_handle_at, -1, handle, + O_RDONLY | O_DIRECTORY); + printf("}, O_RDONLY|O_DIRECTORY) = %d %s (%m)\n", rc, errno2name()); + +# ifndef TEST_SECONTEXT static const struct strval dirfds[] = { { (kernel_ulong_t) 0xdeadca57badda7a1ULL, "-1159878751" }, { (kernel_ulong_t) 0x12345678ffffff9cULL, "AT_FDCWD" }, @@ -171,29 +251,11 @@ main(void) }; static const char str64[] = STR64; - - char *bogus_path1 = tail_memdup(str64, PATH1_SIZE); char *bogus_path2 = tail_memdup(str64, sizeof(str64)); - - struct file_handle *handle = - tail_alloc(sizeof(struct file_handle) + MAX_HANDLE_SZ); - struct file_handle *handle_0 = - tail_alloc(sizeof(struct file_handle) + 0); - struct file_handle *handle_8 = - tail_alloc(sizeof(struct file_handle) + 8); - struct file_handle *handle_128 = - tail_alloc(sizeof(struct file_handle) + 128); - struct file_handle *handle_256 = - tail_alloc(sizeof(struct file_handle) + 256); - TAIL_ALLOC_OBJECT_CONST_PTR(int, bogus_mount_id); - - char handle_0_addr[sizeof("0x") + sizeof(void *) * 2]; - char bogus_path1_addr[sizeof("0x") + sizeof(void *) * 2]; char bogus_path1_after_addr[sizeof("0x") + sizeof(void *) * 2]; - struct strval paths[] = { { (kernel_ulong_t) 0, "NULL" }, { (kernel_ulong_t) (uintptr_t) (bogus_path1 + PATH1_SIZE), @@ -229,62 +291,16 @@ main(void) (kernel_ulong_t) (uintptr_t) bogus_mount_id, }; - const int flags = 0x400; - int mount_id; unsigned int i; unsigned int j; unsigned int k; unsigned int l; unsigned int m; - snprintf(bogus_path1_addr, sizeof(bogus_path1_addr), "%p", bogus_path1); snprintf(bogus_path1_after_addr, sizeof(bogus_path1_after_addr), "%p", bogus_path1 + PATH1_SIZE); - handle_0->handle_bytes = 256; - handle_8->handle_bytes = 0; - handle_128->handle_bytes = 128; - handle_256->handle_bytes = 256; - - fill_memory((char *) handle_128 + sizeof(struct file_handle), 128); - fill_memory((char *) handle_256 + sizeof(struct file_handle), 256); - - snprintf(handle_0_addr, sizeof(handle_0_addr), "%p", - handle_0 + sizeof(struct file_handle)); - - handle->handle_bytes = 0; - - assert(syscall(__NR_name_to_handle_at, fdcwd, ".", handle, &mount_id, - flags | 1) == -1); - if (EINVAL != errno) - perror_msg_and_skip("name_to_handle_at"); - printf("name_to_handle_at(AT_FDCWD, \".\", {handle_bytes=0}, %p" - ", AT_SYMLINK_FOLLOW|0x1) = -1 EINVAL (%m)\n", &mount_id); - - assert(syscall(__NR_name_to_handle_at, fdcwd, ".", handle, &mount_id, - flags) == -1); - if (EOVERFLOW != errno) - perror_msg_and_skip("name_to_handle_at"); - printf("name_to_handle_at(AT_FDCWD, \".\", {handle_bytes=0 => %u}" - ", %p, AT_SYMLINK_FOLLOW) = -1 EOVERFLOW (%m)\n", - handle->handle_bytes, &mount_id); - - assert(syscall(__NR_name_to_handle_at, fdcwd, ".", handle, &mount_id, - flags) == 0); - printf("name_to_handle_at(AT_FDCWD, \".\", {handle_bytes=%u" - ", handle_type=%d, f_handle=", - handle->handle_bytes, handle->handle_type); - print_handle_data(handle->f_handle, handle->handle_bytes); - printf("}, [%d], AT_SYMLINK_FOLLOW) = 0\n", mount_id); - - printf("open_by_handle_at(-1, {handle_bytes=%u, handle_type=%d" - ", f_handle=", handle->handle_bytes, handle->handle_type); - print_handle_data(handle->f_handle, handle->handle_bytes); - int rc = syscall(__NR_open_by_handle_at, -1, handle, - O_RDONLY | O_DIRECTORY); - printf("}, O_RDONLY|O_DIRECTORY) = %d %s (%m)\n", rc, errno2name()); - for (i = 0; i < ARRAY_SIZE(dirfds); i++) { for (j = 0; j < ARRAY_SIZE(paths); j++) { for (k = 0; k < ARRAY_SIZE(name_handles); k++) { @@ -320,6 +336,70 @@ main(void) } } } +# endif + + /* + * Tests with dirfd + */ + + char *cwd = NULL; + int cwd_fd = get_curdir_fd(&cwd); + char *cwd_secontext = SELINUX_FILECONTEXT("."); + + assert(syscall(__NR_name_to_handle_at, cwd_fd, path, handle, &mount_id, + flags) == 0); + printf("%sname_to_handle_at(%d%s, \"%s\"%s, {handle_bytes=%u" + ", handle_type=%d, f_handle=", + my_secontext, + cwd_fd, cwd_secontext, + path, path_secontext, + handle->handle_bytes, handle->handle_type); + print_handle_data((unsigned char *) handle + + sizeof(struct file_handle), + handle->handle_bytes); + printf("}, [%d], AT_SYMLINK_FOLLOW) = 0\n", mount_id); + + printf("%sopen_by_handle_at(-1, {handle_bytes=%u, handle_type=%d" + ", f_handle=", + my_secontext, + handle->handle_bytes, handle->handle_type); + print_handle_data((unsigned char *) handle + + sizeof(struct file_handle), + handle->handle_bytes); + rc = syscall(__NR_open_by_handle_at, -1, handle, + O_RDONLY | O_DIRECTORY); + printf("}, O_RDONLY|O_DIRECTORY) = %s\n", sprintrc(rc)); + + /* cwd_fd ignored when path is absolute */ + if (chdir("..") == -1) + perror_msg_and_fail("chdir"); + + assert(syscall(__NR_name_to_handle_at, cwd_fd, cwd, handle, &mount_id, + flags) == 0); + printf("%sname_to_handle_at(%d%s, \"%s\"%s, {handle_bytes=%u" + ", handle_type=%d, f_handle=", + my_secontext, + cwd_fd, cwd_secontext, + cwd, cwd_secontext, + handle->handle_bytes, handle->handle_type); + print_handle_data((unsigned char *) handle + + sizeof(struct file_handle), + handle->handle_bytes); + printf("}, [%d], AT_SYMLINK_FOLLOW) = 0\n", mount_id); + + printf("%sopen_by_handle_at(-1, {handle_bytes=%u, handle_type=%d" + ", f_handle=", + my_secontext, + handle->handle_bytes, handle->handle_type); + print_handle_data((unsigned char *) handle + + sizeof(struct file_handle), + handle->handle_bytes); + rc = syscall(__NR_open_by_handle_at, -1, handle, + O_RDONLY | O_DIRECTORY); + printf("}, O_RDONLY|O_DIRECTORY) = %s\n", sprintrc(rc)); + + if (fchdir(cwd_fd) == -1) + perror_msg_and_fail("fchdir"); puts("+++ exited with 0 +++"); return 0; diff --git a/tests/gen_tests.in b/tests/gen_tests.in index 145851043c..04e7e022a3 100644 --- a/tests/gen_tests.in +++ b/tests/gen_tests.in @@ -10,6 +10,8 @@ _newselect-P -e trace=_newselect -P /dev/full 9>>/dev/full accept -a22 accept4 -a37 access -a30 --trace-path=access_sample +access--secontext -a30 --secontext --trace-path=access_sample -e trace=access +access--secontext_full -a30 --secontext=full --trace-path=access_sample -e trace=access acct -a20 add_key -a30 -s12 adjtimex -a15 @@ -25,6 +27,8 @@ bpf-v -a20 -v -e trace=bpf btrfs +ioctl.test chdir -a10 chmod -a28 +chmod--secontext -a28 --secontext -e trace=chmod +chmod--secontext_full -a28 --secontext=full -e trace=chmod chown -a28 chown32 -a31 chroot -a13 @@ -81,10 +85,17 @@ epoll_pwait2-P --trace=epoll_pwait2 -P /dev/full epoll_pwait2-y --trace=epoll_pwait2 -y epoll_wait -a26 erestartsys -a34 -e signal=none -e trace=recvfrom +execve--secontext +execve.test --secontext +execve--secontext_full +execve.test --secontext=full execveat +execveat--secontext --secontext --trace=execveat +execveat--secontext_full --secontext=full --trace=execveat execveat-v -v -e trace=execveat +faccessat--secontext +faccessat.test -a24 --secontext +faccessat--secontext_full +faccessat.test -a24 --secontext=full faccessat-P -a23 --trace=faccessat -P /dev/full faccessat-y +faccessat.test -a24 -y +faccessat-y--secontext +faccessat.test -a24 -y --secontext faccessat-yy +faccessat.test -a24 -yy faccessat2-P -a27 --trace=faccessat2 -P /dev/full faccessat2-y +faccessat2.test -a28 -y @@ -93,21 +104,33 @@ fadvise64_64 +fadvise64.test fallocate -a18 fanotify_init fanotify_mark -a32 +fanotify_mark--secontext -a32 --secontext -e trace=fanotify_mark +fanotify_mark--secontext_full -a32 --secontext=full -e trace=fanotify_mark fanotify_mark-Xabbrev -a32 -Xabbrev -e trace=fanotify_mark fanotify_mark-Xraw -a32 -Xraw -e trace=fanotify_mark fanotify_mark-Xverbose -a32 -Xverbose -e trace=fanotify_mark fchdir -a11 fchmod -a15 -fchmodat +fchmod-y -a15 -y -e trace=fchmod +fchmod--secontext -a15 --secontext -e trace=fchmod +fchmod--secontext_full -a15 --secontext=full -e trace=fchmod +fchmod-y--secontext -a15 -y --secontext -e trace=fchmod +fchmodat -a15 +fchmodat--secontext -a15 --secontext -e trace=fchmodat +fchmodat--secontext_full -a15 --secontext=full -e trace=fchmodat fchown -a16 fchown32 -a18 fchownat +fchownat--secontext --secontext -e trace=fchownat +fchownat--secontext_full --secontext=full -e trace=fchownat fcntl -a8 fcntl--pidns-translation test_pidns -a8 -e trace=fcntl fcntl64 -a8 fcntl64--pidns-translation test_pidns -a8 -e trace=fcntl64 fdatasync -a14 file_handle -e trace=name_to_handle_at,open_by_handle_at +file_handle--secontext --secontext -e trace=name_to_handle_at,open_by_handle_at +file_handle--secontext_full --secontext=full -e trace=name_to_handle_at,open_by_handle_at filter_seccomp . "${srcdir=.}/filter_seccomp.sh"; test_prog_set --seccomp-bpf -f filter_seccomp-flag ../$NAME finit_module -a25 @@ -352,6 +375,8 @@ lchown -a30 lchown32 -a32 link linkat +linkat--secontext --secontext -e trace=linkat +linkat--secontext_full --secontext=full -e trace=linkat lookup_dcookie -a27 lstat -a31 --no-abbrev --trace-path=stat.sample --trace-path=/dev/full lstat64 -a32 --no-abbrev --trace-path=stat.sample --trace-path=/dev/full @@ -491,9 +516,13 @@ oldselect-efault -a13 -e trace=select oldselect-efault-P -a13 -e trace=select -P /dev/full 9>>/dev/full oldstat -a32 -v -P stat.sample -P /dev/full open -a30 -P $NAME.sample +open--secontext -a30 -P open.sample --secontext --trace=open +open--secontext_full -a30 -P open.sample --secontext=full --trace=open open_tree -a30 -y open_tree-P -a30 --decode-fds -P /dev/full -e trace=open_tree openat -a36 -P $NAME.sample +openat--secontext -a36 -P openat.sample -P $PWD/openat.sample --secontext -e trace=openat +openat--secontext_full -a36 -P openat.sample -P $PWD/openat.sample --secontext=full -e trace=openat openat2 -a35 openat2-Xabbrev --trace=openat2 -a35 -Xabbrev openat2-Xraw --trace=openat2 -a32 -Xraw diff --git a/tests/linkat--secontext.c b/tests/linkat--secontext.c new file mode 100644 index 0000000000..812defbed4 --- /dev/null +++ b/tests/linkat--secontext.c @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2021 The strace developers. + * All rights reserved. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "tests.h" + +#ifdef HAVE_SELINUX_RUNTIME + +# define TEST_SECONTEXT +# include "linkat.c" + +#else + +SKIP_MAIN_UNDEFINED("HAVE_SELINUX_RUNTIME") + +#endif diff --git a/tests/linkat--secontext_full.c b/tests/linkat--secontext_full.c new file mode 100644 index 0000000000..e6112ba26b --- /dev/null +++ b/tests/linkat--secontext_full.c @@ -0,0 +1,2 @@ +#define PRINT_SECONTEXT_FULL +#include "linkat--secontext.c" diff --git a/tests/linkat.c b/tests/linkat.c index 7e348f120e..52c42f7798 100644 --- a/tests/linkat.c +++ b/tests/linkat.c @@ -11,7 +11,15 @@ #ifdef __NR_linkat # include +# include +# include # include +# include +# include +# include + +# include "selinux.c" +# include "xmalloc.h" int main(void) @@ -21,18 +29,156 @@ main(void) const long fd_old = (long) 0xdeadbeefffffffffULL; const long fd_new = (long) 0xdeadbeeffffffffeULL; + char *my_secontext = SELINUX_MYCONTEXT(); + long rc = syscall(__NR_linkat, fd_old, sample_1, fd_new, sample_2, 0); - printf("linkat(%d, \"%s\", %d, \"%s\", 0) = %ld %s (%m)\n", + printf("%slinkat(%d, \"%s\", %d, \"%s\", 0) = %ld %s (%m)\n", + my_secontext, (int) fd_old, sample_1, (int) fd_new, sample_2, rc, errno2name()); rc = syscall(__NR_linkat, -100, sample_1, -100, sample_2, -1L); - printf("linkat(%s, \"%s\", %s, \"%s\", %s) = %ld %s (%m)\n", + printf("%slinkat(%s, \"%s\", %s, \"%s\", %s) = %ld %s (%m)\n", + my_secontext, "AT_FDCWD", sample_1, "AT_FDCWD", sample_2, "AT_SYMLINK_NOFOLLOW|AT_REMOVEDIR|AT_SYMLINK_FOLLOW" "|AT_NO_AUTOMOUNT|AT_EMPTY_PATH|AT_RECURSIVE|0xffff60ff", rc, errno2name()); + int fd_sample_1; + + unlink(sample_1); + unlink(sample_2); + fd_sample_1 = open(sample_1, O_RDONLY | O_CREAT, 0400); + if (fd_sample_1 < 0) + perror_msg_and_fail("open"); + if (close(fd_sample_1)) + perror_msg_and_fail("close"); + + char *sample_1_secontext = SELINUX_FILECONTEXT(sample_1); + + rc = syscall(__NR_linkat, -100, sample_1, -100, sample_2, 0); + /* no context printed for sample_2 since file doesn't exist yet */ + printf("%slinkat(AT_FDCWD, \"%s\"%s, AT_FDCWD, \"%s\", 0) = %s\n", + my_secontext, + sample_1, sample_1_secontext, + sample_2, + sprintrc(rc)); + + char *sample_2_secontext = sample_1_secontext; + + rc = syscall(__NR_linkat, -100, sample_1, -100, sample_2, 0); + printf("%slinkat(AT_FDCWD, \"%s\"%s, AT_FDCWD, \"%s\"%s, 0) = %s\n", + my_secontext, + sample_1, sample_1_secontext, + sample_2, sample_2_secontext, + sprintrc(rc)); + + int fd_sample_2; + fd_sample_2 = open(sample_2, O_RDONLY | O_CREAT, 0400); + if (fd_sample_2 < 0) + perror_msg_and_fail("open"); + if (close(fd_sample_2)) + perror_msg_and_fail("close"); + + free(sample_1_secontext); + update_context_type(sample_1, "default_t"); + sample_1_secontext = SELINUX_FILECONTEXT(sample_1); + sample_2_secontext = sample_1_secontext; + + rc = syscall(__NR_linkat, -100, sample_1, -100, sample_2, 0); + printf("%slinkat(AT_FDCWD, \"%s\"%s, AT_FDCWD, \"%s\"%s, 0) = %s\n", + my_secontext, + sample_1, sample_1_secontext, + sample_2, sample_2_secontext, + sprintrc(rc)); + + if (unlink(sample_2)) + perror_msg_and_fail("unlink"); + + /* + * Tests with dirfd + */ + + char *cwd = NULL; + int dfd_old = get_curdir_fd(&cwd); + char *dfd_old_secontext = SELINUX_FILECONTEXT("."); + + static const char sample_2_dir[] = "new"; + char *new_sample_2 = xasprintf("%s/%s", sample_2_dir, sample_2); + + unlink(new_sample_2); + rmdir(sample_2_dir); + + int res = mkdir(sample_2_dir, 0700); + if (res == -1) + perror_msg_and_fail("mkdir"); + char *sample_2_dir_realpath = xasprintf("%s/%s", cwd, sample_2_dir); + char *sample_2_dir_secontext = SELINUX_FILECONTEXT(sample_2_dir); + + int dfd_new = get_dir_fd(sample_2_dir); + + rc = syscall(__NR_linkat, dfd_old, sample_1, -100, sample_2, 0); + /* no context printed for sample_2 since file doesn't exist yet */ + printf("%slinkat(%d%s, \"%s\"%s, AT_FDCWD, \"%s\", 0) = %s\n", + my_secontext, + dfd_old, dfd_old_secontext, + sample_1, sample_1_secontext, + sample_2, + sprintrc(rc)); + + rc = syscall(__NR_linkat, dfd_old, sample_1, -100, sample_2, 0); + printf("%slinkat(%d%s, \"%s\"%s, AT_FDCWD, \"%s\"%s, 0) = %s\n", + my_secontext, + dfd_old, dfd_old_secontext, + sample_1, sample_1_secontext, + sample_2, sample_2_secontext, + sprintrc(rc)); + + if (unlink(sample_2)) + perror_msg_and_fail("unlink"); + + rc = syscall(__NR_linkat, dfd_old, sample_1, dfd_new, sample_2, 0); + /* no context printed for sample_2 since file doesn't exist yet */ + printf("%slinkat(%d%s, \"%s\"%s, %d%s, \"%s\", 0) = %s\n", + my_secontext, + dfd_old, dfd_old_secontext, + sample_1, sample_1_secontext, + dfd_new, sample_2_dir_secontext, + sample_2, + sprintrc(rc)); + + rc = syscall(__NR_linkat, dfd_old, sample_1, dfd_new, sample_2, 0); + printf("%slinkat(%d%s, \"%s\"%s, %d%s, \"%s\"%s, 0) = %s\n", + my_secontext, + dfd_old, dfd_old_secontext, + sample_1, sample_1_secontext, + dfd_new, sample_2_dir_secontext, + sample_2, SELINUX_FILECONTEXT(new_sample_2), + sprintrc(rc)); + + char *new_sample_2_realpath = xasprintf("%s/%s", sample_2_dir_realpath, new_sample_2); + + /* dfd ignored when path is absolute */ + if (chdir("..") == -1) + perror_msg_and_fail("chdir"); + + rc = syscall(__NR_linkat, dfd_old, sample_1, -100, new_sample_2_realpath, 0); + printf("%slinkat(%d%s, \"%s\"%s, AT_FDCWD, \"%s\"%s, 0) = %s\n", + my_secontext, + dfd_old, dfd_old_secontext, + sample_1, sample_1_secontext, + new_sample_2_realpath, SELINUX_FILECONTEXT(new_sample_2_realpath), + sprintrc(rc)); + + if (fchdir(dfd_old) == -1) + perror_msg_and_fail("fchdir"); + + unlink(sample_1); + unlink(sample_2); + unlink(new_sample_2); + rmdir(sample_2_dir); + puts("+++ exited with 0 +++"); return 0; } diff --git a/tests/open--secontext.c b/tests/open--secontext.c new file mode 100644 index 0000000000..1dc8e7a536 --- /dev/null +++ b/tests/open--secontext.c @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2021 The strace developers. + * All rights reserved. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "tests.h" + +#ifdef HAVE_SELINUX_RUNTIME + +# define TEST_SECONTEXT +# include "open.c" + +#else + +SKIP_MAIN_UNDEFINED("HAVE_SELINUX_RUNTIME") + +#endif diff --git a/tests/open--secontext_full.c b/tests/open--secontext_full.c new file mode 100644 index 0000000000..e7ede3c07a --- /dev/null +++ b/tests/open--secontext_full.c @@ -0,0 +1,10 @@ +/* + * Copyright (c) 2021 The strace developers. + * All rights reserved. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#define PRINT_SECONTEXT_FULL + +#include "open--secontext.c" diff --git a/tests/open.c b/tests/open.c index 01ece01d72..c7e1ee3f13 100644 --- a/tests/open.c +++ b/tests/open.c @@ -15,14 +15,18 @@ # include # include +# include "selinux.c" + int main(void) { static const char sample[] = "open.sample"; + char *my_secontext = SELINUX_MYCONTEXT(); long fd = syscall(__NR_open, sample, O_RDONLY|O_CREAT, 0400); - printf("open(\"%s\", O_RDONLY|O_CREAT, 0400) = %s\n", - sample, sprintrc(fd)); + printf("%sopen(\"%s\", O_RDONLY|O_CREAT, 0400) = %s%s\n", + my_secontext, + sample, sprintrc(fd), SELINUX_FILECONTEXT(sample)); if (fd != -1) { close(fd); @@ -30,16 +34,18 @@ main(void) perror_msg_and_fail("unlink"); fd = syscall(__NR_open, sample, O_RDONLY); - printf("open(\"%s\", O_RDONLY) = %s\n", sample, sprintrc(fd)); + printf("%sopen(\"%s\", O_RDONLY) = %s\n", + my_secontext, sample, sprintrc(fd)); fd = syscall(__NR_open, sample, O_WRONLY|O_NONBLOCK|0x80000000); - printf("open(\"%s\", O_WRONLY|O_NONBLOCK|0x80000000) = %s\n", - sample, sprintrc(fd)); + printf("%sopen(\"%s\", O_WRONLY|O_NONBLOCK|0x80000000) = %s\n", + my_secontext, sample, sprintrc(fd)); } # ifdef O_TMPFILE fd = syscall(__NR_open, sample, O_WRONLY|O_TMPFILE, 0600); - printf("open(\"%s\", O_WRONLY|O_TMPFILE, 0600) = %s\n", + printf("%sopen(\"%s\", O_WRONLY|O_TMPFILE, 0600) = %s\n", + my_secontext, sample, sprintrc(fd)); # endif /* O_TMPFILE */ diff --git a/tests/openat--secontext.c b/tests/openat--secontext.c new file mode 100644 index 0000000000..f174a8e9e6 --- /dev/null +++ b/tests/openat--secontext.c @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2021 The strace developers. + * All rights reserved. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "tests.h" + +#ifdef HAVE_SELINUX_RUNTIME + +# define TEST_SECONTEXT +# include "openat.c" + +#else + +SKIP_MAIN_UNDEFINED("HAVE_SELINUX_RUNTIME") + +#endif diff --git a/tests/openat--secontext_full.c b/tests/openat--secontext_full.c new file mode 100644 index 0000000000..0515212a1e --- /dev/null +++ b/tests/openat--secontext_full.c @@ -0,0 +1,10 @@ +/* + * Copyright (c) 2021 The strace developers. + * All rights reserved. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#define PRINT_SECONTEXT_FULL + +#include "openat--secontext.c" diff --git a/tests/openat.c b/tests/openat.c index 8d1bca0bb2..eea6a06a26 100644 --- a/tests/openat.c +++ b/tests/openat.c @@ -13,8 +13,12 @@ # include # include +# include +# include # include +# include "selinux.c" + # ifdef O_TMPFILE /* The kernel & C libraries often inline O_DIRECTORY. */ # define STRACE_O_TMPFILE (O_TMPFILE & ~O_DIRECTORY) @@ -29,7 +33,8 @@ test_mode_flag(unsigned int mode_val, const char *mode_str, unsigned int flag_val, const char *flag_str) { long rc = syscall(__NR_openat, -1, sample, mode_val | flag_val, 0); - printf("openat(-1, \"%s\", %s%s%s%s) = %s\n", + printf("%sopenat(-1, \"%s\", %s%s%s%s) = %s\n", + SELINUX_MYCONTEXT(), sample, mode_str, flag_val ? "|" : "", flag_str, flag_val & (O_CREAT | STRACE_O_TMPFILE) ? ", 000" : "", @@ -39,20 +44,7 @@ test_mode_flag(unsigned int mode_val, const char *mode_str, int main(void) { - long fd = syscall(__NR_openat, -100, sample, O_RDONLY|O_CREAT, 0400); - printf("openat(AT_FDCWD, \"%s\", O_RDONLY|O_CREAT, 0400) = %s\n", - sample, sprintrc(fd)); - - if (fd != -1) { - close(fd); - if (unlink(sample) == -1) - perror_msg_and_fail("unlink"); - - fd = syscall(__NR_openat, -100, sample, O_RDONLY); - printf("openat(AT_FDCWD, \"%s\", O_RDONLY) = %s\n", - sample, sprintrc(fd)); - } - + char *my_secontext = SELINUX_MYCONTEXT(); struct { unsigned int val; const char *str; @@ -101,6 +93,65 @@ main(void) test_mode_flag(modes[m].val, modes[m].str, flags[f].val, flags[f].str); + long fd = syscall(__NR_openat, -100, sample, O_RDONLY|O_CREAT, 0400); + + char *sample_secontext = SELINUX_FILECONTEXT(sample); + + /* + * file context in openat() is not displayed because file doesn't exist + * yet, but is displayed in return value since the file got created + */ + printf("%sopenat(AT_FDCWD, \"%s\", O_RDONLY|O_CREAT, 0400) = %s%s\n", + my_secontext, + sample, + sprintrc(fd), sample_secontext); + + close(fd); + + fd = syscall(__NR_openat, -100, sample, O_RDONLY); + printf("%sopenat(AT_FDCWD, \"%s\"%s, O_RDONLY) = %s%s\n", + my_secontext, + sample, sample_secontext, + sprintrc(fd), sample_secontext); + if (fd != -1) + close(fd); + + if (unlink(sample) == -1) + perror_msg_and_fail("unlink"); + + /* + * Tests with dirfd + */ + + int cwd_fd = get_curdir_fd(NULL); + char *cwd_secontext = SELINUX_FILECONTEXT("."); + + fd = syscall(__NR_openat, cwd_fd, sample, O_RDONLY|O_CREAT, 0400); + if (fd == -1) + perror_msg_and_fail("openat"); + close(fd); + + /* + * file context in openat() is not displayed because file doesn't exist + * yet, but is displayed in return value since the file got created + */ + printf("%sopenat(%d%s, \"%s\", O_RDONLY|O_CREAT, 0400) = %s%s\n", + my_secontext, + cwd_fd, cwd_secontext, + sample, + sprintrc(fd), sample_secontext); + + fd = syscall(__NR_openat, cwd_fd, sample, O_RDONLY); + printf("%sopenat(%d%s, \"%s\"%s, O_RDONLY) = %s%s\n", + my_secontext, + cwd_fd, cwd_secontext, + sample, sample_secontext, + sprintrc(fd), sample_secontext); + if (fd != -1) + close(fd); + + unlink(sample); + puts("+++ exited with 0 +++"); return 0; } diff --git a/tests/options-syntax.test b/tests/options-syntax.test index 4c071ac5ab..fa7001aa5c 100755 --- a/tests/options-syntax.test +++ b/tests/options-syntax.test @@ -3,13 +3,15 @@ # Check strace options syntax. # # Copyright (c) 2016 Dmitry V. Levin -# Copyright (c) 2016-2020 The strace developers. +# Copyright (c) 2016-2021 The strace developers. # All rights reserved. # # SPDX-License-Identifier: GPL-2.0-or-later . "${srcdir=.}/syntax.sh" +compiled_with_secontext=$(get_config_option USE_SELINUX "y") + check_e "Invalid process id: '0'" -p 0 check_e "Invalid process id: '0'" --attach=0 check_e "Invalid process id: '-42'" -p -42 @@ -46,6 +48,8 @@ check_e '-t and --absolute-timestamps cannot be provided simultaneously' -t --ti check_e '-t and --absolute-timestamps cannot be provided simultaneously' --absolute-timestamps -ttt -p $$ check_e '-t and --absolute-timestamps cannot be provided simultaneously' -t --timestamps=ns -t -p $$ check_e '-t and --absolute-timestamps cannot be provided simultaneously' --timestamps=ns -t --absolute-timestamps=unix -p $$ +[ -z "$compiled_with_secontext" ] || + check_h "invalid --secontext argument: 'ss'" --secontext=ss check_h 'PROG [ARGS] must be specified with -D/--daemonize' -D -p $$ check_h 'PROG [ARGS] must be specified with -D/--daemonize' -DD -p $$ check_h 'PROG [ARGS] must be specified with -D/--daemonize' -DDD -p $$ @@ -282,6 +286,11 @@ $STRACE_EXE: -y/--decode-fds has no effect with -c/--summary-only $STRACE_EXE: Only the last of -z/--successful-only/-Z/--failed-only options will take effect. See status qualifier for more complex filters. $STRACE_EXE: $umsg" -u :nosuchuser: -cinrtTyzZ true + if [ -n "$compiled_with_secontext" ]; then + check_e "--secontext has no effect with -c/--summary-only +$STRACE_EXE: $umsg" -u :nosuchuser: -c --secontext true + fi + for c in --output-separately -A/--output-append-mode; do check_e "$c has no effect without -o/--output $STRACE_EXE: $umsg" -u :nosuchuser: ${c%%/*} true diff --git a/tests/pure_executables.list b/tests/pure_executables.list index 9c329c4e01..6ff5066b86 100755 --- a/tests/pure_executables.list +++ b/tests/pure_executables.list @@ -84,6 +84,7 @@ fanotify_mark-Xraw fanotify_mark-Xverbose fchdir fchmod +fchmod-y fchmodat fchown fchown32 diff --git a/tests/selinux.c b/tests/selinux.c new file mode 100644 index 0000000000..06fddf3555 --- /dev/null +++ b/tests/selinux.c @@ -0,0 +1,214 @@ +#if defined TEST_SECONTEXT && HAVE_SELINUX_RUNTIME + +# include +# include +# include +# include +# include +# include +# include +# include + +# include "xmalloc.h" + +/* + * Contexts allocate memory which may be freed using a free() call. + */ + +# define CONTEXT_FORMAT_SPACE_BEFORE(string) \ + context_format(string, " [%s]") +# define CONTEXT_FORMAT_SPACE_AFTER(string) \ + context_format(string, "[%s] ") +# define CONTEXT_FORMAT_SPACE_BEFORE_QUOTED(string) \ + context_format(string, " \\[%s\\]") +# define CONTEXT_FORMAT_SPACE_AFTER_QUOTED(string) \ + context_format(string, "\\[%s\\] ") + +# ifdef PRINT_SECONTEXT_FULL + +# define SELINUX_FILECONTEXT(filename) \ + CONTEXT_FORMAT_SPACE_BEFORE(get_file_context_full(filename)) +# define SELINUX_PIDCONTEXT(pid) \ + CONTEXT_FORMAT_SPACE_AFTER(get_pid_context_full(pid)) +# define SELINUX_FILECONTEXT_QUOTED(filename) \ + CONTEXT_FORMAT_SPACE_BEFORE_QUOTED(get_file_context_full(filename)) +# define SELINUX_PIDCONTEXT_QUOTED(pid) \ + CONTEXT_FORMAT_SPACE_AFTER_QUOTED(get_pid_context_full(pid)) + +# else + +# define SELINUX_FILECONTEXT(filename) \ + CONTEXT_FORMAT_SPACE_BEFORE(get_file_context_short(filename)) +# define SELINUX_PIDCONTEXT(pid) \ + CONTEXT_FORMAT_SPACE_AFTER(get_pid_context_short(pid)) +# define SELINUX_FILECONTEXT_QUOTED(filename) \ + CONTEXT_FORMAT_SPACE_BEFORE_QUOTED(get_file_context_short(filename)) +# define SELINUX_PIDCONTEXT_QUOTED(pid) \ + CONTEXT_FORMAT_SPACE_AFTER_QUOTED(get_pid_context_short(pid)) + +# endif + +# define SELINUX_MYCONTEXT() SELINUX_PIDCONTEXT(getpid()) +# define SELINUX_MYCONTEXT_QUOTED() SELINUX_PIDCONTEXT_QUOTED(getpid()) + +char * +context_format(char *context, const char *fmt) +{ + int saved_errno = errno; + char *res = context ? xasprintf(fmt, context) : xstrdup(""); + free(context); + errno = saved_errno; + return res; +} + +static char * +strip_trailing_newlines(char *context) +{ + /* + * On the CI at least, the context may have a trailing \n, + * let's remove it just in case + */ + int index; + for (index = strlen(context) - 1; index >= 0; index--) { + if (context[index] != '\n') + break; + context[index] = '\0'; + } + return context; +} + +char * +get_file_context_full(const char *filename) +{ + int saved_errno = errno; + char *full_context = NULL; + char *secontext; + if (getfilecon(filename, &secontext) >= 0) { + full_context = strip_trailing_newlines(xstrdup(secontext)); + freecon(secontext); + } + errno = saved_errno; + return full_context; +} + +char * +get_file_context_short(const char *filename) +{ + char *ctx = get_file_context_full(filename); + + if (ctx == NULL) + return ctx; + + int saved_errno = errno; + + char *saveptr = NULL; + const char *token; + int i; + + char *ctx_copy = xstrdup(ctx); + char *context = NULL; + for (token = strtok_r(ctx_copy, ":", &saveptr), i = 0; + token; token = strtok_r(NULL, ":", &saveptr), i++) { + if (i == 2) { + context = xstrdup(token); + break; + } + } + if (context == NULL) + context = xstrdup(ctx); + free(ctx_copy); + free(ctx); + + errno = saved_errno; + return context; +} + +char * +get_pid_context_full(pid_t pid) +{ + int saved_errno = errno; + char *full_context = NULL; + char *secontext; + + if (getpidcon(pid, &secontext) == 0) { + full_context = strip_trailing_newlines(xstrdup(secontext)); + freecon(secontext); + } + errno = saved_errno; + return full_context; +} + +char * +get_pid_context_short(pid_t pid) +{ + char *ctx = get_pid_context_full(pid); + + if (ctx == NULL) + return ctx; + + int saved_errno = errno; + + char *saveptr = NULL; + const char *token; + int i; + + char *ctx_copy = xstrdup(ctx); + char *context = NULL; + for (token = strtok_r(ctx_copy, ":", &saveptr), i = 0; + token; token = strtok_r(NULL, ":", &saveptr), i++) { + if (i == 2) { + context = xstrdup(token); + break; + } + } + if (context == NULL) + context = xstrdup(ctx); + free(ctx_copy); + free(ctx); + + errno = saved_errno; + return context; +} + +void +update_context_type(const char *file, const char *newtype) +{ + char *ctx = get_file_context_full(file); + + if (ctx == NULL) + return; + + char *saveptr = NULL; + char *token; + int field; + char *split[4]; + + for (token = strtok_r(ctx, ":", &saveptr), field = 0; + token; token = strtok_r(NULL, ":", &saveptr), field++) { + assert(field < 4); + split[field] = token; + } + assert(field == 4); + + char *newcontext = xasprintf("%s:%s:%s:%s", split[0], split[1], + newtype, split[3]); + + (void) setfilecon(file, newcontext); + + free(newcontext); + free(ctx); +} + +#else + +# include "xmalloc.h" + +# define SELINUX_FILECONTEXT(filename) xstrdup("") +# define SELINUX_PIDCONTEXT(pid) xstrdup("") +# define SELINUX_FILECONTEXT_QUOTED(filename) xstrdup("") +# define SELINUX_PIDCONTEXT_QUOTED(pid) xstrdup("") +# define SELINUX_MYCONTEXT() xstrdup("") +# define SELINUX_MYCONTEXT_QUOTED() xstrdup("") +# define update_context_type(file, newtype) do {} while(0) + +#endif diff --git a/tests/strace-V.test b/tests/strace-V.test index 30f10808c2..b5c620e9af 100755 --- a/tests/strace-V.test +++ b/tests/strace-V.test @@ -33,7 +33,9 @@ aarch64|powerpc64|s390x|sparc64|tile|x32) ;; esac -features="${option_unwind}${option_demangle}${option_m32}${option_mx32}" +option_secontext=$(get_config_option USE_SELINUX " secontext") + +features="${option_unwind}${option_demangle}${option_m32}${option_mx32}${option_secontext}" [ -n "$features" ] || features=" (none)" cat > "$EXP" << __EOF__ diff --git a/tests/tests.h b/tests/tests.h index a3ebceea4b..ab45117e6a 100644 --- a/tests/tests.h +++ b/tests/tests.h @@ -140,6 +140,17 @@ void perror_msg_and_skip(const char *, ...) /* Stat the specified file and skip the test if the stat call failed. */ void skip_if_unavailable(const char *); +/* + * Obtain the dirfd of the specified dir_name, dies on failure. + */ +int get_dir_fd(const char *dir_path); + +/* + * Obtain the dirfd of the current directory and returns the directory in + * curdir pointer if not NULL (memory must be freed), dies on failure. + */ +int get_curdir_fd(char **curdir); + /* * Obtain an exclusive lock on dirname(path_name)/lock_name file * using open and flock.