Usb key func.sh

From NAS-Central Zyxel Wiki
Jump to: navigation, search

Contents

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.

How it works

On all ZyXEL devices the /etc/init.d/rcS script contains code which is responsible for this:

NSA-220 (plus)
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
# 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 ${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
NSA-221
In /etc/init.d/rcS.221
### check upgrade key
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
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
# 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 ${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
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
#Currently, we don't need it.
#                               if [ "$1" == "FW_UPGRADE" ]; then
#                                       ls -la /mnt/parnerkey | grep "${MODEL_NAME}_fw"
#                                       FW=$?
#                                       ls -al /mnt/parnerkey | grep "${MODEL_NAME}_pwr_func_check"
#                                       PWR=$?
#                                       if [ $FW != 0 ] && [ $PWR != 0 ] ; then
#                                               umount /mnt/parnerkey
#                                       fi
#                               fi

                                /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
}
NAS540
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
}

Note: USB_PATH is /mnt/partnerkey, for the NSA540

The main function for all these implementation is the same

  • Enumerate all usb mass devices
  • Mount them one by one on /mnt/parnerkey (/mnt for firmware 2.20 or older, /mnt/partnerkey for NAS540)
  • Do some check (/sbin/check_key) on /mnt/parnerkey/nsa2x0_check_file
  • If check succeeds, run /mnt/parnerkey/usb_key_func.sh
  • Depending on the resultvalue of usb_key_func.sh, exit rcS (For the NSA-221, the first run always exit rcS!)
  • Unmount the device
nsannn_check_file

This file (which is actually called nsa220_check_file, nsa210_check_file, NSA221_check_file, nsa310_check_file_C0, STG212_check_file, nsa2401_check_file or nas5xx_check_file, depending on the boxtype) contains the full path of two or three files, one per line, the last file containing a salted md5sum of the first one(s).

/sbin/check_key

Some investigations learned that /sbin/check_key can do two different things, depending on the number of files in nsannn_check_file. Given the content is

/path/to/file1
/path/to/file2

/sbin/check_key does

cp /path/to/file1 /mnt/parnerkey/usb_key_func.sh
cat /etc/Zy_Private /path/to/file1 | md5sum -c /path/to/file2

For the NSA-3xx and NAS-5xx the last check is

cat /lib/libzy.so /path/to/file1 | md5sum -c /path/to/file2

Starting with firmware 4.40 the NSA-310 uses etc/Zy_Private. The other 300 series (including the 310S and 320S) use libzy.so

Given the content is

/path/to/file1
/path/to/file2
/path/to/file3

/sbin/check_key does

cp /path/to/file1 /mnt/parnerkey/usb_key_func.sh
cp /path/to/file2 /mnt/parnerkey/ras.bin
cat /etc/Zy_Private /path/to/file1 /mnt/parnerkey/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

I hope this will not change over different firmware versions. The version of V4.01(AFO.1) is here. The version for the NAS-540 (V5.00) is here

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

#Run USB_KEY for HW test
Exe_USB_KEY FW_UPGRADE

<snip>

##### Another USB KEY entry after the sysdisk is mounted.
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...).

NAS540

While all other platforms use /mnt/parnerkey to mount the usb thumb, the NAS540 uses /mnt/partnerkey

How to use it

The two line version of nsannn_check_file is the most practical to use. Just write a usb_key_func.sh, generate a checksum file:

cat /path/to/Zy_Private usb_key_func.sh | md5sum > checksumfile

for NSA3xx and NAS5xx:

cat /path/to/libzy.so usb_key_func.sh | md5sum > checksumfile

put both files in the root of the usb device, and create a nsannn_check_file:

/mnt/parnerkey/usb_key_func.sh
/mnt/parnerkey/checksumfile

In case of NSA-220, firmware 2.20 or older:

/mnt/usb_key_func.sh
/mnt/checksumfile

Put this file in the root of the usb device too, plug it in the NSA-nnn, 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 nsannn_check_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.

Comments
  • usb_key_func.sh has to have the executable flag set. On a FAT filesystem this flag is always assumed, on an ext2/3 or reiserfs fs you'll have to do it yourself. For the NSA-210 the same is true for md5sum.
  • When no explicit returnvalue is given, or when the scripts returns 0, /etc/init.d/rcS is stopped. In most cases this is unwanted, so exit the script with exit 1
  • The script is called before the harddisk(s) is mounted. So you cannot access it directly
  • After the script returns, the usb device is unmounted. So if you want the script to 'stay' you'll have to copy it elsewhere.
  • In firmware version 3.24 (and in 4.01 for the NSA-320) the options iocharset=utf8,shortname=mixed are used for mounting the stick, effectively limiting the possible filesystems to vfat only :( . This makes it impossible to use symlinks and Posix file permissions.
  • On the Medion Life P89626 (NSA-212) the options iocharset=utf8,shortname=mixed,ro are used, limiting the filesystems to vfat, only, and mounting read-only.
  • Although I don't see why, looking at the script, tests show that only primary partitions work. An usb_key_func.sh on a logical partition is ignored.
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
#!/bin/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
Projects using usb_key_func.sh

FFP-stick
Debian without need to flash
A simple telnetdaemon stick
The Arch Linux for '320 install script