git: 66cacc141d37 - main - libpfctl: introduce pfctl_handle

From: Kristof Provost <kp_at_FreeBSD.org>
Date: Thu, 04 Jan 2024 22:11:11 UTC
The branch main has been updated by kp:

URL: https://cgit.FreeBSD.org/src/commit/?id=66cacc141d372fdfa624a380bac6880ecf809994

commit 66cacc141d372fdfa624a380bac6880ecf809994
Author:     Kristof Provost <kp@FreeBSD.org>
AuthorDate: 2024-01-04 09:50:14 +0000
Commit:     Kristof Provost <kp@FreeBSD.org>
CommitDate: 2024-01-04 22:10:24 +0000

    libpfctl: introduce pfctl_handle
    
    Consumers of libpfctl can (and in future, should) open a handle. This
    handle is an opaque object which contains the /dev/pf file descriptor
    and a netlink handle. This means that libpfctl users can open the handle
    as root, then drop privileges and still access pf.
    
    Already add the handle to pfctl_startstop() and pfctl_get_creatorids()
    as these are new in main, and not present on stable branches. Other
    calls will have handle-enabled alternatives implemented in subsequent
    commits.
    
    Sponsored by:   Rubicon Communications, LLC ("Netgate")
---
 lib/libpfctl/libpfctl.c | 58 ++++++++++++++++++++++++++++++++++++++-----------
 lib/libpfctl/libpfctl.h | 10 +++++++--
 sbin/pfctl/pfctl.c      | 12 ++++++----
 3 files changed, 61 insertions(+), 19 deletions(-)

diff --git a/lib/libpfctl/libpfctl.c b/lib/libpfctl/libpfctl.c
index f915072c4ea1..94949a5a7337 100644
--- a/lib/libpfctl/libpfctl.c
+++ b/lib/libpfctl/libpfctl.c
@@ -50,11 +50,17 @@
 #include <assert.h>
 #include <err.h>
 #include <errno.h>
+#include <fcntl.h>
 #include <stdlib.h>
 #include <string.h>
 
 #include "libpfctl.h"
 
+struct pfctl_handle {
+	int fd;
+	struct snl_state ss;
+};
+
 const char* PFCTL_SYNCOOKIES_MODE_NAMES[] = {
 	"never",
 	"always",
@@ -64,6 +70,38 @@ const char* PFCTL_SYNCOOKIES_MODE_NAMES[] = {
 static int	_pfctl_clear_states(int , const struct pfctl_kill *,
 		    unsigned int *, uint64_t);
 
+struct pfctl_handle *
+pfctl_open(const char *pf_device)
+{
+	struct pfctl_handle *h;
+
+	h = calloc(1, sizeof(struct pfctl_handle));
+	h->fd = -1;
+
+	h->fd = open(pf_device, O_RDWR);
+	if (h->fd < 0)
+		goto error;
+
+	if (!snl_init(&h->ss, NETLINK_GENERIC))
+		goto error;
+
+	return (h);
+error:
+	close(h->fd);
+	snl_free(&h->ss);
+	free(h);
+
+	return (NULL);
+}
+
+void
+pfctl_close(struct pfctl_handle *h)
+{
+	close(h->fd);
+	snl_free(&h->ss);
+	free(h);
+}
+
 static int
 pfctl_do_ioctl(int dev, uint cmd, size_t size, nvlist_t **nvl)
 {
@@ -183,21 +221,19 @@ pf_nvuint_64_array(const nvlist_t *nvl, const char *name, size_t maxelems,
 }
 
 int
-pfctl_startstop(int start)
+pfctl_startstop(struct pfctl_handle *h, int start)
 {
-	struct snl_state ss = {};
 	struct snl_errmsg_data e = {};
 	struct snl_writer nw;
 	struct nlmsghdr *hdr;
 	uint32_t seq_id;
 	int family_id;
 
-	snl_init(&ss, NETLINK_GENERIC);
-	family_id = snl_get_genl_family(&ss, PFNL_FAMILY_NAME);
+	family_id = snl_get_genl_family(&h->ss, PFNL_FAMILY_NAME);
 	if (family_id == 0)
 		return (ENOTSUP);
 
-	snl_init_writer(&ss, &nw);
+	snl_init_writer(&h->ss, &nw);
 	hdr = snl_create_genl_msg_request(&nw, family_id,
 	    start ? PFNL_CMD_START : PFNL_CMD_STOP);
 
@@ -206,9 +242,9 @@ pfctl_startstop(int start)
 		return (ENOMEM);
 	seq_id = hdr->nlmsg_seq;
 
-	snl_send_message(&ss, hdr);
+	snl_send_message(&h->ss, hdr);
 
-	while ((hdr = snl_read_reply_multi(&ss, seq_id, &e)) != NULL) {
+	while ((hdr = snl_read_reply_multi(&h->ss, seq_id, &e)) != NULL) {
 	}
 
 	return (e.error);
@@ -1288,17 +1324,13 @@ pfctl_get_creators_nl(struct snl_state *ss, uint32_t *creators, size_t *len)
 }
 
 int
-pfctl_get_creatorids(uint32_t *creators, size_t *len)
+pfctl_get_creatorids(struct pfctl_handle *h, uint32_t *creators, size_t *len)
 {
-	struct snl_state ss = {};
 	int error;
 
-	snl_init(&ss, NETLINK_GENERIC);
-	error = pfctl_get_creators_nl(&ss, creators, len);
-	snl_free(&ss);
+	error = pfctl_get_creators_nl(&h->ss, creators, len);
 
 	return (error);
-
 }
 
 static void
diff --git a/lib/libpfctl/libpfctl.h b/lib/libpfctl/libpfctl.h
index 7f3b1b600db7..f128e5340891 100644
--- a/lib/libpfctl/libpfctl.h
+++ b/lib/libpfctl/libpfctl.h
@@ -385,7 +385,13 @@ struct pfctl_syncookies {
 	uint32_t			halfopen_states;
 };
 
-int	pfctl_startstop(int start);
+#define	PF_DEVICE	"/dev/pf"
+
+struct pfctl_handle;
+struct pfctl_handle	*pfctl_open(const char *pf_device);
+void	pfctl_close(struct pfctl_handle *);
+
+int	pfctl_startstop(struct pfctl_handle *h, int start);
 struct pfctl_status* pfctl_get_status(int dev);
 uint64_t pfctl_status_counter(struct pfctl_status *status, int id);
 uint64_t pfctl_status_lcounter(struct pfctl_status *status, int id);
@@ -416,7 +422,7 @@ int	pfctl_add_rule(int dev, const struct pfctl_rule *r,
 	    const char *anchor, const char *anchor_call, uint32_t ticket,
 	    uint32_t pool_ticket);
 int	pfctl_set_keepcounters(int dev, bool keep);
-int	pfctl_get_creatorids(uint32_t *creators, size_t *len);
+int	pfctl_get_creatorids(struct pfctl_handle *h, uint32_t *creators, size_t *len);
 
 struct pfctl_state_filter {
 	char			ifname[IFNAMSIZ];
diff --git a/sbin/pfctl/pfctl.c b/sbin/pfctl/pfctl.c
index 2702c701f9cc..217bf31b3301 100644
--- a/sbin/pfctl/pfctl.c
+++ b/sbin/pfctl/pfctl.c
@@ -132,7 +132,7 @@ static const char	*showopt;
 static const char	*debugopt;
 static char		*anchoropt;
 static const char	*optiopt = NULL;
-static const char	*pf_device = "/dev/pf";
+static const char	*pf_device = PF_DEVICE;
 static char		*ifaceopt;
 static char		*tableopt;
 static const char	*tblcmdopt;
@@ -144,6 +144,7 @@ int			 loadopt;
 int			 altqsupport;
 
 int			 dev = -1;
+struct pfctl_handle	*pfh = NULL;
 static int		 first_title = 1;
 static int		 labels = 0;
 
@@ -312,7 +313,7 @@ pfctl_enable(int dev, int opts)
 {
 	int ret;
 
-	if ((ret = pfctl_startstop(1)) != 0) {
+	if ((ret = pfctl_startstop(pfh, 1)) != 0) {
 		if (ret == EEXIST)
 			errx(1, "pf already enabled");
 		else if (ret == ESRCH)
@@ -335,7 +336,7 @@ pfctl_disable(int dev, int opts)
 {
 	int ret;
 
-	if ((ret = pfctl_startstop(0)) != 0) {
+	if ((ret = pfctl_startstop(pfh, 0)) != 0) {
 		if (ret == ENOENT)
 			errx(1, "pf not enabled");
 		else
@@ -1665,7 +1666,7 @@ pfctl_show_creators(int opts)
 	uint32_t creators[16];
 	size_t count = nitems(creators);
 
-	ret = pfctl_get_creatorids(creators, &count);
+	ret = pfctl_get_creatorids(pfh, creators, &count);
 	if (ret != 0)
 		errx(ret, "Failed to retrieve creators");
 
@@ -3079,6 +3080,9 @@ main(int argc, char *argv[])
 		altqsupport = 1;
 #endif
 	}
+	pfh = pfctl_open(pf_device);
+	if (pfh == NULL)
+		err(1, "Failed to open netlink");
 
 	if (opts & PF_OPT_DISABLE)
 		if (pfctl_disable(dev, opts))