Provide a gmake Makefile for distro builds and packagers.

Define _GNU_SOURCE in the CFLAGS.
Update the README.
Remove the duplicate Gentoo init script ndhc.sh that is in the root.
Remove DESIGN -- it's outdated.
This commit is contained in:
Nicholas J. Kain 2011-07-12 04:09:05 -04:00
parent efe50a30a6
commit 2aff36a274
6 changed files with 159 additions and 191 deletions

View File

@ -2,8 +2,8 @@ project (ndhcp)
cmake_minimum_required (VERSION 2.6)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -s -std=gnu99 -pedantic -Wall -lcap -DHAVE_CLEARENV -DLINUX")
set(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} -s -std=gnu99 -pedantic -Wall -lcap -DHAVE_CLEARENV -DLINUX")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -s -std=gnu99 -pedantic -Wall -lcap -D_GNU_SOURCE -DHAVE_CLEARENV -DLINUX")
set(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} -s -std=gnu99 -pedantic -Wall -lcap -D_GNU_SOURCE -DHAVE_CLEARENV -DLINUX")
include_directories("${PROJECT_SOURCE_DIR}/ncmlib")
add_subdirectory(ncmlib)

70
DESIGN
View File

@ -1,70 +0,0 @@
Goals:
1. Security
a. Divide into seperate processes that each have the minimal
system access necessary to complete their task.
b. Use a well defined IPC mechanism to facilitate cooperation
between processes. In this case, UNIX domain sockets are
used, since they allow for UNIX DAC (on Linux, at least).
c. Write each program to be secure; don't rely on the
privilege seperations for security.
d. Simple error handling is favored rather than complex error
handling that may possibly be caused to "recover" in an
exploitable way.
e. Don't make stupid assumptions. Implement only the minimal
functionality necessary to perform a task. Expect brain
damaged or malicious inputs.
f. Run inside a chroot, with minimal privileges via
capabilities or MAC.
2. Reliability
a. Don't try to handle severe errors.
b. Log errors if program state is still sane.
c. Recover from predictable problems if necessary. Make sure
that recovery behavior is well understood and defined.
d. Complicated or unsafe recoveries should not be performed;
instead the program should promptly exit. Dead programs
don't cause exploits.
3. Portability
a. Portability is good, but portability may not be as wide as
a less secure program. Capabilities or MAC are not well
standardized, but remain necessary features.
4. Miscellaneous
a. Speed: If we aren't required to sacrifice anything more
important, it's always good to be fast.
a. Size: If we aren't required to sacrifice anything more
important, it's always good to be frugal.
Layout:
ndhc daemon (root -> chroot -> drop all !(CAP_NET_BROADCAST|CAP_NET_RAW)
-> nopriv)
* handles dhcp protocol issues, netlink hw link notifications, and ARP checks
* keeps track of leases
* talks to ifchd to perform tasks that require
higher privileges than CAP_NET_BROADCAST or CAP_NET_RAW
ifchd daemon (root -> openfd -> chroot -> drop all !CAP_NET_ADMIN -> nopriv)
* listens for interface change requests via UNIX domain socket
* restricts valid IP ranges that will be accepted
* performs interface changes
* keeps rw fds for system files (such as /etc/resolv.conf) that must
be modified outside the chroot

42
Makefile Normal file
View File

@ -0,0 +1,42 @@
# This is a pretty basic makefile. I generally use CMake, so this is mostly
# for distros that want to avoid build dependencies. Produced exes will be
# at './build/ndhc' and './build/ifchd'.
NCM_SRCS = $(sort $(wildcard ncmlib/*.c))
IFCHD_SRCS = $(sort $(wildcard ifchd/*.c))
NDHC_SRCS = $(sort $(wildcard ndhc/*.c))
NCM_OBJS = $(NCM_SRCS:.c=.o)
IFCHD_OBJS = $(IFCHD_SRCS:.c=.o)
NDHC_OBJS = $(NDHC_SRCS:.c=.o)
NCM_INC = -I./ncmlib
BUILD_DIR = build
OBJ_DIR = objs
CC = gcc
AR = ar
RANLIB = ranlib
CFLAGS = -O2 -s -std=gnu99 -pedantic -Wall -D_GNU_SOURCE -DHAVE_CLEARENV -DLINUX
all: makedir ncmlib.a ifchd ndhc
clean:
rm -Rf $(OBJ_DIR) $(BUILD_DIR)
makedir:
mkdir -p $(OBJ_DIR)/ndhc $(OBJ_DIR)/ifchd $(OBJ_DIR)/ncmlib $(BUILD_DIR)
%.o: %.c
$(CC) $(CFLAGS) $(NCM_INC) -c -o $(OBJ_DIR)/$@ $<
ncmlib.a: $(NCM_OBJS)
$(AR) rc $(BUILD_DIR)/$@ $(subst ncmlib/,$(OBJ_DIR)/ncmlib/,$(NCM_OBJS))
$(RANLIB) $(BUILD_DIR)/$@
ifchd: $(IFCHD_OBJS)
$(CC) $(CFLAGS) $(NCM_INC) -o $(BUILD_DIR)/$@ $(subst ifchd/,$(OBJ_DIR)/ifchd/,$(IFCHD_OBJS)) $(BUILD_DIR)/ncmlib.a -lcap
ndhc: $(NDHC_OBJS)
$(CC) $(CFLAGS) $(NCM_INC) -o $(BUILD_DIR)/$@ $(subst ndhc/,$(OBJ_DIR)/ndhc/,$(NDHC_OBJS)) $(BUILD_DIR)/ncmlib.a -lcap
.PHONY: all clean

140
README
View File

@ -1,4 +1,4 @@
ifchd, copyright (c) 2004-2011 Nicholas Kain. Licensed under GNU GPL.
ndhc + ifchd, copyright (c) 2004-2011 Nicholas Kain. Licensed under GNU GPL2.
Requirements:
@ -47,12 +47,19 @@ taken to prevent unintentional ARP flooding under any circumstance.
ndhc also monitors hardware link status via netlink events and reacts
appropriately when interface carrier status changes or an interface is
explicitly deconfigured.
explicitly deconfigured. This functionality can be useful on wired networks
when transient carrier downtimes occur (or cables are changed), but it is
particularly useful on wireless networks.
USAGE
-----
1) Compile and install ifchd and ndhc.
a) gmake
b) Install the build/ifchd and build/ndhc executables in a normal place. I
would suggest /usr/sbin or /usr/local/sbin.
1alt) Compile and install ifchd and ndhc.
a) Create a build directory:
mkdir build && cd build
b) Create the makefiles:
@ -129,8 +136,7 @@ esac
directions, the script will of course require modifications.
4o) If you encounter problems, I suggest running both ifchd and ndhc in the
foreground, and perhaps compiling ndhc with extra debugging output
(uncomment DEBUG=1 in the Makefile).
foreground and examining the printed output.
BEHAVIOR NOTES
@ -144,31 +150,38 @@ ifchd can be set such that it only allows clients to configure particular
network interfaces. The --interface (-i) argument does the trick, and may
be used multiple times to allow multiple interfaces.
GRSECURITY NOTES
----------------
Make sure that CONFIG_GRKERNSEC_CHROOT_CAPS is disabled. Otherwise, ifchd will
lose its capabilities (in particular, the ability to reconfigure interfaces)
when it chroots.
PORTING NOTES
-------------
There are seven major functions that ifchd depends upon that are not generally
portable. First, it uses the SO_PEERCRED flag of getsockopt() to discriminate
authorized connections by uid, gid, and pid. Similar functionality exists in
at least the BSDs; however, it has a different API. Second, ifchd takes
advantage of Linux capabilities so that it does not need full root privileges.
Capabilities were a proposed POSIX feature that was not made part of the
official standard, so any implemention that may exist will be system-dependent.
Third and fourth, ifchd configures network interfaces and routes. Interface
and route configuration is entirely non-portable, usually requiring calls to
the catch-all ioctl(), and will almost certainly require platform-dependent
code. Fifth and sixth, both ifchd and ndhc use epoll() and signalfd(), which
are Linux-specific. Seventh, ndhc uses netlink sockets extensively for
both fetching data and hardware link state change notification events.
ndhc is rather platform-dependent, and it extensively uses Linux-specific
features. Some of these features are also available on the BSDs.
1) Both ndhc and ifchd use the SO_PEERCRED flag of getsockopt() to discriminate
authorized connections by uid, gid, and pid. Similar functionality exists in
at least the BSDs; however, it has a different API.
2) ifchd takes advantage of Linux capabilities so that it does not need full
root privileges. Capabilities were a proposed POSIX feature that was not made
part of the official standard, so any implemention that may exist will be
system-dependent.
3) ifchd configures network interfaces and routes. Interface and route
configuration is entirely non-portable, usually requiring calls to the
catch-all ioctl(), or even more unusual mechanisms like netlink sockets.
4) ndhc uses netlink sockets extensively for both fetching data and hardware
link state change notification events.
5) ndhc uses the Berkeley Packet Filter / Linux Packet Filter interfaces to
drop unwanted packets in kernelspace. This functionality is available on
most modern unix systems, but it is not standard.
6) ndhc uses epoll() and signalfd(). These are Linux-specific.
7) Numerous socket options are used, and the AF_PACKET socket family is used
for raw sockets and ARP. These are largely Linux-specific, too.
8) ndhc uses strlcpy() and strlcat(). Native versions are provided.
Some standard C libraries include a native implementation of strlcpy() and
strlcat(). Such defines may conflict with my implementations in strl.c/strl.h.
It is up to the user whether the standard C library implementations should be
@ -177,3 +190,80 @@ nonstandard semantics (notably Solaris). On these systems, using the
system-provided implementations may lead to security problems. Such problems
are the fault of the vendor. If you are unsure whether your system is correct
or not, I suggest using the implementation that I provide.
HISTORY
-------
I started writing ndhc back in 2004. My ISP at the time required a dhcp
client for connection authentication, and I was not comfortable with any
of the existing clients, which all ran as root and had colorful security
histories. DHCP is generally not a routed protocol, and lacks real
authentication mechanisms in real world deployments (some largely
abandoned RFCs for such behavior do exist), so no program existed to
fill the niche of a truly secure DHCP client.
My router/server at the time ran a custom Linux distro that was designed
for extreme security. A root privileged DHCP client would be nearly the
only root-owned process running on the machine, so I was highly motivated
to develop an alternative.
ifchd was first written entirely from scratch. It did not take long to write,
since it is by design rather simple, and I was already familiar with
the quirks of Linux capabilities. That left me with the choice of adapting
an existing DHCP client or writing my own from scratch.
At the time, I just wanted something that would work, so my choice was to
adapt udhcpc to work with ifchd. udhcpc was chosen since it was intended to
be used with resource-constrained or embedded systems, and was thus very
small. ISC dhclient was another alternative, but it is an extremely large
program, and it would have been very hard to audit it for correctness.
udhcpc was not all that great of a choice, since it was designed to be small at
all costs, sacrificing correctness when necessary. The code was hard to
follow, and had many quirks. Bounds-checking was rare, type aliasing common,
and state transitions were convoluted. Not all of the client was asynchronous,
and no precautions were taken against conflicting peers. ARP was not used at
all.
However, it was small. With a lot of work, I ripped out the script-calling
mechanisms and replaced them with ifchd requests. Bounds-checking was
aggressively (and somewhat hamfistedly) retrofitted into the code. It was
cleaned to a degree, and importantly it worked for connecting to my ISP.
Then I changed ISPs. My new ISP used PPPoE, not dhcp. Around the same time, I
also switched to using Gentoo rather than a hand-built distribution. I didn't
have time to maintain the old custom setup, and it was very hard keeping up
with library vulnerabilties in eg, zlib or openssl, and ensuring that all
installed binaries, dynamic and static, were updated. ndhc was abandoned for
many years. It wasn't needed on my server, and it was "too much effort" to
deviate from the stock distro dhcp clients on other machines.
Then, around 2008, I changed ISPs again. This time my new ISP used dhcp and
not PPPoE. So, after a few months, I decided to dust off the old ndhc/ifchd
project and adapt it to my modern standards and machines.
ifchd was in good shape and required little work. I ended up rewriting
ndhc. The only parts that remained from the original were the parts that
I had already rewritten before, and some of those were rewritten, too.
The end result is a modern DHCP client is largely RFC-compliant, except where
the RFCs dictate behavior that would be problematic, overly complex, useless,
or exploitable. DHCP is poorly specified, and real-world servers and clients
vary a lot from the RFCs, so these conditions are necessary for a useful
program.
Although ndhc's implementation and behavior are different, I have to credit
the idea of using netlink events to discover hardware link status transitions
to Stefan Rompf and his 'dhcpclient' program. The Linux netlink events that
are used are otherwise rather obscure and poorly documented, and I wouldn't
have known about them otherwise.
GRSECURITY NOTES
----------------
Make sure that CONFIG_GRKERNSEC_CHROOT_CAPS is disabled. Otherwise, ifchd will
lose its capabilities (in particular, the ability to reconfigure interfaces)
when it chroots.

View File

@ -38,7 +38,6 @@
#include <signal.h>
#include <errno.h>
#define _GNU_SOURCE
#include <getopt.h>
#include "ifchd-defines.h"

93
ndhc.sh
View File

@ -1,93 +0,0 @@
# Copyright (c) 2007-2008 Roy Marples <roy@marples.name>
# All rights reserved. Released under the 2-clause BSD license.
ndhc_depend()
{
program start /sbin/ndhc
after interface
provide dhcp
}
_config_vars="$_config_vars dhcp ndhc"
ndhc_start()
{
local args= opt= opts= pidfile="/var/run/ndhc-${IFACE}.pid"
local sendhost=true
local leasefile="/var/state/${IFACE}.lease"
eval args=\$ndhc_${IFVAR}
# Get our options
eval opts=\$dhcp_${IFVAR}
[ -z "${opts}" ] && opts=${dhcp}
# # Map some generic options to ndhc
# for opt in ${opts}; do
# case "${opt}" in
# nodns) args="${args} --env PEER_DNS=no";;
# nontp) args="${args} --env PEER_NTP=no";;
# nogateway) args="${args} --env PEER_ROUTERS=no";;
# nosendhost) sendhost=false;
# esac
# done
# [ "${metric:-0}" != "0" ] && args="${args} --env IF_METRIC=${metric}"
ebegin "Running ndhc"
case " ${args} " in
*" --quit "*|*" -q "*) x="/sbin/ndhc";;
*) x="start-stop-daemon --start --exec /sbin/ndhc \
--pidfile ${pidfile} --";;
esac
case " ${args} " in
*" --hostname="*|*" -h "*|*" -H "*);;
*)
if ${sendhost}; then
local hname="$(hostname)"
if [ "${hname}" != "(none)" ] && [ "${hname}" != "localhost" ]; then
args="${args} --hostname='${hname}'"
fi
fi
;;
esac
# delay until carrier is up
ip link set "${IFACE}" up
ip link show "${IFACE}" | grep NO-CARRIER >/dev/null 2>&1
while [ "$?" != "1" ]; do
sleep 1
ip link show "${IFACE}" | grep NO-CARRIER >/dev/null 2>&1
done
eval "${x}" "${args}" -r `cat /etc/firewall/tmp/OLDEXTIP` \
-n -i "${IFACE}" -u "ndhc" -C "/var/lib/ndhc" \
-p "${pidfile}" -l "${leasefile}" >/dev/null
eend $? || return 1
_show_address
return 0
}
ndhc_stop()
{
local pidfile="/var/lib/ndhc/var/run/ndhc-${IFACE}.pid" opts=
[ ! -f "${pidfile}" ] && return 0
# Get our options
eval opts=\$dhcp_${IFVAR}
[ -z "${opts}" ] && opts=${dhcp}
ebegin "Stopping ndhc on ${IFACE}"
case " ${opts} " in
*" release "*)
start-stop-daemon --stop --quiet --oknodo --signal USR2 \
--exec /sbin/ndhc --pidfile "${pidfile}"
;;
esac
start-stop-daemon --stop --exec /sbin/ndhc --pidfile "${pidfile}"
eend $?
}