# -- #!/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}:[^#]*\" /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:'' \ /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:'' \ /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