This is the mail archive of the
cygwin-apps
mailing list for the Cygwin project.
cygwin-services-helper [was: Re: [ITA] inetutils-1.5-1]
Corinna Vinschen wrote:
On Feb 25 20:46, Charles Wilson wrote:
How about a new package, "cygwin-services-helper" or somesuch, that
contains
(1) a script [*] derived from the appropriate portion of sshd-host-config,
whose job is to create the appropriate priveleged user (I like
'cygwin_svc') -- unless it already exists under either name ('cygwin_svc'
or 'sshd_server').
(2) maybe another script [*] whose job is to ascertain whether such a user
already exists, and return its name (or "" if not).
It would be up to the calling foo-config to use these two scripts
appropriately. And, of course, the user might have to enter the password
for the priveleged user account twice: once when it is created, and then
again (by foo-config) to install the service 'foo'.
Then, openssh (and inetutils, and syslog-ng, and sysvinit, ...) could all
depend on the "cygwin-services-helper" package.
[*] or maybe a script function library somewhere like
/usr/lib/cygwin-services/ that foo-config could 'source', and then call the
functions directly. This would help the "enter the password twice"
problem...
Sounds good! The function library would be cool.
Here's my first draft. Totally untested, almost nuthin' in the way of
documentation...but I figured I'd post it now, because I won't have time
for any more cygwin stuff until the weekend...
TODO: (1) test, documentation, bughunt this function library
(2) rewrite ssh-host-config to use it
(3) rewrite iu-config to use it
(4) rewrite syslog-config to use it
(5) chase setup.exe bug with regards to inetutils' setup.hint
(6) incorporate bugfix for rshd (and rexecd /does/ have a similar
bug)
(7) remove --install-as-service from inetd
(7a) add code to read existing \\Parameters\ConfigFilePath
REG_SZ,, instead of ignoring it completely in favor of
\\Parameters\ConfigFilePaths REG_MULTI_SZ -- probably migrating it over
to the new REG_MULTI_SZ, since new 1.5 code *expects* at least two
entries in the char** config_files array.
(8) batten down the hatches on default inetd.conf
(9) update inetutils.README to reflect #7 & #8
Yeesh. This is gonna take a while...
--
Chuck
# -- #!/bin/bash --
# cygwin_services_helper.sh
#
# This is a script library used to assist installing cygwin
# services, such as sshd. It is derived in part from
#
# ssh-host-config (2008-02-25) Copyright 2000, 2001, 2002, 2003 Red Hat Inc.
# part of the Cygwin port of OpenSSH
#
# cygport (2008-02-25) Copyright (C) 2006, 2007 Yaakov Selkowitz
# GPL v3
#
# 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:
# csh_error
# csh_error_multi
# csh_warning
# csh_inform
# csh_verbose
# csh_request
# csh_is_nt
# csh_is_nt2003
# csh_check_prog
# csh_check_prog_req
# csh_install_config
# csh_make_dir
# csh_privileged_user_name
# csh_privileged_user_exists
# csh_service_should_run_as
# csh_check_mounts
# csh_create_privileged_user
# csh_create_unprivileged_user
#
# MUTABLE VARIABLES:
# csh_FORCE_PRIVILEGED_USER
# if "yes", then create a privileged user even on NT/2k/XP
# where it is not required (on those versions, LocalSystem
# will do fine).
# SYSCONFDIR
# default value = "/etc"
# LOCALSTATEDIR
# default value = "/var"
# csh_auto_answer
# default value = "" (no automatic answers)
csh_progname=$0
csh_progname_base=$(basename $csh_progname)
csh_auto_answer=""
csh_FORCE_PRIVILEGED_USER=no
if [ -z "${SYSCONFDIR}" ]
then
SYSCONFDIR=/etc
fi
if [ -z "${LOCALSTATEDIR}" ]
then
LOCALSTATEDIR=/var
fi
# messaging functions borrowed from cygport
csh_error() {
case $? in
0) local errorcode=1 ;;
*) local errorcode=$? ;;
esac
echo -e "\e[1;31m*** ERROR:\e[0;0m ${1:-no error message provided}";
exit ${errorcode};
}
csh_error_multi() {
# used for multi-line error messages.
case $? in
0) local errorcode=1 ;;
*) local errorcode=$? ;;
esac
while test $# -gt 1
do
echo -e "\e[1;31m*** ERROR:\e[0;0m ${1}";
shift
done
echo -e "\e[1;31m*** ERROR:\e[0;0m ${1:-no error message provided}";
exit ${errorcode};
}
csh_warning() {
echo -e "\e[1;33m*** Warning:\e[0;0m ${1}";
}
csh_inform() {
echo -e "\e[1;32m*** Info:\e[0;0m ${1}";
}
csh_verbose() {
echo "${@}"
"${@}"
return $?
}
csh_request()
{
local answer=""
if [ "${csh_auto_answer}" = "yes" ]
then
echo "$1 (yes/no) yes"
return 0
elif [ "${csh_auto_answer}" = "no" ]
then
echo "$1 (yes/no) no"
return 1
fi
while [ "X${answer}" != "Xyes" -a "X${answer}" != "Xno" ]
do
echo -n "$1 (yes/no) "
read -e answer
done
if [ "X${answer}" = "Xyes" ]
then
return 0
else
return 1
fi
}
readonly -f csh_error csh_error_multi csh_warning csh_inform
readonly -f csh_verbose csh_request
# Check if running on NT
_csh_sys="`uname`"
_csh_nt=`expr "${_csh_sys}" : "CYGWIN_NT"`
_csh_nt2003=0
# If running on NT, check if running under 2003 Server or later
if [ ${_nt} -gt 0 ]
then
_csh_nt2003=`uname | awk -F- '{print ( $2 >= 5.2 ) ? 1 : 0;}'`
fi
csh_is_nt()
{
test ${_csh_nt} -gt 0
}
csh_is_nt2003()
{
test ${_csh_nt2003} -gt 0
}
readonly -f csh_is_nt csh_is_nt2003
readonly _csh_sys _csh_nt _csh_nt2003
# patch check functions borrowed from cygport
# check that a program is found in PATH
csh_check_prog() {
local _prog;
for _prog
do
if ! hash ${_prog} &> /dev/null
then
return 1;
fi
done
return 0;
}
# check for mandatory program, else error
csh_check_prog_req() {
local prog=${1};
local pkg=${2:-${1}};
if ! csh_check_prog ${prog}
then
csh_error "${pkg} is required!";
fi
return 0;
}
readonly -f csh_check_prog csh_check_prog_req
csh_install_config()
{
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 csh_request "Overwrite existing ${dest} file?"
then
rm -f "${dest}"
if [ -f "${dest}" ]
then
csh_warning "Can't overwrite. ${dest} is write protected."
fi
fi
fi
if [ ! -f "${dest}" ]
then
if [ ! -f "${DEFAULTSDIR}/${dest}" ]
then
csh_warning "Can't create ${dest} because default version could not be found."
csh_warning "Check '${DEFAULTSDIR}'"
else
csh_info "Creating default ${dest} file"
cp ${DEFAULTSDIR}/${dest} ${dest}
fi
fi
}
csh_make_dir() {
local DIR;
if [ -z "$1" ]; then
return
fi
DIR=$1
shift
if [ -e "${DIR}" -a ! -d "${DIR}" ]
then
if [ -n "$1" ]
then
csh_error_multi "${DIR} exists, but is not a directory." "$1"
else
csh_error "${DIR} exists, but is not a directory."
fi
fi
if [ ! -e "${DIR}" ]
then
mkdir -p "${DIR}"
if [ ! -e "${DIR}" ]
then
csh_error "Creating ${DIR} directory failed."
fi
fi
}
readonly -f csh_make_dir csh_install_config
_csh_preexisting_privileged_user_name=""
csh_privileged_user_name()
{
local sshd_server_in_sam
local cygwin_svc_in_sam
if csh_is_nt
then
if [ -n "${_csh_preexisting_privileged_user_name}" ]
then
echo "${_csh_preexisting_privileged_user_name}"
return
fi
if net user sshd_server >/dev/null 2>&1
then
sshd_server_in_sam=yes
else
sshd_server_in_sam=no
fi
if net user cygwin_svc >/dev/null 2>&1
then
cygwin_svc_in_sam=yes
else
cygwin_svc_in_sam=no
fi
if [ "${cygwin_svc_in_sam}" != "yes" ]
then
if [ "${sshd_server_in_sam}" != "yes" ]
then
echo ""
return
else
_csh_preexisting_privileged_user_name="sshd_server"
fi
else
_csh_preexisting_privileged_user_name="cygwin_svc"
fi
echo "$_csh_preexisting_privileged_user_name"
else
# no such thing...
echo ""
fi
return
}
csh_privileged_user_exists()
{
local priv_user_name=$(csh_privileged_user_name)
if [ -n "${priv_user_name}" ]
then
return 0
fi
return 1
}
csh_service_should_run_as()
{
local priv_user_name=$(csh_privileged_user_name)
if csh_is_nt
then
if [ -n "${priv_user_name}" ]
then
echo "${priv_user_name}"
else
echo "system"
fi
else
echo ""
fi
}
csh_check_mounts()
{
if ! ( mount | egrep -q 'on /(|usr/(bin|lib)) type system' )
then
csh_warning "It appears that you have user mode mounts (\"Just me\" chosen"
csh_warning "during install.) Any daemons installed as services will fail"
csh_warning "to function unless system mounts are used. To change this,"
csh_warning "re-run setup.exe and choose \"All users\"."
csh_warn
csh_warning "For more information, see http://cygwin.com/faq/faq0.html#TOC33"
fi
}
_csh_setup()
{
if [ -z "${SYSCONFDIR}" ]
then
csh_error "Variable SYSCONFDIR is empty (should be '/etc' ?)"
fi
if [ -z "${LOCALSTATEDIR}" ]
then
csh_error "Variable LOCALSTATEDIR is empty (should be '/var' ?)"
fi
csh_make_dir "${SYSCONFDIR}"
csh_make_dir "${LOCALSTATEDIR}/empty"
}
csh_create_privileged_user()
{
local sshd_server_in_passwd
local cygwin_svc_in_passwd
local cygwin_svc_in_sam
local admingroup
local dos_var_empty
local _password
local password_value
local cygwin_svc_in_admingroup
local passwd_has_expiry_flags
local cygwin_svc_got_all_rights
_csh_setup
if csh_is_nt
then
if csh_privileged_user_exists
then
return 0
else
cygwin_svc_in_sam=no
# sshd_server_in_sam is also no, because !csh_privileged_user_exists
# but we don't care about that
if [ csh_is_nt2003 -o "$csh_FORCE_PRIVILEGED_USER" = "yes" ]
then
csh_inform "You appear to be running Windows 2003 Server or later. On 2003 and"
csh_inform "later systems, it's not possible to use the LocalSystem account"
csh_inform "for services that can change the user id without an expicit password"
csh_inform "(such as passwordless logins [e.g. public key authentication] via sshd)."
csh_inform ""
csh_inform "If you want to enable that functionality, it's required to create a new"
csh_inform "account 'cygwin_svc' with special privileges, which is then used to run"
csh_inform "these special servers."
csh_inform
csh_inform "Note that creating a new user requires that the current account have"
csh_inform "Administrator privileges itself. Should this script create a new local"
if request "account 'cygwin_svc' which has the required privileges?"
then
# check for privileged names in passwd file...
if grep -q '^sshd_server:' ${SYSCONFDIR}/passwd
then
sshd_server_in_passwd=yes
else
sshd_server_in_passwd=no
fi
if grep -q '^cygwin_svc:' ${SYSCONFDIR}/passwd
then
cygwin_svc_in_passwd=yes
else
cygwin_srv_in_passwd=no
fi
# and drop them from passwd since it could have wrong settings
if [ "${sshd_server_in_passwd}" ]
then
grep -v '^sshd_server:' ${SYSCONFDIR}/passwd > ${SYSCONFDIR}/passwd.$$
rm -f ${SYSCONFDIR}/passwd
mv ${SYSCONFDIR}/passwd.$$ ${SYSCONFDIR}/passwd
chmod g-w,o-w ${SYSCONFDIR}/passwd
fi
if [ "${cygwin_svc_in_passwd}" ]
then
grep -v '^cygwin_svc:' ${SYSCONFDIR}/passwd > ${SYSCONFDIR}/passwd.$$
rm -f ${SYSCONFDIR}/passwd
mv ${SYSCONFDIR}/passwd.$$ ${SYSCONFDIR}/passwd
chmod g-w,o-w ${SYSCONFDIR}/passwd
fi
admingroup=`mkgroup -l | awk -F: '{if ( $2 == "S-1-5-32-544" ) print $1;}' `
if [ -z "${admingroup}" ]
then
csh_error "mkgroup -l produces no group with SID S-1-5-32-544 (Local administrators group)."
fi
dos_var_empty=$(cygpath -w ${LOCALSTATEDIR}/empty)
while [ "${cygwin_svc_in_sam}" != "yes" ]
do
if [ -n "${password_value}" ]
then
_password="${password_value}"
# Allow to ask for password if first try fails
password_value=""
else
csh_inform "Please enter a password for new user 'cygwin_svc'. Please be sure"
csh_inform "that this password matches the password rules given on your system."
csh_inform "Entering no password will exit the configuration."
echo -n "PASSWORD="
read -e _password
if [ -z "${_password}" ]
then
csh_error_multi "Exiting configuration. No user cygwin_svc has been created," \
"and no services have been installed."
fi
fi
net user cygwin_svc "${_password}" /add /fullname:"cygwin services account" \
"/homedir:${dos_var_empty}" /yes > /tmp/nu.$$ 2>&1 && cygwin_svc_in_sam=yes
if [ "${cygwin_svc_in_sam}" != "yes" ]
then
csh_warning "Creating the user 'cygwin_svc' failed! Reason:"
cat /tmp/nu.$$
rm /tmp/nu.$$
fi
done
net localgroup "${admingroup}" cygwin_svc /add > /dev/null 2>&1 && cygwin_svc_in_admingroup=yes
if [ "${cygwin_svc_in_admingroup}" != "yes" ]
then
csh_warning "Adding user cygwin_svc to local group ${admingroup} failed!"
csh_warning "Please add cygwin_svc to local group ${admingroup} before"
csh_warning "starting any of the services which depend upon this user!"
fi
passwd_has_expiry_flags=`passwd -v | awk '/^passwd /{print ( $3 >= 1.5 ) ? "yes" : "no";}'`
if [ "${passwd_has_expiry_flags}" != "yes" ]
then
csh_warning "User cygwin_svc has password expiry set to system default."
csh_warning "Please check that password never expires or set it to your needs."
elif ! passwd -e cygwin_svc
then
csh_warning "Setting password expiry for user cygwin_svc failed!"
csh_warning "Please check that password never expires or set it to your needs."
fi
editrights -a SeAssignPrimaryTokenPrivilege -u cygwin_svc &&
editrights -a SeCreateTokenPrivilege -u cygwin_svc &&
editrights -a SeTcbPrivilege -u cygwin_svc &&
editrights -a SeDenyInteractiveLogonRight -u cygwin_svc &&
editrights -a SeDenyNetworkLogonRight -u cygwin_svc &&
editrights -a SeDenyRemoteInteractiveLogonRight -u cygwin_svc &&
editrights -a SeIncreaseQuotaPrivilege -u cygwin_svc &&
editrights -a SeServiceLogonRight -u cygwin_svc &&
cygwin_svc_got_all_rights="yes"
if [ "${cygwin_svc_got_all_rights}" != "yes" ]
then
csh_error_multi "Assigning the appropriate privileges to user 'cygwin_svc' failed!" \
"Can't install services which may need this user!"
fi
csh_inform "User 'cygwin_svc' has been created with password '${_password}'."
csh_inform "If you change the password, please keep in mind to change the password"
csh_inform "for any services which have been installed (or will be installed)"
csh_inform "under the 'cygwin_svc' account."
csh_inform ""
csh_inform "Also keep in mind that the user 'cygwin_svc' needs read permissions"
csh_inform "for on all users' relevant files for the services running as 'cygwin_svc'."
csh_inform "In particular, for the sshd server all users' .ssh/authorized_keys files"
csh_inform "must have appropriate permissions to allow public key authentication."
csh_inform "(Re-)running ssh-user-config for each user will set these permissions"
csh_inform "corrently. [Similary restrictions apply, for instance, for .rhosts files"
csh_inform "if the rshd server is running, etc]."
fi # yes, user allowed script to create cygwin_svc user
fi # csh_is_nt2003 || csh_FORCE_PRIVILEGED_USER
if [ "${cygwin_svc_in_sam}" = "yes" ]
then
# only way to get here is if we just now created the cygwin_svc
# user...which as a side effect removed cygwin_srv from /etc/passwd
# if it was there.
mkpasswd -l -u cygwin_svc | sed -e 's/bash$/false/' >> ${SYSCONFDIR}/passwd
fi
fi # ! csh_privileged_user_exists
else
# not nt
return 1
fi
return 0
}
csh_create_unprivileged_user() {
local unpriv_user
local unpriv_user_in_passwd
local unpriv_user_in_sam
local dos_var_empty
_csh_setup
if [ -n "$1" ]
then
unpriv_user="$1"
fi
if csh_is_nt
then
grep -q "^${unpriv_user}:" ${SYSCONFDIR}/passwd && unpriv_user_in_passwd=yes
net user "${unpriv_user}" >/dev/null 2>&1 && unpriv_user_in_sam=yes
if [ "${unpriv_user_in_passwd}" != "yes" ]
then
if [ "${unpriv_user_in_sam}" != "yes" ]
then
csh_inform "Note that creating a new user requires that the current account have"
csh_inform "Administrator privileges. Should this script attempt to create a"
if request "new local account '${unpriv_user}'?"
then
dos_var_empty=$(cygpath -w ${LOCALSTATEDIR}/empty)
net user "${unpriv_user}" /add /fullname:"${unpriv_user} privsep" "/homedir:${dos_var_empty}" /active:no > /dev/null 2>&1 && unpriv_user_in_sam=yes
if [ "${unpriv_user_in_sam}" != "yes" ]
then
csh_warning "Creating the user '${unpriv_user}' failed!"
fi
fi
fi
if [ "${unpriv_user_in_sam}" = "yes" ]
then
mkpasswd -l -u sshd | sed -e 's/bash$/false/' >> ${SYSCONFDIR}/passwd
unpriv_user_in_passwd=yes
fi
else
if [ "${unpriv_user_in_sam}" != "yes" ]
then
csh_warning "Something is wrong: ${unpriv_user} is in ${SYSCONFDIR}/passwd,"
csh_warning "but Windows does not know anything about ${unpriv_user}."
fi
fi
else # not nt
return 1
fi
return test "${unpriv_user_in_sam}" = "yes" -a "${unpriv_user_in_sam}" = "yes"
}
readonly -f _csh_setup csh_check_mounts csh_service_should_run_as
readonly -f csh_privileged_user_exists csh_privileged_user_name
readonly -f csh_create_privileged_user csh_create_unprivileged_user
if [ "cygwin-services-helper.sh" = "$csh_progname_base" ]
then
csh_error "$csh_progname_base should not be executed directly"
fi