Merge Official Source
Signed-off-by: Tianling Shen <cnsztl@immortalwrt.org>
This commit is contained in:
+2
-2
@@ -1,2 +1,2 @@
|
||||
LINUX_VERSION-6.6 = .143
|
||||
LINUX_KERNEL_HASH-6.6.143 = dace1f8dc9c0dbf5df14f47e3229cd62c298e83049681731ef229f2ba7592932
|
||||
LINUX_VERSION-6.6 = .144
|
||||
LINUX_KERNEL_HASH-6.6.144 = ec19b8307b495078cdc03749e63cb3802ac23cf6122c82eb9719feeb4557ecc3
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=wireless-regdb
|
||||
PKG_VERSION:=2026.03.18
|
||||
PKG_VERSION:=2026.05.30
|
||||
PKG_RELEASE:=1
|
||||
PKG_LICENSE:=ISC
|
||||
PKG_LICENSE_FILES:=LICENSE
|
||||
|
||||
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.xz
|
||||
PKG_SOURCE_URL:=@KERNEL/software/network/wireless-regdb/
|
||||
PKG_HASH:=5fc0000475d8c5368ccc5222827c16aef98b1eb6a69c9b5a3e7b7e98528945ac
|
||||
PKG_HASH:=8a27bfc081bafed8c24dd70fab0d96f098e5a0bfcd08d3da672595f225ab8993
|
||||
|
||||
PKG_MAINTAINER:=Felix Fietkau <nbd@nbd.name>
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
--- a/db.txt
|
||||
+++ b/db.txt
|
||||
@@ -460,9 +460,8 @@ country CL: DFS-JP
|
||||
@@ -469,9 +469,8 @@ country CL: DFS-JP
|
||||
# https://www.miit.gov.cn/cms_files/filemanager/1226211233/attach/20236/d1dc19424d5a4cfe90d631adeee8dd58.pdf
|
||||
# Note: The transmit power for 5250-5350MHz bands can be raised by 3dBm when TPC is implemented
|
||||
country CN: DFS-FCC
|
||||
@@ -12,7 +12,7 @@
|
||||
(5725 - 5850 @ 80), (33)
|
||||
# 60 GHz band channels 1,4: 28dBm, channels 2,3: 44dBm
|
||||
# ref: http://www.miit.gov.cn/n11293472/n11505629/n11506593/n11960250/n11960606/n11960700/n12330791.files/n12330790.pdf
|
||||
@@ -2040,14 +2039,12 @@ country US: DFS-FCC
|
||||
@@ -2081,14 +2080,12 @@ country US: DFS-FCC
|
||||
(920 - 928 @ 8), (30)
|
||||
(2400 - 2472 @ 40), (30)
|
||||
# 5.15 ~ 5.25 GHz: 30 dBm for master mode, 23 dBm for clients
|
||||
|
||||
@@ -10,15 +10,15 @@ include $(INCLUDE_DIR)/kernel.mk
|
||||
|
||||
PKG_NAME:=mac80211
|
||||
|
||||
PKG_VERSION:=6.12.61
|
||||
PKG_VERSION:=6.12.96
|
||||
PKG_RELEASE:=1
|
||||
PKG_LICENSE:=GPL-2.0-only
|
||||
PKG_LICENSE_FILES:=COPYING
|
||||
|
||||
PKG_SOURCE_URL:=http://mirror2.openwrt.org/sources/
|
||||
PKG_HASH:=9db2f836dba7f38ad68f8798720ad4360bce6a3557bde02b88b3a4f068c77118
|
||||
PKG_SOURCE_URL:=https://github.com/openwrt/backports/releases/download/backports-v$(PKG_VERSION)
|
||||
PKG_HASH:=816882e80cd6eb7f10d2851f634ca7964eb47a62f64eff4bf6f92ffdf8593650
|
||||
|
||||
PKG_SOURCE:=backports-$(PKG_VERSION).tar.xz
|
||||
PKG_SOURCE:=backports-$(PKG_VERSION).tar.zst
|
||||
PKG_BUILD_DIR:=$(KERNEL_BUILD_DIR)/$(if $(BUILD_VARIANT),$(PKG_NAME)-$(BUILD_VARIANT)/)backports-$(PKG_VERSION)
|
||||
PKG_BUILD_PARALLEL:=1
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ Signed-off-by: Sven Eckelmann <sven@open-mesh.com>
|
||||
|
||||
--- a/drivers/net/wireless/ath/ath10k/core.c
|
||||
+++ b/drivers/net/wireless/ath/ath10k/core.c
|
||||
@@ -3577,6 +3577,16 @@ int ath10k_core_register(struct ath10k *
|
||||
@@ -3576,6 +3576,16 @@ int ath10k_core_register(struct ath10k *
|
||||
|
||||
queue_work(ar->workqueue, &ar->register_work);
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
--- a/drivers/net/wireless/ath/ath10k/mac.c
|
||||
+++ b/drivers/net/wireless/ath/ath10k/mac.c
|
||||
@@ -9932,6 +9932,21 @@ static int ath10k_mac_init_rd(struct ath
|
||||
@@ -9950,6 +9950,21 @@ static int ath10k_mac_init_rd(struct ath
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
int ath10k_mac_register(struct ath10k *ar)
|
||||
{
|
||||
static const u32 cipher_suites[] = {
|
||||
@@ -10294,6 +10309,12 @@ int ath10k_mac_register(struct ath10k *a
|
||||
@@ -10312,6 +10327,12 @@ int ath10k_mac_register(struct ath10k *a
|
||||
|
||||
ar->hw->weight_multiplier = ATH10K_AIRTIME_WEIGHT_MULTIPLIER;
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ Signed-off-by: Mathias Kresin <dev@kresin.me>
|
||||
if (ret)
|
||||
--- a/drivers/net/wireless/ath/ath10k/mac.c
|
||||
+++ b/drivers/net/wireless/ath/ath10k/mac.c
|
||||
@@ -10310,7 +10310,7 @@ int ath10k_mac_register(struct ath10k *a
|
||||
@@ -10328,7 +10328,7 @@ int ath10k_mac_register(struct ath10k *a
|
||||
ar->hw->weight_multiplier = ATH10K_AIRTIME_WEIGHT_MULTIPLIER;
|
||||
|
||||
#ifdef CPTCFG_MAC80211_LEDS
|
||||
|
||||
+5
-5
@@ -28,8 +28,8 @@ Forwarded: no
|
||||
|
||||
--- a/drivers/net/wireless/ath/ath10k/mac.c
|
||||
+++ b/drivers/net/wireless/ath/ath10k/mac.c
|
||||
@@ -1030,6 +1030,40 @@ static inline int ath10k_vdev_setup_sync
|
||||
return ar->last_wmi_vdev_start_status;
|
||||
@@ -1051,6 +1051,40 @@ static inline int ath10k_vdev_delete_syn
|
||||
return 0;
|
||||
}
|
||||
|
||||
+static u32 ath10k_get_max_antenna_gain(struct ath10k *ar,
|
||||
@@ -69,7 +69,7 @@ Forwarded: no
|
||||
static int ath10k_monitor_vdev_start(struct ath10k *ar, int vdev_id)
|
||||
{
|
||||
struct cfg80211_chan_def *chandef = NULL;
|
||||
@@ -1062,7 +1096,8 @@ static int ath10k_monitor_vdev_start(str
|
||||
@@ -1083,7 +1117,8 @@ static int ath10k_monitor_vdev_start(str
|
||||
arg.channel.min_power = 0;
|
||||
arg.channel.max_power = channel->max_power * 2;
|
||||
arg.channel.max_reg_power = channel->max_reg_power * 2;
|
||||
@@ -79,7 +79,7 @@ Forwarded: no
|
||||
|
||||
reinit_completion(&ar->vdev_setup_done);
|
||||
reinit_completion(&ar->vdev_delete_done);
|
||||
@@ -1508,7 +1543,8 @@ static int ath10k_vdev_start_restart(str
|
||||
@@ -1529,7 +1564,8 @@ static int ath10k_vdev_start_restart(str
|
||||
arg.channel.min_power = 0;
|
||||
arg.channel.max_power = chandef->chan->max_power * 2;
|
||||
arg.channel.max_reg_power = chandef->chan->max_reg_power * 2;
|
||||
@@ -89,7 +89,7 @@ Forwarded: no
|
||||
|
||||
if (arvif->vdev_type == WMI_VDEV_TYPE_AP) {
|
||||
arg.ssid = arvif->u.ap.ssid;
|
||||
@@ -3439,7 +3475,8 @@ static int ath10k_update_channel_list(st
|
||||
@@ -3460,7 +3496,8 @@ static int ath10k_update_channel_list(st
|
||||
ch->min_power = 0;
|
||||
ch->max_power = channel->max_power * 2;
|
||||
ch->max_reg_power = channel->max_reg_power * 2;
|
||||
|
||||
+2
-2
@@ -18,7 +18,7 @@ Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
|
||||
|
||||
--- a/drivers/net/wireless/ath/ath10k/core.c
|
||||
+++ b/drivers/net/wireless/ath/ath10k/core.c
|
||||
@@ -9,6 +9,7 @@
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/firmware.h>
|
||||
#include <linux/of.h>
|
||||
@@ -26,7 +26,7 @@ Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
|
||||
#include <linux/property.h>
|
||||
#include <linux/dmi.h>
|
||||
#include <linux/ctype.h>
|
||||
@@ -3449,6 +3450,8 @@ static int ath10k_core_probe_fw(struct a
|
||||
@@ -3448,6 +3449,8 @@ static int ath10k_core_probe_fw(struct a
|
||||
|
||||
device_get_mac_address(ar->dev, ar->mac_addr);
|
||||
|
||||
|
||||
+1
-1
@@ -140,7 +140,7 @@ Signed-off-by: Robert Marko <robimarko@gmail.com>
|
||||
int ath11k_mhi_register(struct ath11k_pci *ar_pci);
|
||||
--- a/drivers/net/wireless/ath/ath11k/pci.c
|
||||
+++ b/drivers/net/wireless/ath/ath11k/pci.c
|
||||
@@ -374,13 +374,20 @@ static void ath11k_pci_sw_reset(struct a
|
||||
@@ -392,13 +392,20 @@ static void ath11k_pci_sw_reset(struct a
|
||||
static void ath11k_pci_init_qmi_ce_config(struct ath11k_base *ab)
|
||||
{
|
||||
struct ath11k_qmi_ce_cfg *cfg = &ab->qmi.ce_cfg;
|
||||
|
||||
+2
-2
@@ -31,7 +31,7 @@ Signed-off-by: Robert Marko <robimarko@gmail.com>
|
||||
{
|
||||
.hw_rev = ATH11K_HW_IPQ8074,
|
||||
.name = "ipq8074 hw2.0",
|
||||
@@ -2239,7 +2239,8 @@ static void ath11k_core_reset(struct wor
|
||||
@@ -2267,7 +2267,8 @@ static void ath11k_core_reset(struct wor
|
||||
static int ath11k_init_hw_params(struct ath11k_base *ab)
|
||||
{
|
||||
const struct ath11k_hw_params *hw_params = NULL;
|
||||
@@ -41,7 +41,7 @@ Signed-off-by: Robert Marko <robimarko@gmail.com>
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(ath11k_hw_params); i++) {
|
||||
hw_params = &ath11k_hw_params[i];
|
||||
@@ -2255,7 +2256,31 @@ static int ath11k_init_hw_params(struct
|
||||
@@ -2283,7 +2284,31 @@ static int ath11k_init_hw_params(struct
|
||||
|
||||
ab->hw_params = *hw_params;
|
||||
|
||||
|
||||
+3
-3
@@ -291,9 +291,9 @@ Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
|
||||
int pdev_idx;
|
||||
struct ath11k *ar;
|
||||
enum wmi_vdev_type vdev_type;
|
||||
@@ -929,24 +772,14 @@ int ath11k_reg_handle_chan_list(struct a
|
||||
(char *)reg_info->alpha2, 2))
|
||||
goto retfail;
|
||||
@@ -932,24 +775,14 @@ int ath11k_reg_handle_chan_list(struct a
|
||||
return 0;
|
||||
}
|
||||
|
||||
- /* Intersect new rules with default regd if a new country setting was
|
||||
- * requested, i.e a default regd was already set during initialization
|
||||
|
||||
@@ -13,7 +13,7 @@ Signed-off-by: Paweł Owoc <frut3k7@gmail.com>
|
||||
|
||||
--- a/drivers/net/wireless/ath/ath11k/wmi.c
|
||||
+++ b/drivers/net/wireless/ath/ath11k/wmi.c
|
||||
@@ -2682,7 +2682,7 @@ int ath11k_wmi_send_wmm_update_cmd_tlv(s
|
||||
@@ -2688,7 +2688,7 @@ int ath11k_wmi_send_wmm_update_cmd_tlv(s
|
||||
FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE);
|
||||
|
||||
cmd->vdev_id = vdev_id;
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
goto end;
|
||||
--- a/drivers/net/wireless/ath/ath5k/base.c
|
||||
+++ b/drivers/net/wireless/ath/ath5k/base.c
|
||||
@@ -2009,7 +2009,7 @@ ath5k_beacon_send(struct ath5k_hw *ah)
|
||||
@@ -2010,7 +2010,7 @@ ath5k_beacon_send(struct ath5k_hw *ah)
|
||||
}
|
||||
|
||||
if ((ah->opmode == NL80211_IFTYPE_AP && ah->num_ap_vifs +
|
||||
@@ -27,7 +27,7 @@
|
||||
ah->opmode == NL80211_IFTYPE_MESH_POINT) {
|
||||
u64 tsf = ath5k_hw_get_tsf64(ah);
|
||||
u32 tsftu = TSF_TO_TU(tsf);
|
||||
@@ -2095,7 +2095,7 @@ ath5k_beacon_update_timers(struct ath5k_
|
||||
@@ -2096,7 +2096,7 @@ ath5k_beacon_update_timers(struct ath5k_
|
||||
|
||||
intval = ah->bintval & AR5K_BEACON_PERIOD;
|
||||
if (ah->opmode == NL80211_IFTYPE_AP && ah->num_ap_vifs
|
||||
@@ -36,7 +36,7 @@
|
||||
intval /= ATH_BCBUF; /* staggered multi-bss beacons */
|
||||
if (intval < 15)
|
||||
ATH5K_WARN(ah, "intval %u is too low, min 15\n",
|
||||
@@ -2561,6 +2561,7 @@ static const struct ieee80211_iface_limi
|
||||
@@ -2562,6 +2562,7 @@ static const struct ieee80211_iface_limi
|
||||
BIT(NL80211_IFTYPE_MESH_POINT) |
|
||||
#endif
|
||||
BIT(NL80211_IFTYPE_AP) },
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
--- a/drivers/net/wireless/virtual/mac80211_hwsim.c
|
||||
+++ b/drivers/net/wireless/virtual/mac80211_hwsim.c
|
||||
@@ -6637,6 +6637,7 @@ static void hwsim_virtio_rx_done(struct
|
||||
@@ -6636,6 +6636,7 @@ static void hwsim_virtio_rx_done(struct
|
||||
|
||||
static int init_vqs(struct virtio_device *vdev)
|
||||
{
|
||||
@@ -8,7 +8,7 @@
|
||||
struct virtqueue_info vqs_info[HWSIM_NUM_VQS] = {
|
||||
[HWSIM_VQ_TX] = { "tx", hwsim_virtio_tx_done },
|
||||
[HWSIM_VQ_RX] = { "rx", hwsim_virtio_rx_done },
|
||||
@@ -6644,6 +6645,19 @@ static int init_vqs(struct virtio_device
|
||||
@@ -6643,6 +6644,19 @@ static int init_vqs(struct virtio_device
|
||||
|
||||
return virtio_find_vqs(vdev, HWSIM_NUM_VQS,
|
||||
hwsim_vqs, vqs_info, NULL);
|
||||
|
||||
+2
-2
@@ -17,8 +17,8 @@ Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
|
||||
|
||||
--- a/backport-include/net/genetlink.h
|
||||
+++ b/backport-include/net/genetlink.h
|
||||
@@ -172,4 +172,15 @@ static inline int genlmsg_parse(const st
|
||||
}
|
||||
@@ -192,4 +192,15 @@ int backport_genlmsg_multicast_allns(con
|
||||
#define genlmsg_multicast_allns LINUX_BACKPORT(genlmsg_multicast_allns)
|
||||
#endif /* LINUX_VERSION_IS_LESS(5,2,0) */
|
||||
|
||||
+#if LINUX_VERSION_IN_RANGE(5,15,0,5,15,169) || \
|
||||
|
||||
+4
-4
@@ -284,7 +284,7 @@ Link: https://patch.msgid.link/dae7994f-3491-40de-b537-ebf68df084bb@gmail.com
|
||||
.query_rx_desc = rtw8821c_query_rx_desc,
|
||||
--- a/drivers/net/wireless/realtek/rtw88/rtw8822b.c
|
||||
+++ b/drivers/net/wireless/realtek/rtw88/rtw8822b.c
|
||||
@@ -1980,13 +1980,13 @@ static const struct rtw_pwr_seq_cmd tran
|
||||
@@ -1981,13 +1981,13 @@ static const struct rtw_pwr_seq_cmd tran
|
||||
RTW_PWR_CMD_END, 0, 0},
|
||||
};
|
||||
|
||||
@@ -300,7 +300,7 @@ Link: https://patch.msgid.link/dae7994f-3491-40de-b537-ebf68df084bb@gmail.com
|
||||
trans_act_to_cardemu_8822b,
|
||||
trans_cardemu_to_carddis_8822b,
|
||||
NULL
|
||||
@@ -2158,7 +2158,7 @@ static const struct rtw_rqpn rqpn_table_
|
||||
@@ -2159,7 +2159,7 @@ static const struct rtw_rqpn rqpn_table_
|
||||
RTW_DMA_MAPPING_EXTRA, RTW_DMA_MAPPING_HIGH},
|
||||
};
|
||||
|
||||
@@ -309,7 +309,7 @@ Link: https://patch.msgid.link/dae7994f-3491-40de-b537-ebf68df084bb@gmail.com
|
||||
.prio[RTW_DMA_MAPPING_EXTRA] = {
|
||||
.rsvd = REG_FIFOPAGE_INFO_4, .avail = REG_FIFOPAGE_INFO_4 + 2,
|
||||
},
|
||||
@@ -2174,7 +2174,7 @@ static struct rtw_prioq_addrs prioq_addr
|
||||
@@ -2175,7 +2175,7 @@ static struct rtw_prioq_addrs prioq_addr
|
||||
.wsize = true,
|
||||
};
|
||||
|
||||
@@ -318,7 +318,7 @@ Link: https://patch.msgid.link/dae7994f-3491-40de-b537-ebf68df084bb@gmail.com
|
||||
.phy_set_param = rtw8822b_phy_set_param,
|
||||
.read_efuse = rtw8822b_read_efuse,
|
||||
.query_rx_desc = rtw8822b_query_rx_desc,
|
||||
@@ -2523,7 +2523,7 @@ static const struct rtw_reg_domain coex_
|
||||
@@ -2524,7 +2524,7 @@ static const struct rtw_reg_domain coex_
|
||||
{0xc50, MASKBYTE0, RTW_REG_DOMAIN_MAC8},
|
||||
};
|
||||
|
||||
|
||||
+3
-3
@@ -304,7 +304,7 @@ Link: https://patch.msgid.link/913f1747-38fc-4409-85a4-57bb9cee506b@gmail.com
|
||||
static void
|
||||
rtw8822b_set_tx_power_index_by_rate(struct rtw_dev *rtwdev, u8 path,
|
||||
u8 rs, u32 *phy_pwr_idx)
|
||||
@@ -2177,7 +2136,7 @@ static const struct rtw_prioq_addrs prio
|
||||
@@ -2178,7 +2137,7 @@ static const struct rtw_prioq_addrs prio
|
||||
static const struct rtw_chip_ops rtw8822b_ops = {
|
||||
.phy_set_param = rtw8822b_phy_set_param,
|
||||
.read_efuse = rtw8822b_read_efuse,
|
||||
@@ -535,7 +535,7 @@ Link: https://patch.msgid.link/913f1747-38fc-4409-85a4-57bb9cee506b@gmail.com
|
||||
struct rtw_rx_pkt_stat *pkt_stat);
|
||||
--- a/drivers/net/wireless/realtek/rtw88/sdio.c
|
||||
+++ b/drivers/net/wireless/realtek/rtw88/sdio.c
|
||||
@@ -983,8 +983,7 @@ static void rtw_sdio_rxfifo_recv(struct
|
||||
@@ -985,8 +985,7 @@ static void rtw_sdio_rxfifo_recv(struct
|
||||
|
||||
while (true) {
|
||||
rx_desc = skb->data;
|
||||
@@ -547,7 +547,7 @@ Link: https://patch.msgid.link/913f1747-38fc-4409-85a4-57bb9cee506b@gmail.com
|
||||
|
||||
--- a/drivers/net/wireless/realtek/rtw88/usb.c
|
||||
+++ b/drivers/net/wireless/realtek/rtw88/usb.c
|
||||
@@ -625,8 +625,8 @@ static void rtw_usb_rx_handler(struct wo
|
||||
@@ -634,8 +634,8 @@ static void rtw_usb_rx_handler(struct wo
|
||||
|
||||
do {
|
||||
rx_desc = skb->data;
|
||||
|
||||
+2
-2
@@ -21,7 +21,7 @@ Link: https://patch.msgid.link/8becd851-8760-4480-8e8c-c4869ce72507@gmail.com
|
||||
|
||||
--- a/drivers/net/wireless/realtek/rtw88/main.c
|
||||
+++ b/drivers/net/wireless/realtek/rtw88/main.c
|
||||
@@ -1907,6 +1907,9 @@ static int rtw_dump_hw_feature(struct rt
|
||||
@@ -1927,6 +1927,9 @@ static int rtw_dump_hw_feature(struct rt
|
||||
u8 bw;
|
||||
int i;
|
||||
|
||||
@@ -73,7 +73,7 @@ Link: https://patch.msgid.link/8becd851-8760-4480-8e8c-c4869ce72507@gmail.com
|
||||
.lps_deep_mode_supported = BIT(LPS_DEEP_MODE_LCLK),
|
||||
--- a/drivers/net/wireless/realtek/rtw88/rtw8822b.c
|
||||
+++ b/drivers/net/wireless/realtek/rtw88/rtw8822b.c
|
||||
@@ -2511,6 +2511,7 @@ const struct rtw_chip_info rtw8822b_hw_s
|
||||
@@ -2512,6 +2512,7 @@ const struct rtw_chip_info rtw8822b_hw_s
|
||||
.page_size = TX_PAGE_SIZE,
|
||||
.dig_min = 0x1c,
|
||||
.usb_tx_agg_desc_num = 3,
|
||||
|
||||
+1
-1
@@ -155,7 +155,7 @@ Link: https://patch.msgid.link/c3e73c3a-fb2f-4013-9f06-d5274211e282@gmail.com
|
||||
.lps_deep_mode_supported = BIT(LPS_DEEP_MODE_LCLK),
|
||||
--- a/drivers/net/wireless/realtek/rtw88/rtw8822b.c
|
||||
+++ b/drivers/net/wireless/realtek/rtw88/rtw8822b.c
|
||||
@@ -2512,6 +2512,7 @@ const struct rtw_chip_info rtw8822b_hw_s
|
||||
@@ -2513,6 +2513,7 @@ const struct rtw_chip_info rtw8822b_hw_s
|
||||
.dig_min = 0x1c,
|
||||
.usb_tx_agg_desc_num = 3,
|
||||
.hw_feature_report = true,
|
||||
|
||||
+1
-1
@@ -252,7 +252,7 @@ Link: https://patch.msgid.link/98ab839f-9100-44ae-9551-9af743a4aa3a@gmail.com
|
||||
.query_phy_status = query_phy_status,
|
||||
--- a/drivers/net/wireless/realtek/rtw88/rtw8822b.c
|
||||
+++ b/drivers/net/wireless/realtek/rtw88/rtw8822b.c
|
||||
@@ -2134,6 +2134,8 @@ static const struct rtw_prioq_addrs prio
|
||||
@@ -2135,6 +2135,8 @@ static const struct rtw_prioq_addrs prio
|
||||
};
|
||||
|
||||
static const struct rtw_chip_ops rtw8822b_ops = {
|
||||
|
||||
+4
-4
@@ -101,7 +101,7 @@ Link: https://patch.msgid.link/2b3e3e6f-541b-4a3b-8ca3-65b267e6a95a@gmail.com
|
||||
.lps_deep_mode_supported = BIT(LPS_DEEP_MODE_LCLK),
|
||||
--- a/drivers/net/wireless/realtek/rtw88/rtw8822b.c
|
||||
+++ b/drivers/net/wireless/realtek/rtw88/rtw8822b.c
|
||||
@@ -2515,6 +2515,7 @@ const struct rtw_chip_info rtw8822b_hw_s
|
||||
@@ -2516,6 +2516,7 @@ const struct rtw_chip_info rtw8822b_hw_s
|
||||
.usb_tx_agg_desc_num = 3,
|
||||
.hw_feature_report = true,
|
||||
.c2h_ra_report_size = 7,
|
||||
@@ -121,7 +121,7 @@ Link: https://patch.msgid.link/2b3e3e6f-541b-4a3b-8ca3-65b267e6a95a@gmail.com
|
||||
.ht_supported = true,
|
||||
--- a/drivers/net/wireless/realtek/rtw88/sdio.c
|
||||
+++ b/drivers/net/wireless/realtek/rtw88/sdio.c
|
||||
@@ -866,7 +866,7 @@ static void rtw_sdio_tx_skb_prepare(stru
|
||||
@@ -868,7 +868,7 @@ static void rtw_sdio_tx_skb_prepare(stru
|
||||
|
||||
pkt_info->qsel = rtw_sdio_get_tx_qsel(rtwdev, skb, queue);
|
||||
|
||||
@@ -174,7 +174,7 @@ Link: https://patch.msgid.link/2b3e3e6f-541b-4a3b-8ca3-65b267e6a95a@gmail.com
|
||||
void rtw_tx_rsvd_page_pkt_info_update(struct rtw_dev *rtwdev,
|
||||
--- a/drivers/net/wireless/realtek/rtw88/usb.c
|
||||
+++ b/drivers/net/wireless/realtek/rtw88/usb.c
|
||||
@@ -512,7 +512,7 @@ static int rtw_usb_write_data(struct rtw
|
||||
@@ -519,7 +519,7 @@ static int rtw_usb_write_data(struct rtw
|
||||
skb_put_data(skb, buf, size);
|
||||
skb_push(skb, chip->tx_pkt_desc_sz);
|
||||
memset(skb->data, 0, chip->tx_pkt_desc_sz);
|
||||
@@ -183,7 +183,7 @@ Link: https://patch.msgid.link/2b3e3e6f-541b-4a3b-8ca3-65b267e6a95a@gmail.com
|
||||
rtw_tx_fill_txdesc_checksum(rtwdev, pkt_info, skb->data);
|
||||
|
||||
ret = rtw_usb_write_port(rtwdev, qsel, skb,
|
||||
@@ -579,7 +579,7 @@ static int rtw_usb_tx_write(struct rtw_d
|
||||
@@ -588,7 +588,7 @@ static int rtw_usb_tx_write(struct rtw_d
|
||||
pkt_desc = skb_push(skb, chip->tx_pkt_desc_sz);
|
||||
memset(pkt_desc, 0, chip->tx_pkt_desc_sz);
|
||||
ep = qsel_to_ep(rtwusb, pkt_info->qsel);
|
||||
|
||||
+4
-4
@@ -199,7 +199,7 @@ Link: https://patch.msgid.link/904d0ab1-c046-40cd-a3a3-d4fdcf663c9d@gmail.com
|
||||
.bfer_mu_max_num = 1,
|
||||
--- a/drivers/net/wireless/realtek/rtw88/rtw8822b.c
|
||||
+++ b/drivers/net/wireless/realtek/rtw88/rtw8822b.c
|
||||
@@ -2074,12 +2074,6 @@ static const struct rtw_intf_phy_para_ta
|
||||
@@ -2075,12 +2075,6 @@ static const struct rtw_intf_phy_para_ta
|
||||
.n_gen2_para = ARRAY_SIZE(pcie_gen2_param_8822b),
|
||||
};
|
||||
|
||||
@@ -212,7 +212,7 @@ Link: https://patch.msgid.link/904d0ab1-c046-40cd-a3a3-d4fdcf663c9d@gmail.com
|
||||
static const struct rtw_hw_reg rtw8822b_dig[] = {
|
||||
[0] = { .addr = 0xc50, .mask = 0x7f },
|
||||
[1] = { .addr = 0xe50, .mask = 0x7f },
|
||||
@@ -2434,7 +2428,7 @@ static const u8 rtw8822b_pwrtrk_2g_cck_a
|
||||
@@ -2435,7 +2429,7 @@ static const u8 rtw8822b_pwrtrk_2g_cck_a
|
||||
10, 11, 11, 12, 12, 13, 13, 14, 14, 15
|
||||
};
|
||||
|
||||
@@ -221,7 +221,7 @@ Link: https://patch.msgid.link/904d0ab1-c046-40cd-a3a3-d4fdcf663c9d@gmail.com
|
||||
.pwrtrk_5gb_n[RTW_PWR_TRK_5G_1] = rtw8822b_pwrtrk_5gb_n[RTW_PWR_TRK_5G_1],
|
||||
.pwrtrk_5gb_n[RTW_PWR_TRK_5G_2] = rtw8822b_pwrtrk_5gb_n[RTW_PWR_TRK_5G_2],
|
||||
.pwrtrk_5gb_n[RTW_PWR_TRK_5G_3] = rtw8822b_pwrtrk_5gb_n[RTW_PWR_TRK_5G_3],
|
||||
@@ -2457,6 +2451,12 @@ static const struct rtw_pwr_track_tbl rt
|
||||
@@ -2458,6 +2452,12 @@ static const struct rtw_pwr_track_tbl rt
|
||||
.pwrtrk_2g_ccka_p = rtw8822b_pwrtrk_2g_cck_a_p,
|
||||
};
|
||||
|
||||
@@ -234,7 +234,7 @@ Link: https://patch.msgid.link/904d0ab1-c046-40cd-a3a3-d4fdcf663c9d@gmail.com
|
||||
static const struct rtw_reg_domain coex_info_hw_regs_8822b[] = {
|
||||
{0xcb0, MASKDWORD, RTW_REG_DOMAIN_MAC32},
|
||||
{0xcb4, MASKDWORD, RTW_REG_DOMAIN_MAC32},
|
||||
@@ -2537,7 +2537,6 @@ const struct rtw_chip_info rtw8822b_hw_s
|
||||
@@ -2538,7 +2538,6 @@ const struct rtw_chip_info rtw8822b_hw_s
|
||||
.rf_tbl = {&rtw8822b_rf_a_tbl, &rtw8822b_rf_b_tbl},
|
||||
.rfe_defs = rtw8822b_rfe_defs,
|
||||
.rfe_defs_size = ARRAY_SIZE(rtw8822b_rfe_defs),
|
||||
|
||||
+1
-1
@@ -17,7 +17,7 @@ Link: https://patch.msgid.link/e443f5d9-4b53-4f64-985c-64313ec80bef@gmail.com
|
||||
|
||||
--- a/drivers/net/wireless/realtek/rtw88/usb.c
|
||||
+++ b/drivers/net/wireless/realtek/rtw88/usb.c
|
||||
@@ -532,6 +532,7 @@ static int rtw_usb_write_data_rsvd_page(
|
||||
@@ -541,6 +541,7 @@ static int rtw_usb_write_data_rsvd_page(
|
||||
pkt_info.tx_pkt_size = size;
|
||||
pkt_info.qsel = TX_DESC_QSEL_BEACON;
|
||||
pkt_info.offset = chip->tx_pkt_desc_sz;
|
||||
|
||||
+3
-3
@@ -12,7 +12,7 @@ Signed-off-by: Bitterblue Smith <rtl8821cerfe2@gmail.com>
|
||||
|
||||
--- a/drivers/net/wireless/realtek/rtw88/usb.c
|
||||
+++ b/drivers/net/wireless/realtek/rtw88/usb.c
|
||||
@@ -985,6 +985,32 @@ static void rtw_usb_intf_deinit(struct r
|
||||
@@ -993,6 +993,32 @@ static void rtw_usb_intf_deinit(struct r
|
||||
usb_set_intfdata(intf, NULL);
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ Signed-off-by: Bitterblue Smith <rtl8821cerfe2@gmail.com>
|
||||
static int rtw_usb_switch_mode_new(struct rtw_dev *rtwdev)
|
||||
{
|
||||
enum usb_device_speed cur_speed;
|
||||
@@ -1038,7 +1064,8 @@ static int rtw_usb_switch_mode(struct rt
|
||||
@@ -1046,7 +1072,8 @@ static int rtw_usb_switch_mode(struct rt
|
||||
{
|
||||
u8 id = rtwdev->chip->id;
|
||||
|
||||
@@ -55,7 +55,7 @@ Signed-off-by: Bitterblue Smith <rtl8821cerfe2@gmail.com>
|
||||
return 0;
|
||||
|
||||
if (!rtwdev->efuse.usb_mode_switch) {
|
||||
@@ -1053,7 +1080,10 @@ static int rtw_usb_switch_mode(struct rt
|
||||
@@ -1061,7 +1088,10 @@ static int rtw_usb_switch_mode(struct rt
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -19,7 +19,7 @@ Signed-off-by: Bitterblue Smith <rtl8821cerfe2@gmail.com>
|
||||
|
||||
--- a/drivers/net/wireless/realtek/rtw88/usb.c
|
||||
+++ b/drivers/net/wireless/realtek/rtw88/usb.c
|
||||
@@ -843,6 +843,32 @@ static void rtw_usb_dynamic_rx_agg_v1(st
|
||||
@@ -852,6 +852,32 @@ static void rtw_usb_dynamic_rx_agg_v1(st
|
||||
rtw_write16(rtwdev, REG_RXDMA_AGG_PG_TH, val16);
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ Signed-off-by: Bitterblue Smith <rtl8821cerfe2@gmail.com>
|
||||
static void rtw_usb_dynamic_rx_agg(struct rtw_dev *rtwdev, bool enable)
|
||||
{
|
||||
switch (rtwdev->chip->id) {
|
||||
@@ -851,6 +877,10 @@ static void rtw_usb_dynamic_rx_agg(struc
|
||||
@@ -860,6 +886,10 @@ static void rtw_usb_dynamic_rx_agg(struc
|
||||
case RTW_CHIP_TYPE_8821C:
|
||||
rtw_usb_dynamic_rx_agg_v1(rtwdev, enable);
|
||||
break;
|
||||
|
||||
@@ -9,7 +9,7 @@ Used for AP+STA support in OpenWrt - preserve AP mode keys across STA reconnect
|
||||
|
||||
--- a/net/mac80211/cfg.c
|
||||
+++ b/net/mac80211/cfg.c
|
||||
@@ -1663,12 +1663,6 @@ static int ieee80211_stop_ap(struct wiph
|
||||
@@ -1653,12 +1653,6 @@ static int ieee80211_stop_ap(struct wiph
|
||||
|
||||
__sta_info_flush(sdata, true, link_id);
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ Subject: [PATCH] mac80211: allow scans in access point mode (for site survey)
|
||||
|
||||
--- a/net/mac80211/cfg.c
|
||||
+++ b/net/mac80211/cfg.c
|
||||
@@ -2877,6 +2877,8 @@ static int ieee80211_scan(struct wiphy *
|
||||
@@ -2867,6 +2867,8 @@ static int ieee80211_scan(struct wiphy *
|
||||
*/
|
||||
fallthrough;
|
||||
case NL80211_IFTYPE_AP:
|
||||
|
||||
@@ -16,7 +16,7 @@ and we should ignore this.
|
||||
|
||||
--- a/net/wireless/core.c
|
||||
+++ b/net/wireless/core.c
|
||||
@@ -678,21 +678,6 @@ int wiphy_verify_iface_combinations(stru
|
||||
@@ -674,21 +674,6 @@ int wiphy_verify_iface_combinations(stru
|
||||
c->limits[j].max > 1))
|
||||
return -EINVAL;
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ Signed-off-by: David Bauer <mail@david-bauer.net>
|
||||
|
||||
--- a/net/mac80211/sta_info.c
|
||||
+++ b/net/mac80211/sta_info.c
|
||||
@@ -2446,6 +2446,13 @@ static void sta_stats_decode_rate(struct
|
||||
@@ -2447,6 +2447,13 @@ static void sta_stats_decode_rate(struct
|
||||
|
||||
sband = local->hw.wiphy->bands[band];
|
||||
|
||||
|
||||
+3
-3
@@ -23,7 +23,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
|
||||
--- a/net/mac80211/tx.c
|
||||
+++ b/net/mac80211/tx.c
|
||||
@@ -4082,7 +4082,7 @@ struct ieee80211_txq *ieee80211_next_txq
|
||||
@@ -4088,7 +4088,7 @@ struct ieee80211_txq *ieee80211_next_txq
|
||||
|
||||
if (deficit < 0)
|
||||
sta->airtime[txqi->txq.ac].deficit +=
|
||||
@@ -32,7 +32,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
|
||||
if (deficit < 0 || !aql_check) {
|
||||
list_move_tail(&txqi->schedule_order,
|
||||
@@ -4227,7 +4227,8 @@ bool ieee80211_txq_may_transmit(struct i
|
||||
@@ -4233,7 +4233,8 @@ bool ieee80211_txq_may_transmit(struct i
|
||||
}
|
||||
sta = container_of(iter->txq.sta, struct sta_info, sta);
|
||||
if (ieee80211_sta_deficit(sta, ac) < 0)
|
||||
@@ -42,7 +42,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
list_move_tail(&iter->schedule_order, &local->active_txqs[ac]);
|
||||
}
|
||||
|
||||
@@ -4235,7 +4236,7 @@ bool ieee80211_txq_may_transmit(struct i
|
||||
@@ -4241,7 +4242,7 @@ bool ieee80211_txq_may_transmit(struct i
|
||||
if (sta->airtime[ac].deficit >= 0)
|
||||
goto out;
|
||||
|
||||
|
||||
+7
-7
@@ -95,7 +95,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
spin_lock_init(&local->active_txq_lock[i]);
|
||||
--- a/net/mac80211/sta_info.c
|
||||
+++ b/net/mac80211/sta_info.c
|
||||
@@ -2360,13 +2360,28 @@ EXPORT_SYMBOL(ieee80211_sta_recalc_aggre
|
||||
@@ -2361,13 +2361,28 @@ EXPORT_SYMBOL(ieee80211_sta_recalc_aggre
|
||||
|
||||
void ieee80211_sta_update_pending_airtime(struct ieee80211_local *local,
|
||||
struct sta_info *sta, u8 ac,
|
||||
@@ -127,7 +127,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
atomic_add(tx_airtime,
|
||||
--- a/net/mac80211/tx.c
|
||||
+++ b/net/mac80211/tx.c
|
||||
@@ -2554,7 +2554,7 @@ static u16 ieee80211_store_ack_skb(struc
|
||||
@@ -2560,7 +2560,7 @@ static u16 ieee80211_store_ack_skb(struc
|
||||
|
||||
spin_lock_irqsave(&local->ack_status_lock, flags);
|
||||
id = idr_alloc(&local->ack_status_frames, ack_skb,
|
||||
@@ -136,7 +136,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
spin_unlock_irqrestore(&local->ack_status_lock, flags);
|
||||
|
||||
if (id >= 0) {
|
||||
@@ -3983,20 +3983,20 @@ begin:
|
||||
@@ -3989,20 +3989,20 @@ begin:
|
||||
encap_out:
|
||||
info->control.vif = vif;
|
||||
|
||||
@@ -167,7 +167,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
}
|
||||
|
||||
return skb;
|
||||
@@ -4048,6 +4048,7 @@ struct ieee80211_txq *ieee80211_next_txq
|
||||
@@ -4054,6 +4054,7 @@ struct ieee80211_txq *ieee80211_next_txq
|
||||
struct ieee80211_txq *ret = NULL;
|
||||
struct txq_info *txqi = NULL, *head = NULL;
|
||||
bool found_eligible_txq = false;
|
||||
@@ -175,7 +175,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
|
||||
spin_lock_bh(&local->active_txq_lock[ac]);
|
||||
|
||||
@@ -4071,26 +4072,26 @@ struct ieee80211_txq *ieee80211_next_txq
|
||||
@@ -4077,26 +4078,26 @@ struct ieee80211_txq *ieee80211_next_txq
|
||||
if (!head)
|
||||
head = txqi;
|
||||
|
||||
@@ -214,7 +214,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
if (txqi->schedule_round == local->schedule_round[ac])
|
||||
goto out;
|
||||
|
||||
@@ -4157,7 +4158,8 @@ bool ieee80211_txq_airtime_check(struct
|
||||
@@ -4163,7 +4164,8 @@ bool ieee80211_txq_airtime_check(struct
|
||||
return true;
|
||||
|
||||
if (!txq->sta)
|
||||
@@ -224,7 +224,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
|
||||
if (unlikely(txq->tid == IEEE80211_NUM_TIDS))
|
||||
return true;
|
||||
@@ -4206,15 +4208,15 @@ bool ieee80211_txq_may_transmit(struct i
|
||||
@@ -4212,15 +4214,15 @@ bool ieee80211_txq_may_transmit(struct i
|
||||
|
||||
spin_lock_bh(&local->active_txq_lock[ac]);
|
||||
|
||||
|
||||
+5
-5
@@ -204,7 +204,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
if (nl80211_send_iface(msg, info->snd_portid, info->snd_seq, 0,
|
||||
rdev, wdev, NL80211_CMD_NEW_INTERFACE) < 0) {
|
||||
nlmsg_free(msg);
|
||||
@@ -9192,6 +9231,9 @@ static bool cfg80211_off_channel_oper_al
|
||||
@@ -9195,6 +9234,9 @@ static bool cfg80211_off_channel_oper_al
|
||||
|
||||
lockdep_assert_wiphy(wdev->wiphy);
|
||||
|
||||
@@ -214,7 +214,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
if (!cfg80211_beaconing_iface_active(wdev))
|
||||
return true;
|
||||
|
||||
@@ -9404,7 +9446,8 @@ static int nl80211_trigger_scan(struct s
|
||||
@@ -9407,7 +9449,8 @@ static int nl80211_trigger_scan(struct s
|
||||
}
|
||||
|
||||
/* ignore disabled channels */
|
||||
@@ -224,7 +224,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
continue;
|
||||
|
||||
request->channels[i] = chan;
|
||||
@@ -9424,7 +9467,8 @@ static int nl80211_trigger_scan(struct s
|
||||
@@ -9427,7 +9470,8 @@ static int nl80211_trigger_scan(struct s
|
||||
|
||||
chan = &wiphy->bands[band]->channels[j];
|
||||
|
||||
@@ -246,7 +246,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
continue;
|
||||
|
||||
for (i = 0; i < rdev_req->n_channels; i++) {
|
||||
@@ -3513,9 +3514,12 @@ int cfg80211_wext_siwscan(struct net_dev
|
||||
@@ -3516,9 +3517,12 @@ int cfg80211_wext_siwscan(struct net_dev
|
||||
continue;
|
||||
|
||||
for (j = 0; j < wiphy->bands[band]->n_channels; j++) {
|
||||
@@ -263,7 +263,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
/* If we have a wireless request structure and the
|
||||
--- a/net/wireless/util.c
|
||||
+++ b/net/wireless/util.c
|
||||
@@ -2970,3 +2970,32 @@ bool cfg80211_radio_chandef_valid(const
|
||||
@@ -2951,3 +2951,32 @@ bool cfg80211_radio_chandef_valid(const
|
||||
return true;
|
||||
}
|
||||
EXPORT_SYMBOL(cfg80211_radio_chandef_valid);
|
||||
|
||||
+4
-4
@@ -10,7 +10,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
|
||||
--- a/net/mac80211/scan.c
|
||||
+++ b/net/mac80211/scan.c
|
||||
@@ -1175,14 +1175,14 @@ int ieee80211_request_ibss_scan(struct i
|
||||
@@ -1180,14 +1180,14 @@ int ieee80211_request_ibss_scan(struct i
|
||||
unsigned int n_channels)
|
||||
{
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
@@ -27,7 +27,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
|
||||
/* fill internal scan request */
|
||||
if (!channels) {
|
||||
@@ -1199,7 +1199,9 @@ int ieee80211_request_ibss_scan(struct i
|
||||
@@ -1204,7 +1204,9 @@ int ieee80211_request_ibss_scan(struct i
|
||||
&local->hw.wiphy->bands[band]->channels[i];
|
||||
|
||||
if (tmp_ch->flags & (IEEE80211_CHAN_NO_IR |
|
||||
@@ -38,7 +38,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
continue;
|
||||
|
||||
local->int_scan_req->channels[n_ch] = tmp_ch;
|
||||
@@ -1208,21 +1210,23 @@ int ieee80211_request_ibss_scan(struct i
|
||||
@@ -1213,21 +1215,23 @@ int ieee80211_request_ibss_scan(struct i
|
||||
}
|
||||
|
||||
if (WARN_ON_ONCE(n_ch == 0))
|
||||
@@ -66,7 +66,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
|
||||
local->int_scan_req->n_channels = n_ch;
|
||||
}
|
||||
@@ -1232,9 +1236,7 @@ int ieee80211_request_ibss_scan(struct i
|
||||
@@ -1237,9 +1241,7 @@ int ieee80211_request_ibss_scan(struct i
|
||||
memcpy(local->int_scan_req->ssids[0].ssid, ssid, IEEE80211_MAX_SSID_LEN);
|
||||
local->int_scan_req->ssids[0].ssid_len = ssid_len;
|
||||
|
||||
|
||||
+4
-4
@@ -9,7 +9,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
|
||||
--- a/net/mac80211/chan.c
|
||||
+++ b/net/mac80211/chan.c
|
||||
@@ -1169,7 +1169,7 @@ ieee80211_replace_chanctx(struct ieee802
|
||||
@@ -1171,7 +1171,7 @@ ieee80211_replace_chanctx(struct ieee802
|
||||
static bool
|
||||
ieee80211_find_available_radio(struct ieee80211_local *local,
|
||||
const struct ieee80211_chan_req *chanreq,
|
||||
@@ -18,7 +18,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
{
|
||||
struct wiphy *wiphy = local->hw.wiphy;
|
||||
const struct wiphy_radio *radio;
|
||||
@@ -1180,6 +1180,9 @@ ieee80211_find_available_radio(struct ie
|
||||
@@ -1182,6 +1182,9 @@ ieee80211_find_available_radio(struct ie
|
||||
return true;
|
||||
|
||||
for (i = 0; i < wiphy->n_radio; i++) {
|
||||
@@ -28,7 +28,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
radio = &wiphy->radio[i];
|
||||
if (!cfg80211_radio_chandef_valid(radio, &chanreq->oper))
|
||||
continue;
|
||||
@@ -1213,7 +1216,9 @@ int ieee80211_link_reserve_chanctx(struc
|
||||
@@ -1215,7 +1218,9 @@ int ieee80211_link_reserve_chanctx(struc
|
||||
new_ctx = ieee80211_find_reservation_chanctx(local, chanreq, mode);
|
||||
if (!new_ctx) {
|
||||
if (ieee80211_can_create_new_chanctx(local, -1) &&
|
||||
@@ -39,7 +39,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
new_ctx = ieee80211_new_chanctx(local, chanreq, mode,
|
||||
false, radio_idx);
|
||||
else
|
||||
@@ -1884,7 +1889,9 @@ int _ieee80211_link_use_channel(struct i
|
||||
@@ -1886,7 +1891,9 @@ int _ieee80211_link_use_channel(struct i
|
||||
/* Note: context is now reserved */
|
||||
if (ctx)
|
||||
reserved = true;
|
||||
|
||||
+1
-1
@@ -88,7 +88,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
bool cfg80211_any_usable_channels(struct wiphy *wiphy,
|
||||
--- a/net/wireless/core.h
|
||||
+++ b/net/wireless/core.h
|
||||
@@ -518,6 +518,7 @@ static inline unsigned int elapsed_jiffi
|
||||
@@ -519,6 +519,7 @@ static inline unsigned int elapsed_jiffi
|
||||
}
|
||||
|
||||
int cfg80211_set_monitor_channel(struct cfg80211_registered_device *rdev,
|
||||
|
||||
+12
-12
@@ -95,7 +95,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
if (ret)
|
||||
return ret;
|
||||
done:
|
||||
@@ -3064,7 +3072,8 @@ static int ieee80211_set_tx_power(struct
|
||||
@@ -3054,7 +3062,8 @@ static int ieee80211_set_tx_power(struct
|
||||
if (wdev) {
|
||||
sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
|
||||
|
||||
@@ -105,7 +105,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
if (!ieee80211_hw_check(&local->hw, WANT_MONITOR_VIF))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
@@ -3112,7 +3121,8 @@ static int ieee80211_set_tx_power(struct
|
||||
@@ -3102,7 +3111,8 @@ static int ieee80211_set_tx_power(struct
|
||||
}
|
||||
|
||||
list_for_each_entry(sdata, &local->interfaces, list) {
|
||||
@@ -115,7 +115,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
has_monitor = true;
|
||||
continue;
|
||||
}
|
||||
@@ -3122,7 +3132,8 @@ static int ieee80211_set_tx_power(struct
|
||||
@@ -3112,7 +3122,8 @@ static int ieee80211_set_tx_power(struct
|
||||
sdata->vif.bss_conf.txpower_type = txp_type;
|
||||
}
|
||||
list_for_each_entry(sdata, &local->interfaces, list) {
|
||||
@@ -125,7 +125,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
continue;
|
||||
ieee80211_recalc_txpower(sdata, update_txp_type);
|
||||
}
|
||||
@@ -4317,7 +4328,8 @@ static int ieee80211_cfg_get_channel(str
|
||||
@@ -4307,7 +4318,8 @@ static int ieee80211_cfg_get_channel(str
|
||||
if (chanctx_conf) {
|
||||
*chandef = link->conf->chanreq.oper;
|
||||
ret = 0;
|
||||
@@ -156,7 +156,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
case NL80211_IFTYPE_P2P_CLIENT:
|
||||
case NL80211_IFTYPE_P2P_GO:
|
||||
WARN_ON_ONCE(1);
|
||||
@@ -956,6 +959,10 @@ void ieee80211_recalc_smps_chanctx(struc
|
||||
@@ -958,6 +961,10 @@ void ieee80211_recalc_smps_chanctx(struc
|
||||
if (!link->sdata->u.mgd.associated)
|
||||
continue;
|
||||
break;
|
||||
@@ -167,7 +167,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
case NL80211_IFTYPE_AP:
|
||||
case NL80211_IFTYPE_ADHOC:
|
||||
case NL80211_IFTYPE_MESH_POINT:
|
||||
@@ -968,6 +975,11 @@ void ieee80211_recalc_smps_chanctx(struc
|
||||
@@ -970,6 +977,11 @@ void ieee80211_recalc_smps_chanctx(struc
|
||||
if (rcu_access_pointer(link->conf->chanctx_conf) != &chanctx->conf)
|
||||
continue;
|
||||
|
||||
@@ -181,7 +181,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
WARN_ONCE(1, "Invalid SMPS mode %d\n",
|
||||
--- a/net/mac80211/debugfs.c
|
||||
+++ b/net/mac80211/debugfs.c
|
||||
@@ -465,6 +465,7 @@ static const char *hw_flag_names[] = {
|
||||
@@ -461,6 +461,7 @@ static const char *hw_flag_names[] = {
|
||||
FLAG(SUPPORTS_DYNAMIC_PS),
|
||||
FLAG(MFP_CAPABLE),
|
||||
FLAG(WANT_MONITOR_VIF),
|
||||
@@ -217,7 +217,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
}
|
||||
|
||||
/* Regardless of eth_mac_addr() return we still want to add the
|
||||
@@ -722,9 +727,11 @@ static void ieee80211_do_stop(struct iee
|
||||
@@ -726,9 +731,11 @@ static void ieee80211_do_stop(struct iee
|
||||
ieee80211_recalc_idle(local);
|
||||
ieee80211_recalc_offload(local);
|
||||
|
||||
@@ -230,7 +230,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
fallthrough;
|
||||
default:
|
||||
if (!going_down)
|
||||
@@ -1157,7 +1164,8 @@ int ieee80211_add_virtual_monitor(struct
|
||||
@@ -1161,7 +1168,8 @@ int ieee80211_add_virtual_monitor(struct
|
||||
ASSERT_RTNL();
|
||||
lockdep_assert_wiphy(local->hw.wiphy);
|
||||
|
||||
@@ -240,7 +240,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
return 0;
|
||||
|
||||
sdata = kzalloc(sizeof(*sdata) + local->hw.vif_data_size, GFP_KERNEL);
|
||||
@@ -1219,6 +1227,9 @@ void ieee80211_del_virtual_monitor(struc
|
||||
@@ -1223,6 +1231,9 @@ void ieee80211_del_virtual_monitor(struc
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata;
|
||||
|
||||
@@ -250,7 +250,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
ASSERT_RTNL();
|
||||
lockdep_assert_wiphy(local->hw.wiphy);
|
||||
|
||||
@@ -1362,7 +1373,8 @@ int ieee80211_do_open(struct wireless_de
|
||||
@@ -1366,7 +1377,8 @@ int ieee80211_do_open(struct wireless_de
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -284,7 +284,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
vif = &sdata->vif;
|
||||
break;
|
||||
}
|
||||
@@ -3953,7 +3954,8 @@ begin:
|
||||
@@ -3959,7 +3960,8 @@ begin:
|
||||
|
||||
switch (tx.sdata->vif.type) {
|
||||
case NL80211_IFTYPE_MONITOR:
|
||||
|
||||
+1
-1
@@ -22,7 +22,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
fif_probe_req;
|
||||
--- a/net/mac80211/iface.c
|
||||
+++ b/net/mac80211/iface.c
|
||||
@@ -1120,6 +1120,8 @@ void ieee80211_adjust_monitor_flags(stru
|
||||
@@ -1124,6 +1124,8 @@ void ieee80211_adjust_monitor_flags(stru
|
||||
ADJUST(CONTROL, control);
|
||||
ADJUST(CONTROL, pspoll);
|
||||
ADJUST(OTHER_BSS, other_bss);
|
||||
|
||||
+1
-1
@@ -52,7 +52,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
}
|
||||
|
||||
/* Regardless of eth_mac_addr() return we still want to add the
|
||||
@@ -1353,6 +1348,8 @@ int ieee80211_do_open(struct wireless_de
|
||||
@@ -1357,6 +1352,8 @@ int ieee80211_do_open(struct wireless_de
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -45,7 +45,7 @@ Signed-off-by: Johannes Berg <johannes.berg@intel.com>
|
||||
ieee80211_sta_reset_beacon_monitor(sdata);
|
||||
ieee80211_sta_reset_conn_monitor(sdata);
|
||||
}
|
||||
@@ -7893,6 +7908,29 @@ void ieee80211_sta_work(struct ieee80211
|
||||
@@ -7897,6 +7912,29 @@ void ieee80211_sta_work(struct ieee80211
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -45,7 +45,7 @@ Signed-off-by: Johannes Berg <johannes.berg@intel.com>
|
||||
if (!ieee80211_sdata_running(sdata))
|
||||
return;
|
||||
|
||||
@@ -7951,36 +7945,73 @@ static void ieee80211_sta_bcn_mon_timer(
|
||||
@@ -7955,36 +7949,73 @@ static void ieee80211_sta_bcn_mon_timer(
|
||||
&sdata->u.mgd.beacon_connection_loss_work);
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=odhcp6c
|
||||
PKG_RELEASE:=1
|
||||
PKG_RELEASE:=2
|
||||
|
||||
PKG_SOURCE_PROTO:=git
|
||||
PKG_SOURCE_URL=$(PROJECT_GIT)/project/odhcp6c.git
|
||||
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
From 3d812c338eb93d897717627ebb1b7beb01646e6e Mon Sep 17 00:00:00 2001
|
||||
From: Hauke Mehrtens <hauke@hauke-m.de>
|
||||
Date: Sat, 11 Jul 2026 23:29:19 +0200
|
||||
Subject: dhcpv6: fix out-of-bounds end pointer when parsing IA in Advertise
|
||||
|
||||
dhcpv6_handle_advert passes 'odata + olen + sizeof(*ia_hdr)' as the end
|
||||
pointer to dhcpv6_parse_ia. odata points to the start of the IA
|
||||
option's data (just past the 4-byte option header) and olen is the
|
||||
data length, so the valid sub-option range ends at 'odata + olen'.
|
||||
The extra '+ sizeof(*ia_hdr)' (16 bytes) extends the range 16 bytes
|
||||
past the IA option boundary, allowing dhcpv6_parse_ia's sub-option
|
||||
iterator to read into adjacent option bytes (or past the receive
|
||||
buffer when the IA is the last option) and parse them as bogus IA
|
||||
sub-options, polluting STATE_IA_NA / STATE_IA_PD with garbage.
|
||||
|
||||
The sibling call site in dhcpv6_handle_reply already passes the
|
||||
correct 'odata + olen'. Match that here.
|
||||
|
||||
Assisted-by: Claude:claude-opus-4-7
|
||||
Link: https://github.com/openwrt/odhcp6c/pull/160
|
||||
Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
|
||||
(cherry picked from commit b6f0c70f5fc26632d8a6c748efec5aa335aa4fde)
|
||||
---
|
||||
src/dhcpv6.c | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
--- a/src/dhcpv6.c
|
||||
+++ b/src/dhcpv6.c
|
||||
@@ -959,7 +959,7 @@ static int dhcpv6_handle_advert(enum dhc
|
||||
(otype == DHCPV6_OPT_IA_NA && na_mode != IA_MODE_NONE)) &&
|
||||
olen > -4 + sizeof(struct dhcpv6_ia_hdr)) {
|
||||
struct dhcpv6_ia_hdr *ia_hdr = (void*)(&odata[-4]);
|
||||
- dhcpv6_parse_ia(ia_hdr, odata + olen + sizeof(*ia_hdr));
|
||||
+ dhcpv6_parse_ia(ia_hdr, odata + olen);
|
||||
}
|
||||
|
||||
if (otype == DHCPV6_OPT_SERVERID && olen <= 130) {
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
From ee3b760cb42935f4b6578c0481f75b793eadd0f7 Mon Sep 17 00:00:00 2001
|
||||
From: Hauke Mehrtens <hauke@hauke-m.de>
|
||||
Date: Sat, 11 Jul 2026 23:31:09 +0200
|
||||
Subject: script: avoid kill(0) when SIGCHLD races script_call
|
||||
|
||||
script_call() does
|
||||
|
||||
if (running) {
|
||||
...
|
||||
kill(running, SIGTERM);
|
||||
}
|
||||
|
||||
where 'running' is a volatile pid_t cleared to 0 by the SIGCHLD
|
||||
handler when the previous script child exits. The 'if (running)'
|
||||
gate and the 'kill(running, ...)' that follows it are independent
|
||||
reads of the volatile variable. If SIGCHLD fires for the previous
|
||||
child between these two reads, the handler clears 'running' to 0
|
||||
and kill() is called with pid == 0, which delivers SIGTERM to every
|
||||
process in the same process group as odhcp6c, including odhcp6c
|
||||
itself (and any peer/parent that shares the pgrp).
|
||||
|
||||
Snapshot the pid into a local before checking it and using it, so the
|
||||
kill() target is the value we actually tested.
|
||||
|
||||
Assisted-by: Claude:claude-opus-4-7
|
||||
Link: https://github.com/openwrt/odhcp6c/pull/160
|
||||
Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
|
||||
|
||||
(cherry picked from commit 2521bf732b9f643ce36c5ac3b314b17c034a0f50)
|
||||
---
|
||||
src/script.c | 5 +++--
|
||||
1 file changed, 3 insertions(+), 2 deletions(-)
|
||||
|
||||
--- a/src/script.c
|
||||
+++ b/src/script.c
|
||||
@@ -402,10 +402,11 @@ void script_call(const char *status, int
|
||||
time_t now = odhcp6c_get_milli_time() / 1000;
|
||||
bool running_script = false;
|
||||
|
||||
- if (running) {
|
||||
+ pid_t prev = running;
|
||||
+ if (prev > 0) {
|
||||
time_t diff = now - started;
|
||||
|
||||
- kill(running, SIGTERM);
|
||||
+ kill(prev, SIGTERM);
|
||||
|
||||
if (diff > delay)
|
||||
delay -= diff;
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
From 240ca48bb8f783177b86a21425c5b1b5bab2b7ae Mon Sep 17 00:00:00 2001
|
||||
From: Hauke Mehrtens <hauke@hauke-m.de>
|
||||
Date: Sat, 23 May 2026 14:15:36 +0200
|
||||
Subject: odhcp6c: propagate allocation failure from insert_state
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
odhcp6c_insert_state always returned 0, even when odhcp6c_resize_state
|
||||
failed (either because the state would exceed the 1024-byte cap or
|
||||
because realloc() returned NULL). The callers act on the return value:
|
||||
|
||||
* config_add_requested_options() logs and aborts when nonzero, but
|
||||
silently accepts the lost option when zero — so a failed insert
|
||||
becomes "succeeded but not actually added".
|
||||
* dhcpv6_add_server_cand() frees the candidate's IA_NA / IA_PD only
|
||||
when nonzero, so an insert failure both loses the candidate and
|
||||
leaks its IA buffers.
|
||||
|
||||
Return -1 when odhcp6c_resize_state() returns NULL, matching the
|
||||
documented contract of the function and the behaviour of
|
||||
odhcp6c_add_state().
|
||||
|
||||
Assisted-by: Claude:claude-opus-4-7
|
||||
Link: https://github.com/openwrt/odhcp6c/pull/160
|
||||
Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
|
||||
(cherry picked from commit 49d9c0112bb8cd9760cc13495caa5009b61cf2ad)
|
||||
---
|
||||
src/odhcp6c.c | 11 ++++++-----
|
||||
1 file changed, 6 insertions(+), 5 deletions(-)
|
||||
|
||||
--- a/src/odhcp6c.c
|
||||
+++ b/src/odhcp6c.c
|
||||
@@ -735,12 +735,13 @@ int odhcp6c_insert_state(enum odhcp6c_st
|
||||
|
||||
uint8_t *n = odhcp6c_resize_state(state, len);
|
||||
|
||||
- if (n) {
|
||||
- uint8_t *sdata = state_data[state];
|
||||
+ if (!n)
|
||||
+ return -1;
|
||||
|
||||
- memmove(sdata + offset + len, sdata + offset, len_after);
|
||||
- memcpy(sdata + offset, data, len);
|
||||
- }
|
||||
+ uint8_t *sdata = state_data[state];
|
||||
+
|
||||
+ memmove(sdata + offset + len, sdata + offset, len_after);
|
||||
+ memcpy(sdata + offset, data, len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
From 5495a8b3f20df00866946645b01869598d3ddd74 Mon Sep 17 00:00:00 2001
|
||||
From: Hauke Mehrtens <hauke@hauke-m.de>
|
||||
Date: Sat, 11 Jul 2026 23:32:40 +0200
|
||||
Subject: dhcpv6: ensure hostname buffer is NUL-terminated for dn_comp
|
||||
|
||||
POSIX permits gethostname() to leave the destination buffer
|
||||
non-NUL-terminated when the system hostname is at least as long as the
|
||||
buffer (the trailing NUL is dropped during truncation). Linux glibc
|
||||
exhibits this behaviour. The buffer is passed directly to dn_comp(),
|
||||
which scans for the terminator and would otherwise read past
|
||||
fqdn_buf[].
|
||||
|
||||
Pass one fewer byte to gethostname() and explicitly NUL-terminate the
|
||||
last byte so dn_comp() always sees a properly bounded C string.
|
||||
|
||||
Assisted-by: Claude:claude-opus-4-7
|
||||
Link: https://github.com/openwrt/odhcp6c/pull/160
|
||||
Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
|
||||
|
||||
(cherry picked from commit 4bd976fd60c0e2baf00c92991d30507ece2b9075)
|
||||
---
|
||||
src/dhcpv6.c | 3 ++-
|
||||
1 file changed, 2 insertions(+), 1 deletion(-)
|
||||
|
||||
--- a/src/dhcpv6.c
|
||||
+++ b/src/dhcpv6.c
|
||||
@@ -341,7 +341,8 @@ static void dhcpv6_send(enum dhcpv6_msg
|
||||
{
|
||||
// Build FQDN
|
||||
char fqdn_buf[256];
|
||||
- gethostname(fqdn_buf, sizeof(fqdn_buf));
|
||||
+ gethostname(fqdn_buf, sizeof(fqdn_buf) - 1);
|
||||
+ fqdn_buf[sizeof(fqdn_buf) - 1] = '\0';
|
||||
struct {
|
||||
uint16_t type;
|
||||
uint16_t len;
|
||||
+121
@@ -0,0 +1,121 @@
|
||||
From 43f9c5e03c6bf89ef93834c8de57fe6985f88b64 Mon Sep 17 00:00:00 2001
|
||||
From: Hauke Mehrtens <hauke@hauke-m.de>
|
||||
Date: Sat, 23 May 2026 14:19:39 +0200
|
||||
Subject: script: handle allocation failures in env helpers
|
||||
|
||||
ipv6_to_env(), fqdn_to_env(), bin_to_env(), entry_to_env(),
|
||||
search_to_env(), int_to_env() and the inline PASSTHRU=... assembler
|
||||
all called realloc(NULL, ...) / malloc() without checking the result,
|
||||
then immediately wrote into the (possibly NULL) buffer with memcpy(),
|
||||
snprintf() or script_hexlify(). Under memory pressure this dereferences
|
||||
NULL inside the forked script-helper child and crashes it before
|
||||
putenv() / execv(), so the state script does not run for that
|
||||
state-change.
|
||||
|
||||
Check each allocation and skip the corresponding environment variable
|
||||
when it fails. While at it, replace the cosmetic realloc(NULL, ...) idiom
|
||||
with plain malloc() to make intent clearer.
|
||||
|
||||
Assisted-by: Claude:claude-opus-4-7
|
||||
Link: https://github.com/openwrt/odhcp6c/pull/160
|
||||
Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
|
||||
(cherry picked from commit 01130f80338a250f94ae0df786306c11662c88fb)
|
||||
---
|
||||
src/script.c | 42 ++++++++++++++++++++++++++++++++----------
|
||||
1 file changed, 32 insertions(+), 10 deletions(-)
|
||||
|
||||
--- a/src/script.c
|
||||
+++ b/src/script.c
|
||||
@@ -98,7 +98,10 @@ static void ipv6_to_env(const char *name
|
||||
const struct in6_addr *addr, size_t cnt)
|
||||
{
|
||||
size_t buf_len = strlen(name);
|
||||
- char *buf = realloc(NULL, cnt * INET6_ADDRSTRLEN + buf_len + 2);
|
||||
+ char *buf = malloc(cnt * INET6_ADDRSTRLEN + buf_len + 2);
|
||||
+
|
||||
+ if (!buf)
|
||||
+ return;
|
||||
|
||||
memcpy(buf, name, buf_len);
|
||||
buf[buf_len++] = '=';
|
||||
@@ -121,7 +124,10 @@ static void fqdn_to_env(const char *name
|
||||
size_t buf_len = strlen(name);
|
||||
size_t buf_size = len + buf_len + 2;
|
||||
const uint8_t *fqdn_end = fqdn + len;
|
||||
- char *buf = realloc(NULL, len + buf_len + 2);
|
||||
+ char *buf = malloc(buf_size);
|
||||
+
|
||||
+ if (!buf)
|
||||
+ return;
|
||||
|
||||
memcpy(buf, name, buf_len);
|
||||
buf[buf_len++] = '=';
|
||||
@@ -148,9 +154,12 @@ static void bin_to_env(uint8_t *opts, si
|
||||
uint16_t otype, olen;
|
||||
|
||||
dhcpv6_for_each_option(opts, oend, otype, olen, odata) {
|
||||
- char *buf = realloc(NULL, 14 + (olen * 2));
|
||||
+ char *buf = malloc(14 + (olen * 2));
|
||||
size_t buf_len = 0;
|
||||
|
||||
+ if (!buf)
|
||||
+ continue;
|
||||
+
|
||||
snprintf(buf, 14, "OPTION_%hu=", otype);
|
||||
buf_len += strlen(buf);
|
||||
|
||||
@@ -173,7 +182,10 @@ static void entry_to_env(const char *nam
|
||||
// Worst case: ENTRY_PREFIX with iaid != 1 and exclusion
|
||||
const size_t max_entry_len = (INET6_ADDRSTRLEN-1 + 5 + 22 + 15 + 10 +
|
||||
INET6_ADDRSTRLEN-1 + 11 + 1);
|
||||
- char *buf = realloc(NULL, buf_len + 2 + (len / sizeof(*e)) * max_entry_len);
|
||||
+ char *buf = malloc(buf_len + 2 + (len / sizeof(*e)) * max_entry_len);
|
||||
+
|
||||
+ if (!buf)
|
||||
+ return;
|
||||
|
||||
memcpy(buf, name, buf_len);
|
||||
buf[buf_len++] = '=';
|
||||
@@ -238,8 +250,13 @@ static void entry_to_env(const char *nam
|
||||
static void search_to_env(const char *name, const uint8_t *start, size_t len)
|
||||
{
|
||||
size_t buf_len = strlen(name);
|
||||
- char *buf = realloc(NULL, buf_len + 2 + len);
|
||||
- char *c = mempcpy(buf, name, buf_len);
|
||||
+ char *buf = malloc(buf_len + 2 + len);
|
||||
+ char *c;
|
||||
+
|
||||
+ if (!buf)
|
||||
+ return;
|
||||
+
|
||||
+ c = mempcpy(buf, name, buf_len);
|
||||
*c++ = '=';
|
||||
|
||||
for (struct odhcp6c_entry *e = (struct odhcp6c_entry*)start;
|
||||
@@ -262,7 +279,10 @@ static void search_to_env(const char *na
|
||||
static void int_to_env(const char *name, int value)
|
||||
{
|
||||
size_t len = 13 + strlen(name);
|
||||
- char *buf = realloc(NULL, len);
|
||||
+ char *buf = malloc(len);
|
||||
+
|
||||
+ if (!buf)
|
||||
+ return;
|
||||
|
||||
snprintf(buf, len, "%s=%d", name, value);
|
||||
putenv(buf);
|
||||
@@ -495,9 +515,11 @@ void script_call(const char *status, int
|
||||
int_to_env("RA_RETRANSMIT", ra_get_retransmit());
|
||||
|
||||
char *buf = malloc(10 + passthru_len * 2);
|
||||
- strncpy(buf, "PASSTHRU=", 10);
|
||||
- script_hexlify(&buf[9], passthru, passthru_len);
|
||||
- putenv(buf);
|
||||
+ if (buf) {
|
||||
+ strncpy(buf, "PASSTHRU=", 10);
|
||||
+ script_hexlify(&buf[9], passthru, passthru_len);
|
||||
+ putenv(buf);
|
||||
+ }
|
||||
|
||||
execv(argv[0], argv);
|
||||
_exit(128);
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
From 2b2aeabbb9550d9617878f32b6ed1ed2a38e33ad Mon Sep 17 00:00:00 2001
|
||||
From: Hauke Mehrtens <hauke@hauke-m.de>
|
||||
Date: Sat, 23 May 2026 14:20:53 +0200
|
||||
Subject: odhcp6c: refuse to follow symlinks when writing pidfile
|
||||
|
||||
odhcp6c is generally run as root and writes its pidfile with
|
||||
fopen(pidfile, "w"), which calls open(O_WRONLY | O_CREAT | O_TRUNC)
|
||||
underneath. open() follows symlinks by default, so if an attacker (or
|
||||
a broken init script) can place a symlink at the pidfile path before
|
||||
odhcp6c starts, the symlink target gets truncated and overwritten with
|
||||
a single line containing the daemon's PID. With /var/run sometimes
|
||||
shared between services this gives a primitive for trashing arbitrary
|
||||
root-owned files, plus the file is created with the symlink target's
|
||||
ownership instead of root:root.
|
||||
|
||||
Use open() with O_NOFOLLOW | O_CLOEXEC explicitly and fdopen() the
|
||||
result; the file is still truncated and replaced when it is a regular
|
||||
file, but a symlink in the path causes open() to fail with ELOOP and
|
||||
we simply skip pidfile creation rather than clobbering the target.
|
||||
|
||||
Assisted-by: Claude:claude-opus-4-7
|
||||
Link: https://github.com/openwrt/odhcp6c/pull/160
|
||||
Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
|
||||
(cherry picked from commit 0a19052dc9fb73530adeff9b61edb3a35f15bc2a)
|
||||
---
|
||||
src/odhcp6c.c | 15 +++++++++++----
|
||||
1 file changed, 11 insertions(+), 4 deletions(-)
|
||||
|
||||
--- a/src/odhcp6c.c
|
||||
+++ b/src/odhcp6c.c
|
||||
@@ -454,10 +454,17 @@ int main(_unused int argc, char* const a
|
||||
pidfile = (char*)buf;
|
||||
}
|
||||
|
||||
- FILE *fp = fopen(pidfile, "w");
|
||||
- if (fp) {
|
||||
- fprintf(fp, "%i\n", getpid());
|
||||
- fclose(fp);
|
||||
+ int pidfd = open(pidfile,
|
||||
+ O_WRONLY | O_CREAT | O_TRUNC | O_NOFOLLOW | O_CLOEXEC,
|
||||
+ 0644);
|
||||
+ if (pidfd >= 0) {
|
||||
+ FILE *fp = fdopen(pidfd, "w");
|
||||
+ if (fp) {
|
||||
+ fprintf(fp, "%i\n", getpid());
|
||||
+ fclose(fp);
|
||||
+ } else {
|
||||
+ close(pidfd);
|
||||
+ }
|
||||
}
|
||||
}
|
||||
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
From 760b53c7400c2b60cf48723509fc123843ad07f4 Mon Sep 17 00:00:00 2001
|
||||
From: Hauke Mehrtens <hauke@hauke-m.de>
|
||||
Date: Sat, 23 May 2026 14:21:37 +0200
|
||||
Subject: odhcp6c: skip malformed /proc/net/if_inet6 entries
|
||||
|
||||
The validation loop in odhcp6c_addr_in_scope() walked addr_buf
|
||||
checking each character with isxdigit() / !isupper(), but discarded
|
||||
the result: 'i' was a local that went out of scope at the end of the
|
||||
loop, and execution fell straight into the decode loop regardless. If
|
||||
the kernel ever produced unexpected characters (or someone bind-mounts
|
||||
a non-procfs over /proc/net/if_inet6), index(hex, c) would return NULL
|
||||
and the subsequent 'NULL - hex' subtraction is undefined behaviour;
|
||||
the address bytes were then derived from arbitrary pointer arithmetic.
|
||||
|
||||
Track the validation outcome explicitly, also reject inputs whose
|
||||
length isn't 32 hex characters (one IPv6 address worth), and continue
|
||||
to the next /proc line on any failure so the decode loop only sees
|
||||
well-formed inputs.
|
||||
|
||||
Assisted-by: Claude:claude-opus-4-7
|
||||
Link: https://github.com/openwrt/odhcp6c/pull/160
|
||||
Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
|
||||
(cherry picked from commit d6c2fbdc255c143afc4d2c670f602c9cc9308869)
|
||||
---
|
||||
src/odhcp6c.c | 15 +++++++++++----
|
||||
1 file changed, 11 insertions(+), 4 deletions(-)
|
||||
|
||||
--- a/src/odhcp6c.c
|
||||
+++ b/src/odhcp6c.c
|
||||
@@ -940,13 +940,20 @@ bool odhcp6c_addr_in_scope(const struct
|
||||
(flags & (IFA_F_DADFAILED | IFA_F_TENTATIVE | IFA_F_DEPRECATED)))
|
||||
continue;
|
||||
|
||||
- for (i = 0; i < strlen(addr_buf); i++) {
|
||||
- if (!isxdigit(addr_buf[i]) || isupper(addr_buf[i]))
|
||||
- break;
|
||||
+ size_t addr_len = strlen(addr_buf);
|
||||
+ bool valid = (addr_len == 2 * sizeof(inet6_addr.s6_addr));
|
||||
+
|
||||
+ for (i = 0; valid && i < addr_len; i++) {
|
||||
+ if (!isxdigit((unsigned char)addr_buf[i]) ||
|
||||
+ isupper((unsigned char)addr_buf[i]))
|
||||
+ valid = false;
|
||||
}
|
||||
|
||||
+ if (!valid)
|
||||
+ continue;
|
||||
+
|
||||
memset(&inet6_addr, 0, sizeof(inet6_addr));
|
||||
- for (i = 0; i < (strlen(addr_buf) / 2); i++) {
|
||||
+ for (i = 0; i < (addr_len / 2); i++) {
|
||||
unsigned char byte;
|
||||
static const char hex[] = "0123456789abcdef";
|
||||
byte = ((index(hex, addr_buf[i * 2]) - hex) << 4) |
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
From a04d72ac6c809cc9f189b635839ddbb9a48e44a3 Mon Sep 17 00:00:00 2001
|
||||
From: Hauke Mehrtens <hauke@hauke-m.de>
|
||||
Date: Sat, 11 Jul 2026 23:34:04 +0200
|
||||
Subject: script: handle fork() failure when launching state script
|
||||
|
||||
If fork() returns -1 (e.g. under EAGAIN / ENOMEM), neither the parent
|
||||
nor the child branch runs. Control falls through to the bottom of
|
||||
script_call() while 'running' still holds the previous (just-killed)
|
||||
child's pid and 'started' still holds the previous start time. The
|
||||
next call sees running != 0, fires kill() at a process that no longer
|
||||
exists, and accumulates a misleading 'diff = now - started' against
|
||||
the older start time. No diagnostic is logged either.
|
||||
|
||||
Log the failure, clear 'running' so subsequent calls do not chase a
|
||||
ghost pid, and return without going through the (parent-only)
|
||||
post-fork bookkeeping.
|
||||
|
||||
Assisted-by: Claude:claude-opus-4-7
|
||||
Link: https://github.com/openwrt/odhcp6c/pull/160
|
||||
Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
|
||||
|
||||
(cherry picked from commit c938c168cbef5642cab42389d96c340e40f4f8ff)
|
||||
[use syslog() instead of the error() log helper, which is not present in 24.10]
|
||||
---
|
||||
src/script.c | 7 +++++++
|
||||
1 file changed, 7 insertions(+)
|
||||
|
||||
--- a/src/script.c
|
||||
+++ b/src/script.c
|
||||
@@ -14,6 +14,7 @@
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
+#include <errno.h>
|
||||
#include <netdb.h>
|
||||
#include <resolv.h>
|
||||
#include <stdlib.h>
|
||||
@@ -441,6 +442,12 @@ void script_call(const char *status, int
|
||||
|
||||
pid_t pid = fork();
|
||||
|
||||
+ if (pid < 0) {
|
||||
+ syslog(LOG_ERR, "Failed to fork script handler: %s", strerror(errno));
|
||||
+ running = 0;
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
if (pid > 0) {
|
||||
running = pid;
|
||||
started = now;
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
From 49f251805dd5bce1964d4d6549ff1178ae8df2a5 Mon Sep 17 00:00:00 2001
|
||||
From: Hauke Mehrtens <hauke@hauke-m.de>
|
||||
Date: Sat, 30 May 2026 23:17:47 +0200
|
||||
Subject: odhcp6c: bound the address length when parsing the -P argument
|
||||
|
||||
The -P <[pfx/]len> handler copied everything before the '/' into the
|
||||
fixed 134-byte stack buffer "buf" with
|
||||
|
||||
strncpy((char *)buf, optarg, optpos - optarg);
|
||||
buf[optpos - optarg] = '\0';
|
||||
|
||||
without checking that the address portion fits. An argument whose
|
||||
address part is 134 bytes or longer overflows buf on the stack (the
|
||||
trailing NUL store is out of bounds as well).
|
||||
|
||||
Reject any address portion that does not fit in buf before copying.
|
||||
inet_pton() would reject such an over-long string anyway, so no valid
|
||||
invocation changes behaviour.
|
||||
|
||||
Assisted-by: Claude:claude-opus-4-8
|
||||
Link: https://github.com/openwrt/odhcp6c/pull/160
|
||||
Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
|
||||
(cherry picked from commit e9a9e9d45f38bee5d761de0421ca52163efa28d7)
|
||||
---
|
||||
src/odhcp6c.c | 13 +++++++++++--
|
||||
1 file changed, 11 insertions(+), 2 deletions(-)
|
||||
|
||||
--- a/src/odhcp6c.c
|
||||
+++ b/src/odhcp6c.c
|
||||
@@ -245,8 +245,17 @@ int main(_unused int argc, char* const a
|
||||
|
||||
optpos = strchr(optarg, '/');
|
||||
if (optpos) {
|
||||
- strncpy((char *)buf, optarg, optpos - optarg);
|
||||
- buf[optpos - optarg] = '\0';
|
||||
+ size_t addr_len = optpos - optarg;
|
||||
+
|
||||
+ /* Leave room for the terminating NUL; reject anything
|
||||
+ * that cannot be a valid IPv6 literal instead of
|
||||
+ * overflowing buf. */
|
||||
+ if (addr_len >= sizeof(buf)) {
|
||||
+ syslog(LOG_ERR, "invalid argument: '%s'", optarg);
|
||||
+ return 1;
|
||||
+ }
|
||||
+ strncpy((char *)buf, optarg, addr_len);
|
||||
+ buf[addr_len] = '\0';
|
||||
if (inet_pton(AF_INET6, (char *)buf, &prefix.addr) <= 0) {
|
||||
syslog(LOG_ERR, "invalid argument: '%s'", optarg);
|
||||
return 1;
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
From 7d71729e4da366d1c19c205487e4decf6d646dde Mon Sep 17 00:00:00 2001
|
||||
From: Hauke Mehrtens <hauke@hauke-m.de>
|
||||
Date: Sat, 11 Jul 2026 23:36:10 +0200
|
||||
Subject: dhcpv6: require known SERVERID when validating Reconfigure
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
dhcpv6_response_is_valid() previously set serverid_ok = true whenever
|
||||
STATE_SERVER_ID was empty, on the assumption that an empty stored
|
||||
server-id only happens for Solicit/Advertise where the client has not
|
||||
yet bound to a server. The same code path is taken for Reconfigure
|
||||
messages (req_msg_type == DHCPV6_MSG_UNKNOWN), so a Reconfigure
|
||||
message that arrived while STATE_SERVER_ID was somehow empty (state
|
||||
corruption, an early Reconfigure delivered before binding completes,
|
||||
or a server-side bug) would be accepted without server identification.
|
||||
|
||||
RFC 8415 §18.2.11 requires the SERVERID in a Reconfigure to match the
|
||||
server the client received the lease from. Only set serverid_ok = true
|
||||
on an empty stored server-id when we are not validating a Reconfigure;
|
||||
for DHCPV6_MSG_UNKNOWN we leave it false so the response is dropped.
|
||||
|
||||
Assisted-by: Claude:claude-opus-4-7
|
||||
Link: https://github.com/openwrt/odhcp6c/pull/160
|
||||
Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
|
||||
|
||||
(cherry picked from commit 0a4e51db30d5a13175cdda7830a5fcb882e88659)
|
||||
[the message-type parameter is named 'type' in 24.10, not 'req_msg_type']
|
||||
---
|
||||
src/dhcpv6.c | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
--- a/src/dhcpv6.c
|
||||
+++ b/src/dhcpv6.c
|
||||
@@ -819,7 +819,7 @@ static bool dhcpv6_response_is_valid(con
|
||||
if (server_id_len)
|
||||
serverid_ok = (olen + 4U == server_id_len) && !memcmp(
|
||||
&odata[-4], server_id, server_id_len);
|
||||
- else
|
||||
+ else if (type != DHCPV6_MSG_UNKNOWN)
|
||||
serverid_ok = true;
|
||||
} else if (otype == DHCPV6_OPT_AUTH && olen == -4 +
|
||||
sizeof(struct dhcpv6_auth_reconfigure)) {
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
From 1374cbad07c63db19bba0f371c1a2f36167381dd Mon Sep 17 00:00:00 2001
|
||||
From: Hauke Mehrtens <hauke@hauke-m.de>
|
||||
Date: Sat, 23 May 2026 14:46:55 +0200
|
||||
Subject: odhcp6c: do not treat DHCPv6 option type 0 as end-of-list
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
dhcpv6_for_each_option was structured as a chain of '&&' expressions
|
||||
where each link both assigned a loop variable (otype, odata, olen)
|
||||
and gated iteration on the assigned value being truthy. The middle
|
||||
link, '((otype) = _o[0] << 8 | _o[1])', made iteration stop on
|
||||
otype == 0.
|
||||
|
||||
DHCPv6 has no end-of-list sentinel; option code 0 is just reserved
|
||||
(RFC 8415 §21, IANA registry). A server (broken or hostile) that emits
|
||||
an option with code 0 anywhere in the response would silently truncate
|
||||
the client's view of the option list, masking everything that follows
|
||||
— including STATUS, IA_NA / IA_PD, AUTH, etc.
|
||||
|
||||
Switch the assignments inside the for-loop condition to use the comma
|
||||
operator so all three (otype, odata, olen) are unconditionally
|
||||
written, and gate iteration purely on the bounds check
|
||||
'odata + olen <= end'. Callers see the same variables, in the same
|
||||
order, just without the spurious type-0 truncation.
|
||||
|
||||
Assisted-by: Claude:claude-opus-4-7
|
||||
Link: https://github.com/openwrt/odhcp6c/pull/160
|
||||
Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
|
||||
(cherry picked from commit 9177f236c2d74144a8a2cbdc7da0fcf2f582bc2c)
|
||||
---
|
||||
src/odhcp6c.h | 6 ++++--
|
||||
1 file changed, 4 insertions(+), 2 deletions(-)
|
||||
|
||||
--- a/src/odhcp6c.h
|
||||
+++ b/src/odhcp6c.h
|
||||
@@ -267,8 +267,10 @@ struct dhcpv6_s46_rule {
|
||||
|
||||
#define dhcpv6_for_each_option(start, end, otype, olen, odata)\
|
||||
for (uint8_t *_o = (uint8_t*)(start); _o + 4 <= (uint8_t*)(end) &&\
|
||||
- ((otype) = _o[0] << 8 | _o[1]) && ((odata) = (void*)&_o[4]) &&\
|
||||
- ((olen) = _o[2] << 8 | _o[3]) + (odata) <= (uint8_t*)(end); \
|
||||
+ ((otype) = _o[0] << 8 | _o[1],\
|
||||
+ (odata) = (void*)&_o[4],\
|
||||
+ (olen) = _o[2] << 8 | _o[3],\
|
||||
+ (odata) + (olen) <= (uint8_t*)(end)); \
|
||||
_o += 4 + (_o[2] << 8 | _o[3]))
|
||||
|
||||
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
From 80c68194417ab73490fb99a8589d9c02d7fc5ede Mon Sep 17 00:00:00 2001
|
||||
From: Hauke Mehrtens <hauke@hauke-m.de>
|
||||
Date: Sat, 11 Jul 2026 23:38:11 +0200
|
||||
Subject: dhcpv6: enforce monotonic replay counter on Reconfigure RKAP
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
RFC 8415 §20.4.3 requires the client to track the replay-detection
|
||||
field of an authenticated Reconfigure message and to reject any
|
||||
incoming Reconfigure whose replay value does not exceed the highest
|
||||
one already accepted. Without this check, a captured Reconfigure can
|
||||
be replayed at will: the HMAC is still valid because the message
|
||||
itself has not changed, and the client will happily re-trigger
|
||||
Renew / Rebind / Information-Request cycles on demand.
|
||||
|
||||
Add a per-binding (reconf_replay, reconf_replay_seen) tuple,
|
||||
initialised in dhcpv6_promote_server_cand() alongside reconf_key so a
|
||||
fresh binding starts with a fresh window. Compare the wire replay
|
||||
field (network byte order, decoded with be64toh()) against the stored
|
||||
value before running the HMAC verification; only commit the value on
|
||||
a verified HMAC so a forged message cannot move the counter forward.
|
||||
|
||||
Assisted-by: Claude:claude-opus-4-7
|
||||
Link: https://github.com/openwrt/odhcp6c/pull/160
|
||||
Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
|
||||
|
||||
(cherry picked from commit df27a49c98d89cb945767763a47bfafe196165c8)
|
||||
[24.10 has no per-candidate reconf_key and no auth_protocol selector: the replay
|
||||
state is global alongside the existing global reconf_key, and is reset in
|
||||
dhcpv6_promote_server_cand() when a new server binding is established]
|
||||
---
|
||||
src/dhcpv6.c | 19 +++++++++++++++++++
|
||||
1 file changed, 19 insertions(+)
|
||||
|
||||
--- a/src/dhcpv6.c
|
||||
+++ b/src/dhcpv6.c
|
||||
@@ -15,6 +15,7 @@
|
||||
|
||||
#include <time.h>
|
||||
#include <fcntl.h>
|
||||
+#include <endian.h>
|
||||
#include <errno.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdlib.h>
|
||||
@@ -111,6 +112,8 @@ static struct in6_addr server_addr = IN6
|
||||
|
||||
// Reconfigure key
|
||||
static uint8_t reconf_key[16];
|
||||
+static uint64_t reconf_replay;
|
||||
+static bool reconf_replay_seen;
|
||||
|
||||
// client options
|
||||
static unsigned int client_options = 0;
|
||||
@@ -827,6 +830,16 @@ static bool dhcpv6_response_is_valid(con
|
||||
if (r->protocol != 3 || r->algorithm != 1 || r->reconf_type != 2)
|
||||
continue;
|
||||
|
||||
+ /* RFC 8415 §20.4.3: the replay-detection field must be
|
||||
+ * monotonically increasing per (client, server-id) pair.
|
||||
+ * Drop any Reconfigure whose replay value does not exceed
|
||||
+ * the highest one we have already accepted. */
|
||||
+ uint64_t replay;
|
||||
+ memcpy(&replay, &r->replay, sizeof(replay));
|
||||
+ replay = be64toh(replay);
|
||||
+ if (reconf_replay_seen && replay <= reconf_replay)
|
||||
+ continue;
|
||||
+
|
||||
md5_ctx_t md5;
|
||||
uint8_t serverhash[16], secretbytes[64];
|
||||
uint32_t hash[4];
|
||||
@@ -855,6 +868,10 @@ static bool dhcpv6_response_is_valid(con
|
||||
md5_end(hash, &md5);
|
||||
|
||||
rcauth_ok = !memcmp(hash, serverhash, sizeof(hash));
|
||||
+ if (rcauth_ok) {
|
||||
+ reconf_replay = replay;
|
||||
+ reconf_replay_seen = true;
|
||||
+ }
|
||||
} else if (otype == DHCPV6_OPT_RECONF_MESSAGE && olen == 1) {
|
||||
rcmsg = odata[0];
|
||||
} else if ((otype == DHCPV6_OPT_IA_PD || otype == DHCPV6_OPT_IA_NA)) {
|
||||
@@ -1680,6 +1697,8 @@ int dhcpv6_promote_server_cand(void)
|
||||
odhcp6c_add_state(STATE_SERVER_ID, hdr, sizeof(hdr));
|
||||
odhcp6c_add_state(STATE_SERVER_ID, cand->duid, cand->duid_len);
|
||||
accept_reconfig = cand->wants_reconfigure;
|
||||
+ reconf_replay = 0;
|
||||
+ reconf_replay_seen = false;
|
||||
|
||||
if (cand->ia_na_len) {
|
||||
odhcp6c_add_state(STATE_IA_NA, cand->ia_na, cand->ia_na_len);
|
||||
+120
@@ -0,0 +1,120 @@
|
||||
From 35f1c268c8ffb2bfe2c7f4fa0415215ea532570b Mon Sep 17 00:00:00 2001
|
||||
From: Hauke Mehrtens <hauke@hauke-m.de>
|
||||
Date: Sat, 11 Jul 2026 23:47:42 +0200
|
||||
Subject: odhcp6c: fix handling of RFC6603 Prefix Exclude Option
|
||||
|
||||
Several bugs in the encoding and (more importantly) decoding of
|
||||
DHCPV6_OPT_PD_EXCLUDE lead to the option being ignored in some received
|
||||
messages, or the excluded subnet id being mangled to an incorrect value.
|
||||
|
||||
Fix both encoding and decoding, being more explicit and slightly more
|
||||
verbose for clarity.
|
||||
|
||||
Signed-off-by: Mark H. Spatz <mark.h.spatz@gmail.com>
|
||||
Link: https://github.com/openwrt/odhcp6c/pull/151
|
||||
Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
|
||||
|
||||
(cherry picked from commit 07d324ee7222c0e15b9975281f18236fdccc11bd)
|
||||
[24.10 has no dedicated exclusion_length field (dcb53c4 is not backported), so the
|
||||
port keeps reusing entry.priority / e[].priority for the exclusion length. The
|
||||
upstream 'slen > 2' -> 'slen >= 2' guard change is a no-op here: 24.10 already
|
||||
skips on 'slen < 2'. The added info() PD_EXCLUDE trace is dropped, as the log
|
||||
helpers from bfd7597 are not backported. The computed option length is unchanged
|
||||
(excl_opt_len == old ex_len), so the ia_pd buffer sizing is unaffected.]
|
||||
---
|
||||
src/dhcpv6.c | 47 +++++++++++++++++++++++++++--------------------
|
||||
1 file changed, 27 insertions(+), 20 deletions(-)
|
||||
|
||||
--- a/src/dhcpv6.c
|
||||
+++ b/src/dhcpv6.c
|
||||
@@ -437,13 +437,16 @@ static void dhcpv6_send(enum dhcpv6_msg
|
||||
if (e[j].iaid != iaid)
|
||||
continue;
|
||||
|
||||
- uint8_t ex_len = 0;
|
||||
- if (e[j].priority > 0)
|
||||
- ex_len = ((e[j].priority - e[j].length - 1) / 8) + 6;
|
||||
+ uint8_t excl_subnet_id_nbits, excl_subnet_id_nbytes, excl_opt_len = 0;
|
||||
+ if (e[j].priority > 0) {
|
||||
+ excl_subnet_id_nbits = e[j].priority - e[j].length;
|
||||
+ excl_subnet_id_nbytes = ((excl_subnet_id_nbits - 1) / 8) + 1;
|
||||
+ excl_opt_len = excl_subnet_id_nbytes + 4 + 1;
|
||||
+ }
|
||||
|
||||
struct dhcpv6_ia_prefix p = {
|
||||
.type = htons(DHCPV6_OPT_IA_PREFIX),
|
||||
- .len = htons(sizeof(p) - 4U + ex_len),
|
||||
+ .len = htons(sizeof(p) - 4U + excl_opt_len),
|
||||
.prefix = e[j].length,
|
||||
.addr = e[j].target
|
||||
};
|
||||
@@ -456,20 +459,22 @@ static void dhcpv6_send(enum dhcpv6_msg
|
||||
memcpy(ia_pd + ia_pd_len, &p, sizeof(p));
|
||||
ia_pd_len += sizeof(p);
|
||||
|
||||
- if (ex_len) {
|
||||
+ if (excl_opt_len) {
|
||||
ia_pd[ia_pd_len++] = 0;
|
||||
ia_pd[ia_pd_len++] = DHCPV6_OPT_PD_EXCLUDE;
|
||||
ia_pd[ia_pd_len++] = 0;
|
||||
- ia_pd[ia_pd_len++] = ex_len - 4;
|
||||
+ ia_pd[ia_pd_len++] = excl_opt_len - 4;
|
||||
ia_pd[ia_pd_len++] = e[j].priority;
|
||||
|
||||
- uint32_t excl = ntohl(e[j].router.s6_addr32[1]);
|
||||
- excl >>= (64 - e[j].priority);
|
||||
- excl <<= 8 - ((e[j].priority - e[j].length) % 8);
|
||||
-
|
||||
- for (size_t i = ex_len - 5; i > 0; --i, excl >>= 8)
|
||||
- ia_pd[ia_pd_len + i] = excl & 0xff;
|
||||
- ia_pd_len += ex_len - 5;
|
||||
+ uint32_t excluded_bits = ntohl(e[j].router.s6_addr32[1]);
|
||||
+ excluded_bits >>= (64 - e[j].priority); /* Right align subnet ID bits */
|
||||
+ excluded_bits <<= (32 - excl_subnet_id_nbits); /* Left align subnet ID bits */
|
||||
+
|
||||
+ /* Copy subnet ID bits into the option MSB first */
|
||||
+ for (size_t k = 0; k < excl_subnet_id_nbytes; ++k) {
|
||||
+ ia_pd[ia_pd_len++] = excluded_bits >> 24;
|
||||
+ excluded_bits <<= 8;
|
||||
+ }
|
||||
}
|
||||
|
||||
hdr->len = htons(ntohs(hdr->len) + ntohs(p.len) + 4U);
|
||||
@@ -1408,7 +1413,9 @@ static unsigned int dhcpv6_parse_ia(void
|
||||
if (stype != DHCPV6_OPT_PD_EXCLUDE || slen < 2)
|
||||
continue;
|
||||
|
||||
+ /* RFC 6603 §4.2 Prefix Exclude option */
|
||||
uint8_t elen = sdata[0];
|
||||
+ uint8_t *excl_subnet_id = &sdata[1];
|
||||
if (elen > 64)
|
||||
elen = 64;
|
||||
|
||||
@@ -1417,19 +1424,19 @@ static unsigned int dhcpv6_parse_ia(void
|
||||
continue;
|
||||
}
|
||||
|
||||
- uint8_t bytes = ((elen - entry.length - 1) / 8) + 1;
|
||||
- if (slen <= bytes) {
|
||||
+ uint8_t excl_subnet_id_nbits = elen - entry.length;
|
||||
+ uint8_t excl_subnet_id_nbytes = ((excl_subnet_id_nbits - 1) / 8) + 1;
|
||||
+ if ((excl_subnet_id + excl_subnet_id_nbytes) > (sdata + slen)) {
|
||||
ok = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
uint32_t exclude = 0;
|
||||
- do {
|
||||
- exclude = exclude << 8 | sdata[bytes];
|
||||
- } while (--bytes);
|
||||
-
|
||||
- exclude >>= 8 - ((elen - entry.length) % 8);
|
||||
- exclude <<= 64 - elen;
|
||||
+ /* Copy subnet ID bits out of the option MSB first */
|
||||
+ for (size_t k = 0; k < excl_subnet_id_nbytes; k++)
|
||||
+ exclude = (exclude << 8) | excl_subnet_id[k];
|
||||
+ exclude >>= (8 * excl_subnet_id_nbytes) - excl_subnet_id_nbits; /* Right align subnet ID bits */
|
||||
+ exclude <<= (64 - elen); /* Shift subnet ID bits into the low-order bits of the prefix */
|
||||
|
||||
// Abusing router & priority fields for exclusion
|
||||
entry.router = entry.target;
|
||||
@@ -9,7 +9,7 @@ include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=dropbear
|
||||
PKG_VERSION:=2024.86
|
||||
PKG_RELEASE:=2
|
||||
PKG_RELEASE:=3
|
||||
|
||||
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.bz2
|
||||
PKG_SOURCE_URL:= \
|
||||
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
From 91f4832c0a90fc2c2236465e2937dfbfea03c86d Mon Sep 17 00:00:00 2001
|
||||
From: Matt Johnston <matt@ucc.asn.au>
|
||||
Date: Wed, 8 Apr 2026 20:51:47 +0800
|
||||
Subject: [PATCH 1/6] Fix incorrect index closing listeners
|
||||
|
||||
This could read beyond socks[] and close an arbitrary descriptor.
|
||||
The codepath runs when creating TCP listeners for forwarding (client or
|
||||
server).
|
||||
|
||||
(cherry picked from commit 067fd3846c425d1998c7c94d3feafe1f26b514eb)
|
||||
---
|
||||
src/listener.c | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
--- a/src/listener.c
|
||||
+++ b/src/listener.c
|
||||
@@ -95,7 +95,7 @@ struct Listener* new_listener(const int
|
||||
if (ses.listensize > MAX_LISTENERS) {
|
||||
TRACE(("leave newlistener: too many already"))
|
||||
for (j = 0; j < nsocks; j++) {
|
||||
- close(socks[i]);
|
||||
+ close(socks[j]);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
From a89c4ce57a0e2d9c06c1ec08f1ac0ea1e0133c0c Mon Sep 17 00:00:00 2001
|
||||
From: Matt Johnston <matt@ucc.asn.au>
|
||||
Date: Sat, 2 May 2026 08:17:25 +0800
|
||||
Subject: [PATCH 2/6] scp: clear setuid/setgid bits on received files
|
||||
|
||||
From openssh-portable 487e8ac146f7d6616f65c125d5edb210519b833a
|
||||
Tracked as CVE-2026-35385
|
||||
|
||||
upstream: when downloading files as root in legacy (-O) mode and
|
||||
|
||||
without the -p (preserve modes) flag set, clear setuid/setgid bits from
|
||||
downloaded files as one might expect.
|
||||
|
||||
AFAIK this bug dates back to the original Berkeley rcp program.
|
||||
|
||||
Reported by Christos Papakonstantinou of Cantina and Spearbit.
|
||||
|
||||
OpenBSD-Commit-ID: 49e902fca8dd933a92a9b547ab31f63e86729fa1
|
||||
(cherry picked from commit 443ccc1fd1b9a4e3747f6b5cd644b7af49064308)
|
||||
---
|
||||
src/scp.c | 4 +++-
|
||||
1 file changed, 3 insertions(+), 1 deletion(-)
|
||||
|
||||
--- a/src/scp.c
|
||||
+++ b/src/scp.c
|
||||
@@ -850,8 +850,10 @@ sink(int argc, char **argv, const char *
|
||||
|
||||
setimes = targisdir = 0;
|
||||
mask = umask(0);
|
||||
- if (!pflag)
|
||||
+ if (!pflag) {
|
||||
+ mask |= 07000;
|
||||
(void) umask(mask);
|
||||
+ }
|
||||
if (argc != 1) {
|
||||
run_err("ambiguous target");
|
||||
exit(1);
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
From d05b4fc2f7130630b8715d2b7244f090a3e679a5 Mon Sep 17 00:00:00 2001
|
||||
From: Matt Johnston <matt@ucc.asn.au>
|
||||
Date: Sat, 2 May 2026 08:43:26 +0800
|
||||
Subject: [PATCH 3/6] scp: Disallow -r to an existing directory
|
||||
|
||||
Checks of received paths don't catch all cases in -r recursive mode, so
|
||||
disallow -r when the destination directory exists. This mitigates
|
||||
CVE-2019-6111, where unexpected files would be accepted from a malicious
|
||||
scp server.
|
||||
|
||||
Alternatives like rsync could be used in that case.
|
||||
|
||||
The new error message may be misleading if parent path components of the
|
||||
destination are also missing, though the command would have failed
|
||||
regardless.
|
||||
|
||||
(cherry picked from commit 61bd0e3e6573277de17359763aa5827a189c8d7f)
|
||||
---
|
||||
src/scp.c | 9 +++++++++
|
||||
1 file changed, 9 insertions(+)
|
||||
|
||||
--- a/src/scp.c
|
||||
+++ b/src/scp.c
|
||||
@@ -584,6 +584,15 @@ tolocal(int argc, char **argv)
|
||||
if (*suser == '\0')
|
||||
suser = NULL;
|
||||
}
|
||||
+
|
||||
+ char *dest = *(argv + argc - 1);
|
||||
+ if (!(access(dest, F_OK) == 0 && errno == ENOENT)) {
|
||||
+ /* paths sent from the server in recursive mode aren't adequately
|
||||
+ * checked that they match the requested files, so disallow this */
|
||||
+ fprintf(stderr, "-r destination \"%s\" must not already exist\n", dest);
|
||||
+ exit(1);
|
||||
+ }
|
||||
+
|
||||
host = cleanhostname(host);
|
||||
len = strlen(src) + CMDNEEDS + 20;
|
||||
bp = xmalloc(len);
|
||||
@@ -0,0 +1,360 @@
|
||||
From 7f15dd66d850a0a785b31c5e6ca1947e431fe4f9 Mon Sep 17 00:00:00 2001
|
||||
From: Matt Johnston <matt@ucc.asn.au>
|
||||
Date: Mon, 20 Apr 2026 20:57:11 +0800
|
||||
Subject: [PATCH 4/6] scp: more CVE-2019-6111 fix
|
||||
|
||||
Cherrypicked from OpenSSH portable
|
||||
|
||||
commit 3d896c157c722bc47adca51a58dca859225b5874
|
||||
djm Sun Feb 10 11:15:52 2019 +0000
|
||||
|
||||
upstream: when checking that filenames sent by the server side
|
||||
|
||||
match what the client requested, be prepared to handle shell-style brace
|
||||
alternations, e.g. "{foo,bar}".
|
||||
|
||||
"looks good to me" millert@ + in snaps for the last week courtesy
|
||||
deraadt@
|
||||
|
||||
(cherry picked from commit 143291baca6ebb3407a0c25c85bd53bf041a6ab7)
|
||||
---
|
||||
src/scp.c | 281 +++++++++++++++++++++++++++++++++++++++++++++++++++---
|
||||
1 file changed, 269 insertions(+), 12 deletions(-)
|
||||
|
||||
--- a/src/scp.c
|
||||
+++ b/src/scp.c
|
||||
@@ -74,7 +74,6 @@
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
-/*RCSID("$OpenBSD: scp.c,v 1.130 2006/01/31 10:35:43 djm Exp $");*/
|
||||
|
||||
#include <fnmatch.h>
|
||||
|
||||
@@ -609,6 +608,253 @@ tolocal(int argc, char **argv)
|
||||
}
|
||||
}
|
||||
|
||||
+/* Appends a string to an array; returns 0 on success, -1 on alloc failure */
|
||||
+static int
|
||||
+append(char *cp, char ***ap, size_t *np)
|
||||
+{
|
||||
+ char **tmp;
|
||||
+
|
||||
+ if ((tmp = reallocarray(*ap, *np + 1, sizeof(*tmp))) == NULL)
|
||||
+ return -1;
|
||||
+ tmp[(*np)] = cp;
|
||||
+ (*np)++;
|
||||
+ *ap = tmp;
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+/*
|
||||
+ * Finds the start and end of the first brace pair in the pattern.
|
||||
+ * returns 0 on success or -1 for invalid patterns.
|
||||
+ */
|
||||
+static int
|
||||
+find_brace(const char *pattern, int *startp, int *endp)
|
||||
+{
|
||||
+ int i;
|
||||
+ int in_bracket, brace_level;
|
||||
+
|
||||
+ *startp = *endp = -1;
|
||||
+ in_bracket = brace_level = 0;
|
||||
+ for (i = 0; i < INT_MAX && *endp < 0 && pattern[i] != '\0'; i++) {
|
||||
+ switch (pattern[i]) {
|
||||
+ case '\\':
|
||||
+ /* skip next character */
|
||||
+ if (pattern[i + 1] != '\0')
|
||||
+ i++;
|
||||
+ break;
|
||||
+ case '[':
|
||||
+ in_bracket = 1;
|
||||
+ break;
|
||||
+ case ']':
|
||||
+ in_bracket = 0;
|
||||
+ break;
|
||||
+ case '{':
|
||||
+ if (in_bracket)
|
||||
+ break;
|
||||
+ if (pattern[i + 1] == '}') {
|
||||
+ /* Protect a single {}, for find(1), like csh */
|
||||
+ i++; /* skip */
|
||||
+ break;
|
||||
+ }
|
||||
+ if (*startp == -1)
|
||||
+ *startp = i;
|
||||
+ brace_level++;
|
||||
+ break;
|
||||
+ case '}':
|
||||
+ if (in_bracket)
|
||||
+ break;
|
||||
+ if (*startp < 0) {
|
||||
+ /* Unbalanced brace */
|
||||
+ return -1;
|
||||
+ }
|
||||
+ if (--brace_level <= 0)
|
||||
+ *endp = i;
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+ /* unbalanced brackets/braces */
|
||||
+ if (*endp < 0 && (*startp >= 0 || in_bracket))
|
||||
+ return -1;
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+/*
|
||||
+ * Assembles and records a successfully-expanded pattern, returns -1 on
|
||||
+ * alloc failure.
|
||||
+ */
|
||||
+static int
|
||||
+emit_expansion(const char *pattern, int brace_start, int brace_end,
|
||||
+ int sel_start, int sel_end, char ***patternsp, size_t *npatternsp)
|
||||
+{
|
||||
+ char *cp;
|
||||
+ int o = 0, tail_len = strlen(pattern + brace_end + 1);
|
||||
+
|
||||
+ if ((cp = malloc(brace_start + (sel_end - sel_start) +
|
||||
+ tail_len + 1)) == NULL)
|
||||
+ return -1;
|
||||
+
|
||||
+ /* Pattern before initial brace */
|
||||
+ if (brace_start > 0) {
|
||||
+ memcpy(cp, pattern, brace_start);
|
||||
+ o = brace_start;
|
||||
+ }
|
||||
+ /* Current braced selection */
|
||||
+ if (sel_end - sel_start > 0) {
|
||||
+ memcpy(cp + o, pattern + sel_start,
|
||||
+ sel_end - sel_start);
|
||||
+ o += sel_end - sel_start;
|
||||
+ }
|
||||
+ /* Remainder of pattern after closing brace */
|
||||
+ if (tail_len > 0) {
|
||||
+ memcpy(cp + o, pattern + brace_end + 1, tail_len);
|
||||
+ o += tail_len;
|
||||
+ }
|
||||
+ cp[o] = '\0';
|
||||
+ if (append(cp, patternsp, npatternsp) != 0) {
|
||||
+ free(cp);
|
||||
+ return -1;
|
||||
+ }
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+/*
|
||||
+ * Expand the first encountered brace in pattern, appending the expanded
|
||||
+ * patterns it yielded to the *patternsp array.
|
||||
+ *
|
||||
+ * Returns 0 on success or -1 on allocation failure.
|
||||
+ *
|
||||
+ * Signals whether expansion was performed via *expanded and whether
|
||||
+ * pattern was invalid via *invalid.
|
||||
+ */
|
||||
+static int
|
||||
+brace_expand_one(const char *pattern, char ***patternsp, size_t *npatternsp,
|
||||
+ int *expanded, int *invalid)
|
||||
+{
|
||||
+ int i;
|
||||
+ int in_bracket, brace_start, brace_end, brace_level;
|
||||
+ int sel_start, sel_end;
|
||||
+
|
||||
+ *invalid = *expanded = 0;
|
||||
+
|
||||
+ if (find_brace(pattern, &brace_start, &brace_end) != 0) {
|
||||
+ *invalid = 1;
|
||||
+ return 0;
|
||||
+ } else if (brace_start == -1)
|
||||
+ return 0;
|
||||
+
|
||||
+ in_bracket = brace_level = 0;
|
||||
+ for (i = sel_start = brace_start + 1; i < brace_end; i++) {
|
||||
+ switch (pattern[i]) {
|
||||
+ case '{':
|
||||
+ if (in_bracket)
|
||||
+ break;
|
||||
+ brace_level++;
|
||||
+ break;
|
||||
+ case '}':
|
||||
+ if (in_bracket)
|
||||
+ break;
|
||||
+ brace_level--;
|
||||
+ break;
|
||||
+ case '[':
|
||||
+ in_bracket = 1;
|
||||
+ break;
|
||||
+ case ']':
|
||||
+ in_bracket = 0;
|
||||
+ break;
|
||||
+ case '\\':
|
||||
+ if (i < brace_end - 1)
|
||||
+ i++; /* skip */
|
||||
+ break;
|
||||
+ }
|
||||
+ if (pattern[i] == ',' || i == brace_end - 1) {
|
||||
+ if (in_bracket || brace_level > 0)
|
||||
+ continue;
|
||||
+ /* End of a selection, emit an expanded pattern */
|
||||
+
|
||||
+ /* Adjust end index for last selection */
|
||||
+ sel_end = (i == brace_end - 1) ? brace_end : i;
|
||||
+ if (emit_expansion(pattern, brace_start, brace_end,
|
||||
+ sel_start, sel_end, patternsp, npatternsp) != 0)
|
||||
+ return -1;
|
||||
+ /* move on to the next selection */
|
||||
+ sel_start = i + 1;
|
||||
+ continue;
|
||||
+ }
|
||||
+ }
|
||||
+ if (in_bracket || brace_level > 0) {
|
||||
+ *invalid = 1;
|
||||
+ return 0;
|
||||
+ }
|
||||
+ /* success */
|
||||
+ *expanded = 1;
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+/* Expand braces from pattern. Returns 0 on success, -1 on failure */
|
||||
+static int
|
||||
+brace_expand(const char *pattern, char ***patternsp, size_t *npatternsp)
|
||||
+{
|
||||
+ char *cp, *cp2, **active = NULL, **done = NULL;
|
||||
+ size_t i, nactive = 0, ndone = 0;
|
||||
+ int ret = -1, invalid = 0, expanded = 0;
|
||||
+
|
||||
+ *patternsp = NULL;
|
||||
+ *npatternsp = 0;
|
||||
+
|
||||
+ /* Start the worklist with the original pattern */
|
||||
+ if ((cp = strdup(pattern)) == NULL)
|
||||
+ return -1;
|
||||
+ if (append(cp, &active, &nactive) != 0) {
|
||||
+ free(cp);
|
||||
+ return -1;
|
||||
+ }
|
||||
+ while (nactive > 0) {
|
||||
+ cp = active[nactive - 1];
|
||||
+ nactive--;
|
||||
+ if (brace_expand_one(cp, &active, &nactive,
|
||||
+ &expanded, &invalid) == -1) {
|
||||
+ free(cp);
|
||||
+ goto fail;
|
||||
+ }
|
||||
+ if (invalid)
|
||||
+ fatal("%s: invalid brace pattern \"%s\"", __func__, cp);
|
||||
+ if (expanded) {
|
||||
+ /*
|
||||
+ * Current entry expanded to new entries on the
|
||||
+ * active list; discard the progenitor pattern.
|
||||
+ */
|
||||
+ free(cp);
|
||||
+ continue;
|
||||
+ }
|
||||
+ /*
|
||||
+ * Pattern did not expand; append the finename component to
|
||||
+ * the completed list
|
||||
+ */
|
||||
+ if ((cp2 = strrchr(cp, '/')) != NULL)
|
||||
+ *cp2++ = '\0';
|
||||
+ else
|
||||
+ cp2 = cp;
|
||||
+ if (append(xstrdup(cp2), &done, &ndone) != 0) {
|
||||
+ free(cp);
|
||||
+ goto fail;
|
||||
+ }
|
||||
+ free(cp);
|
||||
+ }
|
||||
+ /* success */
|
||||
+ *patternsp = done;
|
||||
+ *npatternsp = ndone;
|
||||
+ done = NULL;
|
||||
+ ndone = 0;
|
||||
+ ret = 0;
|
||||
+ fail:
|
||||
+ for (i = 0; i < nactive; i++)
|
||||
+ free(active[i]);
|
||||
+ free(active);
|
||||
+ for (i = 0; i < ndone; i++)
|
||||
+ free(done[i]);
|
||||
+ free(done);
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
void
|
||||
source(int argc, char **argv)
|
||||
{
|
||||
@@ -850,7 +1096,8 @@ sink(int argc, char **argv, const char *
|
||||
off_t size, statbytes;
|
||||
int setimes, targisdir, wrerrno = 0;
|
||||
char ch, *cp, *np, *targ, *why, *vect[1], buf[2048];
|
||||
- char *src_copy = NULL, *restrict_pattern = NULL;
|
||||
+ char **patterns = NULL;
|
||||
+ size_t n, npatterns = 0;
|
||||
struct timeval tv[2];
|
||||
|
||||
#define atime tv[0]
|
||||
@@ -879,16 +1126,13 @@ sink(int argc, char **argv, const char *
|
||||
* Prepare to try to restrict incoming filenames to match
|
||||
* the requested destination file glob.
|
||||
*/
|
||||
- if ((src_copy = strdup(src)) == NULL)
|
||||
- fatal("strdup failed");
|
||||
- if ((restrict_pattern = strrchr(src_copy, '/')) != NULL) {
|
||||
- *restrict_pattern++ = '\0';
|
||||
- }
|
||||
+ if (brace_expand(src, &patterns, &npatterns) != 0)
|
||||
+ fatal("%s: could not expand pattern", __func__);
|
||||
}
|
||||
for (first = 1;; first = 0) {
|
||||
cp = buf;
|
||||
if (atomicio(read, remin, cp, 1) != 1)
|
||||
- return;
|
||||
+ goto done;
|
||||
if (*cp++ == '\n')
|
||||
SCREWUP("unexpected <newline>");
|
||||
do {
|
||||
@@ -911,7 +1155,7 @@ sink(int argc, char **argv, const char *
|
||||
}
|
||||
if (buf[0] == 'E') {
|
||||
(void) atomicio(vwrite, remout, "", 1);
|
||||
- return;
|
||||
+ goto done;
|
||||
}
|
||||
if (ch == '\n')
|
||||
*--cp = 0;
|
||||
@@ -967,9 +1211,14 @@ sink(int argc, char **argv, const char *
|
||||
run_err("error: unexpected filename: %s", cp);
|
||||
exit(1);
|
||||
}
|
||||
- if (restrict_pattern != NULL &&
|
||||
- fnmatch(restrict_pattern, cp, 0) != 0)
|
||||
- SCREWUP("filename does not match request");
|
||||
+ if (npatterns > 0) {
|
||||
+ for (n = 0; n < npatterns; n++) {
|
||||
+ if (fnmatch(patterns[n], cp, 0) == 0)
|
||||
+ break;
|
||||
+ }
|
||||
+ if (n >= npatterns)
|
||||
+ SCREWUP("filename does not match request");
|
||||
+ }
|
||||
if (targisdir) {
|
||||
static char *namebuf = NULL;
|
||||
static size_t cursize = 0;
|
||||
@@ -1134,7 +1383,15 @@ bad: run_err("%s: %s", np, strerror(er
|
||||
break;
|
||||
}
|
||||
}
|
||||
+done:
|
||||
+ for (n = 0; n < npatterns; n++)
|
||||
+ free(patterns[n]);
|
||||
+ free(patterns);
|
||||
+ return;
|
||||
screwup:
|
||||
+ for (n = 0; n < npatterns; n++)
|
||||
+ free(patterns[n]);
|
||||
+ free(patterns);
|
||||
run_err("protocol error: %s", why);
|
||||
exit(1);
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
From ff819590e3d945320faae956343d0a57e3525a02 Mon Sep 17 00:00:00 2001
|
||||
From: Matt Johnston <matt@ucc.asn.au>
|
||||
Date: Fri, 17 Apr 2026 21:24:08 +0800
|
||||
Subject: [PATCH 5/6] server: open authorized_keys files nonblocking
|
||||
|
||||
This avoids getting stuck with special files.
|
||||
|
||||
(cherry picked from commit 8b579571b5584847caf72345b9073b3b2a0467a7)
|
||||
---
|
||||
src/svr-authpubkey.c | 10 +++++++++-
|
||||
1 file changed, 9 insertions(+), 1 deletion(-)
|
||||
|
||||
--- a/src/svr-authpubkey.c
|
||||
+++ b/src/svr-authpubkey.c
|
||||
@@ -462,6 +462,7 @@ static int checkpubkey(const char* keyal
|
||||
if (checkpubkeyperms() == DROPBEAR_FAILURE) {
|
||||
TRACE(("bad authorized_keys permissions, or file doesn't exist"))
|
||||
} else {
|
||||
+ int fd;
|
||||
/* we don't need to check pw and pw_dir for validity, since
|
||||
* its been done in checkpubkeyperms. */
|
||||
len = strlen(ses.authstate.pw_dir);
|
||||
@@ -471,7 +472,14 @@ static int checkpubkey(const char* keyal
|
||||
snprintf(filename, len + 22, "%s/.ssh/authorized_keys",
|
||||
ses.authstate.pw_dir);
|
||||
|
||||
- authfile = fopen(filename, "r");
|
||||
+ fd = open(filename, O_RDONLY | O_NONBLOCK);
|
||||
+ if (fd >= 0) {
|
||||
+ authfile = fdopen(fd, "r");
|
||||
+ if (!authfile) {
|
||||
+ /* fdopen could fail with ENOMEM */
|
||||
+ m_close(fd);
|
||||
+ }
|
||||
+ }
|
||||
if (!authfile) {
|
||||
TRACE(("checkpubkey: failed opening %s: %s", filename, strerror(errno)))
|
||||
}
|
||||
@@ -0,0 +1,531 @@
|
||||
From d48f79c851110eb673d9738a69a15696ee972767 Mon Sep 17 00:00:00 2001
|
||||
From: Matt Johnston <matt@ucc.asn.au>
|
||||
Date: Thu, 26 Mar 2026 23:03:46 +0800
|
||||
Subject: [PATCH 6/6] Rework pubkey_options struct
|
||||
|
||||
Pubkey info is now included in the restriction struct. A temporary
|
||||
pubkey_options is filled then set in the global session after auth.
|
||||
|
||||
(cherry picked from commit c60db6408178aaca0a8aacd017ac344a2cfb30c8)
|
||||
---
|
||||
fuzz/fuzzer-pubkey.c | 7 +---
|
||||
src/auth.h | 28 ++++++++-----
|
||||
src/fuzz.h | 2 +-
|
||||
src/svr-authpubkey.c | 78 ++++++++++++++++++++----------------
|
||||
src/svr-authpubkeyoptions.c | 80 +++++++++++++++++++------------------
|
||||
src/svr-chansession.c | 5 ++-
|
||||
src/svr-session.c | 4 +-
|
||||
src/sysoptions.h | 4 ++
|
||||
8 files changed, 114 insertions(+), 94 deletions(-)
|
||||
|
||||
--- a/fuzz/fuzzer-pubkey.c
|
||||
+++ b/fuzz/fuzzer-pubkey.c
|
||||
@@ -31,15 +31,10 @@ int LLVMFuzzerTestOneInput(const uint8_t
|
||||
dropbear_exit("fuzzer imagined a bogus algorithm");
|
||||
}
|
||||
|
||||
- int ret = fuzz_checkpubkey_line(line, 5, "/home/me/authorized_keys",
|
||||
+ fuzz_checkpubkey_line(line, 5, "/home/me/authorized_keys",
|
||||
algoname, algolen,
|
||||
keyblob->data, keyblob->len);
|
||||
|
||||
- if (ret == DROPBEAR_SUCCESS) {
|
||||
- /* fuzz_checkpubkey_line() should have cleaned up for failure */
|
||||
- svr_pubkey_options_cleanup();
|
||||
- }
|
||||
-
|
||||
buf_free(line);
|
||||
buf_free(keyblob);
|
||||
m_free(algoname);
|
||||
--- a/src/auth.h
|
||||
+++ b/src/auth.h
|
||||
@@ -44,6 +44,7 @@ void svr_switch_user(void);
|
||||
void svr_raise_gid_utmp(void);
|
||||
void svr_restore_gid(void);
|
||||
|
||||
+struct PubKeyOptions;
|
||||
#if DROPBEAR_SVR_PUBKEY_OPTIONS_BUILT
|
||||
int svr_pubkey_allows_agentfwd(void);
|
||||
int svr_pubkey_allows_tcpfwd(void);
|
||||
@@ -51,8 +52,9 @@ int svr_pubkey_allows_x11fwd(void);
|
||||
int svr_pubkey_allows_pty(void);
|
||||
int svr_pubkey_allows_local_tcpfwd(const char *host, unsigned int port);
|
||||
void svr_pubkey_set_forced_command(struct ChanSess *chansess);
|
||||
-void svr_pubkey_options_cleanup(void);
|
||||
-int svr_add_pubkey_options(buffer *options_buf, int line_num, const char* filename);
|
||||
+void svr_pubkey_options_cleanup(struct PubKeyOptions *pubkey_options);
|
||||
+struct PubKeyOptions*
|
||||
+svr_parse_pubkey_options(buffer *options_buf, int line_num, const char* filename);
|
||||
#else
|
||||
/* no option : success */
|
||||
#define svr_pubkey_allows_agentfwd() 1
|
||||
@@ -62,9 +64,14 @@ int svr_add_pubkey_options(buffer *optio
|
||||
static inline int svr_pubkey_allows_local_tcpfwd(const char *host, unsigned int port)
|
||||
{ (void)host; (void)port; return 1; }
|
||||
|
||||
-static inline void svr_pubkey_set_forced_command(struct ChanSess *chansess) { }
|
||||
-static inline void svr_pubkey_options_cleanup(void) { }
|
||||
-#define svr_add_pubkey_options(x,y,z) DROPBEAR_SUCCESS
|
||||
+static inline void svr_pubkey_set_forced_command(struct ChanSess *chansess) {
|
||||
+ (void)chansess;
|
||||
+}
|
||||
+static inline void svr_pubkey_options_cleanup(struct PubKeyOptions *pubkey_options) {
|
||||
+ (void)pubkey_options;
|
||||
+}
|
||||
+struct PubKeyOptions*
|
||||
+svr_parse_pubkey_options(buffer *options_buf, int line_num, const char* filename);
|
||||
#endif
|
||||
|
||||
/* Client functions */
|
||||
@@ -132,14 +139,10 @@ struct AuthState {
|
||||
char *pw_shell;
|
||||
char *pw_name;
|
||||
char *pw_passwd;
|
||||
-#if DROPBEAR_SVR_PUBKEY_OPTIONS_BUILT
|
||||
struct PubKeyOptions* pubkey_options;
|
||||
- char *pubkey_info;
|
||||
-#endif
|
||||
};
|
||||
|
||||
#if DROPBEAR_SVR_PUBKEY_OPTIONS_BUILT
|
||||
-struct PubKeyOptions;
|
||||
struct PubKeyOptions {
|
||||
/* Flags */
|
||||
int no_port_forwarding_flag;
|
||||
@@ -155,6 +158,13 @@ struct PubKeyOptions {
|
||||
int no_touch_required_flag;
|
||||
int verify_required_flag;
|
||||
#endif
|
||||
+
|
||||
+ /* String from the end of authorized_keys entry, for SSH_PUBKEYINFO
|
||||
+ environment variable. May be omitted (NULL) if
|
||||
+ options contain non-shell-safe characters.
|
||||
+ This is not involved in pubkey option restrictions, but is
|
||||
+ parsed and kept similarly so is stored here. */
|
||||
+ char * info_env;
|
||||
};
|
||||
|
||||
struct PermitTCPFwdEntry {
|
||||
--- a/src/fuzz.h
|
||||
+++ b/src/fuzz.h
|
||||
@@ -30,7 +30,7 @@ const void* fuzz_get_algo(const algo_typ
|
||||
|
||||
// fuzzer functions that intrude into general code
|
||||
void fuzz_kex_fakealgos(void);
|
||||
-int fuzz_checkpubkey_line(buffer* line, int line_num, char* filename,
|
||||
+void fuzz_checkpubkey_line(buffer* line, int line_num, char* filename,
|
||||
const char* algo, unsigned int algolen,
|
||||
const unsigned char* keyblob, unsigned int keybloblen);
|
||||
extern const char * const * fuzz_signkey_names;
|
||||
--- a/src/svr-authpubkey.c
|
||||
+++ b/src/svr-authpubkey.c
|
||||
@@ -72,7 +72,8 @@
|
||||
#define MAX_AUTHKEYS_LINE 4200 /* max length of a line in authkeys */
|
||||
|
||||
static int checkpubkey(const char* keyalgo, unsigned int keyalgolen,
|
||||
- const unsigned char* keyblob, unsigned int keybloblen);
|
||||
+ const unsigned char* keyblob, unsigned int keybloblen,
|
||||
+ struct PubKeyOptions **ret_options);
|
||||
static int checkpubkeyperms(void);
|
||||
static void send_msg_userauth_pk_ok(const char* sigalgo, unsigned int sigalgolen,
|
||||
const unsigned char* keyblob, unsigned int keybloblen);
|
||||
@@ -95,7 +96,8 @@ void svr_auth_pubkey(int valid_user) {
|
||||
char* fp = NULL;
|
||||
enum signature_type sigtype;
|
||||
enum signkey_type keytype;
|
||||
- int auth_failure = 1;
|
||||
+ int auth_failure = 1;
|
||||
+ struct PubKeyOptions *pubkey_options = NULL;
|
||||
|
||||
TRACE(("enter pubkeyauth"))
|
||||
|
||||
@@ -148,8 +150,8 @@ void svr_auth_pubkey(int valid_user) {
|
||||
.pos = 0,
|
||||
.size = 0
|
||||
};
|
||||
- int ret = svr_add_pubkey_options(&temp_buf, 0, "N/A");
|
||||
- if (ret == DROPBEAR_FAILURE) {
|
||||
+ pubkey_options = svr_parse_pubkey_options(&temp_buf, 0, "plugin");
|
||||
+ if (pubkey_options == NULL) {
|
||||
/* Fail immediately as the plugin provided wrong options */
|
||||
send_msg_userauth_failure(0, 0);
|
||||
goto out;
|
||||
@@ -160,7 +162,8 @@ void svr_auth_pubkey(int valid_user) {
|
||||
#endif
|
||||
/* check if the key is valid */
|
||||
if (auth_failure) {
|
||||
- auth_failure = checkpubkey(keyalgo, keyalgolen, keyblob, keybloblen) == DROPBEAR_FAILURE;
|
||||
+ int status = checkpubkey(keyalgo, keyalgolen, keyblob, keybloblen, &pubkey_options);
|
||||
+ auth_failure = (status != DROPBEAR_SUCCESS);
|
||||
}
|
||||
|
||||
if (auth_failure) {
|
||||
@@ -185,13 +188,13 @@ void svr_auth_pubkey(int valid_user) {
|
||||
|
||||
#if DROPBEAR_SK_ECDSA || DROPBEAR_SK_ED25519
|
||||
key->sk_flags_mask = SSH_SK_USER_PRESENCE_REQD;
|
||||
- if (ses.authstate.pubkey_options && ses.authstate.pubkey_options->no_touch_required_flag) {
|
||||
+ if (pubkey_options->no_touch_required_flag) {
|
||||
key->sk_flags_mask &= ~SSH_SK_USER_PRESENCE_REQD;
|
||||
}
|
||||
- if (ses.authstate.pubkey_options && ses.authstate.pubkey_options->verify_required_flag) {
|
||||
+ if (pubkey_options->verify_required_flag) {
|
||||
key->sk_flags_mask |= SSH_SK_USER_VERIFICATION_REQD;
|
||||
}
|
||||
-#endif
|
||||
+#endif /* DROPBEAR_SK_ECDSA || DROPBEAR_SK_ED25519 */
|
||||
|
||||
/* create the data which has been signed - this a string containing
|
||||
* session_id, concatenated with the payload packet up to the signature */
|
||||
@@ -212,6 +215,10 @@ void svr_auth_pubkey(int valid_user) {
|
||||
/* ... and finally verify the signature */
|
||||
fp = sign_key_fingerprint(keyblob, keybloblen);
|
||||
if (buf_verify(ses.payload, key, sigtype, signbuf) == DROPBEAR_SUCCESS) {
|
||||
+ if (ses.authstate.pubkey_options == NULL) {
|
||||
+ ses.authstate.pubkey_options = pubkey_options;
|
||||
+ pubkey_options = NULL;
|
||||
+ }
|
||||
if (svr_opts.multiauthmethod && (ses.authstate.authtypes & ~AUTH_TYPE_PUBKEY)) {
|
||||
/* successful pubkey authentication, but extra auth required */
|
||||
dropbear_log(LOG_NOTICE,
|
||||
@@ -256,9 +263,9 @@ out:
|
||||
sign_key_free(key);
|
||||
key = NULL;
|
||||
}
|
||||
- /* Retain pubkey options only if auth succeeded */
|
||||
- if (!ses.authstate.authdone) {
|
||||
- svr_pubkey_options_cleanup();
|
||||
+ if (pubkey_options) {
|
||||
+ svr_pubkey_options_cleanup(pubkey_options);
|
||||
+ pubkey_options = NULL;
|
||||
}
|
||||
TRACE(("leave pubkeyauth"))
|
||||
}
|
||||
@@ -281,17 +288,22 @@ static void send_msg_userauth_pk_ok(cons
|
||||
|
||||
}
|
||||
|
||||
-/* Content for SSH_PUBKEYINFO is optionally returned malloced in ret_info (will be
|
||||
- freed if already set */
|
||||
+/* Key options are optionally returned in ret_options.
|
||||
+ Should be passed with *ret_options = NULL, will only be populated
|
||||
+ on success return. */
|
||||
static int checkpubkey_line(buffer* line, int line_num, const char* filename,
|
||||
const char* algo, unsigned int algolen,
|
||||
const unsigned char* keyblob, unsigned int keybloblen,
|
||||
- char ** ret_info) {
|
||||
+ struct PubKeyOptions ** ret_options) {
|
||||
buffer *options_buf = NULL;
|
||||
char *info_str = NULL;
|
||||
unsigned int pos, len, infopos, infolen;
|
||||
int ret = DROPBEAR_FAILURE;
|
||||
|
||||
+ if (ret_options) {
|
||||
+ *ret_options = NULL;
|
||||
+ }
|
||||
+
|
||||
if (line->len < MIN_AUTHKEYS_LINE || line->len > MAX_AUTHKEYS_LINE) {
|
||||
TRACE(("checkpubkey_line: bad line length %d", line->len))
|
||||
goto out;
|
||||
@@ -393,7 +405,7 @@ static int checkpubkey_line(buffer* line
|
||||
if (infolen > 0) {
|
||||
info_str = m_malloc(infolen + 1);
|
||||
buf_setpos(line, infopos);
|
||||
- strncpy(info_str, buf_getptr(line, infolen), infolen);
|
||||
+ strncpy(info_str, buf_getptr(line, infolen), infolen);
|
||||
}
|
||||
|
||||
/* truncate to base64 data length */
|
||||
@@ -405,20 +417,19 @@ static int checkpubkey_line(buffer* line
|
||||
ret = cmp_base64_key(keyblob, keybloblen, (const unsigned char *) algo, algolen, line, NULL);
|
||||
|
||||
/* free pubkey_info if it is filled */
|
||||
- if (ret_info && *ret_info) {
|
||||
- m_free(*ret_info);
|
||||
- *ret_info = NULL;
|
||||
- }
|
||||
-
|
||||
if (ret == DROPBEAR_SUCCESS) {
|
||||
- if (options_buf) {
|
||||
- ret = svr_add_pubkey_options(options_buf, line_num, filename);
|
||||
- }
|
||||
- if (ret_info) {
|
||||
+#if DROPBEAR_SVR_PUBKEY_OPTIONS_BUILT
|
||||
+ if (ret_options) {
|
||||
+ *ret_options = svr_parse_pubkey_options(options_buf, line_num, filename);
|
||||
+ if (*ret_options == NULL) {
|
||||
+ ret = DROPBEAR_FAILURE;
|
||||
+ goto out;
|
||||
+ }
|
||||
/* take the (optional) public key information */
|
||||
- *ret_info = info_str;
|
||||
+ (*ret_options)->info_env = info_str;
|
||||
info_str = NULL;
|
||||
}
|
||||
+#endif
|
||||
}
|
||||
|
||||
out:
|
||||
@@ -436,7 +447,8 @@ out:
|
||||
* acceptable key for authentication */
|
||||
/* Returns DROPBEAR_SUCCESS if key is ok for auth, DROPBEAR_FAILURE otherwise */
|
||||
static int checkpubkey(const char* keyalgo, unsigned int keyalgolen,
|
||||
- const unsigned char* keyblob, unsigned int keybloblen) {
|
||||
+ const unsigned char* keyblob, unsigned int keybloblen,
|
||||
+ struct PubKeyOptions **ret_options) {
|
||||
|
||||
FILE * authfile = NULL;
|
||||
char * filename = NULL;
|
||||
@@ -509,7 +521,7 @@ static int checkpubkey(const char* keyal
|
||||
line_num++;
|
||||
|
||||
ret = checkpubkey_line(line, line_num, filename, keyalgo, keyalgolen,
|
||||
- keyblob, keybloblen, &ses.authstate.pubkey_info);
|
||||
+ keyblob, keybloblen, ret_options);
|
||||
if (ret == DROPBEAR_SUCCESS) {
|
||||
break;
|
||||
}
|
||||
@@ -622,10 +634,12 @@ static int checkfileperm(char * filename
|
||||
}
|
||||
|
||||
#if DROPBEAR_FUZZ
|
||||
-int fuzz_checkpubkey_line(buffer* line, int line_num, char* filename,
|
||||
+void fuzz_checkpubkey_line(buffer* line, int line_num, char* filename,
|
||||
const char* algo, unsigned int algolen,
|
||||
const unsigned char* keyblob, unsigned int keybloblen) {
|
||||
- return checkpubkey_line(line, line_num, filename, algo, algolen, keyblob, keybloblen, NULL);
|
||||
+ struct PubKeyOptions *options = NULL;
|
||||
+ checkpubkey_line(line, line_num, filename, algo, algolen, keyblob, keybloblen, &options);
|
||||
+ svr_pubkey_options_cleanup(options);
|
||||
}
|
||||
#endif
|
||||
|
||||
--- a/src/svr-authpubkeyoptions.c
|
||||
+++ b/src/svr-authpubkeyoptions.c
|
||||
@@ -132,25 +132,21 @@ void svr_pubkey_set_forced_command(struc
|
||||
}
|
||||
|
||||
/* Free potential public key options */
|
||||
-void svr_pubkey_options_cleanup() {
|
||||
- if (ses.authstate.pubkey_options) {
|
||||
- if (ses.authstate.pubkey_options->forced_command) {
|
||||
- m_free(ses.authstate.pubkey_options->forced_command);
|
||||
- }
|
||||
- if (ses.authstate.pubkey_options->permit_open_destinations) {
|
||||
- m_list_elem *iter = ses.authstate.pubkey_options->permit_open_destinations->first;
|
||||
+void svr_pubkey_options_cleanup(struct PubKeyOptions *pubkey_options) {
|
||||
+ if (pubkey_options) {
|
||||
+ m_free(pubkey_options->forced_command);
|
||||
+ if (pubkey_options->permit_open_destinations) {
|
||||
+ m_list_elem *iter = pubkey_options->permit_open_destinations->first;
|
||||
while (iter) {
|
||||
struct PermitTCPFwdEntry *entry = (struct PermitTCPFwdEntry*)list_remove(iter);
|
||||
m_free(entry->host);
|
||||
m_free(entry);
|
||||
- iter = ses.authstate.pubkey_options->permit_open_destinations->first;
|
||||
+ iter = pubkey_options->permit_open_destinations->first;
|
||||
}
|
||||
- m_free(ses.authstate.pubkey_options->permit_open_destinations);
|
||||
+ m_free(pubkey_options->permit_open_destinations);
|
||||
}
|
||||
- m_free(ses.authstate.pubkey_options);
|
||||
- }
|
||||
- if (ses.authstate.pubkey_info) {
|
||||
- m_free(ses.authstate.pubkey_info);
|
||||
+ m_free(pubkey_options->info_env);
|
||||
+ m_free(pubkey_options);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -168,58 +164,65 @@ static int match_option(buffer *options_
|
||||
return DROPBEAR_FAILURE;
|
||||
}
|
||||
|
||||
-/* Parse pubkey options and set ses.authstate.pubkey_options accordingly.
|
||||
- * Returns DROPBEAR_SUCCESS if key is ok for auth, DROPBEAR_FAILURE otherwise */
|
||||
-int svr_add_pubkey_options(buffer *options_buf, int line_num, const char* filename) {
|
||||
- int ret = DROPBEAR_FAILURE;
|
||||
+/* Parse pubkey options and return a malloced struct.
|
||||
+ * options_buf may be NULL, treated as empty.
|
||||
+ * Returns NULL on failure */
|
||||
+struct PubKeyOptions*
|
||||
+svr_parse_pubkey_options(buffer *options_buf, int line_num, const char* filename) {
|
||||
+ struct PubKeyOptions *pubkey_options = NULL;
|
||||
|
||||
TRACE(("enter addpubkeyoptions"))
|
||||
|
||||
- ses.authstate.pubkey_options = (struct PubKeyOptions*)m_malloc(sizeof( struct PubKeyOptions ));
|
||||
+ pubkey_options = (struct PubKeyOptions*)m_malloc(sizeof( struct PubKeyOptions ));
|
||||
+
|
||||
+ if (!options_buf) {
|
||||
+ TRACE(("leave addpubkeyoptions, null options"))
|
||||
+ return pubkey_options;
|
||||
+ }
|
||||
|
||||
buf_setpos(options_buf, 0);
|
||||
while (options_buf->pos < options_buf->len) {
|
||||
if (match_option(options_buf, "no-port-forwarding") == DROPBEAR_SUCCESS) {
|
||||
dropbear_log(LOG_WARNING, "Port forwarding disabled.");
|
||||
- ses.authstate.pubkey_options->no_port_forwarding_flag = 1;
|
||||
+ pubkey_options->no_port_forwarding_flag = 1;
|
||||
goto next_option;
|
||||
}
|
||||
if (match_option(options_buf, "no-agent-forwarding") == DROPBEAR_SUCCESS) {
|
||||
#if DROPBEAR_SVR_AGENTFWD
|
||||
dropbear_log(LOG_WARNING, "Agent forwarding disabled.");
|
||||
- ses.authstate.pubkey_options->no_agent_forwarding_flag = 1;
|
||||
+ pubkey_options->no_agent_forwarding_flag = 1;
|
||||
#endif
|
||||
goto next_option;
|
||||
}
|
||||
if (match_option(options_buf, "no-X11-forwarding") == DROPBEAR_SUCCESS) {
|
||||
#if DROPBEAR_X11FWD
|
||||
dropbear_log(LOG_WARNING, "X11 forwarding disabled.");
|
||||
- ses.authstate.pubkey_options->no_x11_forwarding_flag = 1;
|
||||
+ pubkey_options->no_x11_forwarding_flag = 1;
|
||||
#endif
|
||||
goto next_option;
|
||||
}
|
||||
if (match_option(options_buf, "no-pty") == DROPBEAR_SUCCESS) {
|
||||
dropbear_log(LOG_WARNING, "Pty allocation disabled.");
|
||||
- ses.authstate.pubkey_options->no_pty_flag = 1;
|
||||
+ pubkey_options->no_pty_flag = 1;
|
||||
goto next_option;
|
||||
}
|
||||
if (match_option(options_buf, "restrict") == DROPBEAR_SUCCESS) {
|
||||
dropbear_log(LOG_WARNING, "Restrict option set");
|
||||
- ses.authstate.pubkey_options->no_port_forwarding_flag = 1;
|
||||
+ pubkey_options->no_port_forwarding_flag = 1;
|
||||
#if DROPBEAR_SVR_AGENTFWD
|
||||
- ses.authstate.pubkey_options->no_agent_forwarding_flag = 1;
|
||||
+ pubkey_options->no_agent_forwarding_flag = 1;
|
||||
#endif
|
||||
#if DROPBEAR_X11FWD
|
||||
- ses.authstate.pubkey_options->no_x11_forwarding_flag = 1;
|
||||
+ pubkey_options->no_x11_forwarding_flag = 1;
|
||||
#endif
|
||||
- ses.authstate.pubkey_options->no_pty_flag = 1;
|
||||
+ pubkey_options->no_pty_flag = 1;
|
||||
goto next_option;
|
||||
}
|
||||
if (match_option(options_buf, "command=\"") == DROPBEAR_SUCCESS) {
|
||||
int escaped = 0;
|
||||
const unsigned char* command_start = buf_getptr(options_buf, 0);
|
||||
|
||||
- if (ses.authstate.pubkey_options->forced_command) {
|
||||
+ if (pubkey_options->forced_command) {
|
||||
/* multiple command= options */
|
||||
goto bad_option;
|
||||
}
|
||||
@@ -228,10 +231,10 @@ int svr_add_pubkey_options(buffer *optio
|
||||
const char c = buf_getbyte(options_buf);
|
||||
if (!escaped && c == '"') {
|
||||
const int command_len = buf_getptr(options_buf, 0) - command_start;
|
||||
- ses.authstate.pubkey_options->forced_command = m_malloc(command_len);
|
||||
- memcpy(ses.authstate.pubkey_options->forced_command,
|
||||
+ pubkey_options->forced_command = m_malloc(command_len);
|
||||
+ memcpy(pubkey_options->forced_command,
|
||||
command_start, command_len-1);
|
||||
- ses.authstate.pubkey_options->forced_command[command_len-1] = '\0';
|
||||
+ pubkey_options->forced_command[command_len-1] = '\0';
|
||||
goto next_option;
|
||||
}
|
||||
escaped = (!escaped && c == '\\');
|
||||
@@ -244,8 +247,8 @@ int svr_add_pubkey_options(buffer *optio
|
||||
int valid_option = 0;
|
||||
const unsigned char* permitopen_start = buf_getptr(options_buf, 0);
|
||||
|
||||
- if (!ses.authstate.pubkey_options->permit_open_destinations) {
|
||||
- ses.authstate.pubkey_options->permit_open_destinations = list_new();
|
||||
+ if (!pubkey_options->permit_open_destinations) {
|
||||
+ pubkey_options->permit_open_destinations = list_new();
|
||||
}
|
||||
|
||||
while (options_buf->pos < options_buf->len) {
|
||||
@@ -257,7 +260,7 @@ int svr_add_pubkey_options(buffer *optio
|
||||
struct PermitTCPFwdEntry *entry =
|
||||
(struct PermitTCPFwdEntry*)m_malloc(sizeof(struct PermitTCPFwdEntry));
|
||||
|
||||
- list_append(ses.authstate.pubkey_options->permit_open_destinations, entry);
|
||||
+ list_append(pubkey_options->permit_open_destinations, entry);
|
||||
spec = m_malloc(permitopen_len);
|
||||
memcpy(spec, permitopen_start, permitopen_len - 1);
|
||||
spec[permitopen_len - 1] = '\0';
|
||||
@@ -291,14 +294,14 @@ int svr_add_pubkey_options(buffer *optio
|
||||
if (match_option(options_buf, "no-touch-required") == DROPBEAR_SUCCESS) {
|
||||
#if DROPBEAR_SK_ECDSA || DROPBEAR_SK_ED25519
|
||||
dropbear_log(LOG_WARNING, "No user presence check required for U2F/FIDO key.");
|
||||
- ses.authstate.pubkey_options->no_touch_required_flag = 1;
|
||||
+ pubkey_options->no_touch_required_flag = 1;
|
||||
#endif
|
||||
goto next_option;
|
||||
}
|
||||
if (match_option(options_buf, "verify-required") == DROPBEAR_SUCCESS) {
|
||||
#if DROPBEAR_SK_ECDSA || DROPBEAR_SK_ED25519
|
||||
dropbear_log(LOG_WARNING, "User verification required for U2F/FIDO key.");
|
||||
- ses.authstate.pubkey_options->verify_required_flag = 1;
|
||||
+ pubkey_options->verify_required_flag = 1;
|
||||
#endif
|
||||
goto next_option;
|
||||
}
|
||||
@@ -315,17 +318,16 @@ next_option:
|
||||
/* Process the next option. */
|
||||
}
|
||||
/* parsed all options with no problem */
|
||||
- ret = DROPBEAR_SUCCESS;
|
||||
goto end;
|
||||
|
||||
bad_option:
|
||||
- ret = DROPBEAR_FAILURE;
|
||||
- svr_pubkey_options_cleanup();
|
||||
+ svr_pubkey_options_cleanup(pubkey_options);
|
||||
+ pubkey_options = NULL;
|
||||
dropbear_log(LOG_WARNING, "Bad public key options at %s:%d", filename, line_num);
|
||||
|
||||
end:
|
||||
TRACE(("leave addpubkeyoptions"))
|
||||
- return ret;
|
||||
+ return pubkey_options;
|
||||
}
|
||||
|
||||
#endif
|
||||
--- a/src/svr-chansession.c
|
||||
+++ b/src/svr-chansession.c
|
||||
@@ -1026,8 +1026,9 @@ static void execchild(const void *user_d
|
||||
addnewvar("SSH_ORIGINAL_COMMAND", chansess->original_command);
|
||||
}
|
||||
#if DROPBEAR_SVR_PUBKEY_OPTIONS_BUILT
|
||||
- if (ses.authstate.pubkey_info != NULL) {
|
||||
- addnewvar("SSH_PUBKEYINFO", ses.authstate.pubkey_info);
|
||||
+ if (ses.authstate.pubkey_options
|
||||
+ && ses.authstate.pubkey_options->info_env) {
|
||||
+ addnewvar("SSH_PUBKEYINFO", ses.authstate.pubkey_options->info_env);
|
||||
}
|
||||
#endif
|
||||
|
||||
--- a/src/svr-session.c
|
||||
+++ b/src/svr-session.c
|
||||
@@ -84,8 +84,8 @@ static const struct ChanType *svr_chanty
|
||||
|
||||
static void
|
||||
svr_session_cleanup(void) {
|
||||
- /* free potential public key options */
|
||||
- svr_pubkey_options_cleanup();
|
||||
+ svr_pubkey_options_cleanup(ses.authstate.pubkey_options);
|
||||
+ ses.authstate.pubkey_options = NULL;
|
||||
|
||||
m_free(svr_ses.addrstring);
|
||||
m_free(svr_ses.remotehost);
|
||||
--- a/src/sysoptions.h
|
||||
+++ b/src/sysoptions.h
|
||||
@@ -310,6 +310,10 @@
|
||||
#error "You must define DROPBEAR_SVR_PUBKEY_AUTH in order to use plugins"
|
||||
#endif
|
||||
|
||||
+#if (DROPBEAR_PLUGIN && !DROPBEAR_SVR_PUBKEY_OPTIONS_BUILT)
|
||||
+ #error "DROPBEAR_PLUGIN requires DROPBEAR_SVR_PUBKEY_OPTIONS"
|
||||
+#endif
|
||||
+
|
||||
#if !(DROPBEAR_AES128 || DROPBEAR_3DES || DROPBEAR_AES256 || DROPBEAR_CHACHA20POLY1305)
|
||||
#error "At least one encryption algorithm must be enabled. AES128 is recommended."
|
||||
#endif
|
||||
@@ -1,6 +1,6 @@
|
||||
--- a/src/svr-authpubkey.c
|
||||
+++ b/src/svr-authpubkey.c
|
||||
@@ -78,6 +78,13 @@ static void send_msg_userauth_pk_ok(cons
|
||||
@@ -79,6 +79,13 @@ static void send_msg_userauth_pk_ok(cons
|
||||
const unsigned char* keyblob, unsigned int keybloblen);
|
||||
static int checkfileperm(char * filename);
|
||||
|
||||
@@ -14,10 +14,10 @@
|
||||
/* process a pubkey auth request, sending success or failure message as
|
||||
* appropriate */
|
||||
void svr_auth_pubkey(int valid_user) {
|
||||
@@ -462,14 +469,21 @@ static int checkpubkey(const char* keyal
|
||||
if (checkpubkeyperms() == DROPBEAR_FAILURE) {
|
||||
@@ -475,14 +482,21 @@ static int checkpubkey(const char* keyal
|
||||
TRACE(("bad authorized_keys permissions, or file doesn't exist"))
|
||||
} else {
|
||||
int fd;
|
||||
- /* we don't need to check pw and pw_dir for validity, since
|
||||
- * its been done in checkpubkeyperms. */
|
||||
- len = strlen(ses.authstate.pw_dir);
|
||||
@@ -42,9 +42,9 @@
|
||||
+ user_authkeys_dir, authkeys_file);
|
||||
+ }
|
||||
|
||||
authfile = fopen(filename, "r");
|
||||
if (!authfile) {
|
||||
@@ -543,27 +557,41 @@ static int checkpubkeyperms() {
|
||||
fd = open(filename, O_RDONLY | O_NONBLOCK);
|
||||
if (fd >= 0) {
|
||||
@@ -563,27 +577,41 @@ static int checkpubkeyperms() {
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=ead
|
||||
PKG_RELEASE:=1
|
||||
PKG_RELEASE:=2
|
||||
|
||||
PKG_BUILD_DEPENDS:=libpcap
|
||||
PKG_BUILD_DIR:=$(BUILD_DIR)/ead
|
||||
|
||||
@@ -428,11 +428,14 @@ handle_send_a(struct ead_packet *pkt, int len, int *nstate)
|
||||
{
|
||||
struct ead_msg *msg = &pkt->msg;
|
||||
struct ead_msg_number *number = EAD_DATA(msg, number);
|
||||
len = ntohl(msg->len) - sizeof(struct ead_msg_number);
|
||||
uint32_t msg_len = ntohl(msg->len);
|
||||
|
||||
if (len > MAXPARAMLEN + 1)
|
||||
if (msg_len < sizeof(struct ead_msg_number) ||
|
||||
msg_len - sizeof(struct ead_msg_number) > MAXPARAMLEN + 1)
|
||||
return false;
|
||||
|
||||
len = msg_len - sizeof(struct ead_msg_number);
|
||||
|
||||
A.len = len;
|
||||
A.data = abuf;
|
||||
memcpy(A.data, number->data, len);
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=hostapd
|
||||
PKG_RELEASE:=2
|
||||
PKG_RELEASE:=3
|
||||
|
||||
PKG_SOURCE_URL:=https://w1.fi/hostap.git
|
||||
PKG_SOURCE_PROTO:=git
|
||||
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
From 46dd5a4ffc9bcf44cf8fc45120b3e1e5ec922187 Mon Sep 17 00:00:00 2001
|
||||
From: Jouni Malinen <jouni.malinen@oss.qualcomm.com>
|
||||
Date: Tue, 31 Mar 2026 23:24:04 +0300
|
||||
Subject: AP MLD: Fix link ID validation in Basic MLE parsing
|
||||
|
||||
Link ID 15 can be indicated in the field, but that is not a valid value
|
||||
and must be rejected to avoid issues pointing beyond the array of links
|
||||
for a non-AP MLD. Without this, an invalid MLE could result in writing
|
||||
beyond the end of the buffer and causing process termination or
|
||||
unexpected behavior.
|
||||
|
||||
Fixes: 5f5db9366cde ("AP: MLO: Process Multi-Link element from (Re)Association Request frame")
|
||||
Signed-off-by: Jouni Malinen <jouni.malinen@oss.qualcomm.com>
|
||||
---
|
||||
src/ap/ieee802_11_eht.c | 10 ++++++++--
|
||||
1 file changed, 8 insertions(+), 2 deletions(-)
|
||||
|
||||
--- a/src/ap/ieee802_11_eht.c
|
||||
+++ b/src/ap/ieee802_11_eht.c
|
||||
@@ -1290,6 +1290,7 @@ u16 hostapd_process_ml_assoc_req(struct
|
||||
size_t sub_elem_len = *(pos + 1);
|
||||
size_t sta_info_len;
|
||||
u16 control;
|
||||
+ u8 link_id;
|
||||
|
||||
wpa_printf(MSG_DEBUG, "MLD: sub element len=%zu",
|
||||
sub_elem_len);
|
||||
@@ -1330,8 +1331,13 @@ u16 hostapd_process_ml_assoc_req(struct
|
||||
goto out;
|
||||
}
|
||||
control = WPA_GET_LE16(pos);
|
||||
- link_info = &info->links[control &
|
||||
- EHT_PER_STA_CTRL_LINK_ID_MSK];
|
||||
+ link_id = control & EHT_PER_STA_CTRL_LINK_ID_MSK;
|
||||
+ if (link_id >= MAX_NUM_MLD_LINKS) {
|
||||
+ wpa_printf(MSG_DEBUG,
|
||||
+ "MLD: Invalid Link ID in Per-STA Profile subelement");
|
||||
+ goto out;
|
||||
+ }
|
||||
+ link_info = &info->links[link_id];
|
||||
pos += 2;
|
||||
ml_len -= 2;
|
||||
sub_elem_len -= 2;
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
From aa9d345887389a251c63a3781d2ad2940d079193 Mon Sep 17 00:00:00 2001
|
||||
From: Amarnath Hullur Subramanyam <amarnathhs@google.com>
|
||||
Date: Thu, 30 Apr 2026 18:24:35 -0700
|
||||
Subject: BSS: Add bounds check for link_id in Basic MLE parsing
|
||||
|
||||
In wpa_bss_parse_basic_ml_element() in bss.c, an extracted link_id is
|
||||
used without validation against the maximum allowed links
|
||||
(MAX_NUM_MLD_LINKS). Processing a malformed Basic Multi-Link element
|
||||
(MLE) with an out-of-bounds link_id could lead to memory corruption.
|
||||
However, the modified location is within the body of the received frame
|
||||
and as such, this does not result in additional issues since that area
|
||||
is controlled by the transmitter of the frame. In any case, it is better
|
||||
to be explicit with validating the Link ID value.
|
||||
|
||||
This commit introduces a strict bounds check immediately after link_id
|
||||
extraction. If link_id exceeds or equals MAX_NUM_MLD_LINKS, parsing is
|
||||
gracefully aborted with a debug log entry.
|
||||
|
||||
Fixes: de5e01010cb2 ("wpa_supplicant: Support ML probe request")
|
||||
Signed-off-by: Amarnath Hullur Subramanyam <amarnathhs@google.com>
|
||||
---
|
||||
wpa_supplicant/bss.c | 5 +++++
|
||||
1 file changed, 5 insertions(+)
|
||||
|
||||
--- a/wpa_supplicant/bss.c
|
||||
+++ b/wpa_supplicant/bss.c
|
||||
@@ -1773,6 +1773,11 @@ int wpa_bss_parse_basic_ml_element(struc
|
||||
ETH_ALEN);
|
||||
|
||||
link_id = ml_basic_common_info->variable[0] & EHT_ML_LINK_ID_MSK;
|
||||
+ if (link_id >= MAX_NUM_MLD_LINKS) {
|
||||
+ wpa_printf(MSG_DEBUG, "MLD: Invalid link ID %u in Basic MLE",
|
||||
+ link_id);
|
||||
+ goto out;
|
||||
+ }
|
||||
|
||||
bss->mld_link_id = link_id;
|
||||
seen = bss->valid_links = BIT(link_id);
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
From a8531e3d871e6fa72f2f85d91e9f787326b2af8b Mon Sep 17 00:00:00 2001
|
||||
From: Jouni Malinen <jouni.malinen@oss.qualcomm.com>
|
||||
Date: Tue, 31 Mar 2026 23:16:08 +0300
|
||||
Subject: MLD: Validate MLE Link ID fields in association rejection case
|
||||
|
||||
The Link ID Info field in the Common Info field needs to ignore the
|
||||
reserved bits to be more extensible for future. Both that link ID for
|
||||
the association link and the link IDs for other links need to be
|
||||
verified to be within the valid range (0-14), so check that here. The
|
||||
parsed link ID was not used for anything yet, but it is better to make
|
||||
sure this in theory common parser is not exposing invalid data to the
|
||||
caller should it be used for additional purposes in the future.
|
||||
|
||||
Signed-off-by: Jouni Malinen <jouni.malinen@oss.qualcomm.com>
|
||||
---
|
||||
wpa_supplicant/events.c | 11 ++++++++++-
|
||||
1 file changed, 10 insertions(+), 1 deletion(-)
|
||||
|
||||
--- a/wpa_supplicant/events.c
|
||||
+++ b/wpa_supplicant/events.c
|
||||
@@ -3972,7 +3972,12 @@ static unsigned int wpas_ml_parse_assoc(
|
||||
pos = common_info->variable;
|
||||
|
||||
/* Store the information for the association link */
|
||||
- ml_info[i].link_id = *pos;
|
||||
+ ml_info[i].link_id = *pos & EHT_ML_LINK_ID_MSK;
|
||||
+ if (ml_info[i].link_id >= MAX_NUM_MLD_LINKS) {
|
||||
+ wpa_printf(MSG_DEBUG,
|
||||
+ "MLD: Invalid Link ID value for assoc link");
|
||||
+ goto out;
|
||||
+ }
|
||||
pos++;
|
||||
|
||||
/* Skip the BSS Parameters Change Count */
|
||||
@@ -4107,6 +4112,10 @@ static unsigned int wpas_ml_parse_assoc(
|
||||
MAC2STR(pos + 1), nstr_bitmap_len);
|
||||
|
||||
ml_info[i].link_id = ctrl & EHT_PER_STA_CTRL_LINK_ID_MSK;
|
||||
+ if (ml_info[i].link_id >= MAX_NUM_MLD_LINKS) {
|
||||
+ wpa_printf(MSG_DEBUG, "MLD: Invalid Link ID value");
|
||||
+ goto out;
|
||||
+ }
|
||||
os_memcpy(ml_info[i].bssid, pos + 1, ETH_ALEN);
|
||||
|
||||
pos += sta_info_len;
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
From ce1a8612e309fe86133ecf05ffb452b0bdf3b035 Mon Sep 17 00:00:00 2001
|
||||
From: Jouni Malinen <jouni.malinen@oss.qualcomm.com>
|
||||
Date: Mon, 18 May 2026 15:45:15 +0300
|
||||
Subject: AP MLD: Verify AP MLD link ID validity before updating bitmap of
|
||||
links
|
||||
|
||||
Link ID is 0..14, so ignore value 15 if an invalid frame is processed.
|
||||
It does not look like the invalid value was actually used to reference
|
||||
any local array, but in any case, it is better to not mark an invalid
|
||||
link as being specified.
|
||||
|
||||
Signed-off-by: Jouni Malinen <jouni.malinen@oss.qualcomm.com>
|
||||
---
|
||||
src/ap/beacon.c | 5 ++++-
|
||||
1 file changed, 4 insertions(+), 1 deletion(-)
|
||||
|
||||
--- a/src/ap/beacon.c
|
||||
+++ b/src/ap/beacon.c
|
||||
@@ -1366,6 +1366,7 @@ static bool parse_ml_probe_req(const str
|
||||
for_each_element_id(sub, 0, pos, len) {
|
||||
const struct ieee80211_eht_per_sta_profile *sta;
|
||||
u16 sta_control;
|
||||
+ u8 link_id;
|
||||
|
||||
if (*links == 0xffff)
|
||||
*links = 0;
|
||||
@@ -1385,7 +1386,9 @@ static bool parse_ml_probe_req(const str
|
||||
* partial profile was requested.
|
||||
*/
|
||||
sta_control = le_to_host16(sta->sta_control);
|
||||
- *links |= BIT(sta_control & EHT_PER_STA_CTRL_LINK_ID_MSK);
|
||||
+ link_id = sta_control & EHT_PER_STA_CTRL_LINK_ID_MSK;
|
||||
+ if (link_id < MAX_NUM_MLD_LINKS)
|
||||
+ *links |= BIT(link_id);
|
||||
}
|
||||
|
||||
if (!for_each_element_completed(sub, pos, len)) {
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
From 41c86a2ebed50567c73de23c102c2bf83eb883f2 Mon Sep 17 00:00:00 2001
|
||||
From: Jouni Malinen <jouni.malinen@oss.qualcomm.com>
|
||||
Date: Tue, 31 Mar 2026 17:47:03 +0300
|
||||
Subject: MLD: Fix length check in common info for association failure cases
|
||||
|
||||
It is not sufficient to check that the indicated common info length is
|
||||
sufficiently large to contain the information; there needs to be a check
|
||||
for the indicated value to not be too large to go beyond the end of the
|
||||
MLE as well. Without this, invalid MLE might result in ml_len wrapping
|
||||
around to a huge value and reading beyond the end of the buffer for the
|
||||
received frame. This could result in process termination.
|
||||
|
||||
Add the missed check for the Common Info field not being truncated in
|
||||
the MLE in association failure cases.
|
||||
|
||||
Fixes: a58a0c592e20 ("MLD: Fix Multi-Link element parsing for association failures")
|
||||
Signed-off-by: Jouni Malinen <jouni.malinen@oss.qualcomm.com>
|
||||
---
|
||||
wpa_supplicant/events.c | 7 +++++++
|
||||
1 file changed, 7 insertions(+)
|
||||
|
||||
--- a/wpa_supplicant/events.c
|
||||
+++ b/wpa_supplicant/events.c
|
||||
@@ -3960,6 +3960,13 @@ static unsigned int wpas_ml_parse_assoc(
|
||||
goto out;
|
||||
}
|
||||
|
||||
+ if (sizeof(*ml) + common_info->len > ml_len) {
|
||||
+ wpa_printf(MSG_DEBUG,
|
||||
+ "MLD: Truncated common info (common_info->len=%u ml_len=%zu)",
|
||||
+ common_info->len, ml_len);
|
||||
+ goto out;
|
||||
+ }
|
||||
+
|
||||
wpa_printf(MSG_DEBUG, "MLD: address: " MACSTR,
|
||||
MAC2STR(common_info->mld_addr));
|
||||
|
||||
@@ -278,7 +278,7 @@ This allows building both hostapd and wpa_supplicant as a single binary
|
||||
os_memset(&eapol_test, 0, sizeof(eapol_test));
|
||||
--- a/wpa_supplicant/events.c
|
||||
+++ b/wpa_supplicant/events.c
|
||||
@@ -6131,8 +6131,8 @@ static int wpas_pasn_auth(struct wpa_sup
|
||||
@@ -6147,8 +6147,8 @@ static int wpas_pasn_auth(struct wpa_sup
|
||||
#endif /* CONFIG_PASN */
|
||||
|
||||
|
||||
@@ -289,7 +289,7 @@ This allows building both hostapd and wpa_supplicant as a single binary
|
||||
{
|
||||
struct wpa_supplicant *wpa_s = ctx;
|
||||
int resched;
|
||||
@@ -7084,7 +7084,7 @@ void wpa_supplicant_event(void *ctx, enu
|
||||
@@ -7100,7 +7100,7 @@ void wpa_supplicant_event(void *ctx, enu
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ probe/assoc/auth requests via object subscribe.
|
||||
}
|
||||
--- a/src/ap/beacon.c
|
||||
+++ b/src/ap/beacon.c
|
||||
@@ -1418,6 +1418,12 @@ void handle_probe_req(struct hostapd_dat
|
||||
@@ -1421,6 +1421,12 @@ void handle_probe_req(struct hostapd_dat
|
||||
int mld_id;
|
||||
u16 links;
|
||||
#endif /* CONFIG_IEEE80211BE */
|
||||
@@ -66,7 +66,7 @@ probe/assoc/auth requests via object subscribe.
|
||||
|
||||
if (hapd->iconf->rssi_ignore_probe_request && ssi_signal &&
|
||||
ssi_signal < hapd->iconf->rssi_ignore_probe_request)
|
||||
@@ -1604,6 +1610,12 @@ void handle_probe_req(struct hostapd_dat
|
||||
@@ -1607,6 +1613,12 @@ void handle_probe_req(struct hostapd_dat
|
||||
}
|
||||
#endif /* CONFIG_P2P */
|
||||
|
||||
|
||||
@@ -622,7 +622,7 @@ as adding/removing interfaces.
|
||||
CFLAGS += -DEAP_SERVER -DEAP_SERVER_IDENTITY
|
||||
--- a/wpa_supplicant/events.c
|
||||
+++ b/wpa_supplicant/events.c
|
||||
@@ -6167,6 +6167,7 @@ void supplicant_event(void *ctx, enum wp
|
||||
@@ -6183,6 +6183,7 @@ void supplicant_event(void *ctx, enum wp
|
||||
event_to_string(event), event);
|
||||
#endif /* CONFIG_NO_STDOUT_DEBUG */
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ as adding/removing interfaces.
|
||||
"error: %s", name, strerror(errno));
|
||||
--- a/hostapd/main.c
|
||||
+++ b/hostapd/main.c
|
||||
@@ -406,7 +406,11 @@ hostapd_interface_init(struct hapd_inter
|
||||
@@ -365,7 +365,11 @@ hostapd_interface_init(struct hapd_inter
|
||||
struct hostapd_iface *iface;
|
||||
int k;
|
||||
|
||||
@@ -58,7 +58,7 @@ as adding/removing interfaces.
|
||||
return NULL;
|
||||
--- a/src/ap/hostapd.c
|
||||
+++ b/src/ap/hostapd.c
|
||||
@@ -2893,7 +2893,12 @@ hostapd_interface_init_bss(struct hapd_i
|
||||
@@ -3354,8 +3354,13 @@ hostapd_interface_init_bss(struct hapd_i
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,5 +71,6 @@ as adding/removing interfaces.
|
||||
+ wpa_printf(MSG_INFO, "Configuration file: %s (phy %s)%s",
|
||||
+ config_fname, phy, iface ? "" : " --> new PHY");
|
||||
+ }
|
||||
if (iface) {
|
||||
struct hostapd_config *conf;
|
||||
if (iface) {
|
||||
struct hostapd_config *conf;
|
||||
struct hostapd_bss_config **tmp_conf;
|
||||
|
||||
@@ -36,7 +36,7 @@ full device, e.g. in order to deal with hardware/driver limitations
|
||||
* ' ' (ascii 32): all environments
|
||||
--- a/src/ap/beacon.c
|
||||
+++ b/src/ap/beacon.c
|
||||
@@ -1634,7 +1634,7 @@ void handle_probe_req(struct hostapd_dat
|
||||
@@ -1637,7 +1637,7 @@ void handle_probe_req(struct hostapd_dat
|
||||
if (hapd->conf->no_probe_resp_if_max_sta &&
|
||||
is_multicast_ether_addr(mgmt->da) &&
|
||||
is_multicast_ether_addr(mgmt->bssid) &&
|
||||
|
||||
+1
-1
@@ -28,7 +28,7 @@ Signed-off-by: David Bauer <mail@david-bauer.net>
|
||||
|
||||
--- a/src/ap/beacon.c
|
||||
+++ b/src/ap/beacon.c
|
||||
@@ -1477,7 +1477,7 @@ void handle_probe_req(struct hostapd_dat
|
||||
@@ -1480,7 +1480,7 @@ void handle_probe_req(struct hostapd_dat
|
||||
* is less likely to see them (Probe Request frame sent on a
|
||||
* neighboring, but partially overlapping, channel).
|
||||
*/
|
||||
|
||||
@@ -29,7 +29,7 @@ handle reload.
|
||||
|
||||
#ifndef CONFIG_NO_HOSTAPD_LOGGER
|
||||
static void hostapd_logger_cb(void *ctx, const u8 *addr, unsigned int module,
|
||||
@@ -793,6 +794,11 @@ int main(int argc, char *argv[])
|
||||
@@ -797,6 +798,11 @@ int main(int argc, char *argv[])
|
||||
if (os_program_init())
|
||||
return -1;
|
||||
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@ Signed-off-by: Stone Zhang <quic_stonez@quicinc.com>
|
||||
|
||||
--- a/src/ap/hostapd.c
|
||||
+++ b/src/ap/hostapd.c
|
||||
@@ -4813,6 +4813,7 @@ static void hostapd_switch_color_timeout
|
||||
@@ -4818,6 +4818,7 @@ static void hostapd_switch_color_timeout
|
||||
struct cca_settings settings;
|
||||
int ret;
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=odhcpd
|
||||
PKG_RELEASE:=2
|
||||
PKG_RELEASE:=3
|
||||
|
||||
PKG_SOURCE_PROTO:=git
|
||||
PKG_SOURCE_URL=$(PROJECT_GIT)/project/odhcpd.git
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
From f0d855358b86a36efbfeb5a9de5a1d2a4d9d80fe Mon Sep 17 00:00:00 2001
|
||||
From 95a69c4cafc3a064bf4e0e0c9d06cea3411a4ba4 Mon Sep 17 00:00:00 2001
|
||||
From: Haoyi Ci <cihaoyi@outlook.com>
|
||||
Date: Thu, 2 Oct 2025 16:14:05 +0800
|
||||
Subject: [PATCH] ndp: Allow NS loopback for master iface
|
||||
Subject: ndp: Allow NS loopback for master iface
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
From 5eac9c56ff3b0a013c5241f449ca144f70bf4c02 Mon Sep 17 00:00:00 2001
|
||||
From b655d759c4bc87ae729c86fe85617f48d74c9e40 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= <noltari@gmail.com>
|
||||
Date: Tue, 7 Oct 2025 10:30:15 +0200
|
||||
Subject: [PATCH] router: fix SLAAC on subnets > 64
|
||||
Subject: router: fix SLAAC on subnets > 64
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
From d402cdae431668f55f9d82b7072b0afa3b8090df Mon Sep 17 00:00:00 2001
|
||||
From bf4dbad4ba8867cdb570426de252f3d994e2db2d Mon Sep 17 00:00:00 2001
|
||||
From: Stephen Groat <stephen.groat@datadoghq.com>
|
||||
Date: Wed, 8 Oct 2025 11:54:51 -0700
|
||||
Subject: [PATCH] ndp: fix macOS IPv6 compatibility by using link-local source
|
||||
Subject: ndp: fix macOS IPv6 compatibility by using link-local source
|
||||
addresses
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
@@ -1,7 +1,7 @@
|
||||
From 30780debd691aee7567784daf1fdfd8db500a485 Mon Sep 17 00:00:00 2001
|
||||
From d9a202773d18ab9f88fc55f79b11b5f61a200085 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?David=20H=C3=A4rdeman?= <david@hardeman.nu>
|
||||
Date: Thu, 9 Oct 2025 11:08:14 +0200
|
||||
Subject: [PATCH] odhcpd: fix a compilation error
|
||||
Subject: odhcpd: fix a compilation error
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
From be7ca7c0792b185263ad86b961ea61129494a7f9 Mon Sep 17 00:00:00 2001
|
||||
From 7b2d0bd30306dcc014f11b15817b0c5010554e7c Mon Sep 17 00:00:00 2001
|
||||
From: Fei Lv <feilv@asrmicro.com>
|
||||
Date: Fri, 14 Nov 2025 15:31:22 +0800
|
||||
Subject: [PATCH] config: fix memleak during odhcpd reload
|
||||
Subject: config: fix memleak during odhcpd reload
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
+103
@@ -0,0 +1,103 @@
|
||||
From 8ea9f568dfd8164b0483b5508830f51d488a1c4b Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?David=20H=C3=A4rdeman?= <david@hardeman.nu>
|
||||
Date: Fri, 17 Oct 2025 15:32:08 +0200
|
||||
Subject: ndp: correctness fix for BPF filter
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
Note that AF_PACKET sockets start receiving packets as soon as they are
|
||||
created. Thus, a packet can arrive between the creation of the socket
|
||||
and the time the real filter is installed. Fix this using the same
|
||||
technique as used in libpcap, i.e. by installing a drop-all filter,
|
||||
removing any packets from the socket, then installing the real filter
|
||||
later (an atomic operation which replaces the drop filter).
|
||||
|
||||
Signed-off-by: David Härdeman <david@hardeman.nu>
|
||||
Link: https://github.com/openwrt/odhcpd/pull/319
|
||||
Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
|
||||
(cherry picked from commit 650a5df88ebae82d956831c2f87c7c6eedd8c283)
|
||||
---
|
||||
src/ndp.c | 56 +++++++++++++++++++++++++++++++++++++++++++------------
|
||||
1 file changed, 44 insertions(+), 12 deletions(-)
|
||||
|
||||
--- a/src/ndp.c
|
||||
+++ b/src/ndp.c
|
||||
@@ -39,17 +39,6 @@ static void setup_addr_for_relaying(stru
|
||||
static void handle_solicit(void *addr, void *data, size_t len,
|
||||
struct interface *iface, void *dest);
|
||||
|
||||
-/* Filter ICMPv6 messages of type neighbor solicitation */
|
||||
-static struct sock_filter bpf[] = {
|
||||
- BPF_STMT(BPF_LD | BPF_B | BPF_ABS, offsetof(struct ip6_hdr, ip6_nxt)),
|
||||
- BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, IPPROTO_ICMPV6, 0, 3),
|
||||
- BPF_STMT(BPF_LD | BPF_B | BPF_ABS, sizeof(struct ip6_hdr) +
|
||||
- offsetof(struct icmp6_hdr, icmp6_type)),
|
||||
- BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, ND_NEIGHBOR_SOLICIT, 0, 1),
|
||||
- BPF_STMT(BPF_RET | BPF_K, 0xffffffff),
|
||||
- BPF_STMT(BPF_RET | BPF_K, 0),
|
||||
-};
|
||||
-static const struct sock_fprog bpf_prog = {sizeof(bpf) / sizeof(*bpf), bpf};
|
||||
static struct netevent_handler ndp_netevent_handler = { .cb = ndp_netevent_cb, };
|
||||
|
||||
/* Initialize NDP-proxy */
|
||||
@@ -67,6 +56,30 @@ int ndp_init(void)
|
||||
|
||||
int ndp_setup_interface(struct interface *iface, bool enable)
|
||||
{
|
||||
+ /* Drop everything */
|
||||
+ static const struct sock_filter bpf_drop_filter[] = {
|
||||
+ BPF_STMT(BPF_RET | BPF_K, 0),
|
||||
+ };
|
||||
+ static const struct sock_fprog bpf_drop = {
|
||||
+ .len = ARRAY_SIZE(bpf_drop_filter),
|
||||
+ .filter = (struct sock_filter *)bpf_drop_filter,
|
||||
+ };
|
||||
+
|
||||
+ /* Filter ICMPv6 messages of type neighbor solicitation */
|
||||
+ static const struct sock_filter bpf[] = {
|
||||
+ BPF_STMT(BPF_LD | BPF_B | BPF_ABS, offsetof(struct ip6_hdr, ip6_nxt)),
|
||||
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, IPPROTO_ICMPV6, 0, 3),
|
||||
+ BPF_STMT(BPF_LD | BPF_B | BPF_ABS, sizeof(struct ip6_hdr) +
|
||||
+ offsetof(struct icmp6_hdr, icmp6_type)),
|
||||
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, ND_NEIGHBOR_SOLICIT, 0, 1),
|
||||
+ BPF_STMT(BPF_RET | BPF_K, 0xffffffff),
|
||||
+ BPF_STMT(BPF_RET | BPF_K, 0),
|
||||
+ };
|
||||
+ static const struct sock_fprog bpf_prog = {
|
||||
+ .len = ARRAY_SIZE(bpf),
|
||||
+ .filter = (struct sock_filter *)bpf,
|
||||
+ };
|
||||
+
|
||||
int ret = 0, procfd;
|
||||
bool dump_neigh = false;
|
||||
char procbuf[64];
|
||||
@@ -170,8 +183,27 @@ int ndp_setup_interface(struct interface
|
||||
}
|
||||
#endif
|
||||
|
||||
+ /*
|
||||
+ * AF_PACKET sockets can receive packets as soon as they are
|
||||
+ * created, so make sure we don't accept anything...
|
||||
+ */
|
||||
+ if (setsockopt(iface->ndp_event.uloop.fd, SOL_SOCKET, SO_ATTACH_FILTER,
|
||||
+ &bpf_drop, sizeof(bpf_drop))) {
|
||||
+ syslog(LOG_ERR, "setsockopt(SO_ATTACH_FILTER): %m");
|
||||
+ ret = -1;
|
||||
+ goto out;
|
||||
+ }
|
||||
+
|
||||
+ /* ...and remove stray packets... */
|
||||
+ while (true) {
|
||||
+ char null[1];
|
||||
+ if (recv(iface->ndp_event.uloop.fd, null, sizeof(null), MSG_DONTWAIT | MSG_TRUNC) < 0)
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ /* ...until the real filter is installed */
|
||||
if (setsockopt(iface->ndp_event.uloop.fd, SOL_SOCKET, SO_ATTACH_FILTER,
|
||||
- &bpf_prog, sizeof(bpf_prog))) {
|
||||
+ &bpf_prog, sizeof(bpf_prog))) {
|
||||
syslog(LOG_ERR, "setsockopt(SO_ATTACH_FILTER): %m");
|
||||
ret = -1;
|
||||
goto out;
|
||||
@@ -0,0 +1,97 @@
|
||||
From a7100a04a7da0936fd84e0808eded4e30e90696b Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?David=20H=C3=A4rdeman?= <david@hardeman.nu>
|
||||
Date: Sun, 23 Nov 2025 14:46:49 +0100
|
||||
Subject: config: fix (S)NTP realloc handling
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
Make sure that realloc errors are dealt with properly in the (S)NTP
|
||||
option parsing.
|
||||
|
||||
Signed-off-by: David Härdeman <david@hardeman.nu>
|
||||
Link: https://github.com/openwrt/odhcpd/pull/320
|
||||
Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
|
||||
(cherry picked from commit b4ab371c541ab481c7dff001964afd621b5a968d)
|
||||
---
|
||||
src/config.c | 30 ++++++++++++++++--------------
|
||||
1 file changed, 16 insertions(+), 14 deletions(-)
|
||||
|
||||
--- a/src/config.c
|
||||
+++ b/src/config.c
|
||||
@@ -564,10 +564,10 @@ static int parse_ntp_options(uint16_t *d
|
||||
{
|
||||
uint16_t sub_opt = 0, sub_len = htons(IPV6_ADDR_LEN);
|
||||
uint16_t ntp_len = IPV6_ADDR_LEN + 4;
|
||||
- uint8_t *ntp = *dhcpv6_ntp;
|
||||
+ uint8_t *ntp;
|
||||
size_t pos = *dhcpv6_ntp_len;
|
||||
|
||||
- ntp = realloc(ntp, pos + ntp_len);
|
||||
+ ntp = realloc(*dhcpv6_ntp, pos + ntp_len);
|
||||
if (!ntp)
|
||||
return -1;
|
||||
|
||||
@@ -594,7 +594,7 @@ static int parse_ntp_fqdn(uint16_t *dhcp
|
||||
{
|
||||
size_t fqdn_len = strlen(fqdn);
|
||||
uint16_t sub_opt = 0, sub_len = 0, ntp_len = 0;
|
||||
- uint8_t *ntp = *dhcpv6_ntp;
|
||||
+ uint8_t *ntp;
|
||||
size_t pos = *dhcpv6_ntp_len;
|
||||
uint8_t buf[256] = {0};
|
||||
|
||||
@@ -607,7 +607,7 @@ static int parse_ntp_fqdn(uint16_t *dhcp
|
||||
|
||||
ntp_len = len + 4;
|
||||
|
||||
- ntp = realloc(ntp, pos + ntp_len);
|
||||
+ ntp = realloc(*dhcpv6_ntp, pos + ntp_len);
|
||||
if (!ntp)
|
||||
return -1;
|
||||
|
||||
@@ -1472,32 +1472,34 @@ int config_parse_interface(void *data, s
|
||||
continue;
|
||||
|
||||
char *str = blobmsg_get_string(cur);
|
||||
- struct in_addr addr4;
|
||||
- struct in6_addr addr6;
|
||||
+ struct in_addr addr4, *tmp4;
|
||||
+ struct in6_addr addr6, *tmp6;
|
||||
|
||||
if (inet_pton(AF_INET, str, &addr4) == 1) {
|
||||
if (addr4.s_addr == INADDR_ANY)
|
||||
goto err;
|
||||
|
||||
- iface->dhcpv4_ntp = realloc(iface->dhcpv4_ntp,
|
||||
- (++iface->dhcpv4_ntp_cnt) * sizeof(*iface->dhcpv4_ntp));
|
||||
- if (!iface->dhcpv4_ntp)
|
||||
+ tmp4 = realloc(iface->dhcpv4_ntp, (iface->dhcpv4_ntp_cnt + 1) * sizeof(*iface->dhcpv4_ntp));
|
||||
+ if (!tmp4)
|
||||
goto err;
|
||||
|
||||
- iface->dhcpv4_ntp[iface->dhcpv4_ntp_cnt - 1] = addr4;
|
||||
+ iface->dhcpv4_ntp = tmp4;
|
||||
+ iface->dhcpv4_ntp[iface->dhcpv4_ntp_cnt++] = addr4;
|
||||
+
|
||||
} else if (inet_pton(AF_INET6, str, &addr6) == 1) {
|
||||
if (IN6_IS_ADDR_UNSPECIFIED(&addr6))
|
||||
goto err;
|
||||
|
||||
- iface->dhcpv6_sntp = realloc(iface->dhcpv6_sntp,
|
||||
- (++iface->dhcpv6_sntp_cnt) * sizeof(*iface->dhcpv6_sntp));
|
||||
- if (!iface->dhcpv6_sntp)
|
||||
+ tmp6 = realloc(iface->dhcpv6_sntp, (iface->dhcpv6_sntp_cnt + 1) * sizeof(*iface->dhcpv6_sntp));
|
||||
+ if (!tmp6)
|
||||
goto err;
|
||||
|
||||
- iface->dhcpv6_sntp[iface->dhcpv6_sntp_cnt - 1] = addr6;
|
||||
+ iface->dhcpv6_sntp = tmp6;
|
||||
+ iface->dhcpv6_sntp[iface->dhcpv6_sntp_cnt++] = addr6;
|
||||
|
||||
if (!parse_ntp_options(&iface->dhcpv6_ntp_len, addr6, &iface->dhcpv6_ntp))
|
||||
iface->dhcpv6_ntp_cnt++;
|
||||
+
|
||||
} else {
|
||||
if (!parse_ntp_fqdn(&iface->dhcpv6_ntp_len, str, &iface->dhcpv6_ntp))
|
||||
iface->dhcpv6_ntp_cnt++;
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
From 061253933a52a394df7e79eae312212b1911d7fa Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?David=20H=C3=A4rdeman?= <david@hardeman.nu>
|
||||
Date: Sun, 23 Nov 2025 14:50:00 +0100
|
||||
Subject: config: fix realloc() handling for the "upstream" option
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
Deal properly with realloc() failure.
|
||||
|
||||
Signed-off-by: David Härdeman <david@hardeman.nu>
|
||||
Link: https://github.com/openwrt/odhcpd/pull/320
|
||||
Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
|
||||
(cherry picked from commit adb2b8a1afffd805753dcc2e52c4764028116676)
|
||||
---
|
||||
src/config.c | 7 ++++---
|
||||
1 file changed, 4 insertions(+), 3 deletions(-)
|
||||
|
||||
--- a/src/config.c
|
||||
+++ b/src/config.c
|
||||
@@ -1062,16 +1062,17 @@ int config_parse_interface(void *data, s
|
||||
if (overwrite && (c = tb[IFACE_ATTR_UPSTREAM])) {
|
||||
struct blob_attr *cur;
|
||||
unsigned rem;
|
||||
+ char *tmp;
|
||||
|
||||
blobmsg_for_each_attr(cur, c, rem) {
|
||||
if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING || !blobmsg_check_attr(cur, false))
|
||||
continue;
|
||||
|
||||
- iface->upstream = realloc(iface->upstream,
|
||||
- iface->upstream_len + blobmsg_data_len(cur));
|
||||
- if (!iface->upstream)
|
||||
+ tmp = realloc(iface->upstream, iface->upstream_len + blobmsg_data_len(cur));
|
||||
+ if (!tmp)
|
||||
goto err;
|
||||
|
||||
+ iface->upstream = tmp;
|
||||
memcpy(iface->upstream + iface->upstream_len, blobmsg_get_string(cur), blobmsg_data_len(cur));
|
||||
iface->upstream_len += blobmsg_data_len(cur);
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
From 7474f1b5fb05705d9f04ccd45eeeea995ee9f09a Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?David=20H=C3=A4rdeman?= <david@hardeman.nu>
|
||||
Date: Sun, 23 Nov 2025 14:53:35 +0100
|
||||
Subject: config: fix realloc() error handling for "dhcpv6_raw" option
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
Check the return value from realloc() to avoid leaking memory.
|
||||
|
||||
Signed-off-by: David Härdeman <david@hardeman.nu>
|
||||
Link: https://github.com/openwrt/odhcpd/pull/320
|
||||
Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
|
||||
(cherry picked from commit 38b0920e376b236ea0b56686d08a4ef49b4bfcbe)
|
||||
---
|
||||
src/config.c | 11 +++++++++--
|
||||
1 file changed, 9 insertions(+), 2 deletions(-)
|
||||
|
||||
--- a/src/config.c
|
||||
+++ b/src/config.c
|
||||
@@ -1241,8 +1241,15 @@ int config_parse_interface(void *data, s
|
||||
iface->dhcpv4_forcereconf = blobmsg_get_bool(c);
|
||||
|
||||
if ((c = tb[IFACE_ATTR_DHCPV6_RAW])) {
|
||||
- iface->dhcpv6_raw_len = blobmsg_data_len(c) / 2;
|
||||
- iface->dhcpv6_raw = realloc(iface->dhcpv6_raw, iface->dhcpv6_raw_len);
|
||||
+ void *tmp;
|
||||
+ size_t opt_len = blobmsg_data_len(c) / 2;
|
||||
+
|
||||
+ tmp = realloc(iface->dhcpv6_raw, opt_len);
|
||||
+ if (!tmp)
|
||||
+ goto err;
|
||||
+
|
||||
+ iface->dhcpv6_raw = tmp;
|
||||
+ iface->dhcpv6_raw_len = opt_len;
|
||||
odhcpd_unhexlify(iface->dhcpv6_raw, iface->dhcpv6_raw_len, blobmsg_get_string(c));
|
||||
}
|
||||
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
From 4e3aeeb71ba64f5fafff779e08e902510a4baff9 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?David=20H=C3=A4rdeman?= <david@hardeman.nu>
|
||||
Date: Sun, 23 Nov 2025 15:00:45 +0100
|
||||
Subject: config: handle realloc failure in piofolder parsing
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
Another realloc that goes unchecked.
|
||||
|
||||
Signed-off-by: David Härdeman <david@hardeman.nu>
|
||||
Link: https://github.com/openwrt/odhcpd/pull/320
|
||||
Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
|
||||
(cherry picked from commit 2e1042c6c07faad22bc300d1c526b24089b1ced9)
|
||||
---
|
||||
src/config.c | 6 +++++-
|
||||
1 file changed, 5 insertions(+), 1 deletion(-)
|
||||
|
||||
--- a/src/config.c
|
||||
+++ b/src/config.c
|
||||
@@ -1893,7 +1893,11 @@ void config_load_ra_pio(struct interface
|
||||
free(iface->pios);
|
||||
iface->pios = NULL;
|
||||
} else if (iface->pio_cnt != pio_cnt) {
|
||||
- iface->pios = realloc(iface->pios, sizeof(struct ra_pio) * iface->pio_cnt);
|
||||
+ struct ra_pio *tmp;
|
||||
+
|
||||
+ tmp = realloc(iface->pios, sizeof(struct ra_pio) * iface->pio_cnt);
|
||||
+ if (tmp)
|
||||
+ iface->pios = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
From 6bfc8547642eb61c755b36bbcc6616f742832aff Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?David=20H=C3=A4rdeman?= <david@hardeman.nu>
|
||||
Date: Sun, 23 Nov 2025 15:12:01 +0100
|
||||
Subject: dhcpv6: handle realloc failure
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
Another realloc failure case.
|
||||
|
||||
Signed-off-by: David Härdeman <david@hardeman.nu>
|
||||
Link: https://github.com/openwrt/odhcpd/pull/320
|
||||
Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
|
||||
(cherry picked from commit b9db4d7061a08bf82a25222074065cce71973d0c)
|
||||
---
|
||||
src/dhcpv6-ia.c | 5 +++--
|
||||
1 file changed, 3 insertions(+), 2 deletions(-)
|
||||
|
||||
--- a/src/dhcpv6-ia.c
|
||||
+++ b/src/dhcpv6-ia.c
|
||||
@@ -1636,8 +1636,9 @@ ssize_t dhcpv6_ia_handle_IAs(uint8_t *bu
|
||||
hdr->msg_type == DHCPV6_MSG_REQUEST ||
|
||||
hdr->msg_type == DHCPV6_MSG_REBIND)) {
|
||||
if ((!(a->flags & OAF_STATIC) || !a->hostname) && hostname_len > 0) {
|
||||
- a->hostname = realloc(a->hostname, hostname_len + 1);
|
||||
- if (a->hostname) {
|
||||
+ char *hostname = realloc(a->hostname, hostname_len + 1);
|
||||
+ if (hostname) {
|
||||
+ a->hostname = hostname;
|
||||
memcpy(a->hostname, hostname, hostname_len);
|
||||
a->hostname[hostname_len] = 0;
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
From 4ff35533494ea5b531540a69e90ede1124eaede7 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?David=20H=C3=A4rdeman?= <david@hardeman.nu>
|
||||
Date: Thu, 27 Nov 2025 11:35:09 +0100
|
||||
Subject: dhcpv6-ia: fix realloc bug
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
Commit b9db4d7061a08bf82a25222074065cce71973d0c introduced a bug, the
|
||||
"hostname" variable used for the realloc would shadow the real hostname
|
||||
defined at the beginning of the function. Fix this by using a different
|
||||
variable name.
|
||||
|
||||
Fixes: b9db4d7061a0 ("dhcpv6: handle realloc failure")
|
||||
Signed-off-by: David Härdeman <david@hardeman.nu>
|
||||
Link: https://github.com/openwrt/odhcpd/pull/326
|
||||
Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
|
||||
(cherry picked from commit d21e504b38ab4c880c43b7f1649104bb2f0d2d8b)
|
||||
---
|
||||
src/dhcpv6-ia.c | 6 +++---
|
||||
1 file changed, 3 insertions(+), 3 deletions(-)
|
||||
|
||||
--- a/src/dhcpv6-ia.c
|
||||
+++ b/src/dhcpv6-ia.c
|
||||
@@ -1636,9 +1636,9 @@ ssize_t dhcpv6_ia_handle_IAs(uint8_t *bu
|
||||
hdr->msg_type == DHCPV6_MSG_REQUEST ||
|
||||
hdr->msg_type == DHCPV6_MSG_REBIND)) {
|
||||
if ((!(a->flags & OAF_STATIC) || !a->hostname) && hostname_len > 0) {
|
||||
- char *hostname = realloc(a->hostname, hostname_len + 1);
|
||||
- if (hostname) {
|
||||
- a->hostname = hostname;
|
||||
+ char *tmp = realloc(a->hostname, hostname_len + 1);
|
||||
+ if (tmp) {
|
||||
+ a->hostname = tmp;
|
||||
memcpy(a->hostname, hostname, hostname_len);
|
||||
a->hostname[hostname_len] = 0;
|
||||
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
From 67ef12f1fbd69915ed72fbd030afe70ed08c96f8 Mon Sep 17 00:00:00 2001
|
||||
From: Alexander Couzens <lynxis@fe80.eu>
|
||||
Date: Fri, 6 Feb 2026 14:15:03 +0100
|
||||
Subject: dhcpv6-ia: dhcpv6_log_ia_addr(): parse return of snprintf
|
||||
|
||||
snprintf() returns also in the failure case how many characters would have been required
|
||||
to print the full string.
|
||||
Check return of snprintf() and ensure the log_ctxt->buf_idx never points
|
||||
outside of the given log_ctxt->buf.
|
||||
|
||||
Fixes: TOB-OWRT-5
|
||||
Reported-by: Trail of Bits
|
||||
Signed-off-by: Alexander Couzens <lynxis@fe80.eu>
|
||||
(cherry picked from commit 13cec018c0d9bd452f4cb5e583c52fdf206cdc31)
|
||||
---
|
||||
src/dhcpv6-ia.c | 15 +++++++++++++--
|
||||
1 file changed, 13 insertions(+), 2 deletions(-)
|
||||
|
||||
--- a/src/dhcpv6-ia.c
|
||||
+++ b/src/dhcpv6-ia.c
|
||||
@@ -1266,6 +1266,7 @@ static size_t build_ia(uint8_t *buf, siz
|
||||
struct log_ctxt {
|
||||
char *buf;
|
||||
int buf_len;
|
||||
+ /* if full, buf_idx will point to the last valid memory in buf */
|
||||
int buf_idx;
|
||||
};
|
||||
|
||||
@@ -1274,10 +1275,20 @@ static void dhcpv6_log_ia_addr(struct in
|
||||
{
|
||||
struct log_ctxt *ctxt = (struct log_ctxt *)arg;
|
||||
char addrbuf[INET6_ADDRSTRLEN];
|
||||
+ int ret;
|
||||
+
|
||||
+ /* Log buffer full */
|
||||
+ if (ctxt->buf_idx >= ctxt->buf_len - 1)
|
||||
+ return;
|
||||
|
||||
inet_ntop(AF_INET6, addr, addrbuf, sizeof(addrbuf));
|
||||
- ctxt->buf_idx += snprintf(ctxt->buf + ctxt->buf_idx, ctxt->buf_len - ctxt->buf_idx,
|
||||
- "%s/%d ", addrbuf, prefix);
|
||||
+ ret = snprintf(ctxt->buf + ctxt->buf_idx, ctxt->buf_len - ctxt->buf_idx,
|
||||
+ "%s/%d ", addrbuf, prefix);
|
||||
+
|
||||
+ if (ret + ctxt->buf_idx < ctxt->buf_len - 1)
|
||||
+ ctxt->buf_idx += ret;
|
||||
+ else
|
||||
+ ctxt->buf_idx = ctxt->buf_len - 1;
|
||||
}
|
||||
|
||||
static void dhcpv6_log(uint8_t msgtype, struct interface *iface, time_t now,
|
||||
+120
@@ -0,0 +1,120 @@
|
||||
From 3c3223c0aadb8a3949f7cd4050a911a65c0abd70 Mon Sep 17 00:00:00 2001
|
||||
From: Hauke Mehrtens <hauke@hauke-m.de>
|
||||
Date: Sat, 23 May 2026 14:06:55 +0200
|
||||
Subject: dhcpv6: bound nested-relay recursion to HOP_COUNT_LIMIT
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
handle_nested_message() and update_nested_message() recurse once for
|
||||
every OPTION_RELAY_MSG inside a Relay-Forward message, with no depth
|
||||
limit. A crafted UDP datagram packed full of nested Relay-Forward
|
||||
options can drive recursion ~1700 deep on a ~64KB packet and exhaust
|
||||
the daemon's stack.
|
||||
|
||||
Each nested Relay-Forward layer is exactly one relay hop: a relay agent
|
||||
wraps the message it received in a new Relay-Forward and increments
|
||||
hop_count. So the recursion depth here is just the relay-chain length
|
||||
seen structurally. RFC8415 bounds that chain via HOP_COUNT_LIMIT
|
||||
(defined in §7.6; a relay discards an over-limit Relay-forward per
|
||||
§19.1.2), so a conforming message can never nest deeper than
|
||||
HOP_COUNT_LIMIT layers.
|
||||
|
||||
Cap the descent at DHCPV6_HOP_COUNT_LIMIT accordingly. We count the
|
||||
actual recursion depth instead of trusting each header's on-wire
|
||||
hop_count, because an attacker controls the nesting and every per-layer
|
||||
hop_count independently (e.g. nest deeply while stamping hop_count=0
|
||||
everywhere). This is the same limit relay_client_request() already
|
||||
enforces on the outer header of a forwarded message.
|
||||
|
||||
Note: DHCPV6_HOP_COUNT_LIMIT is 32, the value from the obsoleted
|
||||
RFC3315 §5.5; RFC8415 §7.6 lowered the default to 8. Either way 32 is a
|
||||
safe structural ceiling that drops only pathologically nested messages,
|
||||
never a legitimate relay chain.
|
||||
|
||||
Assisted-by: Claude:claude-opus-4-7
|
||||
Link: https://github.com/openwrt/odhcpd/pull/401
|
||||
Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
|
||||
(cherry picked from commit ee0a11f40fa48302482fd6e85458e26d48de6e3a)
|
||||
---
|
||||
src/dhcpv6.c | 23 +++++++++++++++++------
|
||||
1 file changed, 17 insertions(+), 6 deletions(-)
|
||||
|
||||
--- a/src/dhcpv6.c
|
||||
+++ b/src/dhcpv6.c
|
||||
@@ -188,7 +188,7 @@ enum {
|
||||
IOV_TOTAL
|
||||
};
|
||||
|
||||
-static void handle_nested_message(uint8_t *data, size_t len,
|
||||
+static void handle_nested_message(uint8_t *data, size_t len, unsigned depth,
|
||||
struct dhcpv6_client_header **c_hdr, uint8_t **opts,
|
||||
uint8_t **end, struct iovec iov[IOV_TOTAL])
|
||||
{
|
||||
@@ -212,19 +212,26 @@ static void handle_nested_message(uint8_
|
||||
return;
|
||||
}
|
||||
|
||||
+ /* Each nested Relay-Forward is one relay hop; RFC8415 bounds a relay
|
||||
+ * chain via HOP_COUNT_LIMIT (defined in §7.6, enforced in §19.1.2),
|
||||
+ * so refuse to recurse deeper and avoid stack exhaustion on crafted
|
||||
+ * relay loops. */
|
||||
+ if (depth >= DHCPV6_HOP_COUNT_LIMIT)
|
||||
+ return;
|
||||
+
|
||||
dhcpv6_for_each_option(r_hdr->options, data + len, otype, olen, odata) {
|
||||
if (otype == DHCPV6_OPT_RELAY_MSG) {
|
||||
iov[IOV_RELAY_MSG].iov_base = odata + olen;
|
||||
iov[IOV_RELAY_MSG].iov_len = (((uint8_t *)iov[IOV_NESTED].iov_base) +
|
||||
iov[IOV_NESTED].iov_len) - (odata + olen);
|
||||
- handle_nested_message(odata, olen, c_hdr, opts, end, iov);
|
||||
+ handle_nested_message(odata, olen, depth + 1, c_hdr, opts, end, iov);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
-static void update_nested_message(uint8_t *data, size_t len, ssize_t pdiff)
|
||||
+static void update_nested_message(uint8_t *data, size_t len, unsigned depth, ssize_t pdiff)
|
||||
{
|
||||
struct dhcpv6_relay_header *hdr = (struct dhcpv6_relay_header*)data;
|
||||
if (hdr->msg_type != DHCPV6_MSG_RELAY_FORW)
|
||||
@@ -232,6 +239,10 @@ static void update_nested_message(uint8_
|
||||
|
||||
hdr->msg_type = DHCPV6_MSG_RELAY_REPL;
|
||||
|
||||
+ /* Bound recursion to mirror handle_nested_message(). */
|
||||
+ if (depth >= DHCPV6_HOP_COUNT_LIMIT)
|
||||
+ return;
|
||||
+
|
||||
uint16_t otype, olen;
|
||||
uint8_t *odata;
|
||||
dhcpv6_for_each_option(hdr->options, data + len, otype, olen, odata) {
|
||||
@@ -239,7 +250,7 @@ static void update_nested_message(uint8_
|
||||
olen += pdiff;
|
||||
odata[-2] = (olen >> 8) & 0xff;
|
||||
odata[-1] = olen & 0xff;
|
||||
- update_nested_message(odata, olen - pdiff, pdiff);
|
||||
+ update_nested_message(odata, olen - pdiff, depth + 1, pdiff);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -564,7 +575,7 @@ static void handle_client_request(void *
|
||||
};
|
||||
|
||||
if (hdr->msg_type == DHCPV6_MSG_RELAY_FORW)
|
||||
- handle_nested_message(data, len, &hdr, &opts, &opts_end, iov);
|
||||
+ handle_nested_message(data, len, 0, &hdr, &opts, &opts_end, iov);
|
||||
|
||||
switch (hdr->msg_type) {
|
||||
case DHCPV6_MSG_SOLICIT:
|
||||
@@ -730,7 +741,7 @@ static void handle_client_request(void *
|
||||
}
|
||||
|
||||
if (iov[IOV_NESTED].iov_len > 0) /* Update length */
|
||||
- update_nested_message(data, len, iov[IOV_DEST].iov_len + iov[IOV_MAXRT].iov_len +
|
||||
+ update_nested_message(data, len, 0, iov[IOV_DEST].iov_len + iov[IOV_MAXRT].iov_len +
|
||||
iov[IOV_RAPID_COMMIT].iov_len + iov[IOV_DNS].iov_len +
|
||||
iov[IOV_DNS_ADDR].iov_len + iov[IOV_SEARCH].iov_len +
|
||||
iov[IOV_SEARCH_DOMAIN].iov_len + iov[IOV_PDBUF].iov_len +
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
From d1e66d0afb8ed749bee242c5ad9f322a0a9130e0 Mon Sep 17 00:00:00 2001
|
||||
From: Hauke Mehrtens <hauke@hauke-m.de>
|
||||
Date: Sat, 23 May 2026 14:09:09 +0200
|
||||
Subject: dhcpv4: honor Pad/End option encoding when iterating options
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
The dhcpv4_for_each_option() macro always read a length byte at offset 1
|
||||
and advanced by len+2, which is wrong for the Pad (0) and End (255)
|
||||
options. Per RFC2132 §3.1/§3.2 / RFC1497 those two options are a single octet
|
||||
with no length byte, so the parser was effectively interpreting the
|
||||
byte after a Pad as a length and skipping a variable, attacker-chosen
|
||||
number of bytes — letting a crafted request hide later options (e.g.
|
||||
DHCP message type, client-id) from the server. End was likewise
|
||||
treated as a long-with-length option instead of terminating the scan.
|
||||
|
||||
Rewrite the macro to advance by 1 on Pad, stop on End, and otherwise
|
||||
validate that code+len+data all fit in the buffer.
|
||||
|
||||
Assisted-by: Claude:claude-opus-4-7
|
||||
Link: https://github.com/openwrt/odhcpd/pull/401
|
||||
Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
|
||||
(cherry picked from commit db034cfd5eab2d0f3315cfb3cd74002fefa15828)
|
||||
---
|
||||
src/dhcpv4.h | 17 +++++++++++++----
|
||||
1 file changed, 13 insertions(+), 4 deletions(-)
|
||||
|
||||
--- a/src/dhcpv4.h
|
||||
+++ b/src/dhcpv4.h
|
||||
@@ -98,8 +98,17 @@ struct dhcpv4_option {
|
||||
};
|
||||
|
||||
|
||||
+/* RFC2132 §3.1/§3.2 (orig. RFC1497): the Pad (0) and End (255) options are
|
||||
+ * 1 octet long and have no length byte. Every other DHCPv4 option is
|
||||
+ * { code, len, data[len] }. Treat Pad as a 1-byte no-op, End as loop
|
||||
+ * termination, and reject any other option whose declared length runs past
|
||||
+ * the buffer.
|
||||
+ */
|
||||
#define dhcpv4_for_each_option(start, end, opt)\
|
||||
- for (opt = (struct dhcpv4_option*)(start); \
|
||||
- &opt[1] <= (struct dhcpv4_option*)(end) && \
|
||||
- &opt->data[opt->len] <= (end); \
|
||||
- opt = (struct dhcpv4_option*)&opt->data[opt->len])
|
||||
+ for (uint8_t *_o = (uint8_t *)(start); \
|
||||
+ _o < (uint8_t *)(end) && \
|
||||
+ (opt = (struct dhcpv4_option *)_o)->type != DHCPV4_OPT_END && \
|
||||
+ (opt->type == DHCPV4_OPT_PAD || \
|
||||
+ (_o + 2 <= (uint8_t *)(end) && \
|
||||
+ _o + 2 + opt->len <= (uint8_t *)(end))); \
|
||||
+ _o += (opt->type == DHCPV4_OPT_PAD) ? 1 : 2 + opt->len)
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
From 4100329ec73d4767602ca2c507ec1e1c1264752f Mon Sep 17 00:00:00 2001
|
||||
From: Hauke Mehrtens <hauke@hauke-m.de>
|
||||
Date: Sat, 23 May 2026 14:17:55 +0200
|
||||
Subject: dhcpv6-ia: avoid undefined shifts in assign_pd()
|
||||
|
||||
assign_pd() computes (1 << (64 - length)) where the literal is a plain
|
||||
int. When the prefix length is <= 32 the shift count is >= 32 bits,
|
||||
which is undefined behaviour, and even at length == 33 the result
|
||||
(1 << 31) is signed-overflow UB.
|
||||
|
||||
In normal operation the delegated prefix length is bounded by
|
||||
dhcpv6_pd_min_len whose default keeps the shift in safe territory, but
|
||||
that knob is user-configurable down to 1 in UCI, so a misconfigured
|
||||
interface trips UB on every PD assignment. Compute the shift in
|
||||
uint64_t and truncate explicitly to uint32_t so the arithmetic is
|
||||
well-defined; for sane prefix lengths (>=33) the value is unchanged.
|
||||
|
||||
Assisted-by: Claude:claude-opus-4-7
|
||||
Link: https://github.com/openwrt/odhcpd/pull/401
|
||||
Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
|
||||
(cherry picked from commit 19bf3b73580b50030490c04e44adb6a14fe8c31c)
|
||||
---
|
||||
src/dhcpv6-ia.c | 14 ++++++++++----
|
||||
1 file changed, 10 insertions(+), 4 deletions(-)
|
||||
|
||||
--- a/src/dhcpv6-ia.c
|
||||
+++ b/src/dhcpv6-ia.c
|
||||
@@ -769,8 +769,14 @@ static bool assign_pd(struct interface *
|
||||
} else if (iface->addr6_len < 1)
|
||||
return false;
|
||||
|
||||
- /* Try honoring the hint first */
|
||||
- uint32_t current = 1, asize = (1 << (64 - assign->length)) - 1;
|
||||
+ /* Try honoring the hint first.
|
||||
+ *
|
||||
+ * Subnet-id slots per delegated prefix = 2^(64 - length); the literal
|
||||
+ * 1 is plain int, so shifting it by 32-63 bits is undefined behaviour
|
||||
+ * for short prefix lengths (the user can drive this via
|
||||
+ * dhcpv6_pd_min_len). Compute the shift in uint64_t and truncate.
|
||||
+ */
|
||||
+ uint32_t current = 1, asize = (uint32_t)((1ULL << (64 - assign->length)) - 1);
|
||||
if (assign->assigned_subnet_id) {
|
||||
list_for_each_entry(c, &iface->ia_assignments, head) {
|
||||
if (c->flags & OAF_DHCPV6_NA)
|
||||
@@ -785,7 +791,7 @@ static bool assign_pd(struct interface *
|
||||
return true;
|
||||
}
|
||||
|
||||
- current = (c->assigned_subnet_id + (1 << (64 - c->length)));
|
||||
+ current = (uint32_t)(c->assigned_subnet_id + (1ULL << (64 - c->length)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -807,7 +813,7 @@ static bool assign_pd(struct interface *
|
||||
return true;
|
||||
}
|
||||
|
||||
- current = (c->assigned_subnet_id + (1 << (64 - c->length)));
|
||||
+ current = (uint32_t)(c->assigned_subnet_id + (1ULL << (64 - c->length)));
|
||||
}
|
||||
|
||||
return false;
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
From e587d403b689c04141cfeac638c68e898c5b565e Mon Sep 17 00:00:00 2001
|
||||
From: Hauke Mehrtens <hauke@hauke-m.de>
|
||||
Date: Sat, 23 May 2026 14:21:10 +0200
|
||||
Subject: =?UTF-8?q?ndp:=20enforce=20RFC4861=20=C2=A77.1.1=20hop-limit=20an?=
|
||||
=?UTF-8?q?d=20ICMP-code=20checks=20on=20NS?=
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
handle_solicit() previously trusted whatever the AF_PACKET socket
|
||||
delivered, so a Neighbor Solicitation with Hop Limit < 255 or a
|
||||
non-zero ICMP Code was forwarded to peer interfaces (triggering proxy
|
||||
NDP lookups and an NA on the requesting interface). RFC4861 §7.1.1
|
||||
requires NS messages with those properties to be silently discarded —
|
||||
the hop-limit rule in particular is what prevents off-link attackers
|
||||
from forging NS / DAD packets onto our link.
|
||||
|
||||
The router.c equivalent (router_icmpv6_valid + the IPV6_RECVHOPLIMIT
|
||||
cmsg check in odhcpd_receive_packets) already does this for RA/RS;
|
||||
the NDP path needs to enforce it itself because AF_PACKET sees raw
|
||||
frames with no kernel-side L3 validation.
|
||||
|
||||
Assisted-by: Claude:claude-opus-4-7
|
||||
Link: https://github.com/openwrt/odhcpd/pull/401
|
||||
Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
|
||||
(cherry picked from commit 0eade3d4b7a37106f6498f4ce952ecd6ec1b932d)
|
||||
---
|
||||
src/ndp.c | 9 +++++++++
|
||||
1 file changed, 9 insertions(+)
|
||||
|
||||
--- a/src/ndp.c
|
||||
+++ b/src/ndp.c
|
||||
@@ -382,6 +382,15 @@ static void handle_solicit(void *addr, v
|
||||
if (len < sizeof(*ip6) + sizeof(*req))
|
||||
return; // Invalid total length
|
||||
|
||||
+ /* RFC4861 §7.1.1: a Neighbor Solicitation MUST be silently discarded
|
||||
+ * if the IP Hop Limit field is not 255 or the ICMP Code is non-zero.
|
||||
+ * The hop-limit check is what protects against off-link attackers
|
||||
+ * forging NS / DAD messages, so it must not be skipped. Because the
|
||||
+ * NDP socket is AF_PACKET (not a kernel-managed ICMPv6 socket), we
|
||||
+ * have to enforce it ourselves. */
|
||||
+ if (ip6->ip6_hlim != 255 || req->nd_ns_hdr.icmp6_code != 0)
|
||||
+ return;
|
||||
+
|
||||
if (IN6_IS_ADDR_LINKLOCAL(&req->nd_ns_target) ||
|
||||
IN6_IS_ADDR_LOOPBACK(&req->nd_ns_target) ||
|
||||
IN6_IS_ADDR_MULTICAST(&req->nd_ns_target))
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
From 82dca288b46f1cb5c6f5eaffb2080ebca4a3d651 Mon Sep 17 00:00:00 2001
|
||||
From: Hauke Mehrtens <hauke@hauke-m.de>
|
||||
Date: Sat, 30 May 2026 01:29:46 +0200
|
||||
Subject: dhcpv6: validate minimum length in relay_client_request()
|
||||
|
||||
relay_server_response() refuses packets shorter than the relay header,
|
||||
but the client-side relay path skipped any length check. With UDP
|
||||
permitting zero/short payloads, a 0- or 1-byte datagram would let the
|
||||
switch read h->msg_type from data[0] and (if it happened to equal
|
||||
DHCPV6_MSG_RELAY_FORW = 12) h->hop_count from data[1] out of bounds.
|
||||
The bytes come from the kernel receive buffer so it is a contained UB
|
||||
read, but it is reachable.
|
||||
|
||||
relay_client_request() casts data to a struct dhcpv6_relay_header and
|
||||
reads only h->msg_type (offset 0) and, for a RELAY_FORW, h->hop_count
|
||||
(offset 1); the payload is forwarded verbatim and never parsed here.
|
||||
Bound len on offsetof(struct dhcpv6_relay_header, link_address) so the
|
||||
guard names the struct the function actually uses and the exact extent
|
||||
it reads, mirroring how relay_server_response() bounds on its own
|
||||
header.
|
||||
|
||||
Assisted-by: Claude:claude-opus-4-7
|
||||
Link: https://github.com/openwrt/odhcpd/pull/401
|
||||
Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
|
||||
(cherry picked from commit cc04882b3fac7643921a194ab65650274a69b2d6)
|
||||
---
|
||||
src/dhcpv6.c | 8 ++++++++
|
||||
1 file changed, 8 insertions(+)
|
||||
|
||||
--- a/src/dhcpv6.c
|
||||
+++ b/src/dhcpv6.c
|
||||
@@ -902,6 +902,14 @@ static void relay_client_request(struct
|
||||
struct odhcpd_ipaddr *ip;
|
||||
struct sockaddr_in6 s;
|
||||
|
||||
+ /* A bare UDP socket can deliver a zero/short payload; the relay-reply
|
||||
+ * path (relay_server_response) checks this but the client-side relay
|
||||
+ * did not. relay_client_request() reads h->msg_type, plus h->hop_count
|
||||
+ * for a RELAY_FORW, out of the relay header and forwards the rest
|
||||
+ * verbatim, so require at least those two leading header bytes. */
|
||||
+ if (len < offsetof(struct dhcpv6_relay_header, link_address))
|
||||
+ return;
|
||||
+
|
||||
if (h->msg_type == DHCPV6_MSG_RELAY_REPL ||
|
||||
h->msg_type == DHCPV6_MSG_RECONFIGURE ||
|
||||
h->msg_type == DHCPV6_MSG_REPLY ||
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
From 60185a154c404004730c3136cc4cb63c8d61c978 Mon Sep 17 00:00:00 2001
|
||||
From: Hauke Mehrtens <hauke@hauke-m.de>
|
||||
Date: Sat, 11 Jul 2026 22:15:32 +0200
|
||||
Subject: dhcpv4: copy ifname into arpreq without reading past the source
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
dhcpv4_set_dest_addr() filled arp.arp_dev (a fixed 16-byte field) via
|
||||
memcpy(..., iface->ifname, sizeof(arp.arp_dev)). iface->ifname is a
|
||||
strdup'd string whose allocation is just strlen(ifname)+1 bytes — for
|
||||
typical names like "eth0" or "br-lan" that is 5–7 bytes, so the memcpy
|
||||
reads ~10 bytes past the heap allocation. The bytes happen to be
|
||||
inside the same malloc chunk on glibc so it has never blown up in
|
||||
practice, but it is undefined behaviour and would trip valgrind / ASan.
|
||||
|
||||
Switch to the strncpy form already used for ifr_name elsewhere; the
|
||||
struct is zero-initialised so the trailing byte stays NUL.
|
||||
|
||||
Assisted-by: Claude:claude-opus-4-7
|
||||
Link: https://github.com/openwrt/odhcpd/pull/401
|
||||
Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
|
||||
(cherry picked from commit 7c834a54ae6da732edaf2e8021fa3681e69951a3)
|
||||
---
|
||||
src/dhcpv4.c | 6 +++++-
|
||||
1 file changed, 5 insertions(+), 1 deletion(-)
|
||||
|
||||
--- a/src/dhcpv4.c
|
||||
+++ b/src/dhcpv4.c
|
||||
@@ -971,7 +971,11 @@ void dhcpv4_handle_msg(void *addr, void
|
||||
if (!(iface->ifflags & IFF_NOARP)) {
|
||||
memcpy(arp.arp_ha.sa_data, req->chaddr, 6);
|
||||
memcpy(&arp.arp_pa, &dest, sizeof(arp.arp_pa));
|
||||
- memcpy(arp.arp_dev, iface->ifname, sizeof(arp.arp_dev));
|
||||
+ /* arp_dev is a 16-byte fixed buffer; strdup'd ifname is
|
||||
+ * typically shorter than that, so memcpy()ing the full
|
||||
+ * field length reads past the allocation. Match the
|
||||
+ * strncpy pattern used elsewhere for ifr_name. */
|
||||
+ strncpy(arp.arp_dev, iface->ifname, sizeof(arp.arp_dev) - 1);
|
||||
|
||||
if (ioctl(sock, SIOCSARP, &arp) < 0)
|
||||
syslog(LOG_ERR, "ioctl(SIOCSARP): %m");
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
From 5669df2f672f093933dc67de549b765206ae6523 Mon Sep 17 00:00:00 2001
|
||||
From: Hauke Mehrtens <hauke@hauke-m.de>
|
||||
Date: Sat, 23 May 2026 14:39:03 +0200
|
||||
Subject: dhcpv6: avoid unaligned uint16_t reads in ORO option parsing
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
The Option Request Option payload is an array of 16-bit codes, but the
|
||||
buffer (odata) is at an arbitrary offset inside the wire packet — on
|
||||
strict-alignment platforms (ARM v5/v6, MIPS, SPARC) reading it through
|
||||
a uint16_t * pointer triggers a misaligned access, which the kernel
|
||||
either fixes up at high cost or signals as SIGBUS. The same pattern
|
||||
appeared in the top-level ORO scan and again in the inner per-option
|
||||
loop. Use byte-pointer access plus memcpy() to extract each code
|
||||
portably.
|
||||
|
||||
Assisted-by: Claude:claude-opus-4-7
|
||||
Link: https://github.com/openwrt/odhcpd/pull/401
|
||||
Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
|
||||
(cherry picked from commit fa270221504c32861a9457dfa815b4215eaabbb0)
|
||||
---
|
||||
src/dhcpv6.c | 21 ++++++++++++++++-----
|
||||
1 file changed, 16 insertions(+), 5 deletions(-)
|
||||
|
||||
--- a/src/dhcpv6.c
|
||||
+++ b/src/dhcpv6.c
|
||||
@@ -424,7 +424,11 @@ static void handle_client_request(void *
|
||||
|
||||
uint16_t otype, olen;
|
||||
uint8_t *odata;
|
||||
- uint16_t *reqopts = NULL;
|
||||
+ /* OPTION_ORO payload is an array of uint16_t but the underlying buffer
|
||||
+ * isn't guaranteed to be 2-byte aligned (it's at an arbitrary offset
|
||||
+ * inside the packed wire packet), so keep it as a byte pointer and
|
||||
+ * memcpy each value out rather than casting to uint16_t *. */
|
||||
+ uint8_t *reqopts = NULL;
|
||||
size_t reqopts_cnt = 0;
|
||||
|
||||
/* FIXME: this should be merged with the second loop further down */
|
||||
@@ -432,14 +436,17 @@ static void handle_client_request(void *
|
||||
/* Requested options, array of uint16_t, RFC 8415 §21.7 */
|
||||
if (otype == DHCPV6_OPT_ORO) {
|
||||
reqopts_cnt = olen / sizeof(uint16_t);
|
||||
- reqopts = (uint16_t *)odata;
|
||||
+ reqopts = odata;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Requested options */
|
||||
for (size_t i = 0; i < reqopts_cnt; i++) {
|
||||
- uint16_t opt = ntohs(reqopts[i]);
|
||||
+ uint16_t opt;
|
||||
+
|
||||
+ memcpy(&opt, &reqopts[i * sizeof(uint16_t)], sizeof(opt));
|
||||
+ opt = ntohs(opt);
|
||||
|
||||
switch (opt) {
|
||||
case DHCPV6_OPT_SNTP_SERVERS:
|
||||
@@ -647,8 +654,12 @@ static void handle_client_request(void *
|
||||
iov[IOV_RAPID_COMMIT].iov_len = sizeof(rapid_commit);
|
||||
o_rapid_commit = true;
|
||||
} else if (otype == DHCPV6_OPT_ORO) {
|
||||
- for (int i=0; i < olen/2; i++) {
|
||||
- uint16_t option = ntohs(((uint16_t *)odata)[i]);
|
||||
+ for (int i = 0; i < olen / 2; i++) {
|
||||
+ uint16_t option;
|
||||
+
|
||||
+ /* odata is not guaranteed to be uint16-aligned. */
|
||||
+ memcpy(&option, &odata[i * 2], sizeof(option));
|
||||
+ option = ntohs(option);
|
||||
|
||||
switch (option) {
|
||||
#ifdef DHCPV4_SUPPORT
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user