diff --git a/README.md b/README.md index 75c99f4..64e271f 100644 --- a/README.md +++ b/README.md @@ -2,20 +2,26 @@ Firmware builder to convert your Unifi NVR/Unifi NVR Pro into an OpenMediaVault NAS appliance. -**This repo is still under heavy development and should be considered early alpha!** +**This repo is still under heavy development and should be considered alpha!** ## Supported Devices * UNVR **(Currently Untested!)** * UNVR Pro +## Disclaimer + +Note that since prebuilt Ubiquiti software is currently required for this firmware, this repo doesn't have prebuilt images available. This is to prevent redistribution of Ubiquiti's IP, so please DO NOT ASK! Also, by using this repo you accept all risk associated with it including but not limited to voiding your warranty and releasing all parties from any liability associated with your device and this software. PROCEED AT YOUR OWN RISK! + ## Usage 1. Download the required UNVR firmware for your device, and place it in the unifi-firmware directory. Please see the README.md in that directory for more information. -2. Make sure your system has the required packages installed for this repo, which are: +2. Make sure your linux system has the required packages installed for this repo, which are: `docker-ce losetup wget sudo make qemu-user-static squashfs-tools` + Note that building from OSX/Windows is not supported. A Linux host is **REQUIRED**. + 3. Run make with your board name set, and sit back and wait for the firmware image to build. Depending on your computer, this may take around an hour or so. For the UNVR: `BOARD=UNVR make` @@ -139,7 +145,3 @@ To restore back to the factory UNVR/UNVR Pro firmware, you can do the following * Need to simplify the install process, this should be much easier once I can get latest GPL kernel source (no more uboot env stuff) * Reset Button * Only works to reboot the system, may wire this up to reset the WebUI password in OpenMediaVault down the road - -## Disclaimer - -Note that since prebuild Ubiquiti software is required for this tool to work, this repo will never have prebuilt images available. This is to prevent redistribution of Ubiquiti's IP, so please DO NOT ASK! Also, by using this repo you accept all risk associated with it including but not limited to voiding your warranty and releasing all parties from any liability associated with your device and this software. diff --git a/overlay/filesystem/usr/bin/ustorage b/overlay/filesystem/usr/bin/ustorage index fae1aaf..486bbcb 100755 --- a/overlay/filesystem/usr/bin/ustorage +++ b/overlay/filesystem/usr/bin/ustorage @@ -125,12 +125,22 @@ class UNVRDiskInfo: def __parse_disk_temp(self): try: - return int( - self.__parse_smartctl( - self.__smartctl_output, - r"^194 [\w-]+\s+0x\d+\s+\d+\s+\d+\s+\d+\s+[\w-]+\s+\w+\s+\S+\s+(\d+)(?:\s[\(][^)]*[\)])?$", + try: + # First try the expected 194 + return int( + self.__parse_smartctl( + self.__smartctl_output, + r"^194 [\w-]+\s+0x\d+\s+\d+\s+\d+\s+\d+\s+[\w-]+\s+\w+\s+\S+\s+(\d+)(?:\s[\(][^)]*[\)])?$", + ) + ) + except: + # Some other SSDs (cough, samsung) use 190 for airflow temp -_- + return int( + self.__parse_smartctl( + self.__smartctl_output, + r"^190 [\w-]+\s+0x\d+\s+\d+\s+\d+\s+\d+\s+[\w-]+\s+\w+\s+\S+\s+(\d+)(?:\s[\(][^)]*[\)])?$", + ) ) - ) except: return None diff --git a/overlay/filesystem/usr/lib/init/boot/ubnt-init.sh b/overlay/filesystem/usr/lib/init/boot/ubnt-init.sh index 032eade..5d2f842 100755 --- a/overlay/filesystem/usr/lib/init/boot/ubnt-init.sh +++ b/overlay/filesystem/usr/lib/init/boot/ubnt-init.sh @@ -3,7 +3,8 @@ case "$1" in start) # Load our kernel modules - /usr/sbin/modprobe ubnthal + # /usr/sbin/modprobe ubnthal # No longer needed, ubnteeprom replaced it in userspace + /usr/sbin/modprobe ubnt-mtd-lock # Force our /dev/mtd* as RO /usr/sbin/modprobe btrfs # Set our kernel panic timeout SUPER short so we reboot on crash diff --git a/overlay/filesystem/usr/lib/init/boot/ubnt-ulcmd.sh b/overlay/filesystem/usr/lib/init/boot/ubnt-ulcmd.sh index 09af405..2b79977 100755 --- a/overlay/filesystem/usr/lib/init/boot/ubnt-ulcmd.sh +++ b/overlay/filesystem/usr/lib/init/boot/ubnt-ulcmd.sh @@ -4,10 +4,12 @@ case "$(ubnteeprom -systeminfo -key shortname)" in "UNVRPRO") + # Ensure our tmp file with info is generated + ubnteeprom -systeminfo > /tmp/.ubnthal_system_info # Is ulcmd running already? if so, assume it was not done via systemd so let's # kill and respawn as this is our systemd entry script for the service, and we # need to have it foregrounded as we act as the "daemon" here. - if ! pidof -q ulcmd; then + if pidof -q ulcmd; then killall ulcmd fi # Restart ulcmd diff --git a/scripts/docker/build_kernel.sh b/scripts/docker/build_kernel.sh index 2dcec3c..c7982ab 100755 --- a/scripts/docker/build_kernel.sh +++ b/scripts/docker/build_kernel.sh @@ -6,10 +6,10 @@ scripts_path="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && cd .. && pwd )" # Make our temp builddir outside of the world of mounts for SPEEDS kernel_builddir=$(mktemp -d) -tar -xzf ${root_path}/downloads/${kernel_filename} -C ${kernel_builddir} +tar -xzf "${root_path}/downloads/${kernel_filename}" -C ${kernel_builddir} # Exports baby -export PATH=${build_path}/toolchain/${toolchain_bin_path}:${PATH} +export PATH="${build_path}/toolchain/${toolchain_bin_path}":${PATH} export GCC_COLORS=auto export CROSS_COMPILE=${toolchain_cross_compile} export ARCH=arm64 @@ -18,7 +18,7 @@ export ARCH=arm64 cd ${kernel_builddir}/${kernel_filename%.tar.gz} # If we have patches, apply them -if [[ -d ${root_path}/patches/kernel/ ]]; then +if [[ -d "${root_path}/patches/kernel/" ]]; then for file in ${root_path}/patches/kernel/*.patch; do echo "Applying kernel patch ${file}" patch -p1 < ${file} @@ -26,7 +26,7 @@ if [[ -d ${root_path}/patches/kernel/ ]]; then fi # Apply overlay if it exists -if [[ -d ${root_path}/overlay/${kernel_overlay_dir}/ ]]; then +if [[ -d "${root_path}/overlay/${kernel_overlay_dir}/" ]]; then echo "Applying ${kernel_overlay_dir} overlay" cp -R ${root_path}/overlay/${kernel_overlay_dir}/* ./ fi @@ -52,3 +52,10 @@ mv defconfig ${build_path}/kernel/kernel_config #cp ./arch/arm64/boot/Image.gz ${build_path}/kernel #mv uImage ${build_path}/kernel mv ./modules-dir ${build_path}/kernel/kernel-modules + +# Now that the kernel is done, build our out of tree modules! :) +module_builddir=$(mktemp -d) +cp ${root_path}/tools/mtd-lock/* "${module_builddir}" +cd "${module_builddir}" +make -C ${kernel_builddir}/${kernel_filename%.tar.gz} M=$PWD +cp "${module_builddir}/ubnt-mtd-lock.ko" ${build_path}/kernel diff --git a/scripts/docker/run_debootstrap.sh b/scripts/docker/run_debootstrap.sh index 25f5ac5..50ab9a4 100755 --- a/scripts/docker/run_debootstrap.sh +++ b/scripts/docker/run_debootstrap.sh @@ -24,9 +24,10 @@ chroot "${build_path}/rootfs" /debootstrap/debootstrap --second-stage mv -f "${build_path}/fw-extract/${BOARD}-rootfs/lib/modules" "${build_path}/rootfs/lib" cp "${build_path}/fw-extract/${firmware_filename%.bin}/kernel.bin" "${build_path}/rootfs/boot/uImage" -# Now, for the old kernel we built, pull in btrfs + depends modules (we do depmod in bootstrap) +# Now, for the old kernel we built, pull in our extra modules we need! (depmod is done in bootstrap) cp "${build_path}/kernel/kernel-modules/lib/modules/4.19.152-alpine-unvr/kernel/lib/zstd/zstd_compress.ko" "${build_path}/rootfs/lib/modules/4.19.152-alpine-unvr/extra/" cp "${build_path}/kernel/kernel-modules/lib/modules/4.19.152-alpine-unvr/kernel/fs/btrfs/btrfs.ko" "${build_path}/rootfs/lib/modules/4.19.152-alpine-unvr/extra/" +cp "${build_path}/kernel/ubnt-mtd-lock.ko" "${build_path}/rootfs/lib/modules/4.19.152-alpine-unvr/extra/" # Copy over our overlay if we have one if [[ -d ${root_path}/overlay/${fs_overlay_dir}/ ]]; then @@ -52,6 +53,8 @@ if [ "${BOARD}" == "UNVRPRO" ]; then libssl.so.1.1 libcrypto.so.1.1 libabsl*.so.20200923 libatomic.so.1; do cp -H ${build_path}/fw-extract/${BOARD}-rootfs/usr/lib/aarch64-linux-gnu/${file} "${build_path}/rootfs/usr/lib/ubnt-fw/" done + # Now for the REAL JANK! patch ulcmd so it doesn't rely on /proc/ubnthal, so we can use our userspace tool ubnteeprom + sed -i 's|/proc/ubnthal/system.info|/tmp/.ubnthal_system_info|g' "${build_path}/rootfs/usr/bin/ulcmd" else # Remove our ld.so.conf.d as it's not needed for UVNR rm "${build_path}/rootfs/etc/ld.so.conf.d/ubnt.conf" diff --git a/tools/mtd-lock/Kbuild b/tools/mtd-lock/Kbuild new file mode 100644 index 0000000..2d1da96 --- /dev/null +++ b/tools/mtd-lock/Kbuild @@ -0,0 +1 @@ +obj-m := ubnt-mtd-lock.o \ No newline at end of file diff --git a/tools/mtd-lock/README.md b/tools/mtd-lock/README.md new file mode 100644 index 0000000..db1b4ea --- /dev/null +++ b/tools/mtd-lock/README.md @@ -0,0 +1,11 @@ +# mtd-lock + +A stupid basic kernel module to ensure we set /dev/mtd as RO (fully) in our firmware. This is done to prevent users/bad actors from wiping your bootloader/Unifi EEPROM, which are required for your device to function! + +## Building + + make -C ${kernel_source_dir} M=$PWD + +## License + +This code is licensed under the GNU General Public License, version 2. A copy of said license can be found at [https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html#SEC1](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html#SEC1) diff --git a/tools/mtd-lock/ubnt-mtd-lock.c b/tools/mtd-lock/ubnt-mtd-lock.c new file mode 100644 index 0000000..f65ab2c --- /dev/null +++ b/tools/mtd-lock/ubnt-mtd-lock.c @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2024 Chris Blake + * + * Inspired by mtd-rw: https://github.com/jclehner/mtd-rw/tree/master + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include + +#ifndef MODULE +#error "uvnt-mtd-lock must be compiled as a module." +#endif + +#define MOD_INFO KERN_INFO "ubnt-mtd-lock: " +#define MOD_ERR KERN_ERR "ubnt-mtd-lock: " + +static int set_readonly(unsigned n) +{ + struct mtd_info *mtd = get_mtd_device(NULL, n); + int err; + + if (IS_ERR(mtd)) { + if (PTR_ERR(mtd) != -ENODEV) { + printk(MOD_ERR "error probing mtd%d %ld\n", n, PTR_ERR(mtd)); + } + return PTR_ERR(mtd); + } + + err = -EEXIST; + + if (mtd->flags & MTD_WRITEABLE) { + printk(MOD_INFO "setting mtd%d \"%s\" readonly\n", n, mtd->name); + mtd->flags &= ~MTD_WRITEABLE; + err = 0; + } + + put_mtd_device(mtd); + return err; +} + +int ubnt_mtd_lock_init(void) +{ + int i, err; + + /* For all MTD partitions, go RO. Assume <10 for UNVR/UNVRPRO */ + for (i = 0; i < 10; ++i) { + err = set_readonly(i); + if (err == -ENODEV) { + break; + } + } + + return 0; +} + +void ubnt_mtd_lock_exit(void) +{ + /* Do nothing, we wanna keep mtd locked!!! */ +} + +module_init(ubnt_mtd_lock_init); +module_exit(ubnt_mtd_lock_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Chris Blake "); +MODULE_DESCRIPTION("Unifi UNVR/UNVRPRO driver to force MTD partitions RO"); +MODULE_VERSION("1"); diff --git a/tools/ubnteeprom/README.md b/tools/ubnteeprom/README.md new file mode 100644 index 0000000..c43e03e --- /dev/null +++ b/tools/ubnteeprom/README.md @@ -0,0 +1,37 @@ +# ubnteeprom + +A userspace tool to parse/read/render the EEPROM MTD partition on Unifi UNVR/UNVR Pro, and possibly other Dream Machine devices in the future. + +## Purpose + +This tool was created as a userspace replacement for the functions Unifi's ubnthal proprietary kernel module provides, by reporting most of the same information out as `/proc/ubnthal/*` as well as some output from `ubnt-tools id`. + +The idea behind this is so we can get this repo off of using proprietary Unifi code as much as possible, so replacements for things are required. All code for this was reverse engineered and no unifi proprietary code was copied/used in the creation of this tool. + +## Usage + +Get similar output to `/proc/ubnthal/board`: + + ubnteeprom -board + +Get similar output to `/proc/ubnthal/system.info`: + + ubnteeprom -systeminfo + +Get similar output to `ubnt-tools id`: + + ubnteeprom -tools + +Get a specfic value for a selected key in output, for example, `boardid`: + + ubnteeprom -board -key boardid + +## Building + +``` +env GOOS=linux GOARCH=arm64 go build -ldflags="-s -w" -o ubnteeprom main.go +``` + +## License + +This code is licensed under the GNU General Public License, version 2. A copy of said license can be found at [https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html#SEC1](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html#SEC1) diff --git a/tools/ubnteeprom/main.go b/tools/ubnteeprom/main.go index 916e196..1c5a397 100644 --- a/tools/ubnteeprom/main.go +++ b/tools/ubnteeprom/main.go @@ -1,5 +1,8 @@ package main +// Copyright (C) 2024 Chris Blake +// Licensed under the GNU Public License, version 2 + import ( "encoding/hex" "flag"