Usb key func.sh

What it is
usb_key_func.sh is a script which is run on boot from an usb mass storage device, when certain conditions are met.

Scriptlet
On all ZyXEL devices the /etc/init.d/rcS script contains code which basically does: any_usb=` get_usb_device ` if [ -n "${any_usb}" ]; then /bin/mkdir /mountpoint for usb in ${any_usb} do               mount "${usb}"1 /mountpoint if [ $? == 0 ] ; then /sbin/check_key /mountpoint/trigger_file if [ $? == 0 ] ; then /mountpoint/usb_key_func.sh                               test $? -eq 0 && exit 0 fi                       umount /mountpoint fi       done rmdir /mountpoint fi

In this scriptlet trigger_file and mountpoint differ over different boxes and firmwares

So if /sbin/check_key returns 0 then the script usb_key_func.sh on the root of the usb device is executed, and if that script returns 0 then /etc/init.d/rcS is exited here.

/sbin/check_key
Some investigations learned that /sbin/check_key can do two different things, depending on the number of lines in the trigger_file.

2 lines
Given the content is /path/to/file1 /path/to/file2 /sbin/check_key does cp /path/to/file1 /mountpoint/usb_key_func.sh cat salt /path/to/file1 | md5sum -c /path/to/file2

3 lines
Given the content is /path/to/file1 /path/to/file2 /path/to/file3 /sbin/check_key does cp /path/to/file1 /mountpoint/usb_key_func.sh cp /path/to/file2 /mountpoint/ras.bin cat salt /path/to/file1 /mountpoint/ras.bin | md5sum -c /path/to/file3

The result value of /sbin/check_key is in both cases the result value of md5sum

/etc/Zy_Private
The contents of this file is 52103jeenajevol8290i\n, in hex: 35323130336A65656E616A65766F6C38323930690A. Download

/lib/libzy.so
The version of firmware 4.x is here. The version for firmware 5.x is here

Filesystem
On firmware 3.23 or older any filesystem which is supported by the provided linux kernel can be used. But starting with firmware 3.24 the usbstick is mounted with the flags iocharset=utf8,shortname=mixed, limiting the possible filesystems to vfat only.

md5sum
On the NSA-210 md5sum is not available in the firmware, so it has to be provided on the usb device. Download

NSA325
A rear port should be used. The (USB3) front port is, starting with fw 4.60, handled by a kernel module which is loaded too late in the boot process.

NSA221_fw or NSA221_pwr_func_check
The first check of the stick in /etc/init.d/rcS.221 on the NSA-221 checks for the existence of one of these files. When not available the usb_key_func.sh will not be executed, but it will get a second chance in (newly bound)/etc/init.d/rcS2, where these files are not used. The first check will stop the bootprocess if one of these files is present, regardless if the script will be executed or not. As far is I know the file is not used elsewhere, so if you want this you can just create an empty file

Medion P89626/P89630
On the Medion the code is actually called twice Exe_USB_KEY FW_UPGRADE Exe_USB_KEY HW_TEST The fun part is that the argument is *not used*. So when the stick actually has to perform different tasks, it has to keep track of it itself (on a readonly stick...).
 * 1) Run USB_KEY for HW test
 * 1) Another USB KEY entry after the sysdisk is mounted.

How to use it
The two line version of trigger_file is the most practical to use. Just write a usb_key_func.sh, generate a checksum file: cat salt usb_key_func.sh | md5sum > checksumfile put both files in the root of the usb device, and create a trigger_file: /mountpoint/usb_key_func.sh /mountpoint/checksumfile Put this file in the root of the usb device too, plug the stick in and reboot the box.

The cp of check_key will fail (source and target are the same), but that doesn't hurt.

'Universal stick'
Here can you find a 'universal' package, containing an usb_key_func.sh, valid checksum files and trigger_file's. The usb_key_func.sh will pass execution to usb_key_func.sh.2, which you can provide yourself. This script has to end with 'exit 1', when you want booting to continue after calling your script. It contains also some sample scripts which you can rename to usb_key_func.sh.2.

Problems
Some sticks just don't work. As far as I know in most cases this is the timing issue. The USB bus is reset on boot, and then the USB stick needs some time to proclaim itself. If it hasn't done so when the boot script probes the usb sticks, it's just too late, and the usb_key_func.sh won't work.

Example usb_key_func.sh
payload { 	# Do anything } poll { 	# Wait for the harddisk to be mounted while [ 1 ]; do 		if cat /proc/mounts | grep /dev/md0 ; then return 0 fi sleep 5 done } if [ $# -gt 0 ]; then # This is not the original call poll payload else # Copy script to /tmp cp $0 /tmp/script.sh 	# Start it in background /tmp/script.sh poll >/dev/null 2>&1 & # continue /etc/init.d/rcS exit 1 fi
 * 1) !/bin/sh

Projects using usb_key_func.sh
FFP-stick

Debian without need to flash

A simple telnetdaemon stick

The Arch Linux for '320 install script

Self containing Entware-ng stick

firmware 2.20 or older
any_usb=`sg_map -x -i|grep -v " 0 0 0 0"|grep -v " 1 0 0 0"|awk '{print $7}'` echo "${any_usb}" if [ -n "${any_usb}" ]; then for usb in ${any_usb} do mount "${usb}"1 /mnt /sbin/check_key /mnt/nsa220_check_file if [ $? == 0 ] ; then /mnt/usb_key_func.sh test $? -eq 0 && exit 0 fi umount /mnt done fi

firmware 2.30 - 3.23
any_usb=`sg_map -x -i|grep -v " 0 0 0 0"|grep -v " 1 0 0 0"|awk '{print $7}'` echo "${any_usb}" if [ -n "${any_usb}" ]; then /bin/mkdir /mnt/parnerkey for usb in ${any_usb} do                mount "${usb}"1 /mnt/parnerkey if [ $? == 0 ] ; then /sbin/check_key /mnt/parnerkey/nsa220_check_file if [ $? == 0 ] ; then /mnt/parnerkey/usb_key_func.sh                                test $? -eq 0 && exit 0 fi                        umount /mnt/parnerkey fi        done rmdir /mnt/parnerkey fi

firmware 3.24
any_usb=`sg_map -x -i|grep -v " 0 0 0 0"|grep -v " 1 0 0 0"|awk '{print $7}'` echo "${any_usb}" if [ -n "${any_usb}" ]; then /bin/mkdir /mnt/parnerkey for usb in ${any_usb} do                echo "checking ${usb}" #Get the number of question marks qmark_num=`fdisk -l ${usb} | grep "^"${usb} | grep -c "?"` partition_num=`fdisk -l ${usb} | grep "^"${usb}` if [ "${qmark_num}" == "4" ] || [ "${partition_num}" == "" ]; then mnt_point=${usb} echo "Trying to mount ${mnt_point}" mount -o iocharset=utf8,shortname=mixed ${mnt_point} /mnt/parnerkey else mnt_point=`fdisk -l ${usb} | grep "^"${usb} | awk '{print $1}' | sed -n '1p'` echo "Trying to mount ${mnt_point}" mount -o iocharset=utf8,shortname=mixed ${mnt_point} /mnt/parnerkey fi                 mount_SUC=`cat /proc/mounts | grep /mnt/parnerkey` if [ "${mount_SUC}" != "" ]; then /sbin/check_key /mnt/parnerkey/nsa220_check_file if [ $? == 0 ]; then /mnt/parnerkey/usb_key_func.sh                                test $? -eq 0 && exit 0 fi                        umount /mnt/parnerkey else echo "Fail to mount ${mnt_point}" fi        done rmdir /mnt/parnerkey fi

NSA-210
any_usb=`ls /sys/block/ | grep sd` echo "${any_usb}" if [ -n "${any_usb}" ]; then /bin/mkdir /mnt/parnerkey for usb in ${any_usb} do echo "checking ${usb}" #Get the number of question marks qmark_num=`fdisk -l /dev/${usb} | grep "^"/dev/${usb} | grep -c "?"` partition_num=`fdisk -l /dev/${usb} | grep "^"/dev/${usb}` if [ "${qmark_num}" == "4" ] || [ "${partition_num}" == "" ]; then mnt_point=/dev/${usb} echo "Trying to mount ${mnt_point}" mount ${mnt_point} /mnt/parnerkey else mnt_point=`fdisk -l /dev/${usb} | grep "^"/dev/${usb} | awk '{print $1}' | sed -n '1p'` echo "Trying to mount ${mnt_point}" mount ${mnt_point} /mnt/parnerkey fi mount_SUC=`cat /proc/mounts | grep /mnt/parnerkey` if [ "${mount_SUC}" != "" ]; then if [ -e /mnt/parnerkey/md5sum ]; then cp /mnt/parnerkey/md5sum /sbin /sbin/check_key /mnt/parnerkey/nsa210_check_file if [ $? == 0 ]; then /mnt/parnerkey/usb_key_func.sh test $? -eq 0 && exit 0 fi else echo "No md5sum is found." fi umount /mnt/parnerkey else echo "Fail to mount ${mnt_point}" fi done rmdir /mnt/parnerkey fi
 * 1) Scan USB disk for HW test.

In /etc/init.d/rcS.221
any_usb=`sg_map -x -i|grep -v " 0 0 0 0"|grep -v " 1 0 0 0"|grep -v "${NAND_DISK}"|awk '{print $7}'` echo "${any_usb}" if [ -n "${any_usb}" ]; then /bin/mkdir /mnt/parnerkey for usb in ${any_usb} do echo "mount upgrade key" mount "${usb}"1 /mnt/parnerkey ls -la /mnt/parnerkey | grep "NSA221_fw" FW=$? ls -al /mnt/parnerkey | grep "NSA221_pwr_func_check" PWR=$? if [ $FW == 0 ] || [ $PWR == 0 ] ; then /sbin/check_key /mnt/parnerkey/NSA221_check_file if [ $? == 0 ] ; then echo "======== Start USB Upgrade Key  ========" /mnt/parnerkey/usb_key_func.sh test $? -eq 0 && exit 0 fi umount /mnt/parnerkey exit 1 else umount /mnt/parnerkey fi done rmdir /mnt/parnerkey fi
 * 1) check upgrade key

in (newmount)/etc/init.d/rcS2
any_usb=`sg_map -x -i|grep -v " 0 0 0 0"|grep -v " 1 0 0 0"|grep -v "USB DISK"|awk '{print $7}'` echo "${any_usb}" if [ -n "${any_usb}" ]; then /bin/mkdir /mnt/parnerkey for usb in ${any_usb} do                echo "mount USB key" mount "${usb}"1 /mnt/parnerkey if [ $? == 0 ] ; then /sbin/check_key /mnt/parnerkey/${MODEL_NAME}_check_file if [ $? == 0 ] ; then echo "======== Start USB Key  ========" /mnt/parnerkey/usb_key_func.sh                                test $? -eq 0 && exit 0 fi                        umount /mnt/parnerkey fi        done rmdir /mnt/parnerkey fi

NSA-300 series
any_usb=`ls /sys/block/ | grep sd` echo "${any_usb}" if [ -n "${any_usb}" ]; then /bin/mkdir /mnt/parnerkey for usb in ${any_usb} do                echo "checking ${usb}" #Get the number of question marks qmark_num=`fdisk -l /dev/${usb} | grep "^"/dev/${usb} | grep -c "?"` partition_num=`fdisk -l /dev/${usb} | grep "^"/dev/${usb}` if [ "${qmark_num}" == "4" ] || [ "${partition_num}" == "" ]; then mnt_point=/dev/${usb} echo "Trying to mount ${mnt_point}" mount -o iocharset=utf8,shortname=mixed ${mnt_point} /mnt/parnerkey else mnt_point=`fdisk -l /dev/${usb} | grep "^"/dev/${usb} | awk '{print $1}' | sed -n '1p'` echo "Trying to mount ${mnt_point}" mount -o iocharset=utf8,shortname=mixed ${mnt_point} /mnt/parnerkey fi                 mount_SUC=`cat /proc/mounts | grep /mnt/parnerkey` if [ "${mount_SUC}" != "" ]; then /sbin/check_key /mnt/parnerkey/nsa310_check_file_C0 if [ $? == 0 ]; then /mnt/parnerkey/usb_key_func.sh                                test $? -eq 0 && exit 0 fi                        umount /mnt/parnerkey else echo "Fail to mount ${mnt_point}" fi        done rmdir /mnt/parnerkey fi
 * 1) Scan USB disk for HW test.

Medion Life P89626
Exe_USB_KEY {        # Check model name model_id=`cat /zyxel/mnt/info/modelid` if [ "${model_id}" == "A403" ]; then export MODEL_NAME="STG100" elif [ "${model_id}" == "A803" ]; then export MODEL_NAME="STG211" elif [ "${model_id}" == "AB03" ]; then export MODEL_NAME="STG212" fi        # Scan USB disk for HW test. any_usb=`ls /sys/block/ | grep sd` echo "${any_usb}" if [ -n "${any_usb}" ]; then /bin/mkdir /mnt/parnerkey for usb in ${any_usb} do                        echo "checking ${usb}" #Get the number of question marks qmark_num=`fdisk -l /dev/${usb} | grep "^"/dev/${usb} | grep -c "?"` partition_num=`fdisk -l /dev/${usb} | grep "^"/dev/${usb}` if [ "${qmark_num}" == "4" ] || [ "${partition_num}" == "" ]; then mnt_point=/dev/${usb} echo "Trying to mount ${mnt_point}" mount -o iocharset=utf8,shortname=mixed,ro ${mnt_point} /mnt/parnerkey else mnt_point=`fdisk -l /dev/${usb} | grep "^"/dev/${usb} | awk '{print $1}' | sed -n '1p'` echo "Trying to mount ${mnt_point}" mount -o iocharset=utf8,shortname=mixed,ro ${mnt_point} /mnt/parnerkey fi                        mount_SUC=`cat /proc/mounts | grep /mnt/parnerkey` if [ "${mount_SUC}" != "" ]; then /sbin/check_key /mnt/parnerkey/${MODEL_NAME}_check_file if [ $? == 0 ]; then #/mnt/parnerkey/usb_key_func.sh                                        script_path=`cat /mnt/parnerkey/${MODEL_NAME}_check_file | sed -n '1p'` ${script_path} ${script_path} if [ $? == 0 ]; then exit 0 fi                                fi                                 umount /mnt/parnerkey else echo "Fail to mount ${mnt_point}" fi                done rmdir /mnt/parnerkey fi }
 * 1) Currently, we don't need it.
 * 2)                               if [ "$1" == "FW_UPGRADE" ]; then
 * 3)                                       ls -la /mnt/parnerkey | grep "${MODEL_NAME}_fw"
 * 4)                                       FW=$?
 * 5)                                       ls -al /mnt/parnerkey | grep "${MODEL_NAME}_pwr_func_check"
 * 6)                                       PWR=$?
 * 7)                                       if [ $FW != 0 ] && [ $PWR != 0 ] ; then
 * 8)                                               umount /mnt/parnerkey
 * 9)                                       fi
 * 10)                               fi

NAS520/NAS540/NAS542
check_and_run_usbkey {      USB_CHECK_FILE="${USB_PATH}/nas5xx_check_file" any_usb=`ls /sys/block/ | grep sd` ${ECHO} "${any_usb}" if [ -n "${any_usb}" ]; then ${MKDIR} ${USB_PATH} for usb in ${any_usb} do                      ${ECHO} "checking ${usb}" #Get the number of question marks qmark_num=`fdisk -l /dev/${usb} | grep "^"/dev/${usb} | grep -c "?"` partition_num=`fdisk -l /dev/${usb} | grep "^"/dev/${usb}` if [ "${qmark_num}" == "4" ] || [ "${partition_num}" == "" ]; then mnt_point=/dev/${usb} ${ECHO} "Trying to mount ${mnt_point}" ${MOUNT} -o iocharset=utf8,shortname=mixed ${mnt_point} ${USB_PATH} else mnt_point=`fdisk -l /dev/${usb} | grep "^"/dev/${usb} | awk '{print $1}' | sed -n '1p'` ${ECHO} "Trying to mount ${mnt_point}" ${MOUNT} -o iocharset=utf8,shortname=mixed ${mnt_point} ${USB_PATH} fi                       mount_SUC=`${CAT} /proc/mounts | grep ${USB_PATH}` if [ "${mount_SUC}" != "" ]; then /sbin/check_key ${USB_CHECK_FILE} if [ $? == 0 ]; then ${USB_PATH}/usb_key_func.sh                                      test $? -eq 0 && exit 0 fi                              ${UMOUNT} ${USB_PATH} else ${ECHO} "Fail to mount ${mnt_point}" fi              done ${RMDIR} ${USB_PATH} fi }