#!/bin/ksh PATH=/bin:/usr/bin export PATH # $Id: verify_installsets,v 1.6 2004/04/06 04:56:50 jeff Exp $ #-------------------------------------------------- # verify_installsets script Version 1.3 # # Copyright (C) 2003 Jeff Nathan # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE # COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. #-------------------------------------------------- ################################################### # This script contains various shell functions # and a generic command line parser to make the # process of downloading OpenBSD install sets # easier and safer. # # Consult the ksh(1) and ftp(1) man pages for # answers and explanations of the shell syntax # and ftp command line options used below. # (ie: man ksh). # # Editing the variables below will allow you to # customize this scipt to your liking. For # any option listed below that you DO NOT wish # to use, place double quotes after the variable # in the form: typeset FTP1_DIR="" # ################################################### # Part 1: User configurable section # # Location of the mkhybrid binary: #--------------------------------- typeset MKHYBRID=/usr/sbin/mkhybrid # Hardware architecture: #----------------------- typeset ARCH=i386 # Version to label to use when building the ISO install dir: #----------------------------------------------------------- typeset VERSION=3.5 # The name of the installation ISO: #---------------------------------- typeset ISO_NAME=current.iso # Address of the first FTP server: #--------------------------------- typeset FTP1=ftp.openbsd.org # Address of the second FTP server: #---------------------------------- typeset FTP2=mirrors.rcn.net # Path to the install sets on the first FTP server: #-------------------------------------------------- typeset FTP1_PATH=pub/OpenBSD/snapshots # Path to the install sets on the second FTP server: #--------------------------------------------------- typeset FTP2_PATH=pub/OpenBSD/snapshots # Directory to store the files from the first FTP server: #-------------------------------------------------------- typeset FTP1_DIR=ftp1_dir # Directory to store the files from the second FTP server: #--------------------------------------------------------- typeset FTP2_DIR=ftp2_dir # The name of the timestamp file for install sets on the first FTP server: # ------------------------------------------------------------------------ typeset TIMESTAMP1=ftp1_timestamps.txt # The name of the timestamp file for install sets on the second FTP server: # ------------------------------------------------------------------------ typeset TIMESTAMP2=ftp2_timestamps.txt ################################################### # Part 2: This section contains the core of this script. # Reading through this section should describe how the # script functions. Edit below at your own risk. trap 'print "Received SIGINT; cleaning up."; rm -f ${_tmp} ${_tmp1} ${_tmp2}; \ exit 1' INT trap 'print "Received SIGQUIT; cleaning up."; rm -f ${_tmp} ${_tmp1} ${_tmp2}; \ exit 1' QUIT typeset CURDIR=$(/bin/pwd) clean_dir() { typeset _dir=${1##*[ \\$/;()|\>\<& ]} typeset _ans="" echo echo -n "WARNING: ${_dir} already exists, remove? " echo -n "[y/N] " read _ans typeset -l _ans if [[ ${_ans} == "y" ]]; then rm -rf ${_dir} wait else echo echo "ERROR: ${_dir} is not empty. In order for checksumming to" echo " work properly, local install set directories MUST" echo " be empty. Exiting." echo exit 1 fi return } create_dir () { typeset _arch=${1##*[ \\$/;()|\>\<& ]} typeset _dir=${2##*[ \\$/;()|\>\<& ]} typeset _ver=${3##*[ \\$/;()|\>\<& ]} mkdir -p ${CURDIR}/${_dir}/${_ver}/${_arch} if (($? != 0)); then echo echo "ERROR: Unable to create ${CURDIR}/${_dir}/${_ver}/${_arch}." echo " Unknown error. Exiting." echo exit 1 fi return } store_timestamp () { typeset _site=${1##*[ \\$/;()|\>\<& ]} typeset _path=${2##*[ \\$/;()|\>\<& ]} typeset _arch=${3##*[ \\$/;()|\>\<& ]} typeset _dir=${4##*[ \\$/;()|\>\<& ]} typeset _timestampfile=${5##*[ \\$/;()|\>\<& ]} typeset _tmp=$(mktemp /tmp/tmpfile.XXXXXXXXXX) printf "dir ${_path}/${_arch}\n" | ftp -ap -o - ftp://${_site} \ > ${_tmp} 2> /dev/null if (($? != 0)); then echo echo "ERROR: Failed to retrieve install set timestamps from ${_site}." echo rm -f ${_tmp} exit 1 fi while read -r ONE TWO THREE FOUR FIVE SIX SEVEN EIGHT NINE do printf "%-8.8s %.3s %.2s %.5s %s\n" ${FIVE} ${SIX} ${SEVEN} \ ${EIGHT} ${NINE} done < ${_tmp} > ${CURDIR}/${_dir}/${_timestampfile} rm -f ${_tmp} return } download_sets () { typeset _site=${1##*[ \\$/;()|\>\<& ]} typeset _path=${2##*[ \\$/;()|\>\<& ]} typeset _arch=${3##*[ \\$/;()|\>\<& ]} typeset _dir=${4##*[ \\$/;()|\>\<& ]} typeset _ver=${5##*[ \\$/;()|\>\<& ]} cd ${CURDIR}/${_dir}/${_ver}/${_arch} > /dev/null 2>&1 if (($? != 0)); then echo echo "ERROR: Unable to cd ${CURDIR}/${_dir}/${_ver}/${_arch}." echo " Unknown error. Exiting." echo exit 1 fi ftp -ap ftp://${_site}/${_path}/${_arch}/\* if (($? != 0)); then echo echo "ERROR: Failed to retrieve install sets from ${_site}." echo exit 1 fi return } compare_distrib_checksum () { typeset _arch=${1##*[ \\$/;()|\>\<& ]} typeset _dir=${2##*[ \\$/;()|\>\<& ]} typeset _ver=${3##*[ \\$/;()|\>\<& ]} typeset _tmp=$(mktemp /tmp/tmpfile.XXXXXXXXXX) cd ${CURDIR}/${_dir}/${_ver}/${_arch} > /dev/null 2>&1 if (($? != 0)); then echo echo "ERROR: Unable to cd ${CURDIR}/${_dir}/${_ver}/${_arch}." echo " Unknown error. Exiting." echo rm -f ${_tmp} exit 1 fi if [[ ! -f MD5 ]]; then echo echo "ERROR: Distribution MD5 checksum not found in" echo " ${CURDIR}/${_dir}/${_ver}/${_arch}. Exiting." echo rm -f ${_tmp} exit 1 fi while read -r ONE TWO THREE FOUR FIVE do md5 $(echo ${TWO} | tr -d '()') >> ${_tmp} done < MD5 diff MD5 ${_tmp} if (($? != 0)); then echo echo "ERROR: Locally generated MD5 checksums differ from distribution" echo " MD5 checksums in ${CURDIR}/${_dir}! This might " echo " indicate that the install sets have been tampered with." echo " It is advised that you do not use these install sets." echo rm -f ${_tmp} exit 1 else echo echo "Locally generated MD5 checksums match distribution MD5" echo "checksums in ${CURDIR}/${_dir}. Continuing." echo rm -f ${_tmp} wait fi return } compare_local_checksum () { typeset _arch=${1##*[ \\$/;()|\>\<& ]} typeset _dir1=${2##*[ \\$/;()|\>\<& ]} typeset _dir2=${3##*[ \\$/;()|\>\<& ]} typeset _ver=${4##*[ \\$/;()|\>\<& ]} typeset _tmp1=$(mktemp /tmp/tmpfile.XXXXXXXXXX) typeset _tmp2=$(mktemp /tmp/tmpfile.XXXXXXXXXX) cd ${CURDIR}/${_dir1}/${_ver}/${_arch} > /dev/null 2>&1 if (($? != 0)); then echo echo "ERROR: Unable to cd ${CURDIR}/${_dir1}/${_ver}/${_arch}." echo " Unknown error. Exiting." echo rm -f ${_tmp1} ${_tmp2} exit 1 fi for _file in * do md5 ${_file} >> ${_tmp1} done cd ${CURDIR}/${_dir2}/${_ver}/${_arch} > /dev/null 2>&1 if (($? != 0)); then echo echo "ERROR: Unable to cd ${CURDIR}/${_dir2}/${_ver}/${_arch}." echo " Unknown error. Exiting." echo rm -f ${_tmp1} ${_tmp2} exit 1 fi for _file in * do md5 ${_file} >> ${_tmp2} done diff ${_tmp1} ${_tmp2} if (($? != 0)); then echo echo "ERROR: Locally generated MD5 checksums do not match! This might" echo " indicate that the install sets have been tampered with." echo " It is advised that you do not use these install sets." echo echo " Compare the timestamp files in" echo " ${CURDIR}/${_dir1}/${TIMESTAMP1}" echo " and" echo " ${CURDIR}/${_dir2}/${TIMESTAMP2}" echo rm -f ${_tmp1} ${_tmp2} exit 1 else echo echo "Locally generated MD5 checksums for install sets in" echo "${CURDIR}/${_dir1} and ${CURDIR}/${_dir2} match." echo echo "Fully matching MD5 checksums for local and distribution install" echo "sets is a resonable indication that the install sets have not" echo "been tampered with. Continuing on to ISO creation." echo fi rm -f ${_tmp1} ${_tmp2} wait return } build_iso () { typeset _arch=${1##*[ \\$/;()|\>\<& ]} typeset _dir=${2##*[ \\$/;()|\>\<& ]} typeset _ver=${3##*[ \\$/;()|\>\<& ]} typeset _iso=${4##*[ \\$/;()|\>\<& ]} typeset _timestamp=${5##*[ \\$/;()|\>\<& ]} typeset _date=$(date +%m/%d/%Y) typeset _year=$(date +%Y) cd ${CURDIR}/${_dir}/${_ver}/${_arch} > /dev/null 2>&1 if (($? != 0)); then echo echo "ERROR: Unable to cd ${_dir}/${_ver}/${_arch}. Unknown error." echo " Exiting." echo exit 1 fi typeset _cdimage=$(ls cdrom??.fs) if [[ -z ${_cdimage} ]]; then echo echo "ERROR: CD-ROM boot image not found. Exiting." echo exit 1 fi cd ${CURDIR}/${_dir} > /dev/null 2>&1 if (($? != 0)); then echo echo "ERROR: Unable to cd ${CURDIR}/${_dir}. Unknown error." echo " Exiting." echo exit 1 fi ${MKHYBRID} -o ../${_iso} -a -A "OpenBSD ${_date}" \ -b ${_ver}/${_arch}/${_cdimage} -c ${_ver}/${_arch}/boot.catalog -d -D -l \ -L -N -P "Copyright (c) ${_year} Theo de Raadt, The OpenBSD project" \ -p "Jeff Nathan " -r -T \ -V "OpenBSD www.OpenBSD.org" -m ${_timestamp} . wait rm -f ${CURDIR}/${_dir}/${_ver}/${_arch}/boot.catalog return } usage () { echo echo "Usage: verify_installsets [options]" echo echo "options: " echo " -1 " echo " -2 " echo " -a " echo " -d " echo " -D " echo " -h (help/usage)" echo " -i " echo " -p " echo " -P " echo " -t " echo " -T " echo " -v " echo exit 1 } typeset -i _cmdopts=0 while getopts ":1:2:a:d:D:i:p:P:t:T:v:h" _opt; do case ${_opt} in 1 ) FTP1=${OPTARG##*[ \\$/;()|\>\<& ]} ((_cmdopts++)) ;; 2 ) FTP2=${OPTARG##*[ \\$/;()|\>\<& ]} ((_cmdopts++)) ;; a ) ARCH=${OPTARG##*[ \\$/;()|\>\<& ]} ((_cmdopts++)) ;; d ) FTP1_DIR=${OPTARG##*[ \\$/;()|\>\<& ]} ((_cmdopts++)) ;; D ) FTP2_DIR=${OPTARG##*[ \\$/;()|\>\<& ]} ((_cmdopts++)) ;; h ) usage ;; i ) ISO_NAME=${OPTARG##*[ \\$/;()|\>\<& ]} ((_cmdopts++)) ;; p ) FTP1_PATH=${OPTARG##*[ \\$/;()|\>\<& ]} ((_cmdopts++)) ;; P ) FTP2_PATH=${OPTARG##*[ \\$/;()|\>\<& ]} ((_cmdopts++)) ;; t ) TIMESTAMP1=${OPTARG##*[ \\$/;()|\>\<& ]} ((_cmdopts++)) ;; T ) TIMESTAMP2=${OPTARG##*[ \\$/;()|\>\<& ]} ((_cmdopts++)) ;; v ) VERSION=${OPTARG##*[ \\$/;()|\>\<& ]} ((_cmdopts++)) ;; \? ) echo echo "ERROR: Invalid command line option: -${OPTARG}." usage ;; esac done shift $((${OPTIND} - 1)) if ((${_cmdopts} > 0)); then echo echo "WARNING: Command line options override hardcoded values." echo sleep 3 fi typeset -l FTP1 typeset -l FTP2 if [[ ${FTP1} == ${FTP2} ]]; then echo echo "ERROR: You must specify two different FTP servers." echo exit 1 fi if [[ -z ${ARCH} ]]; then echo echo "ERROR: You must specify a hardware architecture." echo exit 1 fi if [[ -z ${FTP1_DIR} ]]; then FTP1_DIR=ftp1_dir fi if [[ -z ${FTP2_DIR} ]]; then FTP2_DIR=ftp2_dir fi if [[ -z ${ISO_NAME} ]]; then ISO_NAME=current.iso fi if [[ -z ${TIMESTAMP1} ]]; then TIMESTAMP1=ftp1_timestamps.txt fi if [[ -z ${TIMESTAMP2} ]]; then TIMESTAMP1=ftp2_timestamps.txt fi cat << __EOT This is a *configurable* shell script to download and verify the integrity of OpenBSD install sets (distribution files) by using two unique FTP mirrors sites. The process consists of 5 steps: 1) Two directories are created to separately store install sets (./ftp1_dir and ./ftp2_dir by default). 2) Timestamps and file sizes for the install sets are stored in the corresponding directory for future use (if necessary). 3) Install sets are retrived from both of the mirror sites and stored in the corresponding directory. 4) A list of MD5 checksums, excluding X11 install sets, is created separately in both of the install set directories and compared to the corresponding distribution MD5 checksum lists. 5) A complete list of MD5 checksums is created separately for both of the install set directories. If the lists of locally generated checksums match the distribution lists and the locally generated lists match each other the install sets are considered safe. If the install sets are considered safe, a bootable CD-ROM ISO image is automatically created. __EOT sleep 50 if [[ -e ${FTP1_DIR} ]]; then clean_dir ${FTP1_DIR} fi if [[ -e ${FTP2_DIR} ]]; then clean_dir ${FTP2_DIR} fi create_dir ${ARCH} ${FTP1_DIR} ${VERSION} wait create_dir ${ARCH} ${FTP2_DIR} ${VERSION} wait store_timestamp ${FTP1} ${FTP1_PATH} ${ARCH} ${FTP1_DIR} ${TIMESTAMP1} wait store_timestamp ${FTP2} ${FTP2_PATH} ${ARCH} ${FTP2_DIR} ${TIMESTAMP2} wait download_sets ${FTP1} ${FTP1_PATH} ${ARCH} ${FTP1_DIR} ${VERSION} wait download_sets ${FTP2} ${FTP2_PATH} ${ARCH} ${FTP2_DIR} ${VERSION} wait compare_distrib_checksum ${ARCH} ${FTP1_DIR} ${VERSION} wait compare_distrib_checksum ${ARCH} ${FTP2_DIR} ${VERSION} wait compare_local_checksum ${ARCH} ${FTP1_DIR} ${FTP2_DIR} ${VERSION} wait build_iso ${ARCH} ${FTP1_DIR} ${VERSION} ${ISO_NAME} ${TIMESTAMP1} wait exit 0