diff --git a/configure.ac b/configure.ac index e98263d1..9ef85f29 100644 --- a/configure.ac +++ b/configure.ac @@ -302,6 +302,15 @@ AX_CHECK_ATM AX_CHECK_PAM(AC_DEFINE([PPP_WITH_PAM], 1, [Support for Pluggable Authentication Modules])) AM_CONDITIONAL(PPP_WITH_PAM, test "x${with_pam}" = "xyes") +# +# With libcap support for capability-based privilege management (Linux only, Solaris uses geteuid check) +AM_COND_IF([LINUX], [ + AX_CHECK_CAP(AC_DEFINE([USE_CAP], 1, [Use libcap for capability checking])) + AM_CONDITIONAL(WITH_LIBCAP, test "x${with_libcap}" = "xyes") +], [ + AM_CONDITIONAL(WITH_LIBCAP, false) +]) + # # With libpcap support, activate pppd on network activity AX_CHECK_PCAP @@ -347,6 +356,7 @@ $PACKAGE_NAME version $PACKAGE_VERSION System CA Path ......: ${SYSTEM_CA_PATH:-not set} With OpenSSL.........: ${with_openssl:-yes} With libatm..........: ${with_atm:-no} + With libcap..........: ${with_libcap:-no} With libpam..........: ${with_pam:-no} With libpcap.........: ${with_pcap:-no} With libsrp..........: ${with_srp:-no} diff --git a/m4/ax_check_cap.m4 b/m4/ax_check_cap.m4 new file mode 100644 index 00000000..423632bb --- /dev/null +++ b/m4/ax_check_cap.m4 @@ -0,0 +1,89 @@ +# SYNOPSIS +# +# AX_CHECK_CAP([action-if-found[, action-if-not-found]]) +# +# DESCRIPTION +# +# Look for libcap in a number of default locations, or in a provided location +# (via --with-libcap=). Sets +# CAP_CFLAGS +# CAP_LDFLAGS +# CAP_LIBS +# +# and calls ACTION-IF-FOUND or ACTION-IF-NOT-FOUND appropriately +# +# LICENSE +# +# Copyright (c) 2025 PPP Project +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 1 + +AC_DEFUN([AX_CHECK_CAP], [ + AC_ARG_WITH([libcap], + [AS_HELP_STRING([--with-libcap=yes|no|DIR], + [With libcap (capabilities) support for fine-grained privilege management])]) + + AS_CASE(["$with_libcap"], + [ye|y], [with_libcap=yes], + [n], [with_libcap=no]) + + AS_IF([test "x$with_libcap" != "xno"], [ + AS_CASE(["$with_libcap"], + [""|yes], [PKG_CHECK_MODULES([CAP], [libcap], [capdirs=], + [capdirs="/usr/local /usr/lib /usr"])], + [capdirs="$with_libcap"]) + + AS_IF([test -n "$capdirs"], [ + CAP_LIBS="-lcap" + for capdir in $capdirs; do + AC_MSG_CHECKING([for sys/capability.h in $capdir]) + if test -f "$capdir/include/sys/capability.h"; then + CAP_CFLAGS="-I$capdir/include" + CAP_LDFLAGS="-L$capdir/lib" + AC_MSG_RESULT([yes]) + break + else + AC_MSG_RESULT([no]) + fi + done + ]) + + # try the preprocessor and linker with our new flags, + # being careful not to pollute the global LIBS, LDFLAGS, and CPPFLAGS + + AC_MSG_CHECKING([if compiling and linking against libcap works]) + + save_LIBS="$LIBS" + save_LDFLAGS="$LDFLAGS" + save_CPPFLAGS="$CPPFLAGS" + LDFLAGS="$LDFLAGS $CAP_LDFLAGS" + LIBS="$CAP_LIBS $LIBS" + CPPFLAGS="$CAP_CFLAGS $CPPFLAGS" + AC_LINK_IFELSE( + [AC_LANG_PROGRAM( + [#include ], + [cap_t cap = cap_get_pid(0);])], + [ + AC_MSG_RESULT([yes]) + with_libcap=yes + $1 + ], [ + AC_MSG_RESULT([no]) + with_libcap="no" + $2 + ]) + CPPFLAGS="$save_CPPFLAGS" + LDFLAGS="$save_LDFLAGS" + LIBS="$save_LIBS" + + AC_SUBST([CAP_CFLAGS]) + AC_SUBST([CAP_LIBS]) + AC_SUBST([CAP_LDFLAGS]) + ]) + AM_CONDITIONAL(WITH_LIBCAP, test "x${with_libcap}" = "xyes") +]) diff --git a/pppd/Makefile.am b/pppd/Makefile.am index 7814b57b..3b1a142c 100644 --- a/pppd/Makefile.am +++ b/pppd/Makefile.am @@ -199,6 +199,12 @@ pppd_CPPFLAGS += $(SYSTEMD_CFLAGS) pppd_LIBS += $(SYSTEMD_LIBS) endif +if WITH_LIBCAP +pppd_CPPFLAGS += $(CAP_CFLAGS) +pppd_LIBS += $(CAP_LIBS) +pppd_LDFLAGS += $(CAP_LDFLAGS) +endif + if WITH_SRP srp_entry_SOURCES = srp-entry.c srp_entry_CPPFLAGS = $(OPENSSL_INCLUDES) $(SRP_CFLAGS) diff --git a/pppd/main.c b/pppd/main.c index b5c0c0e7..0deec7d5 100644 --- a/pppd/main.c +++ b/pppd/main.c @@ -449,11 +449,14 @@ main(int argc, char *argv[]) } /* - * Check that we are running as root. + * Check that we are capable to admin the network. */ - if (geteuid() != 0) { - ppp_option_error("must be root to run %s, since it is not setuid-root", - argv[0]); + if (!net_capable()) { + #ifdef USE_CAP + ppp_option_error("must have CAP_NET_ADMIN or root privilege to run %s", argv[0]); + #else + ppp_option_error("must be root to run %s, since it is not setuid-root", argv[0]); + #endif /* USE_CAP */ exit(EXIT_NOT_ROOT); } diff --git a/pppd/pppd.h b/pppd/pppd.h index 0a2a24ae..6113b5b4 100644 --- a/pppd/pppd.h +++ b/pppd/pppd.h @@ -435,6 +435,11 @@ void ppp_script_setenv(char *, char *, int); */ void ppp_script_unsetenv(char *); +/* + * Test for network management capability + */ +int net_capable(void); + /* * Test whether ppp kernel support exists */ diff --git a/pppd/sys-linux.c b/pppd/sys-linux.c index 9933fd59..e1f3753b 100644 --- a/pppd/sys-linux.c +++ b/pppd/sys-linux.c @@ -164,6 +164,11 @@ #include #endif +#ifdef USE_CAP +#include +#include +#endif /* USE_CAP */ + /* * Instead of system header file use local "termios_linux.h" header * file as it provides additional support for arbitrary baud rates via BOTHER. @@ -2842,6 +2847,47 @@ ppp_registered(void) return ret; } +/*********************************************************** + * + * net_capable - check for any access to the net management + */ + +int net_capable(void) +{ + /* + * Check if we are running as root first, as this is the most common case. + */ + if (geteuid() == 0) + return 1; + +#ifdef USE_CAP + /* + * If not root, check for CAP_NET_ADMIN capability. + */ + cap_t cap; + cap_flag_value_t cap_flag_value; + int ok = 0; + + cap = cap_get_pid(getpid()); + if (cap != NULL) { + if (cap_get_flag(cap, CAP_NET_ADMIN, CAP_EFFECTIVE, &cap_flag_value) == 0) { + if (cap_flag_value == CAP_SET) { + cap_free(cap); + return 1; + } + } + if (cap_get_flag(cap, CAP_NET_ADMIN, CAP_PERMITTED, &cap_flag_value) == 0) { + if (cap_flag_value == CAP_SET) + ok = 1; + } + cap_free(cap); + } + return ok; +#else + return 0; +#endif /* USE_CAP */ +} + /******************************************************************** * * ppp_check_kernel_support - check whether the system has any ppp interfaces diff --git a/pppd/sys-solaris.c b/pppd/sys-solaris.c index 51f284f0..17b25655 100644 --- a/pppd/sys-solaris.c +++ b/pppd/sys-solaris.c @@ -635,6 +635,19 @@ sys_check_options(void) return 1; } +/*********************************************************** + * + * net_capable - check for any access to the net management + */ + +int net_capable(void) +{ + /* + * On Solaris, always check that we are running as root. + */ + return (geteuid() == 0); +} + /* * ppp_check_kernel_support - check whether the system has any ppp interfaces */