Usb key func.sh

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

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

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

Mountpoint, trigger file and salt

NAS Firmware Mountpoint Trigger file Salt Remark
NSA220,NSA220+ 2.2- /mnt nsa220_check_file /etc/ZyPrivate
2.3+ /mnt/parnerkey
NSA2401 nsa2401_check_file
NSA210 nsa210_check_file The firmware doesn't contain an md5sum binary. It has to be provided on the stick.
NSA221 NSA221_check_file
NSA310 4.22- nsa310_check_file_C0 /lib/libzy.so (fw4)
4.40+ /etc/ZyPrivate
NSA310s

NSA320 NSA320s TDC Homebox

/lib/libzy.so (fw4)
NSA325 NSA325v2 The front USB port doesn't work
Medion NSA212 STG212_check_file /etc/ZyPrivate The usb_key_func.sh file is executed twice. The stick is mounted read-only.
NAS520

NAS540 NAS542

/mnt/partnerkey nas5xx_check_file /lib/libzy.so (fw5)
NAS326 nas3xx_check_file

Salt

/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

#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...).

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.

Notes

  • usb_key_func.sh has to have the executable flag set. On a FAT filesystem this is always the case, 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.
  • From firmware version 3.24 and up 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
Self containing Entware-ng stick

Real scriptlet samples

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
}

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
}