#!/bin/sh # # $NetBSD: postinstall,v 1.13 2002/05/14 22:41:36 thorpej Exp $ # # Copyright (c) 2002 The NetBSD Foundation, Inc. # All rights reserved. # # This code is derived from software contributed to The NetBSD Foundation # by Luke Mewburn. # # 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. # 3. All advertising materials mentioning features or use of this software # must display the following acknowledgement: # This product includes software developed by the NetBSD # Foundation, Inc. and its contributors. # 4. Neither the name of The NetBSD Foundation nor the names of its # contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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. # # postinstall # check for or fix configuration changes that occur # over time as NetBSD evolves. # # # checks to add: # - de* -> tlp* migration (/etc/ifconfig.de*, $ifconfig_de*, # dhclient.conf, ...) ? # - support quiet/verbose mode ? # - postfix # - obsolete sets # # # helper functions # err() { exitval=$1 shift echo 1>&2 "${PROGNAME}: $*" exit ${exitval} } warn() { echo 1>&2 "${PROGNAME}: $*" } msg() { echo " $*" } # additem item description # add item to list of supported items to check/fix # additem() { [ $# -eq 2 ] || err 2 "USAGE: additem item description" items="${items}${items:+ }$1" eval desc_$1=\"$2\" } # cmpdir op src dest mode file [file ...] # perform op ("check" or "fix") on files in src/ against dest/ # cmpdir() { [ $# -ge 5 ] || err 2 "USAGE: cmpdir op src dest mode file [file ...]" _op=$1 _src=$2 _dest=$3 _mode=$4 shift 4 _files=$* if [ ! -d "${_dest}" ]; then if [ "${_op}" = "check" ]; then msg "${_dest} is not a directory" return 1 elif ! mkdir ${_dest} ; then msg "Can't create missing ${_dest}" return 1 else msg "Missing ${_dest} created" fi fi _cmpdir_rv=0 for f in ${_files}; do fs=${_src}/${f} fd=${_dest}/${f} error="" if [ ! -f "${fd}" ]; then error="${fd} does not exist" elif ! cmp -s ${fs} ${fd} ; then error="${fd} != ${fs}" else continue fi if [ "${_op}" = "check" ]; then msg ${error} _cmpdir_rv=1 elif ! cp -f ${fs} ${fd}; then msg "Can't copy ${fs} to ${fd}" _cmpdir_rv=1 elif ! chmod ${_mode} ${fd}; then msg "Can't change mode of ${fd} to ${_mode}" _cmpdir_rv=1 else msg "Copied ${fs} to ${fd}" fi done return $_cmpdir_rv } # rcconf_isset name var -- # load the rcconf for name, and check if obsolete rc.conf(5) variable # var is defined or not. returns 0 if defined (even to ""), otherwise 1. # rcconf_isset() { [ $# -eq 2 ] || err 2 "USAGE: rcconf_isset name var" _name=$1 _var=$2 ( for f in \ ${DEST_DIR}/etc/rc.conf \ ${DEST_DIR}/etc/rc.conf.d/${_name}; do [ -f "${f}" ] && . "${f}"; done if eval "[ -n \"\${${_var}}\" \ -o \"\${${_var}-UNSET}\" != \"UNSET\" ]"; then msg "Obsolete rc.conf(5) variable '\$${_var}' found." exit 0 else exit 1 fi ) } # move_file op src dest -- # check (op == "check") or move (op != "check") from src to dest. # returns 0 if ok, 1 otherwise. # move_file() { [ $# -eq 3 ] || err 2 "USAGE: move_file op src dest" _fm_op=$1 _fm_src=$2 _fm_dest=$3 if [ -f "${_fm_src}" -a ! -f "${_fm_dest}" ]; then if [ "${_fm_op}" = "check" ]; then msg "Move ${_fm_src} to ${_fm_dest}" return 1 fi if ! mv ${_fm_src} ${_fm_dest}; then msg "Can't move ${_fm_src} to ${_fm_dest}" return 1 fi msg "Moved ${_fm_src} to ${_fm_dest}" fi return 0 } # # items # ----- # # # defaults # additem defaults "/etc/defaults being up to date" do_defaults() { [ -n "$1" ] || err 2 "USAGE: do_defaults fix|check" cmpdir $1 ${SRC_DIR}/etc/defaults ${DEST_DIR}/etc/defaults 444 \ daily.conf monthly.conf rc.conf security.conf weekly.conf } # # mtree # additem mtree "/etc/mtree being up to date" do_mtree() { [ -n "$1" ] || err 2 "USAGE: do_mtree fix|check" cmpdir $1 ${SRC_DIR}/etc/mtree ${DEST_DIR}/etc/mtree 444 \ NetBSD.dist special } # # periodic # additem periodic "/etc/{daily,weekly,monthly,security} being up to date" do_periodic() { [ -n "$1" ] || err 2 "USAGE: do_periodic fix|check" cmpdir $1 ${SRC_DIR}/etc ${DEST_DIR}/etc 644 \ daily weekly monthly security } # # rc # additem rc "/etc/rc* and /etc/rc.d/ being up to date" do_rc() { [ -n "$1" ] || err 2 "USAGE: do_rc fix|check" op=$1 failed=0 cmpdir ${op} ${SRC_DIR}/etc ${DEST_DIR}/etc 644 \ rc rc.subr rc.shutdown failed=$(( ${failed} + $? )) cmpdir ${op} ${SRC_DIR}/etc/rc.d ${DEST_DIR}/etc/rc.d 555 \ DAEMON LOGIN NETWORKING SERVERS accounting altqd amd \ apmd bootparams bootconf.sh ccd cleartmp cron \ dhclient dhcpd dhcrelay dmesg downinterfaces fsck \ ifwatchd inetd ipfilter ipfs ipmon ipnat ipsec isdnd \ kdc ldconfig lkm1 lkm2 lkm3 local lpd mopd motd \ mountall mountcritlocal mountcritremote mountd moused \ mrouted named ndbootd network newsyslog nfsd \ nfslocking ntpd ntpdate poffd postfix ppp pwcheck \ quota racoon rpcbind raidframe rarpd rbootd root \ route6d routed rtadvd rtsold rwho savecore \ screenblank sendmail securelevel sshd swap1 swap2 \ sysdb sysctl syslogd timed ttys virecover wscons xdm \ xfs ypbind yppasswdd ypserv failed=$(( ${failed} + $? )) # check for obsolete rc.d files for f in NETWORK gated; do fd=${DEST_DIR}/etc/rc.d/${f} [ ! -e "${fd}" ] && continue if [ "${op}" = "check" ]; then msg "Remove obsolete ${fd}" failed=1 elif ! rm ${fd}; then msg "Can't remove obsolete ${fd}" failed=1 else msg "Removed obsolete ${fd}" fi done # check for obsolete rc.conf(5) variables set -- amd amd_master \ mountcritlocal critical_filesystems_beforenet \ mountcritremote critical_filesystems \ network ip6forwarding \ sysctl defcorename \ sysctl nfsiod_flags while [ $# -gt 1 ]; do if rcconf_isset $1 $2; then failed=1 fi shift 2 done return ${failed} } # # ssh # additem ssh "ssh configuration file update" do_ssh() { [ -n "$1" ] || err 2 "USAGE: do_ssh fix|check" op=$1 failed=0 _etcssh=${DEST_DIR}/etc/ssh if [ ! -d "${_etcssh}" ]; then if [ "${op}" = "check" ]; then msg "${_etcssh} is not a directory" failed=1 elif ! mkdir ${_etcssh} ; then msg "Can't create missing ${_etcssh}" failed=1 else msg "Missing ${_etcssh} created" fi fi if [ ${failed} -eq 0 ]; then for f in \ ssh_known_hosts ssh_known_hosts2 \ ssh_host_dsa_key ssh_host_dsa_key.pub \ ssh_host_rsa_key ssh_host_rsa_key.pub \ ssh_host_key ssh_host_key.pub \ ; do if ! move_file ${op} \ ${DEST_DIR}/etc/${f} ${_etcssh}/${f} ; then failed=1 fi done for f in sshd.conf ssh.conf ; do # /etc/ssh/ssh{,d}.conf -> ssh{,d}_config # if ! move_file ${op} \ ${_etcssh}/${f} ${_etcssh}/${f%.conf}_config ; then failed=1 fi # /etc/ssh{,d}.conf -> /etc/ssh/ssh{,d}_config # if ! move_file ${op} \ ${DEST_DIR}/etc/${f} ${_etcssh}/${f%.conf}_config ; then failed=1 fi done fi sshdconf="" for f in \ ${_etcssh}/sshd_config \ ${_etcssh}/sshd.conf \ ${DEST_DIR}/etc/sshd.conf ; do if [ -f "${f}" ]; then sshdconf=${f} break; fi done if [ -n "${sshdconf}" ]; then awk ' $1 ~ /^[Hh][Oo][Ss][Tt][Kk][Ee][Yy]$/ && $2 ~ /^\/etc\/+ssh_host(_[dr]sa)?_key$/ { sub(/\/etc\/+/, "/etc/ssh/"); } { print } ' < ${sshdconf} > ${SCRATCHDIR}/sshd_config if ! cmp -s ${sshdconf} ${SCRATCHDIR}/sshd_config; then diff ${sshdconf} ${SCRATCHDIR}/sshd_config > \ ${SCRATCHDIR}/sshd_config.diffs if [ "${op}" = "check" ]; then msg "${sshdconf} needs the following changes:" failed=1 elif ! cp -f ${SCRATCHDIR}/sshd_config ${sshdconf}; then msg "${sshdconf} changes not applied:" failed=1 else msg "${sshdconf} changes applied:" fi while read _line; do msg " ${_line}" done < ${SCRATCHDIR}/sshd_config.diffs fi fi if rcconf_isset sshd sshd_conf_dir ; then failed=1 fi return ${failed} } # # wscons # additem wscons "wscons configuration file update" do_wscons() { [ -n "$1" ] || err 2 "USAGE: do_wscons fix|check" op=$1 [ -f ${DEST_DIR}/etc/wscons.conf ] || return 0 failed=0 while read _type _arg1 _rest; do if [ "$_type" = "mux" -a "$_arg1" = "1" ]; then msg \ "Obsolete wscons.conf(5) entry \""$_type $_arg1"\" found." failed=1 fi done < ${DEST_DIR}/etc/wscons.conf return ${failed} } # # uid # additem uid "required UID" do_uid() { [ -n "$1" ] || err 2 "USAGE: do_uid fix|check" op=$1 failed=0 _oIFS="$IFS" IFS=: _named=0 _ntpd=0 while read _name _junk; do if [ "$_name" = "named" ]; then _named=1 fi if [ "$_name" = "ntpd" ]; then _ntpd=1 fi done < ${DEST_DIR}/etc/passwd if [ "$_named" = "0" ]; then msg "Required UID named is not found." failed=1 fi if [ "$_ntpd" = "0" ]; then msg "Required UID ntpd is not found." failed=1 fi IFS="$_oIFS" return ${failed} } # # gid # additem gid "required GID" do_gid() { [ -n "$1" ] || err 2 "USAGE: do_gid fix|check" op=$1 failed=0 _oIFS="$IFS" IFS=: _named=0 _ntpd=0 while read _name _junk; do if [ "$_name" = "named" ]; then _named=1 fi if [ "$_name" = "ntpd" ]; then _ntpd=1 fi done < ${DEST_DIR}/etc/group if [ "$_named" = "0" ]; then msg "Required GID named is not found." failed=1 fi if [ "$_ntpd" = "0" ]; then msg "Required GID ntpd is not found." failed=1 fi IFS="$_oIFS" return ${failed} } # # end of items # ------------ # usage() { cat 1>&2 << _USAGE_ Usage: ${PROGNAME} [-s srcdir] [-d destdir] operation [item [...]] Perform post-installation checks and/or fixes on a system's configuration files. If no items are provided, all checks or fixes are applied. Options: -s srcdir Source directory to compare from. [${SRC_DIR}] -d destdir Destination directory to check. [${DEST_DIR}] Operation may be one of: help display this help list list available items check perform post-installation checks on items fix apply fixes that 'check' determines need to be applied usage display this usage _USAGE_ exit 1 } list() { echo "Supported items:" echo " Item Description" echo " ---- -----------" for i in ${items}; do eval desc="\${desc_${i}}" printf " %-12s %s\n" "${i}" "${desc}" done } main() { while getopts s:d: ch; do case ${ch} in s) SRC_DIR=${OPTARG} ;; d) DEST_DIR=${OPTARG} ;; *) usage ;; esac done shift $((${OPTIND} - 1)) [ $# -gt 0 ] || usage [ -d "${SRC_DIR}" ] || err 1 "${SRC_DIR} is not a directory" [ -d "${DEST_DIR}" ] || err 1 "${DEST_DIR} is not a directory" op=$1 shift case "${op}" in usage|help) usage ;; list) echo "Source directory: ${SRC_DIR}" echo "Target directory: ${DEST_DIR}" list ;; check|fix) todo=$* : ${todo:=${items}} # ensure that all supplied items are valid # for i in ${todo}; do eval desc=\"\${desc_${i}}\" [ -n "${desc}" ] || err 1 "Unsupported ${op} '"${i}"'" done # perform each check/fix # echo "Source directory: ${SRC_DIR}" echo "Target directory: ${DEST_DIR}" items_passed= items_failed= for i in ${todo}; do echo "${i} ${op}:" ( eval do_${i} ${op} ) if [ $? -eq 0 ]; then items_passed="${items_passed} ${i}" else items_failed="${items_failed} ${i}" fi done if [ "${op}" = "check" ]; then plural="checks" else plural="fixes" fi echo "${PROGNAME} ${plural} passed:${items_passed}" echo "${PROGNAME} ${plural} failed:${items_failed}" ;; *) warn "Unknown operation '"${op}"'" usage ;; esac } mkdtemp () { # Make sure we don't loop forever if mkdir will always fail. [ -d /tmp ] || err 1 /tmp is not a directory [ -w /tmp ] || err 1 /tmp is not writeable _base=/tmp/_postinstall.$$ _serial=0 while true; do _dir=${_base}.${_serial} mkdir -m 0700 ${_dir} && break _serial=$((${_serial} + 1)) done echo ${_dir} } # defaults # PROGNAME=${0##*/} SRC_DIR="/usr/src" DEST_DIR="/" SCRATCHDIR=$( mkdtemp ) || err 1 "Can't create scratch directory" trap "/bin/rm -rf ${SCRATCHDIR} ; exit 0" 0 1 2 3 15 # EXIT HUP INT QUIT TERM umask 022 main $* exit 0