firewall: merge branch 'th/firewall'

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/841
This commit is contained in:
Thomas Haller 2021-05-07 11:50:14 +02:00
commit 661934e48e
No known key found for this signature in database
GPG key ID: 29C2366E4DFC5728
9 changed files with 420 additions and 257 deletions

View file

@ -2569,6 +2569,8 @@ src_core_libNetworkManager_la_SOURCES = \
src/core/nm-dhcp-config.h \
src/core/nm-dispatcher.c \
src/core/nm-dispatcher.h \
src/core/nm-firewall-utils.c \
src/core/nm-firewall-utils.h \
src/core/nm-firewalld-manager.c \
src/core/nm-firewalld-manager.h \
src/core/nm-proxy-config.c \

View file

@ -15,6 +15,7 @@
#include "libnm-glib-aux/nm-c-list.h"
#include "libnm-glib-aux/nm-uuid.h"
#include "libnm-glib-aux/nm-str-buf.h"
#include "libnm-base/nm-net-aux.h"
#include "libnm-core-aux-intern/nm-common-macros.h"
#include "nm-utils.h"
@ -1637,206 +1638,6 @@ nm_utils_ip_routes_to_dbus(int addr_family,
/*****************************************************************************/
typedef struct {
char *table;
char *rule;
} ShareRule;
struct _NMUtilsShareRules {
GArray *rules;
};
static void
_share_rule_clear(gpointer data)
{
ShareRule *rule = data;
g_free(rule->table);
g_free(rule->rule);
}
NMUtilsShareRules *
nm_utils_share_rules_new(void)
{
NMUtilsShareRules *self;
self = g_slice_new(NMUtilsShareRules);
*self = (NMUtilsShareRules){
.rules = g_array_sized_new(FALSE, FALSE, sizeof(ShareRule), 10),
};
g_array_set_clear_func(self->rules, _share_rule_clear);
return self;
}
void
nm_utils_share_rules_free(NMUtilsShareRules *self)
{
if (!self)
return;
g_array_unref(self->rules);
nm_g_slice_free(self);
}
void
nm_utils_share_rules_add_rule_take(NMUtilsShareRules *self, const char *table, char *rule_take)
{
ShareRule *rule;
g_return_if_fail(self);
g_return_if_fail(table);
g_return_if_fail(rule_take);
rule = nm_g_array_append_new(self->rules, ShareRule);
*rule = (ShareRule){
.table = g_strdup(table),
.rule = g_steal_pointer(&rule_take),
};
}
void
nm_utils_share_rules_apply(NMUtilsShareRules *self, gboolean shared)
{
guint i;
g_return_if_fail(self);
if (self->rules->len == 0)
return;
/* depending on whether we share or unshare, we add/remote the rules
* in opposite order. */
if (shared)
i = self->rules->len - 1;
else
i = 0;
for (;;) {
gs_free_error GError *error = NULL;
ShareRule * rule;
gs_free const char ** argv = NULL;
gs_free char * cmd = NULL;
int status;
rule = &g_array_index(self->rules, ShareRule, i);
cmd = g_strdup_printf("%s --table %s %s %s",
IPTABLES_PATH,
rule->table,
shared ? "--insert" : "--delete",
rule->rule);
argv = nm_utils_strsplit_set(cmd, " ");
nm_log_info(LOGD_SHARING, "Executing: %s", cmd);
if (!g_spawn_sync("/",
(char **) argv,
(char **) NM_PTRARRAY_EMPTY(const char *),
G_SPAWN_STDOUT_TO_DEV_NULL | G_SPAWN_STDERR_TO_DEV_NULL,
NULL,
NULL,
NULL,
NULL,
&status,
&error)) {
nm_log_warn(LOGD_SHARING, "Error executing command: %s", error->message);
goto next;
}
if (WEXITSTATUS(status)) {
nm_log_warn(LOGD_SHARING, "** Command returned exit status %d.", WEXITSTATUS(status));
}
next:
if (shared) {
if (i == 0)
break;
i--;
} else {
i++;
if (i >= self->rules->len)
break;
}
}
}
void
nm_utils_share_rules_add_all_rules(NMUtilsShareRules *self,
const char * ip_iface,
in_addr_t addr,
guint plen)
{
in_addr_t netmask;
in_addr_t network;
char str_mask[NM_UTILS_INET_ADDRSTRLEN];
char str_addr[NM_UTILS_INET_ADDRSTRLEN];
nm_assert(self);
netmask = _nm_utils_ip4_prefix_to_netmask(plen);
_nm_utils_inet4_ntop(netmask, str_mask);
network = addr & netmask;
_nm_utils_inet4_ntop(network, str_addr);
nm_utils_share_rules_add_rule_v(
self,
"nat",
"POSTROUTING --source %s/%s ! --destination %s/%s --jump MASQUERADE",
str_addr,
str_mask,
str_addr,
str_mask);
nm_utils_share_rules_add_rule_v(
self,
"filter",
"FORWARD --destination %s/%s --out-interface %s --match state --state "
"ESTABLISHED,RELATED --jump ACCEPT",
str_addr,
str_mask,
ip_iface);
nm_utils_share_rules_add_rule_v(self,
"filter",
"FORWARD --source %s/%s --in-interface %s --jump ACCEPT",
str_addr,
str_mask,
ip_iface);
nm_utils_share_rules_add_rule_v(self,
"filter",
"FORWARD --in-interface %s --out-interface %s --jump ACCEPT",
ip_iface,
ip_iface);
nm_utils_share_rules_add_rule_v(self,
"filter",
"FORWARD --out-interface %s --jump REJECT",
ip_iface);
nm_utils_share_rules_add_rule_v(self,
"filter",
"FORWARD --in-interface %s --jump REJECT",
ip_iface);
nm_utils_share_rules_add_rule_v(
self,
"filter",
"INPUT --in-interface %s --protocol udp --destination-port 67 --jump ACCEPT",
ip_iface);
nm_utils_share_rules_add_rule_v(
self,
"filter",
"INPUT --in-interface %s --protocol tcp --destination-port 67 --jump ACCEPT",
ip_iface);
nm_utils_share_rules_add_rule_v(
self,
"filter",
"INPUT --in-interface %s --protocol udp --destination-port 53 --jump ACCEPT",
ip_iface);
nm_utils_share_rules_add_rule_v(
self,
"filter",
"INPUT --in-interface %s --protocol tcp --destination-port 53 --jump ACCEPT",
ip_iface);
}
/*****************************************************************************/
/* Singleton NMPlatform subclass instance and cached class object */
NM_DEFINE_SINGLETON_INSTANCE(NMPlatform);

View file

@ -224,33 +224,6 @@ NM_AUTO_DEFINE_FCN(NMDhcpLease *, _nm_auto_unref_dhcplease, nm_dhcp_lease_unref)
/*****************************************************************************/
typedef struct _NMUtilsShareRules NMUtilsShareRules;
NMUtilsShareRules *nm_utils_share_rules_new(void);
void nm_utils_share_rules_free(NMUtilsShareRules *self);
void
nm_utils_share_rules_add_rule_take(NMUtilsShareRules *self, const char *table, char *rule_take);
static inline void
nm_utils_share_rules_add_rule(NMUtilsShareRules *self, const char *table, const char *rule)
{
nm_utils_share_rules_add_rule_take(self, table, g_strdup(rule));
}
#define nm_utils_share_rules_add_rule_v(self, table, ...) \
nm_utils_share_rules_add_rule_take((self), (table), g_strdup_printf(__VA_ARGS__))
void nm_utils_share_rules_add_all_rules(NMUtilsShareRules *self,
const char * ip_iface,
in_addr_t addr,
guint plen);
void nm_utils_share_rules_apply(NMUtilsShareRules *self, gboolean shared);
/*****************************************************************************/
void nm_platform_setup(NMPlatform *instance);
NMPlatform *nm_platform_get(void);

View file

@ -52,6 +52,7 @@
#include "dnsmasq/nm-dnsmasq-manager.h"
#include "nm-dhcp-config.h"
#include "nm-rfkill-manager.h"
#include "nm-firewall-utils.h"
#include "nm-firewalld-manager.h"
#include "settings/nm-settings-connection.h"
#include "settings/nm-settings.h"
@ -11571,7 +11572,7 @@ start_sharing(NMDevice *self, NMIP4Config *config, GError **error)
NMConnection * conn;
NMSettingConnection * s_con;
gboolean announce_android_metered;
NMUtilsShareRules * share_rules;
NMFirewallConfig * firewall_config;
g_return_val_if_fail(config, FALSE);
@ -11596,13 +11597,9 @@ start_sharing(NMDevice *self, NMIP4Config *config, GError **error)
req = nm_device_get_act_request(self);
g_return_val_if_fail(req, FALSE);
share_rules = nm_utils_share_rules_new();
firewall_config = nm_firewall_config_new(ip_iface, ip4_addr->address, ip4_addr->plen);
nm_utils_share_rules_add_all_rules(share_rules, ip_iface, ip4_addr->address, ip4_addr->plen);
nm_utils_share_rules_apply(share_rules, TRUE);
nm_act_request_set_shared(req, share_rules);
nm_act_request_set_shared(req, firewall_config);
conn = nm_act_request_get_applied_connection(req);
s_con = nm_connection_get_setting_connection(conn);

View file

@ -163,6 +163,7 @@ libNetworkManager = static_library(
'nm-dcb.c',
'nm-dhcp-config.c',
'nm-dispatcher.c',
'nm-firewall-utils.c',
'nm-firewalld-manager.c',
'nm-hostname-manager.c',
'nm-keep-alive.c',

View file

@ -13,17 +13,19 @@
#include <unistd.h>
#include "c-list/src/c-list.h"
#include "nm-setting-wireless-security.h"
#include "nm-setting-8021x.h"
#include "devices/nm-device.h"
#include "nm-active-connection.h"
#include "settings/nm-settings-connection.h"
#include "libnm-core-aux-intern/nm-auth-subject.h"
#include "nm-setting-8021x.h"
#include "nm-setting-wireless-security.h"
#include "devices/nm-device.h"
#include "nm-active-connection.h"
#include "nm-firewall-utils.h"
#include "settings/nm-settings-connection.h"
typedef struct {
CList call_ids_lst_head;
NMUtilsShareRules *share_rules;
CList call_ids_lst_head;
NMFirewallConfig *firewall_config;
} NMActRequestPrivate;
struct _NMActRequest {
@ -248,31 +250,31 @@ nm_act_request_clear_secrets(NMActRequest *self)
/*****************************************************************************/
NMUtilsShareRules *
NMFirewallConfig *
nm_act_request_get_shared(NMActRequest *req)
{
g_return_val_if_fail(NM_IS_ACT_REQUEST(req), FALSE);
return NM_ACT_REQUEST_GET_PRIVATE(req)->share_rules;
return NM_ACT_REQUEST_GET_PRIVATE(req)->firewall_config;
}
void
nm_act_request_set_shared(NMActRequest *req, NMUtilsShareRules *rules)
nm_act_request_set_shared(NMActRequest *req, NMFirewallConfig *rules)
{
NMActRequestPrivate *priv = NM_ACT_REQUEST_GET_PRIVATE(req);
g_return_if_fail(NM_IS_ACT_REQUEST(req));
if (priv->share_rules == rules)
if (priv->firewall_config == rules)
return;
if (priv->share_rules) {
nm_utils_share_rules_apply(priv->share_rules, FALSE);
priv->share_rules = NULL;
if (priv->firewall_config) {
nm_firewall_config_apply(priv->firewall_config, FALSE);
priv->firewall_config = NULL;
}
if (rules) {
priv->share_rules = rules;
nm_utils_share_rules_apply(priv->share_rules, TRUE);
priv->firewall_config = rules;
nm_firewall_config_apply(priv->firewall_config, TRUE);
}
}
@ -506,9 +508,9 @@ dispose(GObject *object)
c_list_for_each_entry_safe (call_id, call_id_safe, &priv->call_ids_lst_head, call_ids_lst)
_do_cancel_secrets(self, call_id, TRUE);
if (priv->share_rules) {
nm_utils_share_rules_apply(priv->share_rules, FALSE);
nm_clear_pointer(&priv->share_rules, nm_utils_share_rules_free);
if (priv->firewall_config) {
nm_firewall_config_apply(priv->firewall_config, FALSE);
nm_clear_pointer(&priv->firewall_config, nm_firewall_config_free);
}
G_OBJECT_CLASS(nm_act_request_parent_class)->dispose(object);

View file

@ -38,11 +38,11 @@ NMConnection *nm_act_request_get_applied_connection(NMActRequest *req);
/*****************************************************************************/
struct _NMUtilsShareRules;
struct _NMFirewallConfig;
struct _NMUtilsShareRules *nm_act_request_get_shared(NMActRequest *req);
struct _NMFirewallConfig *nm_act_request_get_shared(NMActRequest *req);
void nm_act_request_set_shared(NMActRequest *req, struct _NMUtilsShareRules *rules);
void nm_act_request_set_shared(NMActRequest *req, struct _NMFirewallConfig *rules);
/*****************************************************************************/

View file

@ -0,0 +1,369 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (C) 2004 - 2016 Red Hat, Inc.
* Copyright (C) 2005 - 2008 Novell, Inc.
*/
#include "src/core/nm-default-daemon.h"
#include "nm-firewall-utils.h"
#include "libnm-glib-aux/nm-str-buf.h"
#include "libnm-platform/nm-platform.h"
/*****************************************************************************/
#define _SHARE_IPTABLES_SUBNET_TO_STR_LEN (INET_ADDRSTRLEN + 1 + 2 + 1)
static const char *
_share_iptables_subnet_to_str(char buf[static _SHARE_IPTABLES_SUBNET_TO_STR_LEN],
in_addr_t addr,
guint8 plen)
{
char buf_addr[INET_ADDRSTRLEN];
in_addr_t netmask;
int l;
netmask = _nm_utils_ip4_prefix_to_netmask(plen);
l = g_snprintf(buf,
_SHARE_IPTABLES_SUBNET_TO_STR_LEN,
"%s/%u",
_nm_utils_inet4_ntop(addr & netmask, buf_addr),
plen);
nm_assert(l < _SHARE_IPTABLES_SUBNET_TO_STR_LEN);
return buf;
}
static char *
_share_iptables_get_name(gboolean is_comment, const char *prefix, const char *ip_iface)
{
NMStrBuf strbuf = NM_STR_BUF_INIT(NM_UTILS_GET_NEXT_REALLOC_SIZE_40, FALSE);
gsize ip_iface_len;
nm_assert(prefix);
nm_assert(ip_iface);
/* This function is used to generate iptables chain names and comments.
* Chain names must be shorter than 29 chars. Comments don't have this
* limitation.
*
* Below we sanitize the ip_iface. If it's all benign, we use
* - either "-$IP_IFACE" (at most 16 chars)
* - otherwise, we base64 encode the name as "$(base64 $IP_IFACE)", at
* most 20 chars.
*
* Since for benign names we already add a '-', prefix probably should not
* contain a '-'. The '-' is necessary to distinguish between base64 encoding
* an plain name.
*
* That means, for chain names the prefix must be at most 8 chars long. */
nm_assert(is_comment || (strlen(prefix) <= 8));
nm_str_buf_append(&strbuf, prefix);
ip_iface_len = strlen(ip_iface);
G_STATIC_ASSERT_EXPR(NMP_IFNAMSIZ == 16);
if (ip_iface_len >= NMP_IFNAMSIZ) {
nm_assert_not_reached();
ip_iface_len = NMP_IFNAMSIZ - 1;
}
if (NM_STRCHAR_ALL(ip_iface,
ch,
(ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'z')
|| (ch >= 'A' && ch <= 'Z') || NM_IN_SET(ch, '.', '_', '-', '+'))) {
nm_str_buf_append_c(&strbuf, '-');
nm_str_buf_append(&strbuf, ip_iface);
} else {
gs_free char *s = NULL;
s = g_base64_encode((const guchar *) ip_iface, ip_iface_len);
nm_str_buf_append(&strbuf, s);
}
return nm_str_buf_finalize(&strbuf, NULL);
}
static gboolean
_share_iptables_call_v(const char *const *argv)
{
gs_free_error GError *error = NULL;
gs_free char * argv_str = NULL;
int status;
nm_log_dbg(LOGD_SHARING, "iptables: %s", (argv_str = g_strjoinv(" ", (char **) argv)));
if (!g_spawn_sync("/",
(char **) argv,
(char **) NM_PTRARRAY_EMPTY(const char *),
G_SPAWN_STDOUT_TO_DEV_NULL | G_SPAWN_STDERR_TO_DEV_NULL,
NULL,
NULL,
NULL,
NULL,
&status,
&error)) {
nm_log_warn(LOGD_SHARING,
"iptables: error executing command %s: %s",
argv[0],
error->message);
return FALSE;
}
if (!g_spawn_check_exit_status(status, &error)) {
nm_log_warn(LOGD_SHARING, "iptables: command %s failed: %s", argv[0], error->message);
return FALSE;
}
return TRUE;
}
#define _share_iptables_call(...) _share_iptables_call_v(NM_MAKE_STRV(__VA_ARGS__))
static gboolean
_share_iptables_chain_op(const char *table, const char *chain, const char *op)
{
return _share_iptables_call("" IPTABLES_PATH "", "--table", table, op, chain);
}
static gboolean
_share_iptables_chain_delete(const char *table, const char *chain)
{
_share_iptables_chain_op(table, chain, "--flush");
return _share_iptables_chain_op(table, chain, "--delete-chain");
}
static gboolean
_share_iptables_chain_add(const char *table, const char *chain)
{
if (_share_iptables_chain_op(table, chain, "--new-chain"))
return TRUE;
_share_iptables_chain_delete(table, chain);
return _share_iptables_chain_op(table, chain, "--new-chain");
}
static void
_share_iptables_set_masquerade(gboolean add, const char *ip_iface, in_addr_t addr, guint8 plen)
{
char str_subnet[_SHARE_IPTABLES_SUBNET_TO_STR_LEN];
gs_free char *comment_name = NULL;
comment_name = _share_iptables_get_name(TRUE, "nm-shared", ip_iface);
_share_iptables_subnet_to_str(str_subnet, addr, plen);
_share_iptables_call("" IPTABLES_PATH "",
"--table",
"nat",
add ? "--insert" : "--delete",
"POSTROUTING",
"--source",
str_subnet,
"!",
"--destination",
str_subnet,
"--jump",
"MASQUERADE",
"-m",
"comment",
"--comment",
comment_name);
}
static void
_share_iptables_set_shared_chains_add(const char *chain_input,
const char *chain_forward,
const char *ip_iface,
in_addr_t addr,
guint plen)
{
const char *const input_params[][2] = {
{
"tcp",
"67",
},
{
"udp",
"67",
},
{
"tcp",
"53",
},
{
"udp",
"53",
},
};
char str_subnet[_SHARE_IPTABLES_SUBNET_TO_STR_LEN];
int i;
_share_iptables_subnet_to_str(str_subnet, addr, plen);
_share_iptables_chain_add("filter", chain_input);
for (i = 0; i < (int) G_N_ELEMENTS(input_params); i++) {
_share_iptables_call("" IPTABLES_PATH "",
"--table",
"filter",
"--append",
chain_input,
"--protocol",
input_params[i][0],
"--destination-port",
input_params[i][1],
"--jump",
"ACCEPT");
}
_share_iptables_chain_add("filter", chain_forward);
_share_iptables_call("" IPTABLES_PATH "",
"--table",
"filter",
"--append",
chain_forward,
"--destination",
str_subnet,
"--out-interface",
ip_iface,
"--match",
"state",
"--state",
"ESTABLISHED,RELATED",
"--jump",
"ACCEPT");
_share_iptables_call("" IPTABLES_PATH "",
"--table",
"filter",
"--append",
chain_forward,
"--source",
str_subnet,
"--in-interface",
ip_iface,
"--jump",
"ACCEPT");
_share_iptables_call("" IPTABLES_PATH "",
"--table",
"filter",
"--append",
chain_forward,
"--in-interface",
ip_iface,
"--out-interface",
ip_iface,
"--jump",
"ACCEPT");
_share_iptables_call("" IPTABLES_PATH "",
"--table",
"filter",
"--append",
chain_forward,
"--out-interface",
ip_iface,
"--jump",
"REJECT");
_share_iptables_call("" IPTABLES_PATH "",
"--table",
"filter",
"--append",
chain_forward,
"--in-interface",
ip_iface,
"--jump",
"REJECT");
}
static void
_share_iptables_set_shared_chains_delete(const char *chain_input, const char *chain_forward)
{
_share_iptables_chain_delete("filter", chain_input);
_share_iptables_chain_delete("filter", chain_forward);
}
_nm_unused static void
_share_iptables_set_shared(gboolean add, const char *ip_iface, in_addr_t addr, guint plen)
{
gs_free char *comment_name = NULL;
gs_free char *chain_input = NULL;
gs_free char *chain_forward = NULL;
comment_name = _share_iptables_get_name(TRUE, "nm-shared", ip_iface);
chain_input = _share_iptables_get_name(FALSE, "nm-sh-in", ip_iface);
chain_forward = _share_iptables_get_name(FALSE, "nm-sh-fw", ip_iface);
if (add)
_share_iptables_set_shared_chains_add(chain_input, chain_forward, ip_iface, addr, plen);
_share_iptables_call("" IPTABLES_PATH "",
"--table",
"filter",
add ? "--insert" : "--delete",
"INPUT",
"--in-interface",
ip_iface,
"--jump",
chain_input,
"-m",
"comment",
"--comment",
comment_name);
_share_iptables_call("" IPTABLES_PATH "",
"--table",
"filter",
add ? "--insert" : "--delete",
"FORWARD",
"--jump",
chain_forward,
"-m",
"comment",
"--comment",
comment_name);
if (!add)
_share_iptables_set_shared_chains_delete(chain_input, chain_forward);
}
struct _NMFirewallConfig {
char * ip_iface;
in_addr_t addr;
guint8 plen;
};
NMFirewallConfig *
nm_firewall_config_new(const char *ip_iface, in_addr_t addr, guint8 plen)
{
NMFirewallConfig *self;
nm_assert(ip_iface);
nm_assert(addr != 0u);
nm_assert(plen <= 32);
self = g_slice_new(NMFirewallConfig);
*self = (NMFirewallConfig){
.ip_iface = g_strdup(ip_iface),
.addr = addr,
.plen = plen,
};
return self;
}
void
nm_firewall_config_free(NMFirewallConfig *self)
{
if (!self)
return;
g_free(self->ip_iface);
nm_g_slice_free(self);
}
void
nm_firewall_config_apply(NMFirewallConfig *self, gboolean shared)
{
_share_iptables_set_masquerade(shared, self->ip_iface, self->addr, self->plen);
_share_iptables_set_shared(shared, self->ip_iface, self->addr, self->plen);
}

View file

@ -0,0 +1,18 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (C) 2004 - 2016 Red Hat, Inc.
* Copyright (C) 2005 - 2008 Novell, Inc.
*/
#ifndef __NM_FIREWALL_UTILS_H__
#define __NM_FIREWALL_UTILS_H__
typedef struct _NMFirewallConfig NMFirewallConfig;
NMFirewallConfig *nm_firewall_config_new(const char *ip_iface, in_addr_t addr, guint8 plen);
void nm_firewall_config_free(NMFirewallConfig *self);
void nm_firewall_config_apply(NMFirewallConfig *self, gboolean shared);
#endif /* __NM_FIREWALL_UTILS_H__ */