From 7c07e1634ed4a07ba5c3024d60e65e56ce3f0e18 Mon Sep 17 00:00:00 2001 From: Sander Vanheule Date: Sat, 11 Jul 2020 23:06:54 +0200 Subject: [PATCH] firmware-utils/tplink-safeloader: add compat level TP-Link has introduced a compatibility level to prevent certain downgrades. This information is stored in the soft-version partition, changing the data length from 0xc to 0x10. The compatibility level doesn't change frequently. For example, it has the following values for the EAP245v3 (released 2018-Q4): * FW v2.2.0 (2019-05-30): compat_level=0 * FW v2.3.0 (2019-07-31): compat_level=0 * FW v2.3.1 (2019-10-29): compat_level=1 * FW v2.20.0 (2020-04-23): compat_level=1 Empty flash values (0xffffffff) are interpreted as compat_level=0. If a firmware upgrade file has a soft-version block without compatibility level (data length < 0x10), this is also interpreted as compat_level=0. By including a high enough compatibility level in factory images, stock firmware can be convinced to accept the image. A compatibility level aware firmware will keep the original value. Example upgrade log of TP-Link EAP245v3 FWv2.3.0 to FWv2.20.0: [NM_Debug](nm_fwup_verifyFwupFile) 02073: curSoftVer:2.3.0 Build 20190731 Rel. 51932,newSoftVer:2.20.0 Build 20200423 Rel. 36779 ... AddiHardwareVer check: NEW(0x1) >= CUR(0x0), Success. ... [NM_NOTICE](updateDataToNvram) 00575: Restore old additionalHardVer: 0x0.(new 0x1) [NM_NOTICE](updateDataToNvram) 00607: PTN 07: name = soft-version, base = 0x00092000, size = 0x00000100 Bytes, upDataType = 1, upDataStart = 7690604b, upDataLen = 00000018 [NM_Debug](updateDataToNvram) 00738: PTN 07: write bytes = 000002eb Other firmware upgrades have been observed to modify the compabitility stored level (e.g. TP-Link EAP225-Outdoor FWv1.4.1 to FWv1.7.0). Therefore, it seems to be the safest option to set the OpenWrt compatibility level to the highest known value instead of the highest possible value (0xfffffffe), to ensure users do not get unexpectedly refused firmware upgrades when using a device reverted back to stock. To remain compatible with existing devices and not produce different images, the image builder doesn't store a compatibility level if it is zero. Signed-off-by: Sander Vanheule --- src/tplink-safeloader.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/tplink-safeloader.c b/src/tplink-safeloader.c index 145e808..9005ffa 100644 --- a/src/tplink-safeloader.c +++ b/src/tplink-safeloader.c @@ -77,6 +77,7 @@ struct device_info { const char *support_list; char support_trail; const char *soft_ver; + uint32_t soft_ver_compat_level; struct flash_partition_entry partitions[MAX_PARTITIONS+1]; const char *first_sysupgrade_partition; const char *last_sysupgrade_partition; @@ -95,7 +96,6 @@ struct __attribute__((__packed__)) soft_version { uint8_t month; uint8_t day; uint32_t rev; - uint8_t pad2; }; @@ -2140,8 +2140,13 @@ static inline uint8_t bcd(uint8_t v) { /** Generates the soft-version partition */ -static struct image_partition_entry make_soft_version(uint32_t rev) { - struct image_partition_entry entry = alloc_image_partition("soft-version", sizeof(struct soft_version)); +static struct image_partition_entry make_soft_version(struct device_info *info, uint32_t rev) { + size_t part_len = sizeof(struct soft_version); + if (info->soft_ver_compat_level > 0) + part_len += sizeof(uint32_t); + + struct image_partition_entry entry = + alloc_image_partition("soft-version", part_len+1); struct soft_version *s = (struct soft_version *)entry.data; time_t t; @@ -2168,7 +2173,11 @@ static struct image_partition_entry make_soft_version(uint32_t rev) { s->day = bcd(tm->tm_mday); s->rev = htonl(rev); - s->pad2 = 0xff; + if (info->soft_ver_compat_level > 0) + *(uint32_t *)(entry.data + sizeof(struct soft_version)) = + htonl(info->soft_ver_compat_level); + + entry.data[entry.size-1] = 0xff; return entry; } @@ -2480,7 +2489,7 @@ static void build_image(const char *output, if (info->soft_ver) parts[1] = make_soft_version_from_string(info->soft_ver); else - parts[1] = make_soft_version(rev); + parts[1] = make_soft_version(info, rev); parts[2] = make_support_list(info); parts[3] = read_file("os-image", kernel_image, false, NULL); -- 2.30.2