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")
This commit is contained in:
Kristof Provost 2024-01-04 10:50:14 +01:00
parent 32df0124f4
commit 66cacc141d
3 changed files with 61 additions and 19 deletions

View file

@ -50,11 +50,17 @@
#include <assert.h> #include <assert.h>
#include <err.h> #include <err.h>
#include <errno.h> #include <errno.h>
#include <fcntl.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include "libpfctl.h" #include "libpfctl.h"
struct pfctl_handle {
int fd;
struct snl_state ss;
};
const char* PFCTL_SYNCOOKIES_MODE_NAMES[] = { const char* PFCTL_SYNCOOKIES_MODE_NAMES[] = {
"never", "never",
"always", "always",
@ -64,6 +70,38 @@ const char* PFCTL_SYNCOOKIES_MODE_NAMES[] = {
static int _pfctl_clear_states(int , const struct pfctl_kill *, static int _pfctl_clear_states(int , const struct pfctl_kill *,
unsigned int *, uint64_t); 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 static int
pfctl_do_ioctl(int dev, uint cmd, size_t size, nvlist_t **nvl) 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 int
pfctl_startstop(int start) pfctl_startstop(struct pfctl_handle *h, int start)
{ {
struct snl_state ss = {};
struct snl_errmsg_data e = {}; struct snl_errmsg_data e = {};
struct snl_writer nw; struct snl_writer nw;
struct nlmsghdr *hdr; struct nlmsghdr *hdr;
uint32_t seq_id; uint32_t seq_id;
int family_id; int family_id;
snl_init(&ss, NETLINK_GENERIC); family_id = snl_get_genl_family(&h->ss, PFNL_FAMILY_NAME);
family_id = snl_get_genl_family(&ss, PFNL_FAMILY_NAME);
if (family_id == 0) if (family_id == 0)
return (ENOTSUP); return (ENOTSUP);
snl_init_writer(&ss, &nw); snl_init_writer(&h->ss, &nw);
hdr = snl_create_genl_msg_request(&nw, family_id, hdr = snl_create_genl_msg_request(&nw, family_id,
start ? PFNL_CMD_START : PFNL_CMD_STOP); start ? PFNL_CMD_START : PFNL_CMD_STOP);
@ -206,9 +242,9 @@ pfctl_startstop(int start)
return (ENOMEM); return (ENOMEM);
seq_id = hdr->nlmsg_seq; 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); return (e.error);
@ -1288,17 +1324,13 @@ pfctl_get_creators_nl(struct snl_state *ss, uint32_t *creators, size_t *len)
} }
int 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; int error;
snl_init(&ss, NETLINK_GENERIC); error = pfctl_get_creators_nl(&h->ss, creators, len);
error = pfctl_get_creators_nl(&ss, creators, len);
snl_free(&ss);
return (error); return (error);
} }
static void static void

View file

@ -385,7 +385,13 @@ struct pfctl_syncookies {
uint32_t halfopen_states; 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); struct pfctl_status* pfctl_get_status(int dev);
uint64_t pfctl_status_counter(struct pfctl_status *status, int id); uint64_t pfctl_status_counter(struct pfctl_status *status, int id);
uint64_t pfctl_status_lcounter(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, const char *anchor, const char *anchor_call, uint32_t ticket,
uint32_t pool_ticket); uint32_t pool_ticket);
int pfctl_set_keepcounters(int dev, bool keep); 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 { struct pfctl_state_filter {
char ifname[IFNAMSIZ]; char ifname[IFNAMSIZ];

View file

@ -132,7 +132,7 @@ static const char *showopt;
static const char *debugopt; static const char *debugopt;
static char *anchoropt; static char *anchoropt;
static const char *optiopt = NULL; static const char *optiopt = NULL;
static const char *pf_device = "/dev/pf"; static const char *pf_device = PF_DEVICE;
static char *ifaceopt; static char *ifaceopt;
static char *tableopt; static char *tableopt;
static const char *tblcmdopt; static const char *tblcmdopt;
@ -144,6 +144,7 @@ int loadopt;
int altqsupport; int altqsupport;
int dev = -1; int dev = -1;
struct pfctl_handle *pfh = NULL;
static int first_title = 1; static int first_title = 1;
static int labels = 0; static int labels = 0;
@ -312,7 +313,7 @@ pfctl_enable(int dev, int opts)
{ {
int ret; int ret;
if ((ret = pfctl_startstop(1)) != 0) { if ((ret = pfctl_startstop(pfh, 1)) != 0) {
if (ret == EEXIST) if (ret == EEXIST)
errx(1, "pf already enabled"); errx(1, "pf already enabled");
else if (ret == ESRCH) else if (ret == ESRCH)
@ -335,7 +336,7 @@ pfctl_disable(int dev, int opts)
{ {
int ret; int ret;
if ((ret = pfctl_startstop(0)) != 0) { if ((ret = pfctl_startstop(pfh, 0)) != 0) {
if (ret == ENOENT) if (ret == ENOENT)
errx(1, "pf not enabled"); errx(1, "pf not enabled");
else else
@ -1665,7 +1666,7 @@ pfctl_show_creators(int opts)
uint32_t creators[16]; uint32_t creators[16];
size_t count = nitems(creators); size_t count = nitems(creators);
ret = pfctl_get_creatorids(creators, &count); ret = pfctl_get_creatorids(pfh, creators, &count);
if (ret != 0) if (ret != 0)
errx(ret, "Failed to retrieve creators"); errx(ret, "Failed to retrieve creators");
@ -3079,6 +3080,9 @@ main(int argc, char *argv[])
altqsupport = 1; altqsupport = 1;
#endif #endif
} }
pfh = pfctl_open(pf_device);
if (pfh == NULL)
err(1, "Failed to open netlink");
if (opts & PF_OPT_DISABLE) if (opts & PF_OPT_DISABLE)
if (pfctl_disable(dev, opts)) if (pfctl_disable(dev, opts))