This is the mail archive of the cygwin-apps mailing list for the Cygwin project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

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 


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]