Skip to content

Commit 3a4fbe1

Browse files
committed
pppd: Add libcap support to check CAP_NET_ADMIN instead of requiring root
Add optional libcap support (--with-libcap) for fine-grained privilege management. When enabled, pppd checks for CAP_NET_ADMIN capability instead of requiring euid 0, allowing it to run with reduced privileges. This addresses issue #98 by allowing pppd to run without full root access when given appropriate capabilities via setcap(8). Details: - Add configure option --with-libcap to enable capability checking (Linux only) - Check for CAP_NET_ADMIN (required to open /dev/ppp) instead of CAP_NET_RAW - Add net_capable() function in sys-linux.c and sys-solaris.c - Fallback to geteuid()==0 check when libcap is not available or not built - Add m4/ax_check_cap.m4 macro for autotools detection The feature is disabled by default to maintain backward compatibility. Distributions can enable it with --with-libcap during build. Signed-off-by: Alexey Andreyev <a.andreev@omprussia.ru>
1 parent 1affa97 commit 3a4fbe1

7 files changed

Lines changed: 176 additions & 4 deletions

File tree

configure.ac

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,15 @@ AX_CHECK_ATM
302302
AX_CHECK_PAM(AC_DEFINE([PPP_WITH_PAM], 1, [Support for Pluggable Authentication Modules]))
303303
AM_CONDITIONAL(PPP_WITH_PAM, test "x${with_pam}" = "xyes")
304304

305+
#
306+
# With libcap support for capability-based privilege management (Linux only, Solaris uses geteuid check)
307+
AM_COND_IF([LINUX], [
308+
AX_CHECK_CAP(AC_DEFINE([USE_CAP], 1, [Use libcap for capability checking]))
309+
AM_CONDITIONAL(WITH_LIBCAP, test "x${with_libcap}" = "xyes")
310+
], [
311+
AM_CONDITIONAL(WITH_LIBCAP, false)
312+
])
313+
305314
#
306315
# With libpcap support, activate pppd on network activity
307316
AX_CHECK_PCAP
@@ -347,6 +356,7 @@ $PACKAGE_NAME version $PACKAGE_VERSION
347356
System CA Path ......: ${SYSTEM_CA_PATH:-not set}
348357
With OpenSSL.........: ${with_openssl:-yes}
349358
With libatm..........: ${with_atm:-no}
359+
With libcap..........: ${with_libcap:-no}
350360
With libpam..........: ${with_pam:-no}
351361
With libpcap.........: ${with_pcap:-no}
352362
With libsrp..........: ${with_srp:-no}

m4/ax_check_cap.m4

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
# SYNOPSIS
2+
#
3+
# AX_CHECK_CAP([action-if-found[, action-if-not-found]])
4+
#
5+
# DESCRIPTION
6+
#
7+
# Look for libcap in a number of default locations, or in a provided location
8+
# (via --with-libcap=). Sets
9+
# CAP_CFLAGS
10+
# CAP_LDFLAGS
11+
# CAP_LIBS
12+
#
13+
# and calls ACTION-IF-FOUND or ACTION-IF-NOT-FOUND appropriately
14+
#
15+
# LICENSE
16+
#
17+
# Copyright (c) 2025 PPP Project
18+
#
19+
# Copying and distribution of this file, with or without modification, are
20+
# permitted in any medium without royalty provided the copyright notice
21+
# and this notice are preserved. This file is offered as-is, without any
22+
# warranty.
23+
24+
#serial 1
25+
26+
AC_DEFUN([AX_CHECK_CAP], [
27+
AC_ARG_WITH([libcap],
28+
[AS_HELP_STRING([--with-libcap=yes|no|DIR],
29+
[With libcap (capabilities) support for fine-grained privilege management])])
30+
31+
AS_CASE(["$with_libcap"],
32+
[ye|y], [with_libcap=yes],
33+
[n], [with_libcap=no])
34+
35+
AS_IF([test "x$with_libcap" != "xno"], [
36+
AS_CASE(["$with_libcap"],
37+
[""|yes], [PKG_CHECK_MODULES([CAP], [libcap], [capdirs=],
38+
[capdirs="/usr/local /usr/lib /usr"])],
39+
[capdirs="$with_libcap"])
40+
41+
AS_IF([test -n "$capdirs"], [
42+
CAP_LIBS="-lcap"
43+
for capdir in $capdirs; do
44+
AC_MSG_CHECKING([for sys/capability.h in $capdir])
45+
if test -f "$capdir/include/sys/capability.h"; then
46+
CAP_CFLAGS="-I$capdir/include"
47+
CAP_LDFLAGS="-L$capdir/lib"
48+
AC_MSG_RESULT([yes])
49+
break
50+
else
51+
AC_MSG_RESULT([no])
52+
fi
53+
done
54+
])
55+
56+
# try the preprocessor and linker with our new flags,
57+
# being careful not to pollute the global LIBS, LDFLAGS, and CPPFLAGS
58+
59+
AC_MSG_CHECKING([if compiling and linking against libcap works])
60+
61+
save_LIBS="$LIBS"
62+
save_LDFLAGS="$LDFLAGS"
63+
save_CPPFLAGS="$CPPFLAGS"
64+
LDFLAGS="$LDFLAGS $CAP_LDFLAGS"
65+
LIBS="$CAP_LIBS $LIBS"
66+
CPPFLAGS="$CAP_CFLAGS $CPPFLAGS"
67+
AC_LINK_IFELSE(
68+
[AC_LANG_PROGRAM(
69+
[#include <sys/capability.h>],
70+
[cap_t cap = cap_get_pid(0);])],
71+
[
72+
AC_MSG_RESULT([yes])
73+
with_libcap=yes
74+
$1
75+
], [
76+
AC_MSG_RESULT([no])
77+
with_libcap="no"
78+
$2
79+
])
80+
CPPFLAGS="$save_CPPFLAGS"
81+
LDFLAGS="$save_LDFLAGS"
82+
LIBS="$save_LIBS"
83+
84+
AC_SUBST([CAP_CFLAGS])
85+
AC_SUBST([CAP_LIBS])
86+
AC_SUBST([CAP_LDFLAGS])
87+
])
88+
AM_CONDITIONAL(WITH_LIBCAP, test "x${with_libcap}" = "xyes")
89+
])

pppd/Makefile.am

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,12 @@ pppd_CPPFLAGS += $(SYSTEMD_CFLAGS)
199199
pppd_LIBS += $(SYSTEMD_LIBS)
200200
endif
201201

202+
if WITH_LIBCAP
203+
pppd_CPPFLAGS += $(CAP_CFLAGS)
204+
pppd_LIBS += $(CAP_LIBS)
205+
pppd_LDFLAGS += $(CAP_LDFLAGS)
206+
endif
207+
202208
if WITH_SRP
203209
srp_entry_SOURCES = srp-entry.c
204210
srp_entry_CPPFLAGS = $(OPENSSL_INCLUDES) $(SRP_CFLAGS)

pppd/main.c

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -449,11 +449,14 @@ main(int argc, char *argv[])
449449
}
450450

451451
/*
452-
* Check that we are running as root.
452+
* Check that we are capable to admin the network.
453453
*/
454-
if (geteuid() != 0) {
455-
ppp_option_error("must be root to run %s, since it is not setuid-root",
456-
argv[0]);
454+
if (!net_capable()) {
455+
#ifdef USE_CAP
456+
ppp_option_error("must have CAP_NET_ADMIN or root privilege to run %s", argv[0]);
457+
#else
458+
ppp_option_error("must be root to run %s, since it is not setuid-root", argv[0]);
459+
#endif /* USE_CAP */
457460
exit(EXIT_NOT_ROOT);
458461
}
459462

pppd/pppd.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -435,6 +435,11 @@ void ppp_script_setenv(char *, char *, int);
435435
*/
436436
void ppp_script_unsetenv(char *);
437437

438+
/*
439+
* Test for network management capability
440+
*/
441+
int net_capable(void);
442+
438443
/*
439444
* Test whether ppp kernel support exists
440445
*/

pppd/sys-linux.c

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,11 @@
164164
#include <sys/locks.h>
165165
#endif
166166

167+
#ifdef USE_CAP
168+
#include <sys/types.h>
169+
#include <sys/capability.h>
170+
#endif /* USE_CAP */
171+
167172
/*
168173
* Instead of system header file <termios.h> use local "termios_linux.h" header
169174
* file as it provides additional support for arbitrary baud rates via BOTHER.
@@ -2842,6 +2847,47 @@ ppp_registered(void)
28422847
return ret;
28432848
}
28442849

2850+
/***********************************************************
2851+
*
2852+
* net_capable - check for any access to the net management
2853+
*/
2854+
2855+
int net_capable(void)
2856+
{
2857+
/*
2858+
* Check if we are running as root first, as this is the most common case.
2859+
*/
2860+
if (geteuid() == 0)
2861+
return 1;
2862+
2863+
#ifdef USE_CAP
2864+
/*
2865+
* If not root, check for CAP_NET_ADMIN capability.
2866+
*/
2867+
cap_t cap;
2868+
cap_flag_value_t cap_flag_value;
2869+
int ok = 0;
2870+
2871+
cap = cap_get_pid(getpid());
2872+
if (cap != NULL) {
2873+
if (cap_get_flag(cap, CAP_NET_ADMIN, CAP_EFFECTIVE, &cap_flag_value) == 0) {
2874+
if (cap_flag_value == CAP_SET) {
2875+
cap_free(cap);
2876+
return 1;
2877+
}
2878+
}
2879+
if (cap_get_flag(cap, CAP_NET_ADMIN, CAP_PERMITTED, &cap_flag_value) == 0) {
2880+
if (cap_flag_value == CAP_SET)
2881+
ok = 1;
2882+
}
2883+
cap_free(cap);
2884+
}
2885+
return ok;
2886+
#else
2887+
return 0;
2888+
#endif /* USE_CAP */
2889+
}
2890+
28452891
/********************************************************************
28462892
*
28472893
* ppp_check_kernel_support - check whether the system has any ppp interfaces

pppd/sys-solaris.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -635,6 +635,19 @@ sys_check_options(void)
635635
return 1;
636636
}
637637

638+
/***********************************************************
639+
*
640+
* net_capable - check for any access to the net management
641+
*/
642+
643+
int net_capable(void)
644+
{
645+
/*
646+
* On Solaris, always check that we are running as root.
647+
*/
648+
return (geteuid() == 0);
649+
}
650+
638651
/*
639652
* ppp_check_kernel_support - check whether the system has any ppp interfaces
640653
*/

0 commit comments

Comments
 (0)