GSP-Agent-Windows/OGP64/usr/share/csih/cygwin-service-installation-helper.sh

3457 lines
120 KiB
Bash

# -- #!/bin/bash --
# cygwin-service-installation-helper.sh
#
# Copyright (c) 2010-2015 Charles S. Wilson, Corinna Vinschen,
# Pierre Humblett, and others listed in
# AUTHORS
#
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation
# files (the "Software"), to deal in the Software without
# restriction, including without limitation the rights to use,
# copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following
# conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
# OTHER DEALINGS IN THE SOFTWARE
#
# -#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-
#
# This is a script library used to assist installing cygwin
# services, such as sshd. It is derived in part from various
# other sources (see AUTHORS).
#
# Do not attempt to run this file. Instead, it should be "sourced" by
# configuration scripts (such as a newer version of ssh-host-config,
# syslog-config, or iu-config) -- and that script should then use
# the shell functions defined here.
#
# -#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-
#
# REQUIREMENTS:
# SHELL must be bash
#
# PROVIDES:
# csih_error
# csih_error_multi
# csih_error_recoverable
# csih_warning
# csih_inform
# csih_verbose
# csih_request
# csih_get_value
# csih_get_cygenv
# csih_is_nt
# csih_is_2k
# csih_is_xp
# csih_is_nt2003
# csih_is_vista
# csih_is_windows7
# csih_is_windows8
# csih_is_windows8_1
# csih_is_windows10
# csih_is_exactly_vista
# csih_is_exactly_server2008
# csih_is_exactly_windows7
# csih_is_exactly_windows8
# csih_is_exactly_windows8_1
# csih_is_exactly_windows10
# csih_is_exactly_server2008r2
# csih_is_exactly_server2012
# csih_is_exactly_server2012r2
# csih_is_exactly_server2016
# csih_version_ge
# csih_version_le
# csih_version_gt
# csih_version_lt
# csih_version_eq
# csih_check_program
# csih_check_program_or_warn
# csih_check_program_or_error
# csih_invoke_helper
# csih_call_winsys32
# csih_get_localized_account_name
# csih_get_guest_account_name
# csih_guest_account_active
# csih_install_config
# csih_make_dir
# csih_get_system_and_admins_ids
# csih_check_passwd_and_group
# csih_old_cygwin
# csih_use_file_etc
# csih_check_user
# csih_check_dir_perms
# csih_check_access
# csih_check_sys_mount
# csih_privileged_accounts
# csih_privileged_account_exists
# csih_account_has_necessary_privileges
# csih_select_privileged_username
# csih_create_privileged_user
# csih_create_unprivileged_user
# csih_create_local_group
# csih_service_should_run_as
# csih_disable_color
# csih_enable_color
# csih_path_supports_acls
# csih_cygver
# csih_cygver_is_oneseven
# csih_cygwin_is_64bit
# csih_win_product_name
# csih_writable_tmpdir
# csih_mktemp
#
# DEBUG SUPPORT:
# csih_stacktrace
# csih_trace_on
# csih_trace_off
#
# MUTABLE VARIABLES:
# csih_sanity_check_server
# if "yes", the initial sanity check will test for tools only used when
# installing a service. As of the time of writing these are "cygrunsrv"
# and "editrights".
# This variable must be set in the calling installer script *before*
# sourcing cygwin-service-installation-helper.sh, otherwise it has no
# effect.
# csih_required_commands[]
# This variable, too, must be set in the calling installer script *before*
# sourcing cygwin-service-installation-helper.sh, otherwise it has no
# effect.
# An array which contains commands used by the calling installer script
# and the Cygwin package the command is part of. The commands are tested
# for existence at startup. If one or more commands don't exist or are
# not executable, cygwin-service-installation-helper.sh provides a helpful
# error message and refuses to run.
# The array has to be organized in pairs command/package. The commands
# *shall* be given with full path, and the installer script *shall* use
# all external commands always with full paths as well to make sure the
# required commands are not clobbered by other commands with the same
# name due to the settings of $PATH.
# Here's an example how csih_required_commands should be declared in the
# calling installer script:
# declare -a csih_required_commands[]=(
# /usr/bin/ls coreutils
# /usr/bin/passwd cygwin
# /usr/sbin/telnetd inteutils
# )
# csih_FORCE_PRIVILEGED_USER
# if "yes", then create a privileged user even on NT/2k/XP(32)
# where it is not required (on those versions, LocalSystem
# will do fine).
# Set by caller foo-config.
# NOTE: on NT/2k/XP(32), IF a well-known privileged user already
# exists and has all necessary capabilities, then it will
# be used regardless. This variable forces the creation
# and use of a privileged user, on NT/2k/XP(32), when one does
# not already exist.
# SYSCONFDIR
# default value = "/etc", or set by caller foo-config.
# LOCALSTATEDIR
# default value = "/var", or set by caller foo-config.
# csih_auto_answer
# default value = "" (no automatic answers)
# Set by caller foo-config
# csih_value
# set by csih_get_value
# foo-config should treat as read-only
# csih_cygenv
# set by csih_get_cygenv
# foo-config should treat as read-only
# csih_localized_account_name
# set by csih_get_localized_account_name, csih_get_guest_account_name
# foo-config should treat as read-only
# csih_helper_stdout
# csih_helper_stderr
# set by csih_invoke_helper and stores the stdout/stderr of the target
# program. However, if csih_invoke_helper is called in a subshell (e.g.
# myResult=$(csih_invoke_helper target arg1 arg2)
# then these values are not available to the caller outside that subshell.
# foo-config should treat as read-only
# csih_ADMINSGID
# csih_ADMINSUID
# csih_SYSTEMGID
# csih_SYSTEMUID
# these four are set by csih_get_system_and_admins_ids (called by _csih_setup)
# foo-config should treat as read-only
# csih_PRIVILEGED_USERNAME
# Set by csih_select_privileged_username (or by calling
# csih_create_privileged_user, or as a side-effect of calling
# csih_service_should_run_as) but may be set explicitly by foo-config
# foo-config should treat as read-only after first call to any csih_*
# function
# csih_PRIVILEGED_USERWINNAME
# Set by csih_select_privileged_username. While csih_PRIVILEGED_USERNAME
# is the Cygwin username, csih_PRIVILEGED_USERWINNAME is the Windows
# username. This can be used in conjunction with...
# csih_PRIVILEGED_USERDOMAIN
# Set by csih_select_privileged_username. This is the Windows domain or,
# on non-domain machines, the machine name.
# csih_PRIVILEGED_PASSWORD
# Set by csih_create_privileged_user
# foo-config treat as read-only. To "prime" the value, pass as first argument
# when calling csih_create_privileged_user
# csih_UNPRIVILEGED_USERNAME
# Set by calling csih_create_unprivileged_user.
# csih_UNPRIVILEGED_USERWINNAME
# Set by calling csih_create_unprivileged_user.
# While csih_UNPRIVILEGED_USERNAME is the Cygwin username,
# csih_UNPRIVILEGED_USERWINNAME is the Windows username. This can be
# used in conjunction with...
# csih_UNPRIVILEGED_USERDOMAIN
# Set by calling csih_create_unprivileged_user. This is the Windows
# domain or, on non-domain machines, the machine name.
# csih_LOCAL_GROUPNAME
# Set by calling csih_create_local_group.
# csih_LOCAL_GROUPWINNAME
# Set by calling csih_create_local_group.
# While csih_LOCAL_GROUPNAME is the Cygwin username,
# csih_LOCAL_GROUPWINNAME is the Windows username. This can be
# used in conjunction with...
# csih_LOCAL_GROUPDOMAIN
# Set by calling csih_create_local_group. This is the Windows
# domain or, on non-domain machines, the machine name.
# csih_WIN32_VOLS_WITH_ACLS
# a ;-separated list of windows volumes that are guaranteed to support
# ACLS, even if the getVolInfo program doesn't think so. Used to override
# getVolInfo when it is wrong (use only as a last resort; getVolInfo is
# actually faster)
# Set by caller foo-config
# csih_WIN32_VOLS_WITHOUT_ACLS=
# a ;-separated list of windows volumes that do NOT support ACLS, even if
# the getVolInfo program thinks so. Used to override getVolInfo when it
# is wrong (use only as a last resort; getVolInfo is actually faster)
# Set by caller foo-config
#
# CONST variables
# csih_VERSION
# csih_progname
# csih_progname_base
#
# ======================================================================
# Initial setup, default values, etc. PART 1
# ======================================================================
csih_progname=$0
csih_VERSION=0.9.8
readonly csih_progname csih_VERSION
csih_auto_answer=""
csih_value=
csih_cygenv=
csih_localized_account_name=
csih_FORCE_PRIVILEGED_USER=no
csih_ADMINSGID=
csih_ADMINSUID=
csih_SYSTEMGID=
csih_SYSTEMUID=
csih_PRIVILEGED_USERNAME=
csih_PRIVILEGED_USERWINNAME=
csih_PRIVILEGED_USERDOMAIN=
csih_PRIVILEGED_PASSWORD=
csih_UNPRIVILEGED_USERNAME=
csih_UNPRIVILEGED_USERWINNAME=
csih_UNPRIVILEGED_USERDOMAIN=
csih_LOCAL_GROUPNAME=
csih_LOCAL_GROUPWINNAME=
csih_LOCAL_GROUPDOMAIN=
csih_helper_stdout=
csih_helper_stderr=
csih_WIN32_VOLS_WITH_ACLS=
csih_WIN32_VOLS_WITHOUT_ACLS=
# delay initialization
_csih_exec_dir=
_csih_script_dir=
_csih_trace=
_csih_win_product_name="unknown"
if [ -z "${SYSCONFDIR}" ]
then
SYSCONFDIR=/etc
fi
if [ -z "${LOCALSTATEDIR}" ]
then
LOCALSTATEDIR=/var
fi
_csih_all_preexisting_privileged_accounts=
_csih_preferred_preexisting_privileged_account=
_csih_setup_already_called=0
_csih_writable_tmpdir_cache_value=
_csih_version_parse_pkg_major=
_csih_version_parse_pkg_minor=
_csih_version_parse_pkg_micro=
_csih_w32vol_as_shell_pattern=
_csih_w32vol_as_shell_pattern_trailing_slash=
_csih_well_known_privileged_accounts="cyg_server
sshd_server
cron_server
$COMPUTERNAME+cyg_server
$COMPUTERNAME+sshd_server
$COMPUTERNAME+cron_server"
_csih_well_known_privileged_accounts_quoted="'cyg_server'
'sshd_server'
'cron_server'
'$COMPUTERNAME+cyg_server'
'$COMPUTERNAME+sshd_server'
'$COMPUTERNAME+cron_server'"
readonly _csih_well_known_privileged_accounts _csih_well_known_privileged_accounts_quoted
_csih_ERROR_STR_COLOR="\e[1;31m*** ERROR:\e[0;0m"
_csih_WARN_STR_COLOR="\e[1;33m*** Warning:\e[0;0m"
_csih_INFO_STR_COLOR="\e[1;32m*** Info:\e[0;0m"
_csih_QUERY_STR_COLOR="\e[1;35m*** Query:\e[0;0m"
_csih_STACKTRACE_STR_COLOR="\e[1;36m*** STACKTRACE:\e[0;0m"
readonly _csih_ERROR_STR_COLOR _csih_WARN_STR_COLOR
readonly _csih_INFO_STR_COLOR _csih_QUERY_STR_COLOR _csih_STACKTRACE_STR_COLOR
_csih_ERROR_STR_PLAIN="*** ERROR:"
_csih_WARN_STR_PLAIN="*** Warning:"
_csih_INFO_STR_PLAIN="*** Info:"
_csih_QUERY_STR_PLAIN="*** Query:"
_csih_STACKTRACE_STR_PLAIN="*** STACKTRACE:"
readonly _csih_ERROR_STR_PLAIN _csih_WARN_STR_PLAIN
readonly _csih_INFO_STR_PLAIN _csih_QUERY_STR_PLAIN _csih_STACKTRACE_STR_PLAIN
_csih_ERROR_STR="${_csih_ERROR_STR_COLOR}"
_csih_WARN_STR="${_csih_WARN_STR_COLOR}"
_csih_INFO_STR="${_csih_INFO_STR_COLOR}"
_csih_QUERY_STR="${_csih_QUERY_STR_COLOR}"
_csih_STACKTRACE_STR="${_csih_STACKTRACE_STR_COLOR}"
# ======================================================================
# Routine: csih_disable_color
# Provided so that scripts which are invoked via postinstall
# can prevent escape codes from showing up in /var/log/setup.log
# ======================================================================
csih_disable_color()
{
_csih_ERROR_STR="${_csih_ERROR_STR_PLAIN}"
_csih_WARN_STR="${_csih_WARN_STR_PLAIN}"
_csih_INFO_STR="${_csih_INFO_STR_PLAIN}"
_csih_QUERY_STR="${_csih_QUERY_STR_PLAIN}"
_csih_STACKTRACE_STR="${_csih_STACKTRACE_STR_PLAIN}"
} # === End of csih_disable_color() === #
readonly -f csih_disable_color
# ======================================================================
# Routine: csih_enable_color
# ======================================================================
csih_enable_color()
{
_csih_ERROR_STR="${_csih_ERROR_STR_COLOR}"
_csih_WARN_STR="${_csih_WARN_STR_COLOR}"
_csih_INFO_STR="${_csih_INFO_STR_COLOR}"
_csih_QUERY_STR="${_csih_QUERY_STR_COLOR}"
_csih_STACKTRACE_STR="${_csih_STACKTRACE_STR_COLOR}"
} # === End of csih_enable_color() === #
readonly -f csih_enable_color
# ======================================================================
# Routine: csih_stacktrace
# ======================================================================
csih_stacktrace()
{
set +x # don''t trace this!
local -i n=$(( ${#FUNCNAME} - 1 ))
local val=""
if [ -n "$_csih_trace" ]
then
while [ $n -gt 0 ]
do
if [ -n "${FUNCNAME[$n]}" ]
then
if [ -z "$val" ]
then
val="${FUNCNAME[$n]}[${BASH_LINENO[$(($n-1))]}]"
else
val="${val}->${FUNCNAME[$n]}[${BASH_LINENO[$(($n-1))]}]"
fi
fi
n=$(($n-1))
done
echo -e "${_csih_STACKTRACE_STR} ${val} ${@}" 1>&2
fi
} # === End of csih_stacktrace() === #
readonly -f csih_stacktrace
# ======================================================================
# Routine: csih_trace_on
# turns on shell tracing of csih functions
# ======================================================================
csih_trace_on()
{
_csih_trace='set -x'
trap 'csih_stacktrace "returning with" $?; set -x' RETURN
set -T
csih_stacktrace "${@}"
} # === End of csih_trace_on() === #
readonly -f csih_trace_on
# ======================================================================
# Routine: csih_trace_off
# turns off shell tracing of csih functions
# ======================================================================
csih_trace_off()
{
trap '' RETURN
csih_stacktrace "${@}"
_csih_trace=
set +x
set +T
} # === End of csih_trace_off() === #
readonly -f csih_trace_off
# ======================================================================
# Routine: csih_error
# Prints the (optional) error message $1, then
# Exits with the error code contained in $? if $? is non-zero, otherwise
# exits with status 1
# All other arguments are ignored
# Example: csih_error "missing file"
# NEVER RETURNS
# ======================================================================
csih_error()
{
local errorcode=$?
set +x # don't trace this, but we are interested in who called
csih_stacktrace # we'll see the arguments in the next statement
if ((errorcode == 0))
then
errorcode=1
fi
echo -e "${_csih_ERROR_STR} ${1:-no error message provided}"
exit ${errorcode};
} # === End of csih_error() === #
readonly -f csih_error
# ======================================================================
# Routine: csih_error_multi
# Prints the (optional) error messages in the positional arguments, one
# per line, and then
# Exits with the error code contained in $? if $? is non-zero, otherwise
# exits with status 1
# All other arguments are ignored
# Example: csih_error_multi "missing file" "see documentation"
# NEVER RETURNS
# ======================================================================
csih_error_multi()
{
local errorcode=$?
set +x # don't trace this, but we are interested in who called
csih_stacktrace # we'll see the arguments in the next statement
if ((errorcode == 0))
then
errorcode=1
fi
while test $# -gt 1
do
echo -e "${_csih_ERROR_STR} ${1}"
shift
done
echo -e "${_csih_ERROR_STR} ${1:-no error message provided}"
exit ${errorcode};
} # === End of csih_error_multi() === #
readonly -f csih_error_multi
# ======================================================================
# Routine: csih_error_recoverable
# Prints the supplied errormessage, and propagates the $? value
# Example: csih_error_recoverable "an error message"
# ======================================================================
csih_error_recoverable()
{
local errorcode=$?
set +x # don't trace this, but we are interested in who called
csih_stacktrace # we'll see the arguments in the next statement
echo -e "${_csih_ERROR_STR} ${1}"
$_csih_trace
return $errorcode
} # === End of csih_error_recoverable() === #
readonly -f csih_error_recoverable
# ======================================================================
# Routine: csih_warning
# Prints the supplied warning message
# Example: csih_warning "replacing default file foo"
# ======================================================================
csih_warning()
{
set +x # don't trace this, but we are interested in who called
csih_stacktrace # we'll see the arguments in the next statement
echo -e "${_csih_WARN_STR} ${1}"
$_csih_trace
} # === End of csih_warning() === #
readonly -f csih_warning
# ======================================================================
# Routine: csih_inform
# Prints the supplied informational message
# Example: csih_inform "beginning dependency analysis..."
# ======================================================================
csih_inform()
{
set +x # don't trace this, but we are interested in who called
csih_stacktrace # we'll see the arguments in the next statement
echo -e "${_csih_INFO_STR} ${1}"
$_csih_trace
} # === End of csih_inform() === #
readonly -f csih_inform
# ======================================================================
# Routine: csih_verbose
# prints the supplied command line
# executes it
# returns the error status of the command
# Example: csih_verbose /usr/bin/rm -f /etc/config-file
# ======================================================================
csih_verbose()
{
set +x # don't trace this, but we are interested in who called
local rstatus
csih_stacktrace # we'll see the arguments in the next statement
echo "${@}" 1>&2
"${@}"
rstatus=$?
$_csih_trace
return $rstatus
} # === End of csih_verbose() === #
readonly -f csih_verbose
# ======================================================================
# Routine: csih_check_program
# Check to see that the specified program(s) ($1, $2, ...) are installed
# and executable by this script. Returns as soon as it encounters a
# missing or non-executable program. Returns 1 if it can't find the
# program, 2 if it's not executable.
# ======================================================================
csih_check_program()
{
csih_stacktrace "${@}"
$_csih_trace
local prog
local fullpath
for prog
do
# To maintain backward compatibility, check if hash can find programs
# which are not given with full path.
if ! [[ "${prog}" =~ ^/ ]]
then
hash "${prog}" 2>/dev/null || return 1
prog="$(hash -t "${prog}")"
fi
test -e ${prog} || return 1
test -x ${prog} || return 2
done
return 0;
} # === End of csih_check_program() === #
readonly -f csih_check_program
# ======================================================================
# Routine: csih_check_program_or_warn
# Check to see that a specified program ($1) is installed and executable
# by this script. If not, warn the user that a particular package
# ($2) is required and return non-zero (false)
# Otherwise, return 0 (true)
# ======================================================================
csih_check_program_or_warn()
{
csih_stacktrace "${@}"
$_csih_trace
local prog=${1};
local pkg=${2:-${1}};
csih_check_program "${prog}"
case $? in
0)
return 0
;;
1)
csih_warning "Cannot find required command $prog."
;;
2)
csih_warning "Cannot execute required program $prog."
;;
esac
csih_warning "Please (re)install the ${pkg} package.";
return 1;
} # === End of csih_check_program_or_warn() === #
readonly -f csih_check_program_or_warn
# ======================================================================
# Routine: csih_check_program_or_error
# Check to see that a specified program ($1) is installed and executable
# by this script. If not, inform the user that a particular package
# ($2) is required, and exit with error
# ======================================================================
csih_check_program_or_error()
{
csih_stacktrace "${@}"
$_csih_trace
local prog=${1};
local pkg=${2:-${1}};
csih_check_program "${prog}"
case $? in
0)
return 0
;;
1)
csih_error_recoverable "Cannot find required command $prog."
;;
2)
csih_error_recoverable "Cannot execute required program $prog."
;;
esac
csih_error "Please (re)install the ${pkg} package.";
return 1;
} # === End of csih_check_program_or_error() === #
readonly -f csih_check_program_or_error
# ======================================================================
# Routine: _csih_sanity_check
# Check for the set of programs that are used by this script.
# Exits on failure
# ======================================================================
_csih_sanity_check()
{
csih_stacktrace "${@}"
$_csih_trace
local ret=0
# Check for programs that this script uses.
local -a cmd=(
/usr/bin/awk gawk
/usr/bin/basename coreutils
/usr/bin/chmod coreutils
/usr/bin/cat coreutils
/usr/bin/chown coreutils
/usr/bin/cp coreutils
/usr/bin/cut coreutils
/usr/bin/cygpath cygwin
/usr/bin/dirname coreutils
/usr/bin/expr coreutils
/usr/bin/getent getent
/usr/bin/getfacl cygwin
/usr/bin/grep grep
/usr/bin/id coreutils
/usr/bin/ls coreutils
/usr/bin/mkdir coreutils
/usr/bin/mkgroup cygwin
/usr/bin/mkpasswd cygwin
/usr/bin/mktemp coreutils
/usr/bin/mount cygwin
/usr/bin/mv cygwin
/usr/bin/rm coreutils
/usr/bin/sed sed
/usr/bin/setfacl cygwin
/usr/bin/stat coreutils
/usr/bin/tr coreutils
/usr/bin/uname coreutils
# do not check for editrights and cygrunsrv here -- clients which use this
# library may not need their functionality. See below for the function
# csih_sanity_check_server which can be called by the main script to test
# for these commands.
)
for (( i=0; i < ${#cmd[*]}; i+=2 ))
do
csih_check_program_or_warn "${cmd[i]}" "${cmd[i+1]}" || ret=1
done
if [ "${csih_sanity_check_server}" = "yes" \
-o "${csih_sanity_check_server}" = "1" ]
then
csih_check_program_or_warn /usr/bin/cygrunsrv cygrunsrv || ret=1
csih_check_program_or_warn /usr/bin/editrights editrights || ret=1
fi
# The calling script may add commands which should be checked.
# It can do so by defining an array called 'csih_required_commands'.
# It must consist of pairs command/package, just like the above 'cmd' array.
for (( i=0; i < ${#csih_required_commands[*]}; i+=2 ))
do
if ! [[ "${cmd[@]}" =~ "${csih_required_commands[i]}" ]]
then
csih_check_program_or_warn "${csih_required_commands[i]}" "${csih_required_commands[i+1]}" || ret=1
fi
done
if [ $ret -ne 0 ]
then
csih_error_multi \
"There is something badly wrong with your cygwin installation." \
"" \
"Please install the missing command(s), and make sure all required" \
"command are executable. Otherwise the installation provided by this" \
"script will fail." \
"" \
"To fix this problem, run {rh}setup.exe and (re)install the" \
"packages mentioned in the warnings above."
fi
} # === End of _csih_sanity_check() === #
readonly -f _csih_sanity_check
# ======================================================================
# DEPRECATED: csih_sanity_check
# Stub for backward compatibility
# ======================================================================
function csih_sanity_check()
{
csih_warning "$csih_progname_base is using deprecated "\
"function csih_sanity_check. Continuing..."
true
} # === End of csih_sanity_check() === #
readonly -f csih_sanity_check
# ======================================================================
# Initial setup, default values, etc. PART 2
#
# This part of the setup has to be deferred since first we have to make
# sure that all required tools are available. This makes for a bit of
# mess as far as the order of function definitions goes, but since the
# file is sourced, we have to make sure that certain tests are running
# first, especially the test for missing commands used by this script.
#
# Consequentially the call to _csih_sanity_check is done first.
# ======================================================================
_csih_sanity_check
csih_progname_base=$(/usr/bin/basename -- $csih_progname)
readonly csih_progname_base
_csih_sys="$(/usr/bin/uname)"
_csih_xp=0
_csih_nt2003=0
_csih_vista=0
_csih_windows7=0
_csih_windows8=0
_csih_windows8_1=0
_csih_windows10=0
_csih_exactly_server2008=0
_csih_exactly_server2008r2=0
_csih_exactly_server2012=0
_csih_exactly_vista=0
_csih_exactly_windows7=0
_csih_exactly_windows8=0
_csih_exactly_windows8_1=0
_csih_exactly_windows10=0
# If running on NT, check if running under XP(64), 2003 Server, or later
_csih_xp=$(/usr/bin/uname | /usr/bin/awk -F- '{print ( $2 >= 5.1 ) ? 1 : 0;}')
_csih_nt2003=$(/usr/bin/uname | /usr/bin/awk -F- '{print ( $2 >= 5.2 ) ? 1 : 0;}') # also true for XP(64)
_csih_vista=$(/usr/bin/uname | /usr/bin/awk -F- '{print ( $2 >= 6.0 ) ? 1 : 0;}')
_csih_windows7=$(/usr/bin/uname | /usr/bin/awk -F- '{print ( $2 >= 6.1 ) ? 1 : 0;}')
_csih_windows8=$(/usr/bin/uname | /usr/bin/awk -F- '{print ( $2 >= 6.2 ) ? 1 : 0;}')
_csih_windows8_1=$(/usr/bin/uname | /usr/bin/awk -F- '{print ( $2 >= 6.3 ) ? 1 : 0;}')
_csih_windows10=$(/usr/bin/uname | /usr/bin/awk -F- '{print ( $2 >= 6.4 ) ? 1 : 0;}')
readonly _csih_sys _csih_xp _csih_nt2003 _csih_vista _csih_windows7 _csih_windows8 _csih_windows8_1 _csih_windows10
_csih_cygver=$(b=$(/usr/bin/uname -r) && echo "${b%%(*}")
_csih_cygver_is_oneseven=$(echo ${_csih_cygver} | /usr/bin/awk -F. '{print ( $1 > 1 || ($1 == 1 && $2 >= 7) ) ? 1 : 0;}')
_csih_cygwin_is_64bit=$(/usr/bin/uname -m | grep 'x86_64' >/dev/null && echo 1 || echo 0)
readonly _csih_cygver _csih_cygver_is_oneseven _csih_cygwin_is_64bit
# ======================================================================
# Routine: csih_is_nt
# returns 0 (true)
# ======================================================================
csih_is_nt()
{
csih_stacktrace "${@}"
$_csih_trace
return 0
} # === End of csih_is_nt() === #
readonly -f csih_is_nt
# ======================================================================
# Routine: csih_is_2k
# returns 0 (true)
# ======================================================================
csih_is_2k()
{
csih_stacktrace "${@}"
$_csih_trace
return 0
} # === End of csih_is_2k() === #
readonly -f csih_is_2k
# ======================================================================
# Routine: csih_is_xp
# returns 0 (true)
# ======================================================================
csih_is_xp()
{
csih_stacktrace "${@}"
$_csih_trace
return 0
} # === End of csih_is_xp() === #
readonly -f csih_is_xp
# ======================================================================
# Routine: csih_is_nt2003
# returns 0 (true) if the system is Windows XP 64bit, or Windows
# Server 2003 or above.
# returns 1 (false) otherwise
#
# Note that this routine is somewhat misnamed, since it returns true for
# 64bit Windows XP, but not 32bit Windows XP, as well as the nominal
# "NT Server 2003 and above" operating systems. This is because 64bit XP
# was developed by Microsoft after the release of the 32bit version, and
# unlike the older 32bit XP, the 64bit XP uses the same kernel as the
# (then also under development) Windows Server 2003. The key point is
# that all Windows operating systems with kernel >= 5.2 (e.g. 64bit
# Windows XP, Windows NT Server 2003, and above) share the same
# restriction with regards to the capabilities of the SYSTEM
# (LocalSystem) account: on those platforms, SYSTEM cannot change the
# effective user. This is a key capability for services, and many
# 'foo-config' clients use csih_is_nt2003 to check whether a special
# "privileged" user must be created, as do several csih_* functions
# such as csih_select_privileged_username().
# ======================================================================
csih_is_nt2003()
{
csih_stacktrace "${@}"
$_csih_trace
test ${_csih_nt2003} -gt 0
} # === End of csih_is_nt2003() === #
readonly -f csih_is_nt2003
# ======================================================================
# Routine: csih_is_vista
# returns 0 (true) if the system is Windows Vista/Windows Server 2008
# or above.
# returns 1 (false) otherwise
# ======================================================================
csih_is_vista()
{
csih_stacktrace "${@}"
$_csih_trace
test ${_csih_vista} -gt 0
} # === End of csih_is_vista() === #
readonly -f csih_is_vista
# ======================================================================
# Routine: csih_is_windows7
# returns 0 (true) if the system is Windows 7/Windows Server 2008r2
# or above.
# returns 1 (false) otherwise
# ======================================================================
csih_is_windows7()
{
csih_stacktrace "${@}"
$_csih_trace
test ${_csih_windows7} -gt 0
} # === End of csih_is_windows7() === #
readonly -f csih_is_windows7
# ======================================================================
# Routine: csih_is_windows8
# returns 0 (true) if the system is Windows 8/Windows 8 Server
# or above.
# returns 1 (false) otherwise
# ======================================================================
csih_is_windows8()
{
csih_stacktrace "${@}"
$_csih_trace
test ${_csih_windows8} -gt 0
} # === End of csih_is_windows8() === #
readonly -f csih_is_windows8
# ======================================================================
# Routine: csih_is_windows8_1
# returns 0 (true) if the system is Windows 8.1/Windows 8.1 Server
# or above.
# returns 1 (false) otherwise
# ======================================================================
csih_is_windows8_1()
{
csih_stacktrace "${@}"
$_csih_trace
test ${_csih_windows8_1} -gt 0
} # === End of csih_is_windows8_1() === #
readonly -f csih_is_windows8_1
# ======================================================================
# Routine: csih_is_windows10
# returns 0 (true) if the system is Windows 10/Windows 10 Server
# or above.
# returns 1 (false) otherwise
# ======================================================================
csih_is_windows10()
{
csih_stacktrace "${@}"
$_csih_trace
test ${_csih_windows10} -gt 0
} # === End of csih_is_windows10() === #
readonly -f csih_is_windows10
# ======================================================================
# Routine: csih_cygver
# returns the dotted-triple version number of the currently-running
# cygwin dll. Avoids forking uname multiple times.
# ======================================================================
csih_cygver()
{
csih_stacktrace "${@}"
$_csih_trace
echo $_csih_cygver
} # === End of csih_cygver() === #
readonly -f csih_cygver
# ======================================================================
# Routine: csih_cygver_is_oneseven
# returns 0 (true) if the currently-running cygwin dll is version
# 1.7.0 or above (including pre-release betas).
# returns 1 (false) otherwise
# ======================================================================
csih_cygver_is_oneseven()
{
csih_stacktrace "${@}"
$_csih_trace
return 0
} # === End of csih_cygver_is_oneseven() === #
readonly -f csih_cygver_is_oneseven
# ======================================================================
# Routine: csih_cygwin_is_64bit
# returns 0 (true) if the currently-running cygwin dll is 64bit
# returns 1 (false) otherwise
# ======================================================================
csih_cygwin_is_64bit()
{
csih_stacktrace "${@}"
$_csih_trace
test ${_csih_cygwin_is_64bit} -gt 0
} # === End of csih_cygwin_is_64bit() === #
readonly -f csih_cygwin_is_64bit
# ======================================================================
# Routine: csih_is_exactly_vista
# returns 0 (true) if the system is one of the variants of
# Windows Vista (Home Basic, Home Premium, Business, etc) but NOT
# Server2008 or some newer edition (like Windows7)
# returns 1 (false) otherwise
# ======================================================================
csih_is_exactly_vista()
{
csih_stacktrace "${@}"
$_csih_trace
test ${_csih_exactly_vista} -gt 0
} # === End of csih_is_exactly_vista() === #
#NOTE: do not make _csih_exactly_vista readonly YET
# ======================================================================
# Routine: csih_is_exactly_server2008
# returns 0 (true) if the system is one of the variants of
# Windows Server 2008 but NOT one of the variants of Vista, nor
# some newer edition (like Windows7)
# returns 1 (false) otherwise
# ======================================================================
csih_is_exactly_server2008()
{
csih_stacktrace "${@}"
$_csih_trace
test ${_csih_exactly_server2008} -gt 0
} # === End of csih_is_exactly_server2008() === #
#NOTE: do not make _csih_exactly_server2008 readonly YET
# ======================================================================
# Routine: csih_is_exactly_windows7
# returns 0 (true) if the system is one of the variants of
# Windows 7 (Home Premium, Professional, etc) but NOT Server2008R2
# or some newer edition like Windows 8.
# returns 1 (false) otherwise
# ======================================================================
csih_is_exactly_windows7()
{
csih_stacktrace "${@}"
$_csih_trace
test ${_csih_exactly_windows7} -gt 0
} # === End of csih_is_exactly_windows7() === #
#NOTE: do not make _csih_exactly_windows7 readonly YET
# ======================================================================
# Routine: csih_is_exactly_server2008r2
# returns 0 (true) if the system is one of the variants of
# Windows Server 2008 R2 but NOT one of the variants of Windows7,
# nor some newer edition.
# returns 1 (false) otherwise
# ======================================================================
csih_is_exactly_server2008r2()
{
csih_stacktrace "${@}"
$_csih_trace
test ${_csih_exactly_server2008r2} -gt 0
} # === End of csih_is_exactly_server2008r2() === #
#NOTE: do not make _csih_exactly_server2008r2 readonly YET
# ======================================================================
# Routine: csih_is_exactly_windows8
# returns 0 (true) if the system is one of the variants of
# Windows 8 (Home Premium, Professional, etc) but NOT
# Windows 8 Server or some newer edition.
# returns 1 (false) otherwise
# ======================================================================
csih_is_exactly_windows8()
{
csih_stacktrace "${@}"
$_csih_trace
test ${_csih_exactly_windows8} -gt 0
} # === End of csih_is_exactly_windows8() === #
#NOTE: do not make _csih_exactly_windows8 readonly YET
# ======================================================================
# Routine: csih_is_exactly_server2012
# returns 0 (true) if the system is one of the variants of
# Windows 2012 Server but NOT one of the variants of Windows 8,
# nor some newer edition.
# returns 1 (false) otherwise
# ======================================================================
csih_is_exactly_server2012()
{
csih_stacktrace "${@}"
$_csih_trace
test ${_csih_exactly_server2012} -gt 0
} # === End of csih_is_exactly_server2012() === #
#NOTE: do not make _csih_exactly_server2012 readonly YET
# ======================================================================
# Routine: csih_is_exactly_windows8_1
# returns 0 (true) if the system is one of the variants of
# Windows 8.1 (Home Premium, Professional, etc) but NOT
# Windows 8.1 Server or some newer edition.
# returns 1 (false) otherwise
# ======================================================================
csih_is_exactly_windows8_1()
{
csih_stacktrace "${@}"
$_csih_trace
e test ${_csih_exactly_windows8_1} -gt 0
} # === End of csih_is_exactly_windows8_1() === #
#NOTE: do not make _csih_exactly_windows8_1 readonly YET
# ======================================================================
# Routine: csih_is_exactly_server2012r2
# returns 0 (true) if the system is one of the variants of
# Windows 2012 Server but NOT one of the variants of Windows 8,
# nor some newer edition.
# returns 1 (false) otherwise
# ======================================================================
csih_is_exactly_server2012r2()
{
csih_stacktrace "${@}"
$_csih_trace
test ${_csih_exactly_server2012r2} -gt 0
} # === End of csih_is_exactly_server2012r2() === #
#NOTE: do not make _csih_exactly_server2012r2 readonly YET
# ======================================================================
# Routine: csih_is_exactly_windows10
# returns 0 (true) if the system is one of the variants of
# Windows 8 (Home Premium, Professional, etc) but NOT
# Windows 8 Server or some newer edition.
# returns 1 (false) otherwise
# ======================================================================
csih_is_exactly_windows10()
{
csih_stacktrace "${@}"
$_csih_trace
test ${_csih_exactly_windows10} -gt 0
} # === End of csih_is_exactly_windows10() === #
#NOTE: do not make _csih_exactly_windows10 readonly YET
# ======================================================================
# Routine: csih_is_exactly_server2016
# returns 0 (true) if the system is one of the variants of
# Windows 2016 Server.
# returns 1 (false) otherwise
# ======================================================================
csih_is_exactly_server2016()
{
csih_stacktrace "${@}"
$_csih_trace
test ${_csih_exactly_server2016} -gt 0
} # === End of csih_is_exactly_server2016() === #
#NOTE: do not make _csih_exactly_server2016 readonly YET
# ======================================================================
# Routine: csih_win_product_name
# Allows to cache the result of calling winProductName.
# ======================================================================
csih_win_product_name()
{
csih_stacktrace "${@}"
$_csih_trace
echo $_csih_win_product_name
} # === End of csih_win_product_name() === #
#NOTE: do not make _csih_win_product_name readonly YET
# ======================================================================
# Routine: csih_writable_tmpdir
# Echos to stdout the name of a writable temporary directory, based
# on the values of $TMP, $TEMP, $TMPDIR, or $HOME, with a fallback to
# /tmp. As a last resort, attempts to create /tmp.
#
# Returns 0 on success, nonzero on error.
# ======================================================================
csih_writable_tmpdir()
{
csih_stacktrace "${@}"
$_csih_trace
local tdir
if [ -z "${_csih_writable_tmpdir_cache_value}" ]
then
if [ -n "${TMP}" -a -d "${TMP}" -a -w "${TMP}" ]
then
tdir="${TMP}"
elif [ -n "${TEMP}" -a -d "${TEMP}" -a -w "${TEMP}" ]
then
tdir="${TEMP}"
elif [ -n "${TMPDIR}" -a -d "${TMPDIR}" -a -w "${TMPDIR}" ]
then
tdir="${TMPDIR}"
elif [ -d "/tmp" -a -w "/tmp" ]
then
tdir="/tmp"
elif [ -n "${HOME}" -a -d "${HOME}" -a -w "${HOME}" ]
then
tdir="${HOME}"
else
# fall back to creating /tmp manually
/usr/bin/mkdir -p -m 1777 /tmp
if [ $? -eq 0 ]
then
tdir="/tmp"
else
csih_warning "Cannot find or create a writable temporary directory"
return 1
fi
fi
_csih_writable_tmpdir_cache_value="${tdir}"
fi
echo "${_csih_writable_tmpdir_cache_value}"
} # === End of csih_writable_tmpdir() === #
readonly -f csih_writable_tmpdir
# ======================================================================
# Routine: csih_mktemp
# Safe wrapper around mktemp. Should be called in a subshell in order
# to capture the name of the created file: tmpfile="$(csih_mktemp)"
# By default, mktemp is invoked with
# --tmpfile="$(csih_writable_tmpdir)"
# but that may be overridden by explictly specifying a different
# --tmpdir value.
#
# Return value is 0 on success, nonzero otherwise.
# ======================================================================
csih_mktemp ()
{
csih_stacktrace "${@}"
$_csih_trace
local __umask=$(umask)
local rval
local tmpdir
tmpdir=$(csih_writable_tmpdir) || return 1
umask 0077
/usr/bin/mktemp --tmpdir="${tmpdir}" "$@"
rval=$?
umask ${__umask}
return $rval
} # === End of csih_mktemp() === #
readonly -f csih_mktemp
# ======================================================================
# Routine: csih_version_parse
# safely parses a version string of the form x.y.z into three
# separate values.
# ======================================================================
csih_version_parse() {
local pkg_version="$1"
# remove any non-digit characters from the version numbers to permit numeric
_csih_version_parse_pkg_major=$(echo $pkg_version | /usr/bin/cut -d. -f1 | /usr/bin/sed s/[a-zA-Z\-].*//g)
_csih_version_parse_pkg_minor=$(echo $pkg_version | /usr/bin/cut -d. -f2 | /usr/bin/sed s/[a-zA-Z\-].*//g)
_csih_version_parse_pkg_micro=$(echo $pkg_version | /usr/bin/cut -d. -f3 | /usr/bin/sed s/[a-zA-Z\-].*//g)
test -z "$_csih_version_parse_pkg_major" && _csih_version_parse_pkg_major=0
test -z "$_csih_version_parse_pkg_minor" && _csih_version_parse_pkg_minor=0
test -z "$_csih_version_parse_pkg_micro" && _csih_version_parse_pkg_micro=0
} # === End of csih_version_parse() === #
readonly -f csih_version_parse
# ======================================================================
# Routine: csih_version_ge
# Compares two version strings: $1 ad $2 should both be version
# strings of the form x.y.z
# returns true if $1 >= $2, when compared as normal version strings
# returns false otherwise
# Intended use: if client script requires csih of vintage x.y.z or above,
# if ! csih_version_ge $csih_VERSION x.y.z ; then some error ; fi
# ======================================================================
csih_version_ge() {
local lh_pkg_version="$1"
local rh_pkg_version="$2"
local lh_pkg_major
local lh_pkg_minor
local lh_pkg_micro
local rh_pkg_major
local rh_pkg_minor
local rh_pkg_micro
csih_version_parse "$lh_pkg_version"
lh_pkg_major=$_csih_version_parse_pkg_major
lh_pkg_minor=$_csih_version_parse_pkg_minor
lh_pkg_micro=$_csih_version_parse_pkg_micro
csih_version_parse "$rh_pkg_version"
rh_pkg_major=$_csih_version_parse_pkg_major
rh_pkg_minor=$_csih_version_parse_pkg_minor
rh_pkg_micro=$_csih_version_parse_pkg_micro
if [ $lh_pkg_major -gt $rh_pkg_major ]
then
return 0
elif [ $lh_pkg_major -eq $rh_pkg_major ]
then
if [ $lh_pkg_minor -gt $rh_pkg_minor ]
then
return 0
elif [ $lh_pkg_minor -eq $rh_pkg_minor ]
then
if [ $lh_pkg_micro -ge $rh_pkg_micro ]
then
return 0
fi
fi
fi
return 1
} # === End of csih_version_ge() === #
readonly -f csih_version_ge
# ======================================================================
# Routine: csih_version_le
# Compares two version strings: $1 ad $2 should both be version
# strings of the form x.y.z
# returns true if $1 <= $2, when compared as normal version strings
# returns false otherwise
# Intended use: if new versions of csih (above x.y.z) break a client script,
# it can warn the user:
# if ! csih_version_le $csih_VERSION x.y.z ; then some warning ; fi
# (However, rather than adding a warning about an incompatibility, it
# would probably be better to fix the script, or csih...)
# ======================================================================
csih_version_le() {
local lh_pkg_version="$1"
local rh_pkg_version="$2"
local lh_pkg_major
local lh_pkg_minor
local lh_pkg_micro
local rh_pkg_major
local rh_pkg_minor
local rh_pkg_micro
csih_version_parse "$lh_pkg_version"
lh_pkg_major=$_csih_version_parse_pkg_major
lh_pkg_minor=$_csih_version_parse_pkg_minor
lh_pkg_micro=$_csih_version_parse_pkg_micro
csih_version_parse "$rh_pkg_version"
rh_pkg_major=$_csih_version_parse_pkg_major
rh_pkg_minor=$_csih_version_parse_pkg_minor
rh_pkg_micro=$_csih_version_parse_pkg_micro
if [ $lh_pkg_major -lt $rh_pkg_major ]
then
return 0
elif [ $lh_pkg_major -eq $rh_pkg_major ]
then
if [ $lh_pkg_minor -lt $rh_pkg_minor ]
then
return 0
elif [ $lh_pkg_minor -eq $rh_pkg_minor ]
then
if [ $lh_pkg_micro -le $rh_pkg_micro ]
then
return 0
fi
fi
fi
return 1
} # === End of csih_version_le() === #
readonly -f csih_version_le
# ======================================================================
# Routine: csih_version_lt
# Compares two version strings: $1 ad $2 should both be version
# strings of the form x.y.z
# returns true if $1 < $2, when compared as normal version strings
# ======================================================================
csih_version_lt() {
if csih_version_ge "$1" "$2"
then
return 1
fi
return 0
} # === End of csih_version_lt() === #
readonly -f csih_version_lt
# ======================================================================
# Routine: csih_version_gt
# Compares two version strings: $1 ad $2 should both be version
# strings of the form x.y.z
# returns true if $1 > $2, when compared as normal version strings
# ======================================================================
csih_version_gt() {
if csih_version_le "$1" "$2"
then
return 1
fi
return 0
} # === End of csih_version_gt() === #
readonly -f csih_version_gt
# ======================================================================
# Routine: csih_version_eq
# Compares two version strings: $1 ad $2 should both be version
# strings of the form x.y.z
# returns true if $1 == $2, when compared as normal version strings
# ======================================================================
csih_version_eq() {
local lh_pkg_version="$1"
local rh_pkg_version="$2"
local lh_pkg_major
local lh_pkg_minor
local lh_pkg_micro
local rh_pkg_major
local rh_pkg_minor
local rh_pkg_micro
csih_version_parse "$lh_pkg_version"
lh_pkg_major=$_csih_version_parse_pkg_major
lh_pkg_minor=$_csih_version_parse_pkg_minor
lh_pkg_micro=$_csih_version_parse_pkg_micro
csih_version_parse "$rh_pkg_version"
rh_pkg_major=$_csih_version_parse_pkg_major
rh_pkg_minor=$_csih_version_parse_pkg_minor
rh_pkg_micro=$_csih_version_parse_pkg_micro
if [ $lh_pkg_major -eq $rh_pkg_major -a $lh_pkg_minor -eq $rh_pkg_minor -a $lh_pkg_micro -eq $rh_pkg_micro ]
then
return 0
fi
return 1
} # === End of csih_version_eq() === #
readonly -f csih_version_eq
# ======================================================================
# Routine: _csih_warning_for_etc_file
# Display a warning message for the user about overwriting the
# specified file in /etc.
# ======================================================================
_csih_warning_for_etc_file()
{
csih_stacktrace "${@}"
csih_warning "The command above overwrites any existing /etc/$1."
csih_warning "You may want to preserve /etc/$1 before generating"
csih_warning "a new one, and then compare your saved /etc/$1 file"
csih_warning "with the newly-generated one in case you need to restore"
csih_warning "other entries."
} # === End of _csih_warning_for_etc_file() === #
readonly -f _csih_warning_for_etc_file
# ======================================================================
# Routine: csih_request
# Retrieve user response to a question (in the optional argument $1)
# Accepts only "yes" or "no", repeats until valid response
# If csih_auto_answer=="yes", acts as though user entered "yes"
# If csih_auto_answer=="no", acts as though user entered "no"
# If "yes" then return 0 (true)
# If "no" then return 1 (false)
# ======================================================================
csih_request()
{
csih_stacktrace "${@}"
local answer=""
if [ "${csih_auto_answer}" = "yes" ]
then
echo -e "${_csih_QUERY_STR} $1 (yes/no) yes"
return 0
elif [ "${csih_auto_answer}" = "no" ]
then
echo -e "${_csih_QUERY_STR} $1 (yes/no) no"
return 1
fi
while true
do
echo -n -e "${_csih_QUERY_STR} $1 (yes/no) "
if read -e answer
then
if [ "X${answer}" = "Xyes" ]
then
return 0
fi
if [ "X${answer}" = "Xno" ]
then
return 1
fi
else
# user did a ^D
echo -e "Quitting.\n"
exit 1
fi
done
} # === End of csih_request() === #
readonly -f csih_request
# ======================================================================
# Routine: csih_get_value
# Get a verified non-empty string in variable "csih_value"
# Prompt with the first argument.
# The 2nd argument if not empty must be -s. (for "silent" password
# entry)
# NO AUTOANSWER SUPPORT.
# SETS GLOBAL VARIABLE: csih_value
# ======================================================================
csih_get_value()
{
csih_stacktrace "${@}"
local value
local verify
while true
do
echo -n -e "${_csih_QUERY_STR} "
if read $2 -p "$1 " value
then
[ -n "$2" ] && echo
if [ -n "${value}" ]
then
echo -n -e "${_csih_QUERY_STR} "
read $2 -p "Reenter: " verify
[ -n "$2" ] && echo
[ "${verify}" = "${value}" ] && break
fi
else
# user did a ^D
echo -e "Quitting.\n"
exit 1
fi
done
echo
csih_value="${value}"
return 0
} # === End of csih_get_value() === #
readonly -f csih_get_value
# ======================================================================
# Routine: csih_get_cygenv
# Retrieves from user the appropriate value for the CYGWIN environment
# variable to be used with the installed service.
# All arguments are interpreted together as the desired default value
# for CYGWIN.
#
# csih_auto_answer behavior:
# if set (to either "yes" or "no"), use default value
#
# SETS GLOBAL VARIABLE: csih_cygenv
# ======================================================================
csih_get_cygenv()
{
csih_stacktrace "${@}"
$_csih_trace
local default=$*
# if there is any auto_answer setting, then accept default
if [ -n "${csih_auto_answer}" ]
then
echo -e "${_csih_QUERY_STR} Enter the value of CYGWIN for the daemon: [${default}] ${default}"
csih_cygenv="${default}"
return 0
fi
while true
do
echo -n -e "${_csih_QUERY_STR} Enter the value of CYGWIN for the daemon: [${default}] "
if read csih_cygenv
then
if [ -z "${csih_cygenv}" ]
then
csih_cygenv="${default}"
fi
return
else
# user did a ^D
echo -e "Quitting.\n"
exit 1
fi
done
} # === End of csih_get_cygenv() === #
readonly -f csih_get_cygenv
# ======================================================================
# Routine: _csih_get_script_dir
# private routine to initialize _csih_script_dir
# ======================================================================
_csih_get_script_dir() {
# should be /usr/share/csih
local d=$(/usr/bin/dirname -- ${BASH_SOURCE[0]})
local D=$(cd "$d" && pwd)
echo "$D"
} # === End of csih_get_script_dir() === #
readonly -f _csih_get_script_dir
# ======================================================================
# Routine: _csih_get_exec_dir
# private routine to initialize _csih_exec_dir
# ======================================================================
_csih_get_exec_dir() {
# should be /usr/lib/csih
local d=$(/usr/bin/dirname -- ${BASH_SOURCE[0]})
local b=$(/usr/bin/basename -- "$d")
local D=$(cd "$d/../../lib/$b" >/dev/null 2>&1 && pwd)
local fullpath=
if [ -z "$D" ]
then
# try /usr/lib/csih explicitly
if [ -f "/usr/lib/csih/getAccountName.exe" ]
then
D=/usr/lib/csih
else
# try $PATH
if hash getAccountName 2>/dev/null
then
hash ${prog} 2>/dev/null
fullpath=$(hash -t getAccountName 2>/dev/null)
D=$(/usr/bin/dirname -- "$fullpath")
else
csih_error_recoverable "Could not locate csih helper programs!"
fi
fi
fi
echo "$D"
} # === End of csih_get_exec_dir() === #
readonly -f _csih_get_exec_dir
# ======================================================================
# Routine: csih_invoke_helper
# Launches a helper program shipped with cish
# returns the exit code of the helper program
# output:
# stores stdout in 'csih_helper_stdout', and copies to real stdout
# stores stderr in 'csih_helper_stderr', and emits as a recoverable
# error to the real stderr
# Note that if csih_invoke_helper is executed in a subshell, then
# the contents of csih_helper_stdout/csih_helper_stderr are not
# available to the caller.
#
# SETS GLOBAL VARIABLE: csih_helper_stdout csih_helper_stderr
# ======================================================================
csih_invoke_helper()
{
set +x # don''t trace this, but we are interested in who called
local result
local stdout
local var_out
local var_err
local SEP="----- separator -----"
local helper=
csih_stacktrace "${@}"
helper="$1"
shift
csih_helper_stdout=""
csih_helper_stderr=""
if [ -n "${_csih_exec_dir}" ]
then
if [ -f "${_csih_exec_dir}/${helper}" ]
then
result=$( { stdout=$("${_csih_exec_dir}/${helper}" "${@}"); } 2>&1; \
printf "%s\n%d\n%s\n" "${SEP}" $? "${SEP}"; printf "%s" "$stdout" )
var_out=${result##*${SEP}$'\n'}
var_err=${result%%$'\n'${SEP}*}
var_out=${var_out/*${SEP}/}
var_err=${var_err/${SEP}*/}
result=${result%$'\n'${SEP}*}
result=${result#*${SEP}$'\n'}
if [ -n "${var_err}" ]; then
csih_helper_stderr=$(echo "$var_err" | /usr/bin/sed -e 's/\r//g')
csih_error_recoverable "${helper}: ${csih_helper_stderr//\\/\\\\}" 1>&2
fi
if [ -n "${var_out}" ]; then
csih_helper_stdout=$(echo "$var_out" | /usr/bin/sed -e 's/\r//g')
echo "${csih_helper_stdout}"
fi
else
csih_error "Could not find ${helper} in ${_csih_exec_dir}"
fi
else
csih_error "Could not find csih helper programs!"
fi
$_csih_trace
return $result
} # === End of csih_invoke_helper() === #
readonly -f csih_invoke_helper
# ======================================================================
# Routine: csih_call_winsys32
# Calls a Windows CLI tool ($1) from the /Windows/System32
# directory. The trick here is that we use a full path to call
# the tool to avoid clobbering by other commands with the same name
# earlier in $PATH. In addition, to avoid a problem where different
# versions of Windows spell the 'system32' directory with different
# capitalization, AND because we are not sure if the ${SYSTEMROOT}
# volume is case-sensitive, we exploit a loophole in cygwin's case-
# sensitive support. When a command is invoked via a full, DOS-style
# path, case-insensitive path traversal is used. Note that
# 'cygpath -m' is used rather than 'cygpath -d' to avoid the
# "MS-DOS style path detected" warning.
# ======================================================================
csih_call_winsys32()
{
local tool=$1
shift
"$(/usr/bin/cygpath -ma "${SYSTEMROOT}")/system32/${tool}" "$@"
return $?
}
readonly -f csih_call_winsys32
# ======================================================================
# Routine: csih_get_localized_account_name
# Obtains the localized account name for the specified well-known
# account, where $1 indicates by number which account is of interest.
# The account name is stored in the variable csih_localized_account_name
# See WELL_KNOWN_SID_TYPE documentation for the association between
# well known accounts and the $1 numbers accepted here. Of interest:
# Local Guest: 39
# Domain Guest: 43
# Administrator: 38
# Returns 0 on success, non-zero on failure
#
# SETS GLOBAL VARIABLE: csih_localized_account_name
# ======================================================================
csih_get_localized_account_name()
{
csih_stacktrace "${@}"
$_csih_trace
local rstatus
local name
name=$(csih_invoke_helper getAccountName --number $1)
rstatus=$?
if [ "$rstatus" -eq 0 ]
then
csih_localized_account_name="${name}"
else
csih_localized_account_name=""
fi
return $rstatus
} # === End of csih_get_localized_account_name() === #
readonly -f csih_get_localized_account_name
# ======================================================================
# Routine: csih_get_guest_account_name
# Obtains the localized account name for the Guest user.
# The account name is stored in the variable csih_localized_account_name
# Returns 0 on success, non-zero on failure
#
# SETS GLOBAL VARIABLE: csih_localized_account_name
# ======================================================================
csih_get_guest_account_name()
{
csih_stacktrace "${@}"
$_csih_trace
local name
local rstatus
name=$(csih_invoke_helper getAccountName -g)
rstatus=$?
if [ "$rstatus" -eq 0 ]
then
csih_localized_account_name="${name}"
else
csih_localized_account_name=""
fi
return $rstatus
} # === End of csih_get_guest_account_name() === #
readonly -f csih_get_guest_account_name
# ======================================================================
# Routine: _csih_convert_w32vol_to_shell_pattern
# Converts the path specified as $1 to a shell pattern suitable
# for use in a case statement. For instance:
# E: -> [Ee]:
# //server/share -> [\\/][\\/]server[\\/]share
# $1 must be in win32 format
#
# SETS GLOBAL VARIABLES:
# _csih_w32vol_as_shell_pattern
# _csih_w32vol_as_shell_pattern_trailing_slash
# ======================================================================
_csih_convert_w32vol_to_shell_pattern()
{
csih_stacktrace "${@}" 1>&2 # redirect to avoid clobbering stdout
$_csih_trace
local has_trailing_slash
local upperD
local lowerD
local w32vol="$1"
# determine if ends in '/'
case "${w32vol}" in
*/ ) has_trailing_slash=yes
w32vol="${w32vol%%/*}"
;;
esac
# replace all '/' with '[\\/]'
w32vol="${w32vol//\//[\\\\\/]}"
# replace leading 'X:' with '[Xx]:'
if [ "${w32vol:1:1}" = ":" ]
then
case "${w32vol:0:1}" in
[A-Za-z] )
lowerD=$(echo "${w32vol:0:1}" | /usr/bin/tr '[:upper:]' '[:lower:]')
upperD=$(echo "${w32vol:0:1}" | /usr/bin/tr '[:lower:]' '[:upper:]')
w32vol="[${upperD}${lowerD}]:${w32vol:2}"
;;
esac
fi
_csih_w32vol_as_shell_pattern="${w32vol}"
_csih_w32vol_as_shell_pattern_trailing_slash="${has_trailing_slash}"
} # === End of _csih_convert_w32vol_to_shell_pattern() === #
readonly -f _csih_convert_w32vol_to_shell_pattern
# ======================================================================
# Routine: _csih_path_in_volumelist_core
# Given:
# $1 == a path in win32 format
# $2 == a win32-format pathlist (;-separated)
# Prints 'found' on stdout if
# $1 is located on one of the volumes specified in $2
# Silent otherwise
#
# Implementation note: matches are executed in a subshell, so
# we can't use variables to communicate the results. This is why
# stdout is used, and why stacktrace is redirected.
# ======================================================================
_csih_path_in_volumelist_core()
{
csih_stacktrace "${@}" 1>&2 # redirect to avoid clobbering stdout
$_csih_trace
local w32path="$1"
local volumelist="$2"
echo "$volumelist" | /usr/bin/tr ';' '\n' | while read p
do
_csih_convert_w32vol_to_shell_pattern "$p"
case "$w32path" in
${_csih_w32vol_as_shell_pattern}* )
printf "found\n"
;;
${_csih_w32vol_as_shell_pattern}[\\\\/]* )
if [ -n "${_csih_w32vol_as_shell_pattern_trailing_slash}" ]
then
printf "found\n"
fi
;;
esac
done
} # === End of _csih_path_in_volumelist_core() === #
readonly -f _csih_path_in_volumelist_core
# ======================================================================
# Routine: _csih_path_in_volumelist
# Given:
# $1 == a path in win32 format
# $2 == a win32-format pathlist (;-separated)
# Returns 0 if $1 is in $2
# Returns 1 otherwise
#
# Implementation note; a simple wrapper around *_core(), to hide
# the use of stdout.
# ======================================================================
_csih_path_in_volumelist()
{
csih_stacktrace "${@}"
$_csih_trace
local result=$(_csih_path_in_volumelist_core "$1" "$2")
if [ "x${result}" = "xfound" ]
then
return 0
fi
return 1
} # === End of _csih_path_in_volumelist() === #
readonly -f _csih_path_in_volumelist
# ======================================================================
# Routine: csih_path_supports_acls
# Determines if the specified path is on a volume that supports
# persistent ACLs. This is used to skip access-permission checks
# if files are located on a volume that does not support them.
# Returns 0 on success (yes, path is on a volume that supports ACLs),
# non-zero on failure (no, it doesn't, or there was some problem making
# the determination)
#
# $1 may be in unix or win32 format
#
# NOTE:
# first checks to see if $1, after conversion to win32 format,
# is located on one of the volumes listed in the win32-format
# pathlist csih_WIN32_VOLS_WITH_ACLS. If so, returns true (0).
# NOTE: the elements of csih_WIN32_VOLS_WITH_ACLS need not
# exist, although specifying non-existent shares will cause
# execution delays.
# then checks to see if $1, after conversion to win32 format,
# is located on one of the volumes listed in the win32-format
# pathlist csih_WIN32_VOLS_WITHOUT_ACLS. If so, returns false (1).
# NOTE: the elements of csih_WIN32_VOLS_WITHOUT_ACLS need not
# exist, although specifying non-existent shares will cause
# execution delays.
# finally, runs the helper program getVolInfo for $1. If the result
# contains 'FILE_PERSISTENT_ACLS: TRUE', then returns true (0)
# otherwise, returns false (1)
# NOTE: this test will return false if $1 specifies a volume that
# does not exist.
# ======================================================================
csih_path_supports_acls()
{
csih_stacktrace "${@}"
$_csih_trace
local output
local rstatus
if [ -z "$1" ]
then
csih_warning "Internal: csih_path_supports_acls called with no argument"
return 1
fi
# convert to w32 format
w32path=$(/usr/bin/cygpath -m "$1")
if _csih_path_in_volumelist "$w32path" "$csih_WIN32_VOLS_WITH_ACLS"
then
return 0
fi
if _csih_path_in_volumelist "$w32path" "$csih_WIN32_VOLS_WITHOUT_ACLS"
then
return 1
fi
output=$(csih_invoke_helper getVolInfo "$1" | /usr/bin/grep "FILE_PERSISTENT_ACLS" 2>/dev/null)
rstatus=$?
if [ "$rstatus" -eq 0 ]
then
echo "$output" | /usr/bin/grep "TRUE" || rstatus=2
fi
return $rstatus
} # === End of csih_path_supports_acls() === #
readonly -f csih_path_supports_acls
# ======================================================================
# Routine: csih_guest_account_active
# Returns true (0) if the Guest account is active
# Returns false (non-zero) otherwise.
#
# SETS GLOBAL VARIABLE: csih_localized_account_name
# ======================================================================
csih_guest_account_active() {
csih_stacktrace "${@}"
$_csih_trace
local rstatus=1
local str
if csih_get_guest_account_name
then
str=$(csih_call_winsys32 net user "$csih_localized_account_name" |\
/usr/bin/sed -n -e '/^Account active/s/^Account active *//p' |\
/usr/bin/tr '[:upper:]' '[:lower:]')
if [ "$str" = "yes" ]
then
rstatus=0
return $rstatus
fi
fi
return $rstatus
} # === End of csih_guest_account_active() === #
readonly -f csih_guest_account_active
# ======================================================================
# Routine: csih_install_config
# Installs the specified file ($1) by copying the default file
# located in the defaults directory ($2). Asks before overwriting.
# ${2}/${1} must exist. All directory components on ${1} must exist.
# Returns 0 on success, non-zero on failure
# ======================================================================
csih_install_config()
{
csih_stacktrace "${@}"
$_csih_trace
local dest;
local DEFAULTSDIR;
if [ -z "$1" ]
then
return
fi
if [ -z "$2" ]
then
return
fi
dest=$1;
DEFAULTSDIR=$2
if [ -f "$dest" ]
then
if ! csih_request "Overwrite existing ${dest} file?"
then
return 0
fi
/usr/bin/rm -f "${dest}"
if [ -f "${dest}" ]
then
csih_warning "Can't overwrite. ${dest} is write protected."
fi
fi
if [ ! -f "${dest}" ]
then
if [ ! -f "${DEFAULTSDIR}/${dest}" ]
then
csih_warning "Can't create ${dest} because default version could not be found."
csih_warning "Check '${DEFAULTSDIR}'"
else
csih_inform "Creating default ${dest} file"
/usr/bin/cp ${DEFAULTSDIR}/${dest} ${dest}
return $?
fi
fi
return 1
} # === End of csih_install_config() === #
readonly -f csih_install_config
# ======================================================================
# Routine: csih_make_dir
# Creates the specified directory if it does not already exist.
# Exits on failure.
# ======================================================================
csih_make_dir()
{
csih_stacktrace "${@}"
$_csih_trace
local DIR;
if [ -z "$1" ]
then
return
fi
DIR=$1
shift
if [ -e "${DIR}" -a ! -d "${DIR}" ]
then
if [ -n "$1" ]
then
csih_error_multi "${DIR} exists, but is not a directory." "$1"
else
csih_error "${DIR} exists, but is not a directory."
fi
fi
if [ ! -e "${DIR}" ]
then
/usr/bin/mkdir -p "${DIR}"
if [ ! -e "${DIR}" ]
then
csih_error "Creating ${DIR} directory failed."
fi
fi
} # === End of csih_make_dir() === #
readonly -f csih_make_dir
# ======================================================================
# Routine: csih_get_system_and_admins_ids
# Get the ADMINs ids from user and group account databases
# Returns 0 (true) on success, 1 otherwise.
# SETS GLOBAL VARIABLES:
# csih_ADMINSGID
# csih_ADMINSUID
# csih_SYSTEMGID
# csih_SYSTEMUID
# ======================================================================
csih_get_system_and_admins_ids()
{
csih_stacktrace "${@}"
$_csih_trace
local ret=0
# require Administrators group and SYSTEM
csih_ADMINSGID="$(/usr/bin/getent -w group S-1-5-32-544)"
csih_ADMINSGID="${csih_ADMINSGID#*:}"
csih_ADMINSGID="${csih_ADMINSGID%%:*}"
csih_SYSTEMGID="$(/usr/bin/getent -w group S-1-5-18)"
csih_SYSTEMGID="${csih_SYSTEMGID#*:}"
csih_SYSTEMGID="${csih_SYSTEMGID%%:*}"
if [ -z "$csih_ADMINSGID" -o -z "$csih_SYSTEMGID" ]
then
csih_warning "It appears that you do not have entries for the local"
csih_warning "ADMINISTRATORS and/or SYSTEM sids in /etc/group."
csih_warning ""
csih_warning "Use the 'mkgroup' utility to generate them or allow \"db\""
csih_warning "search of group accounts in /etc/nsswitch.conf"
csih_warning ""
ret=1;
fi
# only require SYSTEM passwd entry; warn if either is missing
csih_ADMINSUID="$(/usr/bin/getent -w passwd S-1-5-32-544)"
csih_ADMINSUID="${csih_ADMINSUID#*:}"
csih_ADMINSUID="${csih_ADMINSUID%%:*}"
csih_SYSTEMUID="$(/usr/bin/getent -w passwd S-1-5-18)"
csih_SYSTEMUID="${csih_SYSTEMUID#*:}"
csih_SYSTEMUID="${csih_SYSTEMUID%%:*}"
if [ -z "$csih_ADMINSUID" -o -z "$csih_SYSTEMUID" ]
then
csih_warning "It appears that you do not have an entry for the local"
csih_warning "ADMINISTRATORS (group) and/or SYSTEM sids."
csih_warning ""
csih_warning "Use the 'mkpasswd' utility to generate it or allow \"db\""
csih_warning "search of passwd accounts in /etc/nsswitch.conf"
csih_warning ""
if [ -z "$csih_SYSTEMUID" ]
then
ret=1
fi
fi
return "${ret}"
} # === End of csih_get_system_and_admins_ids() === #
readonly -f csih_get_system_and_admins_ids
# ======================================================================
# Routine: csih_check_passwd_and_group
# Check to see whether the user's password ID and group exist in the
# system account databases, respectively.
# Returns 0 (true) on success, 1 otherwise.
# ======================================================================
csih_check_passwd_and_group()
{
csih_stacktrace "${@}"
$_csih_trace
local ret=0
# Check for mkpasswd/mkgroup only valid up to Cygwin 1.7.33
if csih_old_cygwin
then
if [ "$(/usr/bin/id -un)" = "mkpasswd" ]
then
csih_warning "It appears that you do not have an entry for your user ID"
csih_warning "in /etc/passwd."
csih_warning ""
csih_warning "If so, use the 'mkpasswd' utility to generate an"
csih_warning "entry for your User ID in the password file:"
csih_warning " mkpasswd -l -u User_ID >> /etc/passwd"
csih_warning "or"
csih_warning " mkpasswd -d -u User_ID >> /etc/passwd."
csih_warning ""
_csih_warning_for_etc_file passwd
ret=1
fi
if [ "$(/usr/bin/id -gn)" = mkgroup ]
then
csih_warning "It appears that you do not have an entry for your group ID"
csih_warning "in /etc/group. If this check is incorrect, then re-run"
csih_warning "this script with the '-f' command-line option."
csih_warning ""
csih_warning "Otherwise, use the 'mkgroup' utility to generate an"
csih_warning "entry for your group ID in the password file:"
csih_warning " mkgroup -l -g Group_id >> /etc/group"
csih_warning "or"
csih_warning " mkgroup -d -g Group_id >> /etc/group."
csih_warning ""
_csih_warning_for_etc_file group
ret=1
fi
else
if ! getent passwd $(/usr/bin/id -un) >/dev/null 2>&1
then
csih_warning "It appears that you do not have an entry for your user ID"
csih_warning "in the user account database."
csih_warning ""
csih_warning "Please check the settings in /etc/nsswitch.conf."
ret=1
fi
if ! getent group $(/usr/bin/id -gn) >/dev/null 2>&1
then
csih_warning "It appears that you do not have an entry for your user ID"
csih_warning "in the group account database."
csih_warning ""
csih_warning "Please check the settings in /etc/nsswitch.conf."
ret=1
fi
fi
return "${ret}"
} # === End of csih_check_passwd_and_group() === #
readonly -f csih_check_passwd_and_group
# ======================================================================
# Routine: csih_check_user
# Check to see that the specified user exists in the user account database
# Returns 0 (true) if so, 1 otherwise.
# ======================================================================
csih_check_user()
{
csih_stacktrace "${@}"
$_csih_trace
if ! /usr/bin/getent passwd "$1" >/dev/null 2>&1
then
csih_warning "User $1 does not appear in the user account database."
return 1;
fi
return 0
} # === End of csih_check_user() === #
readonly -f csih_check_user
# ======================================================================
# Routine: _csih_warning_for_missing_ACL_support
# Display a warning message for the user about files located on
# volumes that do not support persistent ACLS -- e.g. FAT drives,
# noacl, etc.
# ======================================================================
_csih_warning_for_missing_ACL_support()
{
csih_stacktrace "${@}"
csih_warning "$1 exists on a volume that does not support accurate"
csih_warning "permissions (perhaps formatted FAT?). Therefore, we"
csih_warning "cannot verify access to the file or directory."
csih_warning "Please consider, if possible and appropriate, one of the"
csih_warning "following remedies:"
csih_warning " 1) converting to NTFS using 'convert.exe'"
csih_warning " 2) mounting the relevant volume using the 'acl' option"
}
# === End of _csih_warning_for_missing_ACL_support() === #
readonly -f _csih_warning_for_missing_ACL_support
# ======================================================================
# Routine: csih_check_dir_perms
# Check to see that the specified directory ($1) exists and has the
# permissions ($2).
# Returns 0 (true) if so, 1 otherwise.
# ======================================================================
csih_check_dir_perms()
{
csih_stacktrace "${@}"
$_csih_trace
if [ ! -e "$1" ]
then
csih_warning "Your computer is missing $1".
return 1
fi
if ! csih_path_supports_acls "$1"
then
_csih_warning_for_missing_ACL_support "$1"
csih_warning "Continuing, but something might break..."
return 0
fi
if /usr/bin/stat -c "%A" "$1" | /usr/bin/grep -Eq ^"$2"
then
true
else
csih_warning "The permissions on the directory $1 are not correct."
csih_warning "They must match the regexp $2"
return 1
fi
} # === End of csih_check_dir_perms() === #
readonly -f csih_check_dir_perms
# ======================================================================
# Routine: csih_check_access
# Check to see that the owner and Administrators have
# proper access to the file or directory.
# $1 -- the file or directory to check
# $2 -- three character symbolic permissions: 'rwx'. '.w.', etc.
# On installations older than Windows 2003 and Vista, allow access
# by System
# NOTE: must call csih_get_system_and_admins_ids first
# ======================================================================
csih_check_access()
{
csih_stacktrace "${@}"
$_csih_trace
local file="$1"
local perm="$2"
local msg="$3"
local notify=0;
local ls_result="$(/usr/bin/ls -dLln "$file" 2> /dev/null)"
if [ -z "$csih_ADMINSGID" ]
then
csih_get_system_and_admins_ids
if [ $? -ne 0 ]
then
return $?
fi
fi
if ! csih_path_supports_acls "$file"
then
_csih_warning_for_missing_ACL_support "$fild"
csih_warning "Continuing, but something might break..."
return 0
fi
# If the owner of the file does not have access,
# then notify the user.
if [ -z "$(echo "$ls_result" | /usr/bin/sed -n /^."$perm"/p)" ]
then
notify=1;
# If the 'Administrators' group has owner or group access to the file,
# but does not have the desired access, then notify the user.
elif ( [ -n "$csih_ADMINSUID" -a "$(echo "$ls_result" | /usr/bin/awk '{ print $3 }')" -eq $csih_ADMINSUID ] || \
( ! csih_is_nt2003 && [ -n "$csih_SYSTEMUID" -a "$(echo "$ls_result" | /usr/bin/awk '{ print $3 }')" -eq $csih_SYSTEMUID ] ))
then
# Administrators group owns the file, or (on < NT2003,XP64) SYSTEM owns the file
# this is ok; do nothing.
true;
elif ( [ "$(echo "$ls_result" | /usr/bin/awk '{ print $4 }')" -eq $csih_ADMINSGID ] || \
( ! csih_is_nt2003 && [ "$(echo "$ls_result" | /usr/bin/awk '{ print $4 }')" -eq $csih_SYSTEMGID ] ))
then
# The Administrators group has group access to the file, or
# (on < NT2003,XP64) SYSTEM has group access to the file. Check to
# see if the chmod bits for group allow these groups to have
# "owner-like" access to the file. If not, notify.
[ -z "$(echo "$ls_result" | /usr/bin/sed -n /^...."$perm"/p)" ] && notify=1
elif ( [ -n "$(/usr/bin/getfacl -n "$file" | /usr/bin/sed -n /^group:"$csih_ADMINSGID":"$perm"/p )" ] || \
( ! csih_is_nt2003 && [ -n "$(/usr/bin/getfacl -n "$file" | /usr/bin/sed -e /^group:"$csih_SYSTEMGID":"$perm"/p )" ] ))
then
# There exists an extended ACL entry for the Administrators (or
# SYSTEM group, pre NT2003,XP64), with the desired owner-like permissions.
# This is ok.
true;
else
# otherwise, we do /not/ have sufficient access to the file.
notify=1
fi
if [ "$notify" -eq 1 ]; then
csih_warning "The owner and the Administrators need"
csih_warning "to have $perm permission to $file."
csih_warning "Here are the current permissions and ACLS:"
ls_result=$(/usr/bin/ls -dlL "${file}" 2>&1)
csih_warning " $ls_result"
/usr/bin/getfacl "${file}" 2>&1 | while read LN
do
csih_warning " $LN"
done
csih_warning "Please change the user and/or group ownership, "
csih_warning "permissions, or ACLs of $file."
echo
[ -z "${msg}" ] || csih_warning "${msg}"
return 1;
fi
} # === End of csih_check_access() === #
readonly -f csih_check_access
# ======================================================================
# Routine: csih_check_sys_mount
# Deprecated. Always return 0.
# ======================================================================
csih_check_sys_mount()
{
csih_stacktrace "${@}"
$_csih_trace
return 0
} # === End of csih_check_sys_mount() === #
readonly -f csih_check_sys_mount
# ======================================================================
# DEPRECATED: csih_check_basic_mounts
# Stub for backward compatibility
# ======================================================================
csih_check_basic_mounts()
{
csih_warning "$csih_progname_base is using deprecated "\
"function csih_check_basic_mounts. Continuing..."
true
} # === End of csih_check_basic_mounts() === #
readonly -f csih_check_basic_mounts
# ======================================================================
# Routine: csih_privileged_accounts [-u username]
# Determines the names of all known "privileged" users already created
# on this system, or known to this system (e.g. domain users)
# Checks for cyg_server, sshd_server, cron_server as well as 'username'
# passed as an argument to the -u option, if specified. The -u username
# will be preferred over the default names, but of those default names,
# cyg_server is preferred. However, it is taken on faith that if
# 'username' exists, it is, in fact, privileged and not an "ordinary" user.
# Avoids rechecking if already set.
#
# SETS GLOBAL (PRIVATE) VARIABLES:
# _csih_all_preexisting_privileged_accounts
# _csih_preferred_preexisting_privileged_account
# ======================================================================
csih_privileged_accounts()
{
csih_stacktrace "${@}"
$_csih_trace
local opt_username
local pwd_entries
local domain
local username
local take_it
local accounts
local first_account
# always parse "command line"
OPTIND=0
while getopts ":u:" options; do
case $options in
u ) opt_username="$OPTARG" ;;
\? ) csih_warning "${FUNCNAME[0]} ignoring invalid option: $OPTARG" ;;
\: ) csih_warning "${FUNCNAME[0]} ignoring option missing required argument: $OPTARG" ;;
esac
done
shift $(($OPTIND - 1))
[ -n "${1}" ] && opt_servicename="${1}"
if [ -z "${_csih_all_preexisting_privileged_accounts}" ]
then
# First check optional username from command line
if [ -n "$opt_username" ]
then
pwd_entries=$(/usr/bin/getent -w passwd "$opt_username")
# Extract Cygwin username and Windows domain
username="${pwd_entries%%:*}"
domain="${pwd_entries#*:*:}"
domain="${domain%\\*}"
take_it=1
# Local SAM account? Check privileges
[ "${COMPUTERNAME,,*}" = "${domain,,*}" ] \
&& ! csih_account_has_necessary_privileges "$username" && take_it=0
if [ $take_it -eq 0 ]
then
# -u $opt_username does NOT have the required privileges,
# even though it exists. Warn, and skip
csih_warning "Privileged account '$opt_username' was specified,"
csih_warning "but it does not have the necessary privileges."
csih_warning "Continuing, but will probably use a different account."
else
first_account="${username}"
accounts="'${username}' "
fi
fi
# Then check predefined Cygwin service accounts
pwd_entries=$(/usr/bin/getent passwd $_csih_well_known_privileged_accounts \
| /usr/bin/cut -d: -f 1)
for username in $pwd_entries
do
[ -z "${first_account}" ] && first_account="${username}"
accounts="${accounts}'${username}' "
done
if [ -n "${accounts}" ]
then
_csih_all_preexisting_privileged_accounts="${accounts}"
_csih_preferred_preexisting_privileged_account="${first_account}"
fi
fi
} # === End of csih_privileged_accounts() === #
readonly -f csih_privileged_accounts
# ======================================================================
# Routine: csih_privileged_account_exists
# On Windows NT and above, determines if the specified
# user ${1} exists and is one of the well-known privileged users
# (cyg_server, sshd_server, cron_server), or actually does possess
# the required privileges.
# Returns 0 (true) if so, 1 otherwise.
# ======================================================================
csih_privileged_account_exists()
{
csih_stacktrace "${@}"
$_csih_trace
csih_privileged_accounts -u "$1"
if [ -n "${_csih_all_preexisting_privileged_accounts}" ]
then
case "${_csih_all_preexisting_privileged_accounts}" in
*" '$1' "* | "'$1' "* | *" '$1'" | "'$1'") true;;
*) false;;
esac
else
false
fi
} # === End of csih_privileged_account_exists() === #
readonly -f csih_privileged_account_exists
# ======================================================================
# Routine: csih_account_has_necessary_privileges
# On Windows NT and above, checks whether the specified account $1
# has the privileges necessary to change user contexts.
# Returns 0 (true) if so, 1 otherwise.
# Also returns false if $1 is unspecified, or $1 doesn't exist.
# ======================================================================
csih_account_has_necessary_privileges() {
csih_stacktrace "${@}"
$_csih_trace
local user="$1"
if [ -n "${user}" ]
then
if ! csih_check_program_or_warn /usr/bin/editrights editrights
then
csih_warning "The 'editrights' program cannot be found or is not executable."
csih_warning "Unable to ensure that '${user}' has the appropriate privileges."
return 1
else
# Don't attempt to validate membership in Administrators group
# Instead, just try to set the appropriate rights; if it fails
# then handle that, instead.
/usr/bin/editrights -u "${user}" -t SeAssignPrimaryTokenPrivilege >/dev/null 2>&1 &&
/usr/bin/editrights -u "${user}" -t SeCreateTokenPrivilege >/dev/null 2>&1 &&
/usr/bin/editrights -u "${user}" -t SeTcbPrivilege >/dev/null 2>&1 &&
/usr/bin/editrights -u "${user}" -t SeServiceLogonRight >/dev/null 2>&1
return # status of previous command-list
fi
fi
false
} # === End of csih_account_has_necessary_privileges() === #
readonly -f csih_account_has_necessary_privileges
# ======================================================================
# Routine: _csih_setup
# Basic checks for all clients of this script. It is the final
# initialization phase for csih, and unlike the other phases -- which
# execute as soon as csih is sourced by the client script -- this
# initialization is peformed at "runtime". That is, it is called
# internally by any "public" API function for which the initialiation
# is needed.
#
# _csih_setup internally handles repeated calls, and initialization
# is skipped if already performed.
#
# Exits if anything is wrong.
# ======================================================================
_csih_setup()
{
local uid
local gid
local user_sid
local grp_sid
local perms="d..x..x..[xt]"
csih_stacktrace "${@}"
$_csih_trace
if [ "$_csih_setup_already_called" -eq 0 ]
then
if [ -z "${SYSCONFDIR}" ]
then
csih_error "Variable SYSCONFDIR is empty (should be '/etc' ?)"
fi
if [ -z "${LOCALSTATEDIR}" ]
then
csih_error "Variable LOCALSTATEDIR is empty (should be '/var' ?)"
fi
if ! csih_get_system_and_admins_ids
then
csih_error "Problem with LocalSystem or Adminstrator IDs"
fi
uid=$(/usr/bin/stat -c '%u' ${LOCALSTATEDIR})
gid=$(/usr/bin/stat -c '%g' ${LOCALSTATEDIR})
user_sid=$(/usr/bin/getent -w passwd $uid | awk -F: '{print $4}')
grp_sid=$(/usr/bin/getent -w group $gid | awk -F: '{print $4}')
if [ "${user_sid}" = "${grp_sid}" ]
then
perms="d..x.....[xt]"
fi
if ! csih_check_dir_perms "${LOCALSTATEDIR}" "${perms}"
then
csih_error "Problem with ${LOCALSTATEDIR} directory. Exiting."
fi
# attempt to set permissions, but not an error if fail
# will verify that we actually HAVE correct permissions below.
csih_make_dir "${LOCALSTATEDIR}/run"
/usr/bin/chmod 1777 "${LOCALSTATEDIR}/run" >&/dev/null || true
/usr/bin/setfacl -m u:system:rwx "${LOCALSTATEDIR}/run" >&/dev/null || true
/usr/bin/setfacl -m g:544:rwx "${LOCALSTATEDIR}/run" >&/dev/null || true
csih_make_dir "${LOCALSTATEDIR}/log"
/usr/bin/chmod 1777 "${LOCALSTATEDIR}/log" >&/dev/null || true
/usr/bin/setfacl -m u:system:rwx "${LOCALSTATEDIR}/log" >&/dev/null || true
/usr/bin/setfacl -m g:544:rwx "${LOCALSTATEDIR}/log" >&/dev/null || true
csih_make_dir "${LOCALSTATEDIR}/empty"
/usr/bin/chmod 755 "${LOCALSTATEDIR}/empty" >&/dev/null || true
/usr/bin/setfacl -m u:system:r-x "${LOCALSTATEDIR}/empty" >&/dev/null || true
/usr/bin/setfacl -m g:544:r-x "${LOCALSTATEDIR}/empty" >&/dev/null || true
# daemons need write access to /var/run to create pid file
if ! csih_check_access "${LOCALSTATEDIR}/run" .w.
then
csih_error "Problem with ${LOCALSTATEDIR}/run directory. Exiting."
fi
# daemons need write access to /var/log if they do their own logging
if ! csih_check_access "${LOCALSTATEDIR}/log" .w.
then
csih_error "Problem with ${LOCALSTATEDIR}/log directory. Exiting."
fi
# daemons need access to /var/empty for chrooting
if ! csih_check_access "${LOCALSTATEDIR}/empty" r.x
then
csih_error "Problem with ${LOCALSTATEDIR}/empty directory. Exiting."
fi
# just ensure that /etc exists. It is up to clients of this
# script to explicitly check accees to the specific configuration
# files inside /etc...
csih_make_dir "${SYSCONFDIR}"
/usr/bin/chmod 755 "${SYSCONFDIR}" >&/dev/null || true
_csih_setup_already_called=1
fi
} # === End of _csih_setup() === #
readonly -f _csih_setup
# ======================================================================
# Routine: csih_old_cygwin
# Check Cygwin version, account databases are avaiable since 1.7.34
# On Cygwin versions <= 1.7.33 return 0
# On Cygwin versions > 1.7.33 return 1
# ======================================================================
csih_old_cygwin()
{
local old_cygwin
/usr/bin/uname -r |
/usr/bin/awk -F. '{
if ( $1 < 1 || \
($1 == 1 && $2 < 7) || \
($1 == 1 && $2 == 7 && strtonum($3) <= 33))
exit 0;
exit 1;
}'
old_cygwin=$?
return ${old_cygwin}
} # === End of csih_old_cygwin() === #
readonly -f csih_old_cygwin
# ======================================================================
# Routine: csih_use_file_etc passwd|group
# Check if /etc/passwd or /etc/group file is in use.
# On Cygwin versions < 1.7.33, files are always used.
# On Cygwin versions >= 1.7.33 it depends on /etc/nsswitch.conf.
#
# If /etc/nsswitch.conf doesn't exit, "db" is used and we don't
# need the files.
#
# If /etc/nsswitch.conf exists, and passwd/group lines contain
# the "db" entry, "db" is used and we don't need the files.
#
# If /etc/nsswitch.conf exists, and no passwd/group lines are
# present, "db" is used by default and we don't need the files.
#
# Otherwise, we need the files.
#
# Returns 1 if files shall be used, 0 otherwise.
# ======================================================================
csih_use_file_etc()
{
local file="$1"
local use_file
if [ "$file" != "passwd" -a "$file" != "group" ]
then
csih_error 'Script error: csih_use_file_etc requires argument "passwd" or "group".'
fi
csih_old_cygwin ; use_file=$?
if [ ${use_file} -ne 0 -a -f /etc/nsswitch.conf ]
then
if grep -Eq "^${file}:" /etc/nsswitch.conf
then
grep -Eq "^${file}:[^#]*\<db\>" /etc/nsswitch.conf || use_file=0
fi
fi
return ${use_file}
} # === End of csih_use_file_etc() === #
readonly -f csih_use_file_etc
# ======================================================================
# Routine: csih_select_privileged_username [-q] [-f] [-u default_user] [service_name]
# On NT and above, get the desired privileged account name.
#
# If the optional argument '-q' is specified, then this function will
# operate in query mode, which is more appropriate for user-config
# scripts that need information ABOUT a service, but do not
# themselves install the service.
#
# If the optional argument '-f' is specified, then no confirmation
# questions will be asked about the selected username. This is
# useful mainly in unattended installations.
#
# If the optional argument '-u' is specified, then the next
# argument will be used as the "privileged" username -- UNLESS
# [service_name] is provided, and that service is already
# installed under some OTHER username. In the latter case, the
# current user under which [service_name] is installed will be
# used, and the value specified by -u will be IGNORED.
#
# If the optional [service_name] argument is present, then that value
# may be used in some of the messages. Also, this function will
# then check to see if [service_name] is already installed. If so,
# the account under which it is installed will be selected, assuming
# it passes validation (has necessary permissions, group memberships,
# etc)
#
# Usually [service_name] and [-q] should be specified together.
# [-f] can be set regardless of other options.
#
# SETS GLOBAL VARIABLE:
# csih_PRIVILEGED_USERNAME
# csih_PRIVILEGED_USERWINNAME
# csih_PRIVILEGED_USERDOMAIN
# OPTIND
# OPTARG
#
# csih_auto_answer=no behavior
# if [service_name] and [-q] and [service_name] is installed
# get account under which it is installed
# validate account (may issue error!)
# use that account name
# elif $default_username is specified, exists, and has necessary privileges,
# use $default_username
# elif any(cyg_server cron_server sshd_server) exists,
# use first in list
# else
# if csih_is_nt2003 || csih_FORCE_PRIVILEGED_USER # note: csih_is_nt2003 true for XP64 too
# if $default_username is specified
# use $default_username
# else
# use cyg_server
# else
# do nothing (csih_PRIVILEGED_USERNAME="")
#
# ======================================================================
csih_select_privileged_username()
{
csih_stacktrace "${@}"
$_csih_trace
local domain
local winusername
local username
local newname
local opt_query=0
local opt_force=0
local opt_servicename=""
local opt_default_username=""
local options
local theservice
local map_entry
local use_files
_csih_setup
# always parse "command line"
OPTIND=0
while getopts ":qfu:" options; do
case $options in
q ) opt_query=1 ;;
f ) opt_force=1 ;;
u ) opt_default_username="$OPTARG" ;;
\? ) csih_warning "${FUNCNAME[0]} ignoring invalid option: $OPTARG" ;;
\: ) csih_warning "${FUNCNAME[0]} ignoring option missing required argument: $OPTARG" ;;
esac
done
shift $(($OPTIND - 1))
[ -n "${1}" ] && opt_servicename="${1}"
# save time if csih_PRIVILEGED_USERNAME already set
if [ -n "${csih_PRIVILEGED_USERNAME}" ]
then
return
fi
csih_privileged_accounts -u "$opt_default_username"
# if query mode and opt_servicename has been specified,
# first check to see if the service has already been
# installed. If so, get the service's account name.
# If (and only if) that account is privileged, then
# record it in csih_PRIVILEGED_USERNAME.
if [ $opt_query -ne 0 -a -n "${opt_servicename}" ]
then
if /usr/bin/cygrunsrv -Q "${opt_servicename}" >/dev/null 2>&1
then
username=$(/usr/bin/cygrunsrv -V -Q "${opt_servicename}" 2>&1 | /usr/bin/sed -n -e '/^Account/s/^.* : //p')
domain="${username%\\*}"
winusername="${username#*\\}"
if [ "${winusername}" = "LocalSystem" ]
then
username=#empty; SYSTEM is not a "privileged user"
else
username=$(/usr/bin/getent passwd "${winusername}" "${domain}+${winusername}" | /usr/bin/head -n1 | /usr/bin/cut -d: -f 1)
fi
if [ -n "${username}" ]
then
if csih_privileged_account_exists "${username}"
then
# ${opt_servicename} is installed under account "${username}",
# which is one of the pre-declared privileged users -- or,
# we have already validated that ${username} has the necessary
# privilegeds. Great!
csih_PRIVILEGED_USERNAME="${username}"
csih_PRIVILEGED_USERWINNAME="${winusername}"
csih_PRIVILEGED_USERDOMAIN="${domain}"
return
else
if csih_account_has_necessary_privileges "${username}"
then
# ${opt_servicename} is installed under account "${username}",
# but ${username} is not one of the pre-declared privileged users
# (cyg_server, sshd_server, cron_server) -- nor had we previously
# validated that it has the necessary privileges. However, we just
# did validate that, and it DOES have the necessary privileges.
# Add it to the list.
csih_PRIVILEGED_USERNAME="${username}"
csih_PRIVILEGED_USERWINNAME="${winusername}"
csih_PRIVILEGED_USERDOMAIN="${domain}"
_csih_all_preexisting_privileged_accounts="${_csih_all_preexisting_privileged_accounts}'${username}' "
return
else
# ${opt_servicename} is installed under account "${username}", but
# ${username} is not one of the pre-declared privileged users
# (cyg_server, sshd_server, cron_server), nor does it have the
# necessary privileges. Ignore it...
return
fi
fi # privileged_user_exists
else
# ${opt_servicename} installed under LocalSystem, or there was
# some error in determining the account name under which it is installed.
return
fi # $username empty
fi # ${opt_servicename} is not installed
fi # not (opt_query && opt_servicename)
if [ $opt_query -eq 0 ]
then
if csih_is_nt2003
then
csih_inform "It's not possible to use the LocalSystem account for services"
csih_inform "that can change the user id without an explicit password"
csih_inform "(such as passwordless logins [e.g. public key authentication]"
csih_inform "via sshd) when having to create the user token from scratch."
csih_inform "For more information on this requirement, see"
csih_inform "https://cygwin.com/cygwin-ug-net/ntsec.html#ntsec-nopasswd1"
echo ""
csih_inform "If you want to enable that functionality, it's required to create"
csih_inform "a new account with special privileges (unless such an account"
csih_inform "already exists). This account is then used to run these special"
csih_inform "servers."
echo ""
csih_inform "Note that creating a new user requires that the current account"
csih_inform "have Administrator privileges itself."
elif [ "x$csih_FORCE_PRIVILEGED_USER" = "xyes" ]
then
csih_inform "You have requested that a special privileged user be used"
csih_inform "by the service, and are running on 32 bit Windows XP"
csih_inform "where this is not actually required (LocalSystem would also work)."
echo ""
csih_inform "Note that creating a new user requires that the current account"
csih_inform "have Administrator privileges itself."
else
# hmm. XP(32), but not csih_FORCE_PRIVILEGED_USER
# in this case, we emit no messages. If a privileged
# user already exists, we'll use it. Otherwise, don't
# specify a "privileged" user. Callers will know to
# silently use LocalSystem.
:
fi
fi
if [ -n "${_csih_all_preexisting_privileged_accounts}" -a -z "$opt_default_username" ]
then
echo ""
csih_inform "The following privileged accounts were found: ${_csih_all_preexisting_privileged_accounts}."
username="${_csih_preferred_preexisting_privileged_account}"
else
if ( csih_is_nt2003 || [ "x$csih_FORCE_PRIVILEGED_USER" = "xyes" ] )
then
if [ $opt_query -eq 0 -a -z "$opt_default_username" ]
then
echo ""
csih_inform "No privileged account could be found."
fi
if [ -n "$opt_default_username" ]
then
username="$opt_default_username"
else
username="cyg_server"
fi
else
# nt/2k/xp32 and not csih_FORCE_PRIVILEGED_USER and username is empty
# we couldn't find a pre-existing privileged user, but we don't
# really need one (nt/2k/xp32) and haven't explicitly requested one
# via csih_FORCE... In this case, we're done: just return. (this
# is true regardless of the value of $opt_query)
return
fi
fi
# if we get here, then $username WILL be set to something
if [ $opt_query -eq 0 ]
then
echo ""
csih_inform "This script plans to use '${username}'."
csih_inform "'${username}' will only be used by registered services."
if [ $opt_force -eq 0 ]
then
if csih_request "Do you want to use a different name?"
then
csih_get_value "Enter the new user name:"
username="${csih_value}"
fi
fi
else
theservice=${opt_servicename:-the service}
csih_inform "This script will assume that ${theservice} will run"
csih_inform "under the '${username}' account."
if [ $opt_force -eq 0 ]
then
if csih_request "Will ${theservice} run under a different account?"
then
csih_get_value "Enter the user name used by the service:"
username="${csih_value}"
fi
fi
fi
# faster than checking SAM -- see if username is one that we
# already know about
if csih_privileged_account_exists "$username"
then
_csih_preferred_preexisting_privileged_account="${username}"
else
# perhaps user specified a pre-existing privileged account we
# don't know about
if /usr/bin/getent passwd "${username}" >/dev/null 2>&1
then
if ! csih_account_has_necessary_privileges "${username}"
then
csih_warning "The specified account '${username}' does not have the"
csih_warning "required permissions or group memberships. This may"
csih_warning "cause problems if not corrected; continuing..."
elif [ ${opt_query} -eq 0 ]
then
csih_inform "'${username}' already exists...and has all necessary permissions."
fi
# so, add it to our list, so that csih_privileged_account_exists
# will return true...
_csih_preferred_preexisting_privileged_account="${username}"
_csih_all_preexisting_privileged_accounts="${_csih_all_preexisting_privileged_accounts}'${username}' "
fi
# if it doesn't exist, we're probably in the midst of creating it.
# so don't issue any warnings.
fi
map_entry=$(/usr/bin/getent -w passwd "${username}")
if [ -n "${map_entry}" ]
then
local dw
csih_PRIVILEGED_USERNAME="${map_entry%%:*}"
dw="${map_entry#*:*:}"
dw="${dw%:*}"
csih_PRIVILEGED_USERDOMAIN="${dw%\\*}"
csih_PRIVILEGED_USERWINNAME="${dw#*\\}"
else
csih_PRIVILEGED_USERNAME="${username}"
if ! csih_use_file_etc "passwd"
then
# This test succeeds on domain member machines only, not on DCs.
if [ "${USERDOMAIN,,*}" != "${COMPUTERNAME,,*}" \
-o "$(/usr/bin/mkpasswd -c | /usr/bin/awk -F: '{print $1}')" = "${USERDOMAIN}+${USERNAME}" ]
then
# Lowercase of USERDOMAIN
csih_PRIVILEGED_USERNAME="${COMPUTERNAME,,*}+${username}"
fi
fi
csih_PRIVILEGED_USERDOMAIN="${COMPUTERNAME}"
csih_PRIVILEGED_USERWINNAME="${username}"
fi
} # === End of csih_select_privileged_username() === #
readonly -f csih_select_privileged_username
# ======================================================================
# Routine: csih_create_privileged_user
# On 64bit Windows XP, Windows Server 2003 and above (including Windows
# Vista), or if csih_FORCE_PRIVILEGED_USER == "yes" for Windows NT and
# above, allows user to select a pre-existing privileged user, or to
# create a new privileged user.
# $1 (optional) will be used as the password if non-empty
#
# NOTE: For using special behaviours triggered by optional parameters
# to the csih_select_privileged_username function, you should first
# call that function with all required parameters, and then call this
# function. The selected username will already be stored in
# $csih_PRIVILEGED_USERNAME.
#
# Exits on catastrophic error (or if user enters empty password)
# Returns 0 on total success
# Returns 1 on partial success (created user, but could not add
# to admin group, or set privileges, etc). Recommend caller
# check return value, and offer user opportunity to quit.
#
# On success, the username and password will be available in
# csih_PRIVILEGED_USERNAME
# csih_PRIVILEGED_USERDOMAIN
# csih_PRIVILEGED_USERWINNAME
# csih_PRIVILEGED_PASSWORD
#
# csih_auto_answer=no behavior
# if csih_is_nt2003 || FORCE # note: csih_is_nt2003 is true for XP64, too
# if pre-existing privileged user
# make sure its group membership and perms are correct
# else
# do nothing, return 1
# else
# do nothing, return 1
# ======================================================================
csih_create_privileged_user()
{
csih_stacktrace "${@}"
$_csih_trace
local username_in_sam
local admingroup
local dos_var_empty
local _password
local password_value="$1"
local passwd_has_expiry_flags
local ret=0
local username_in_admingroup
local username_got_all_rights
local tmpfile1
_csih_setup
csih_select_privileged_username
if ( csih_is_nt2003 || [ "x$csih_FORCE_PRIVILEGED_USER" = "xyes" ] )
then
if ! csih_privileged_account_exists "$csih_PRIVILEGED_USERNAME"
then
username_in_sam=no
# give auto-answer a chance to veto, because we can't enter password
# from setup.exe...
if csih_request "Create new privileged user account '${csih_PRIVILEGED_USERDOMAIN}\\\\${csih_PRIVILEGED_USERWINNAME}' (Cygwin name: '${csih_PRIVILEGED_USERNAME}')?"
then
dos_var_empty=$(/usr/bin/cygpath -w ${LOCALSTATEDIR}/empty)
if [ -n "${password_value}" ]
then
_password="${password_value}"
# Allow to ask for password if first try fails
password_value=""
else
csih_inform "Please enter a password for new user ${csih_PRIVILEGED_USERNAME}. Please be sure"
csih_inform "that this password matches the password rules given on your system."
csih_inform "Entering no password will exit the configuration."
csih_get_value "Please enter the password:" -s
_password="${csih_value}"
if [ -z "${_password}" ]
then
csih_error_multi "Exiting configuration. No user ${csih_PRIVILEGED_USERNAME} has been created," \
"and no services have been installed."
fi
fi
tmpfile1=$(csih_mktemp) || csih_error "Could not create temp file"
csih_call_winsys32 net user "${csih_PRIVILEGED_USERWINNAME}" \
"${_password}" \
/fullname:"Privileged server" \
/homedir:"${dos_var_empty}" \
/comment:'<cygwin home="/var/empty" shell="/bin/false"/>' \
/add /yes > "${tmpfile1}" 2>&1 && username_in_sam=yes
if [ "${username_in_sam}" != "yes" ]
then
csih_warning "Creating the user '${csih_PRIVILEGED_USERNAME}' failed! Reason:"
/usr/bin/cat "${tmpfile1}"
echo
fi
/usr/bin/rm -f "${tmpfile1}"
if [ "${username_in_sam}" = "yes" ]
then
csih_PRIVILEGED_PASSWORD="${_password}"
csih_inform "User '${csih_PRIVILEGED_USERNAME}' has been created with password '${_password}'."
csih_inform "If you change the password, please remember also to change the"
csih_inform "password for the installed services which use (or will soon use)"
csih_inform "the '${csih_PRIVILEGED_USERNAME}' account."
echo ""
# If we use /etc account DB only, write new account to /etc/passwd
if csih_use_file_etc passwd
then
/usr/bin/mkpasswd -l -u "${csih_PRIVILEGED_USERNAME}" >> "${SYSCONFDIR}/passwd"
fi
if ! passwd -e "${csih_PRIVILEGED_USERNAME}" >/dev/null
then
csih_warning "Setting password expiry for user '${csih_PRIVILEGED_USERNAME}' failed!"
csih_warning "Please check that password never expires or set it to your needs."
fi
fi
fi # user allowed us to create account
else
# ${csih_PRIVILEGED_USERNAME} already exists. Use it, and make no changes.
# use passed-in value as first guess
csih_PRIVILEGED_PASSWORD="${password_value}"
return 0
fi
# Username did NOT previously exist, but has been successfully created.
# set group memberships, privileges, and passwd timeout.
if [ "$username_in_sam" = "yes" ]
then
# always try to set group membership and privileges
admingroup=$(/usr/bin/getent -w group S-1-5-32-544)
admingroup="${admingroup#*:*:*\\}"
admingroup="${admingroup%:*}"
if [ -z "${admingroup}" ]
then
csih_warning "Cannot obtain the Administrators group name from 'getent -w'."
ret=1
elif csih_call_winsys32 net localgroup "${admingroup}" | /usr/bin/grep -Eiq "^${csih_PRIVILEGED_USERWINNAME}.?$"
then
true
else
csih_call_winsys32 net localgroup "${admingroup}" "${csih_PRIVILEGED_USERWINNAME}" /add > /dev/null 2>&1 && username_in_admingroup=yes
if [ "${username_in_admingroup}" != "yes" ]
then
csih_warning "Adding user '${csih_PRIVILEGED_USERNAME}' to local group '${admingroup}' failed!"
csih_warning "Please add '${csih_PRIVILEGED_USERNAME}' to local group '${admingroup}' before"
csih_warning "starting any of the services which depend upon this user!"
ret=1
fi
fi
if ! csih_check_program_or_warn /usr/bin/editrights editrights
then
csih_warning "The 'editrights' program cannot be found or is not executable."
csih_warning "Unable to ensure that '${csih_PRIVILEGED_USERNAME}' has the appropriate privileges."
ret=1
else
/usr/bin/editrights -a SeAssignPrimaryTokenPrivilege -u ${csih_PRIVILEGED_USERNAME} &&
/usr/bin/editrights -a SeCreateTokenPrivilege -u ${csih_PRIVILEGED_USERNAME} &&
/usr/bin/editrights -a SeTcbPrivilege -u ${csih_PRIVILEGED_USERNAME} &&
/usr/bin/editrights -a SeDenyInteractiveLogonRight -u ${csih_PRIVILEGED_USERNAME} &&
/usr/bin/editrights -a SeDenyRemoteInteractiveLogonRight -u ${csih_PRIVILEGED_USERNAME} &&
/usr/bin/editrights -a SeServiceLogonRight -u ${csih_PRIVILEGED_USERNAME} &&
username_got_all_rights="yes"
if [ "${username_got_all_rights}" != "yes" ]
then
csih_warning "Assigning the appropriate privileges to user '${csih_PRIVILEGED_USERNAME}' failed!"
ret=1
fi
fi
return "${ret}"
fi # ! username_in_sam
return 1 # failed to create user (or prevented by auto-answer veto)
fi # csih_is_nt2003 (also XP64) || csih_FORCE_PRIVILEGED_USER
return 1 # nt/2k/xp32 without FORCE
} # === End of csih_create_privileged_user() === #
readonly -f csih_create_privileged_user
# ======================================================================
# Routine: csih_create_unprivileged_user
# Creates a new (unprivileged) user as specified by $1.
# Useful for running services that do not require elevated privileges,
# or running servers like sshd in "privilege separation" mode.
#
# Exits on catastrophic error
# Returns 0 on total success
# Returns 1 on failure
#
# csih_auto_answer=no behavior
# if already exists
# use it
# else
# do nothing, return 1
# ======================================================================
csih_create_unprivileged_user()
{
csih_stacktrace "${@}"
$_csih_trace
local unpriv_user="$1"
local map_entry
local user_exists=no
local dos_var_empty=
_csih_setup
map_entry="$(/usr/bin/getent -w passwd "U-${unpriv_user}")"
if [ -n "${map_entry}" ]
then
user_exists=yes
else
csih_inform "Note that creating a new user requires that the current account have"
csih_inform "Administrator privileges. Should this script attempt to create a"
# give auto-answer a chance to veto
if csih_request "new local account '${unpriv_user}'?"
then
dos_var_empty=$(/usr/bin/cygpath -w ${LOCALSTATEDIR}/empty)
csih_call_winsys32 net user "${unpriv_user}" \
/homedir:"${dos_var_empty}" \
/comment:'<cygwin home="/var/empty" shell="/bin/false"/>' \
/add /active:no >/dev/null 2>&1 && user_exists=yes
if [ "${user_exists}" != "yes" ]
then
csih_warning "Creating the user '${unpriv_user}' failed!"
else
# If we use /etc account DB only, write new account to /etc/passwd
if csih_use_file_etc passwd
then
/usr/bin/mkpasswd -l -u "${unpriv_user}" >> "${SYSCONFDIR}/passwd"
fi
fi
fi
fi
if [ "${user_exists}" = "yes" ]
then
local dw
map_entry="$(/usr/bin/getent -w passwd "U-${unpriv_user}")"
csih_UNPRIVILEGED_USERNAME="${map_entry%%:*}"
dw="${map_entry#*:*:}"
dw="${dw%:*}"
csih_UNPRIVILEGED_USERDOMAIN="${dw%\\*}"
csih_UNPRIVILEGED_USERWINNAME="${dw#*\\}"
return 0
fi
return 1
} # === End of csih_create_unprivileged_user() === #
readonly -f csih_create_unprivileged_user
# ======================================================================
# Routine: csih_create_local_group
# Creates a new local group as specified by $1.
#
# Exits on catastrophic error
# Returns 0 on total success
# Returns 1 on failure
#
# csih_auto_answer=no behavior
# if already exists
# use it
# else
# do nothing, return 1
# ======================================================================
csih_create_local_group()
{
csih_stacktrace "${@}"
$_csih_trace
local group="$1"
local map_entry
local grp_exists=no
_csih_setup
map_entry="$(/usr/bin/getent -w group "U-${group}")"
if [ -n "${map_entry}" ]
then
grp_exists=yes
else
csih_inform "Note that creating a new local group requires that the current account have"
csih_inform "Administrator privileges. Should this script attempt to create a"
# give auto-answer a chance to veto
if csih_request "new local group '${group}'?"
then
csih_call_winsys32 net localgroup "${group}" \
/add >/dev/null 2>&1 && grp_exists=yes
if [ "${grp_exists}" != "yes" ]
then
csih_warning "Creating the group '${group}' failed!"
else
# If we use /etc account DB only, write new group to /etc/group
if csih_use_file_etc group
then
/usr/bin/mkgroup -l -g "${group}" >> "${SYSCONFDIR}/group"
fi
fi
fi
fi
if [ "${grp_exists}" = "yes" ]
then
local dw
map_entry="$(/usr/bin/getent -w group "U-${group}")"
csih_LOCAL_GROUPNAME="${map_entry%%:*}"
dw="${map_entry#*:*:}"
dw="${dw%:*}"
csih_LOCAL_GROUPDOMAIN="${dw%\\*}"
csih_LOCAL_GROUPWINNAME="${dw#*\\}"
return 0
fi
return 1
} # === End of csih_create_local_group() === #
readonly -f csih_create_local_group
# ======================================================================
# Routine: csih_service_should_run_as [service_name]
# If [service_name] is specified, check to see if service_name is
# already installed. If so, return that user (after verifying
# that it has the necessary privileges). If not installed, behave
# as described below.
# Should call csih_select_privileged_username first, unless SURE
# that [service_name] has already been installed.
#
# Otherwise:
#
# On 64bit Windows XP, Windows Server 2003 and above (including
# Windows Vista), or if csih_FORCE_PRIVILEGED_USER == "yes"
# returns the selected privileged account name, IF it exists (e.g.
# already existed, or was successfully created). Otherwise,
# returns "SYSTEM". Callers should check this and warn.
#
# On Windows NT/2k/XP32, if csih_FORCE_PRIVILEGED_USER != yes, then
# if a privileged user already exists, return it
# else return "SYSTEM"
#
# MUST call either csih_select_privileged_username
# or csih_create_privileged_user
# first.
# ======================================================================
csih_service_should_run_as()
{
csih_stacktrace "${@}"
$_csih_trace
local opt_servicename
local domain
local winusername
# caller specified a service, so first check to see if that service
# is already installed, and if so, analyze that account. (If not,
# fall thru...)
if [ -n "$1" ]
then
opt_servicename="$1"
if /usr/bin/cygrunsrv -Q "${opt_servicename}" >/dev/null 2>&1
then
username=$(/usr/bin/cygrunsrv -V -Q "${opt_servicename}" 2>&1 | /usr/bin/sed -n -e '/^Account/s/^.* : //p')
domain="${username/\\*/}"
winusername="${username/*\\/}"
if [ "${username}" = "LocalSystem" ]
then
username=SYSTEM
else
username=$(/usr/bin/getent passwd "${winusername}" "${domain}+${winusername}" | /usr/bin/head -n1 | /usr/bin/cut -d: -f 1)
fi
if ( csih_is_nt2003 || [ "x$csih_FORCE_PRIVILEGED_USER" = "xyes" ] )
then
if [ -n "${username}" -a "${username}" != SYSTEM ]
then
if csih_privileged_account_exists "${username}"
then
echo "${username}"
return
else
if csih_account_has_necessary_privileges "${username}"
then
echo "${username}"
return
else
csih_error_multi \
"${opt_servicename} is installed under custom account '${username}'," \
"but '${username}' does not have the necessary permissions or " \
"group membership. Please correct this problem before continuing." 1>&2
fi
fi
else
# two different error cases: -z $username, or $username=SYSTEM
if [ -z "${username}" ]
then
csih_error_multi \
"${opt_servicename} is installed, but there was a problem determining" \
"the user account under which it runs. Please correct this problem" \
"before continuing." 1>&2
else
csih_error_multi \
"${opt_servicename} is installed under account 'SYSTEM', but that" \
"conflicts with privileged user requirement. ${opt_servicename}" \
"must be installed under a special privileged account: either" \
"because the OS is 64bit Windows XP, Windows Server 2003, or above," \
"or you requested -privileged." 1>&2
fi
fi
else
# not nt2003|xp64 nor (nt && csih_FORCE_PRIVILEGED_USER=yes)
# we don't care about properties of $username...
if [ -z "${username}" ]
then
csih_error_multi \
"${opt_servicename} is installed, but there was a problem determining" \
"the user account under which it runs. Please correct this problem" \
"before continuing." 1>&2
else
echo "${username}"
return
fi
fi
fi # ${opt_servicename} not installed
fi # ${opt_servicename} not specified
# Caller did not specify a specific service, or the specified service
# is not yet installed, so compute the "expected" account under which
# privileged services should run.
# use the following procedure if a privileged account is required:
if ( csih_is_nt2003 || [ "x$csih_FORCE_PRIVILEGED_USER" = "xyes" ] )
then
if [ -z "${csih_PRIVILEGED_USERNAME}" ]
then
csih_warning "INTERNAL: should call 'csih_select_privileged_username()' before 'csih_service_should_run_as()'" 1>&2
fi
if csih_privileged_account_exists "$csih_PRIVILEGED_USERNAME" 1>&2
then
# it already existed before this script was launched
echo "$csih_PRIVILEGED_USERNAME"
return
elif /usr/bin/getent passwd "${csih_PRIVILEGED_USERNAME}" >/dev/null 2>&1
then
# we probably just created it
echo "$csih_PRIVILEGED_USERNAME"
return
else
# a failure somewhere
csih_warning "Expected privileged user '${csih_PRIVILEGED_USERNAME}' does not exist." 1>&2
csih_warning "Defaulting to 'SYSTEM'" 1>&2
echo "SYSTEM"
return
fi
fi
# not nt2003|xp64, and csih_FORCE_PRIVILEGED != yes).
# Use fallback: if any privileged user exists, report that. Otherwise,
# report SYSTEM
csih_privileged_accounts
if [ -n "${_csih_preferred_preexisting_privileged_account}" ]
then
echo "${_csih_preferred_preexisting_privileged_account}"
else
echo "SYSTEM"
fi
} # === End of csih_service_should_run_as() === #
readonly -f csih_service_should_run_as
# ======================================================================
# Routine: _csih_late_initialization_code
# Initializes variables that require complex script support, such
# as csih_invoke_helper.
#
# SETS GLOBAL VARIABLE:
# _csih_script_dir
# _csih_exec_dir
# _csih_exactly_vista
# _csih_exactly_server2008
# _csih_exactly_windows7
# _csih_exactly_server2008r2
# _csih_exactly_windows8
# _csih_exactly_server2012
# _csih_win_product_name
# ======================================================================
_csih_late_initialization_code()
{
local rstatus
local productName
# These two variables must be initialized before calling
# csih_invoke_helper
_csih_script_dir=$(_csih_get_script_dir)
_csih_exec_dir=$(_csih_get_exec_dir)
productName=$(csih_invoke_helper winProductName)
rstatus=$?
if [ "$rstatus" -eq 0 ]
then
if echo "${productName}" | /usr/bin/grep -q " Server 2016 "
then
_csih_exactly_server2016=1
elif echo "${productName}" | /usr/bin/grep -q " Windows 10 "
then
_csih_exactly_windows10=1
elif echo "${productName}" | /usr/bin/grep -q " Server 2012 R2 "
then
_csih_exactly_server2012r2=1
elif echo "${productName}" | /usr/bin/grep -q " Windows 8\.1 "
then
_csih_exactly_windows8_1=1
elif echo "${productName}" | /usr/bin/grep -q " Server 2012 "
then
_csih_exactly_server2012=1
elif echo "${productName}" | /usr/bin/grep -q " Windows 8 "
then
_csih_exactly_windows8=1
elif echo "${productName}" | /usr/bin/grep -q " Server 2008 R2 "
then
_csih_exactly_server2008r2=1
elif echo "${productName}" | /usr/bin/grep -q " Windows 7 "
then
_csih_exactly_windows7=1
elif echo "${productName}" | /usr/bin/grep -q " Server 2008 "
then
_csih_exactly_server2008=1
elif echo "${productName}" | /usr/bin/grep -q " Vista "
then
_csih_exactly_vista=1
fi
_csih_win_product_name="${productName}";
fi
} # === End of _csih_late_initialization_code() === #
readonly -f _csih_late_initialization_code
# ======================================================================
# Initial setup, default values, etc. PART 3
#
# This part of the setup has to be deferred to the end of the csih
# script, since we need many of the previously defined functions to
# be available (such as csih_invoke_helper) before the variable values
# can be obtained. Most of this initialization is encapsulated in the
# _csih_late_initialization_code() function.
#
# Finally, we ensure that this file is being used properly (that is,
# sourced by another script rather than executed directly), and that
# the current cygwin and Windows versions are supported. csih requires
# WinNT or above and cygwin-1.7.x or above.
# ======================================================================
_csih_late_initialization_code
readonly _csih_script_dir _csih_exec_dir
readonly _csih_exactly_vista _csih_exactly_server2008
readonly _csih_exactly_server2008r2 _csih_exactly_windows7
readonly _csih_exactly_server2012 _csih_exactly_windows8
readonly _csih_exactly_server2012r2 _csih_exactly_windows8_1
readonly _csih_exactly_server2016 _csih_exactly_windows10
readonly _csih_win_product_name
if [ "cygwin-service-installation-helper.sh" = "$csih_progname_base" ]
then
csih_error "$csih_progname_base should not be executed directly"
fi