3 # ssh-host-config, Copyright 2000-2014 Red Hat Inc.
5 # This file is part of the Cygwin port of OpenSSH.
7 # Permission to use, copy, modify, and distribute this software for any
8 # purpose with or without fee is hereby granted, provided that the above
9 # copyright notice and this permission notice appear in all copies.
11 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
12 # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
13 # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
14 # IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
15 # DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
16 # OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
17 # THE USE OR OTHER DEALINGS IN THE SOFTWARE.
19 # ======================================================================
21 # ======================================================================
23 CSIH_SCRIPT=/usr/share/csih/cygwin-service-installation-helper.sh
25 # List of apps used. This is checkad for existance in csih_sanity_check
26 # Don't use *any* transient commands before sourcing the csih helper script,
27 # otherwise the sanity checks are short-circuited.
28 declare -a csih_required_commands=(
29 /usr/bin/basename coreutils
30 /usr/bin/cat coreutils
31 /usr/bin/chmod coreutils
32 /usr/bin/dirname coreutils
36 /usr/bin/cygpath cygwin
37 /usr/bin/mkpasswd cygwin
40 /usr/bin/umount cygwin
41 /usr/bin/cmp diffutils
44 /usr/bin/ssh-keygen openssh
45 /usr/sbin/sshd openssh
48 csih_sanity_check_server=yes
51 PROGNAME=$(/usr/bin/basename $0)
52 _tdir=$(/usr/bin/dirname $0)
53 PROGDIR=$(cd $_tdir && pwd)
55 # Subdirectory where the new package is being installed
58 # Directory where the config files are stored
62 sshd_config_configured=no
72 # ======================================================================
73 # Routine: update_services_file
74 # ======================================================================
75 update_services_file() {
76 local _my_etcdir="/ssh-host-config.$$"
84 _win_etcdir="${SYSTEMROOT}\\system32\\drivers\\etc"
85 _services="${_my_etcdir}/services"
87 _serv_tmp="${_my_etcdir}/srv.out.$$"
89 /usr/bin/mount -o text,posix=0,noacl -f "${_win_etcdir}" "${_my_etcdir}"
91 # Depends on the above mount
92 _wservices=`cygpath -w "${_services}"`
94 # Add ssh 22/tcp and ssh 22/udp to services
95 if [ `/usr/bin/grep -q 'ssh[[:space:]][[:space:]]*22' "${_services}"; echo $?` -ne 0 ]
97 if /usr/bin/awk '{ if ( $2 ~ /^23\/tcp/ ) print "ssh 22/tcp'"${_spaces}"'SSH Remote Login Protocol\nssh 22/udp'"${_spaces}"'SSH Remote Login Protocol"; print $0; }' < "${_services}" > "${_serv_tmp}"
99 if /usr/bin/mv "${_serv_tmp}" "${_services}"
101 csih_inform "Added ssh to ${_wservices}"
103 csih_warning "Adding ssh to ${_wservices} failed!"
106 /usr/bin/rm -f "${_serv_tmp}"
108 csih_warning "Adding ssh to ${_wservices} failed!"
112 /usr/bin/umount "${_my_etcdir}"
114 } # --- End of update_services_file --- #
116 # ======================================================================
117 # Routine: sshd_strictmodes
118 # MODIFIES: strictmodes
119 # ======================================================================
121 if [ "${sshd_config_configured}" != "yes" ]
124 csih_inform "StrictModes is set to 'yes' by default."
125 csih_inform "This is the recommended setting, but it requires that the POSIX"
126 csih_inform "permissions of the user's home directory, the user's .ssh"
127 csih_inform "directory, and the user's ssh key files are tight so that"
128 csih_inform "only the user has write permissions."
129 csih_inform "On the other hand, StrictModes don't work well with default"
130 csih_inform "Windows permissions of a home directory mounted with the"
131 csih_inform "'noacl' option, and they don't work at all if the home"
132 csih_inform "directory is on a FAT or FAT32 partition."
133 if ! csih_request "Should StrictModes be used?"
141 # ======================================================================
142 # Routine: sshd_privsep
143 # MODIFIES: privsep_used
144 # ======================================================================
148 if [ "${sshd_config_configured}" != "yes" ]
151 csih_inform "Privilege separation is set to 'sandbox' by default since"
152 csih_inform "OpenSSH 6.1. This is unsupported by Cygwin and has to be set"
153 csih_inform "to 'yes' or 'no'."
154 csih_inform "However, using privilege separation requires a non-privileged account"
155 csih_inform "called 'sshd'."
156 csih_inform "For more info on privilege separation read /usr/share/doc/openssh/README.privsep."
157 if csih_request "Should privilege separation be used?"
160 if ! csih_create_unprivileged_user sshd
162 csih_error_recoverable "Couldn't create user 'sshd'!"
163 csih_error_recoverable "Privilege separation set to 'no' again!"
164 csih_error_recoverable "Check your ${SYSCONFDIR}/sshd_config file!"
173 } # --- End of sshd_privsep --- #
175 # ======================================================================
176 # Routine: sshd_config_tweak
177 # ======================================================================
178 sshd_config_tweak() {
182 csih_inform "Updating ${SYSCONFDIR}/sshd_config file"
183 if [ "${port_number}" -ne 22 ]
185 /usr/bin/sed -i -e "s/^#\?[[:space:]]*Port[[:space:]].*/Port ${port_number}/" \
186 ${SYSCONFDIR}/sshd_config
189 csih_warning "Setting listening port to ${port_number} failed!"
190 csih_warning "Check your ${SYSCONFDIR}/sshd_config file!"
194 if [ "${strictmodes}" = "no" ]
196 /usr/bin/sed -i -e "s/^#\?[[:space:]]*StrictModes[[:space:]].*/StrictModes no/" \
197 ${SYSCONFDIR}/sshd_config
200 csih_warning "Setting StrictModes to 'no' failed!"
201 csih_warning "Check your ${SYSCONFDIR}/sshd_config file!"
205 if [ "${sshd_config_configured}" != "yes" ]
208 s/^#\?UsePrivilegeSeparation .*/UsePrivilegeSeparation ${privsep_used}/" \
209 ${SYSCONFDIR}/sshd_config
212 csih_warning "Setting privilege separation failed!"
213 csih_warning "Check your ${SYSCONFDIR}/sshd_config file!"
218 } # --- End of sshd_config_tweak --- #
220 # ======================================================================
221 # Routine: update_inetd_conf
222 # ======================================================================
223 update_inetd_conf() {
224 local _inetcnf="${SYSCONFDIR}/inetd.conf"
225 local _inetcnf_tmp="${SYSCONFDIR}/inetd.conf.$$"
226 local _inetcnf_dir="${SYSCONFDIR}/inetd.d"
227 local _sshd_inetd_conf="${_inetcnf_dir}/sshd-inetd"
228 local _sshd_inetd_conf_tmp="${_inetcnf_dir}/sshd-inetd.$$"
229 local _with_comment=1
232 if [ -d "${_inetcnf_dir}" ]
234 # we have inetutils-1.5 inetd.d support
235 if [ -f "${_inetcnf}" ]
237 /usr/bin/grep -q '^[[:space:]]*ssh' "${_inetcnf}" && _with_comment=0
239 # check for sshd OR ssh in top-level inetd.conf file, and remove
240 # will be replaced by a file in inetd.d/
241 if [ $(/usr/bin/grep -q '^[# \t]*ssh' "${_inetcnf}"; echo $?) -eq 0 ]
243 /usr/bin/grep -v '^[# \t]*ssh' "${_inetcnf}" >> "${_inetcnf_tmp}"
244 if [ -f "${_inetcnf_tmp}" ]
246 if /usr/bin/mv "${_inetcnf_tmp}" "${_inetcnf}"
248 csih_inform "Removed ssh[d] from ${_inetcnf}"
250 csih_warning "Removing ssh[d] from ${_inetcnf} failed!"
253 /usr/bin/rm -f "${_inetcnf_tmp}"
255 csih_warning "Removing ssh[d] from ${_inetcnf} failed!"
261 csih_install_config "${_sshd_inetd_conf}" "${SYSCONFDIR}/defaults"
262 if /usr/bin/cmp "${SYSCONFDIR}/defaults${_sshd_inetd_conf}" "${_sshd_inetd_conf}" >/dev/null 2>&1
264 if [ "${_with_comment}" -eq 0 ]
266 /usr/bin/sed -e 's/@COMMENT@[[:space:]]*//' < "${_sshd_inetd_conf}" > "${_sshd_inetd_conf_tmp}"
268 /usr/bin/sed -e 's/@COMMENT@[[:space:]]*/# /' < "${_sshd_inetd_conf}" > "${_sshd_inetd_conf_tmp}"
270 if /usr/bin/mv "${_sshd_inetd_conf_tmp}" "${_sshd_inetd_conf}"
272 csih_inform "Updated ${_sshd_inetd_conf}"
274 csih_warning "Updating ${_sshd_inetd_conf} failed!"
279 elif [ -f "${_inetcnf}" ]
281 /usr/bin/grep -q '^[[:space:]]*sshd' "${_inetcnf}" && _with_comment=0
283 # check for sshd in top-level inetd.conf file, and remove
284 # will be replaced by a file in inetd.d/
285 if [ `/usr/bin/grep -q '^#\?[[:space:]]*sshd' "${_inetcnf}"; echo $?` -eq 0 ]
287 /usr/bin/grep -v '^#\?[[:space:]]*sshd' "${_inetcnf}" >> "${_inetcnf_tmp}"
288 if [ -f "${_inetcnf_tmp}" ]
290 if /usr/bin/mv "${_inetcnf_tmp}" "${_inetcnf}"
292 csih_inform "Removed sshd from ${_inetcnf}"
294 csih_warning "Removing sshd from ${_inetcnf} failed!"
297 /usr/bin/rm -f "${_inetcnf_tmp}"
299 csih_warning "Removing sshd from ${_inetcnf} failed!"
304 # Add ssh line to inetd.conf
305 if [ `/usr/bin/grep -q '^[# \t]*ssh' "${_inetcnf}"; echo $?` -ne 0 ]
307 if [ "${_with_comment}" -eq 0 ]
309 echo 'ssh stream tcp nowait root /usr/sbin/sshd sshd -i' >> "${_inetcnf}"
311 echo '# ssh stream tcp nowait root /usr/sbin/sshd sshd -i' >> "${_inetcnf}"
315 csih_inform "Added ssh to ${_inetcnf}"
317 csih_warning "Adding ssh to ${_inetcnf} failed!"
323 } # --- End of update_inetd_conf --- #
325 # ======================================================================
326 # Routine: check_service_files_ownership
327 # Checks that the files in /etc and /var belong to the right owner
328 # ======================================================================
329 check_service_files_ownership() {
330 local run_service_as=$1
333 if [ -z "${run_service_as}" ]
335 accnt_name=$(/usr/bin/cygrunsrv -VQ sshd |
336 /usr/bin/sed -ne 's/^Account *: *//gp')
337 if [ "${accnt_name}" = "LocalSystem" ]
339 # Convert "LocalSystem" to "SYSTEM" as is the correct account name
340 run_service_as="SYSTEM"
342 dom="${accnt_name%%\\*}"
343 accnt_name="${accnt_name#*\\}"
344 if [ "${dom}" = '.' ]
346 # Check local account
347 run_service_as=$(/usr/bin/mkpasswd -l -u "${accnt_name}" |
348 /usr/bin/awk -F: '{print $1;}')
351 run_service_as=$(/usr/bin/mkpasswd -d "${dom}" -u "${accnt_name}" |
352 /usr/bin/awk -F: '{print $1;}')
355 if [ -z "${run_service_as}" ]
357 csih_warning "Couldn't determine name of user running sshd service from account database!"
358 csih_warning "As a result, this script cannot make sure that the files used"
359 csih_warning "by the sshd service belong to the user running the service."
363 for i in "${SYSCONFDIR}"/ssh_config "${SYSCONFDIR}"/sshd_config "${SYSCONFDIR}"/ssh_host_*key "${SYSCONFDIR}"/ssh_host_*key.pub
367 if ! chown "${run_service_as}".544 "$i" >/dev/null 2>&1
369 csih_warning "Couldn't change owner of $i!"
374 if ! chown "${run_service_as}".544 ${LOCALSTATEDIR}/empty >/dev/null 2>&1
376 csih_warning "Couldn't change owner of ${LOCALSTATEDIR}/empty!"
379 if ! chown "${run_service_as}".544 ${LOCALSTATEDIR}/log/lastlog >/dev/null 2>&1
381 csih_warning "Couldn't change owner of ${LOCALSTATEDIR}/log/lastlog!"
384 if [ -f ${LOCALSTATEDIR}/log/sshd.log ]
386 if ! chown "${run_service_as}".544 ${LOCALSTATEDIR}/log/sshd.log >/dev/null 2>&1
388 csih_warning "Couldn't change owner of ${LOCALSTATEDIR}/log/sshd.log!"
394 csih_warning "Couldn't change owner of important files to ${run_service_as}!"
395 csih_warning "This may cause the sshd service to fail! Please make sure that"
396 csih_warning "you have suufficient permissions to change the ownership of files"
397 csih_warning "and try to run the ssh-host-config script again."
400 } # --- End of check_service_files_ownership --- #
402 # ======================================================================
403 # Routine: install_service
404 # Install sshd as a service
405 # ======================================================================
412 if /usr/bin/cygrunsrv -Q ${service_name} >/dev/null 2>&1
414 csih_inform "Sshd service is already installed."
415 check_service_files_ownership "" || let ret+=$?
417 echo -e "${_csih_QUERY_STR} Do you want to install sshd as a service?"
418 if csih_request "(Say \"no\" if it is already installed as a service)"
420 csih_get_cygenv "${cygwin_value}"
422 if ( csih_is_nt2003 || [ "$csih_FORCE_PRIVILEGED_USER" = "yes" ] )
424 csih_inform "On Windows Server 2003, Windows Vista, and above, the"
425 csih_inform "SYSTEM account cannot setuid to other users -- a capability"
426 csih_inform "sshd requires. You need to have or to create a privileged"
427 csih_inform "account. This script will help you do so."
430 [ "${opt_force}" = "yes" ] && opt_f=-f
431 [ -n "${user_account}" ] && opt_u="-u ""${user_account}"""
432 csih_select_privileged_username ${opt_f} ${opt_u} sshd
434 if ! csih_create_privileged_user "${password_value}"
436 csih_error_recoverable "There was a serious problem creating a privileged user."
437 csih_request "Do you want to proceed anyway?" || exit 1
442 # Never returns empty if NT or above
443 run_service_as=$(csih_service_should_run_as)
445 if [ "${run_service_as}" = "${csih_PRIVILEGED_USERNAME}" ]
447 password="${csih_PRIVILEGED_PASSWORD}"
448 if [ -z "${password}" ]
450 csih_get_value "Please enter the password for user '${run_service_as}':" "-s"
451 password="${csih_value}"
455 # At this point, we either have $run_service_as = "system" and
456 # $password is empty, or $run_service_as is some privileged user and
457 # (hopefully) $password contains the correct password. So, from here
458 # out, we use '-z "${password}"' to discriminate the two cases.
460 csih_check_user "${run_service_as}"
462 if [ -n "${csih_cygenv}" ]
464 cygwin_env=( -e "CYGWIN=${csih_cygenv}" )
466 if [ -z "${password}" ]
468 if /usr/bin/cygrunsrv -I ${service_name} -d "CYGWIN ${service_name}" -p /usr/sbin/sshd \
469 -a "-D" -y tcpip "${cygwin_env[@]}"
472 csih_inform "The sshd service has been installed under the LocalSystem"
473 csih_inform "account (also known as SYSTEM). To start the service now, call"
474 csih_inform "\`net start sshd' or \`cygrunsrv -S sshd'. Otherwise, it"
475 csih_inform "will start automatically after the next reboot."
478 if /usr/bin/cygrunsrv -I ${service_name} -d "CYGWIN ${service_name}" -p /usr/sbin/sshd \
479 -a "-D" -y tcpip "${cygwin_env[@]}" \
480 -u "${run_service_as}" -w "${password}"
482 /usr/bin/editrights -u "${run_service_as}" -a SeServiceLogonRight
484 csih_inform "The sshd service has been installed under the '${run_service_as}'"
485 csih_inform "account. To start the service now, call \`net start ${service_name}' or"
486 csih_inform "\`cygrunsrv -S ${service_name}'. Otherwise, it will start automatically"
487 csih_inform "after the next reboot."
491 if /usr/bin/cygrunsrv -Q ${service_name} >/dev/null 2>&1
493 check_service_files_ownership "${run_service_as}" || let ret+=$?
495 csih_error_recoverable "Installing sshd as a service failed!"
498 fi # user allowed us to install as service
499 fi # service not yet installed
501 } # --- End of install_service --- #
503 # ======================================================================
505 # ======================================================================
507 # Check how the script has been started. If
508 # (1) it has been started by giving the full path and
509 # that path is /etc/postinstall, OR
510 # (2) Otherwise, if the environment variable
511 # SSH_HOST_CONFIG_AUTO_ANSWER_NO is set
512 # then set auto_answer to "no". This allows automatic
513 # creation of the config files in /etc w/o overwriting
514 # them if they already exist. In both cases, color
515 # escape sequences are suppressed, so as to prevent
516 # cluttering setup's logfiles.
517 if [ "$PROGDIR" = "/etc/postinstall" ]
519 csih_auto_answer="no"
523 if [ -n "${SSH_HOST_CONFIG_AUTO_ANSWER_NO}" ]
525 csih_auto_answer="no"
530 # ======================================================================
532 # ======================================================================
586 csih_FORCE_PRIVILEGED_USER=yes
590 echo "usage: ${progname} [OPTION]..."
592 echo "This script creates an OpenSSH host configuration."
595 echo " --debug -d Enable shell's debug output."
596 echo " --yes -y Answer all questions with \"yes\" automatically."
597 echo " --no -n Answer all questions with \"no\" automatically."
598 echo " --cygwin -c <options> Use \"options\" as value for CYGWIN environment var."
599 echo " --name -N <name> sshd windows service name."
600 echo " --port -p <n> sshd listens on port n."
601 echo " --user -u <account> privileged user for service, default 'cyg_server'."
602 echo " --pwd -w <passwd> Use \"pwd\" as password for privileged user."
603 echo " --privileged On Windows XP, require privileged user"
604 echo " instead of LocalSystem for sshd service."
612 # ======================================================================
614 # ======================================================================
616 # Check for running ssh/sshd processes first. Refuse to do anything while
617 # some ssh processes are still running
618 if /usr/bin/ps -ef | /usr/bin/grep -q '/sshd\?$'
621 csih_error "There are still ssh processes running. Please shut them down first."
624 # Make sure the user is running in an administrative context
625 admin=$(/usr/bin/id -G | /usr/bin/grep -Eq '\<544\>' && echo yes || echo no)
626 if [ "${admin}" != "yes" ]
629 csih_warning "Running this script typically requires administrator privileges!"
630 csih_warning "However, it seems your account does not have these privileges."
631 csih_warning "Here's the list of groups in your user token:"
633 /usr/bin/id -Gnz | xargs -0n1 echo " "
635 csih_warning "This usually means you're running this script from a non-admin"
636 csih_warning "desktop session, or in a non-elevated shell under UAC control."
638 csih_warning "Make sure you have the appropriate privileges right now,"
639 csih_warning "otherwise parts of this script will probably fail!"
641 echo -e "${_csih_QUERY_STR} Are you sure you want to continue? (Say \"no\" if you're not sure"
642 if ! csih_request "you have the required privileges)"
645 csih_inform "Ok. Exiting. Make sure to switch to an administrative account"
646 csih_inform "or to start this script from an elevated shell."
655 # Create /var/log/lastlog if not already exists
656 if [ -e ${LOCALSTATEDIR}/log/lastlog -a ! -f ${LOCALSTATEDIR}/log/lastlog ]
659 csih_error_multi "${LOCALSTATEDIR}/log/lastlog exists, but is not a file." \
660 "Cannot create ssh host configuration."
662 if [ ! -e ${LOCALSTATEDIR}/log/lastlog ]
664 /usr/bin/cat /dev/null > ${LOCALSTATEDIR}/log/lastlog
665 if ! /usr/bin/chmod 644 ${LOCALSTATEDIR}/log/lastlog >/dev/null 2>&1
667 csih_warning "Can't set permissions on ${LOCALSTATEDIR}/log/lastlog!"
672 # Create /var/empty file used as chroot jail for privilege separation
673 csih_make_dir "${LOCALSTATEDIR}/empty" "Cannot create ${LOCALSTATEDIR}/empty directory."
674 if ! /usr/bin/chmod 755 "${LOCALSTATEDIR}/empty" >/dev/null 2>&1
676 csih_warning "Can't set permissions on ${LOCALSTATEDIR}/empty!"
680 # generate missing host keys
681 csih_inform "Generating missing SSH host keys"
682 /usr/bin/ssh-keygen -A || let warning_cnt+=$?
685 csih_install_config "${SYSCONFDIR}/ssh_config" "${SYSCONFDIR}/defaults" || let ++warning_cnt
686 if /usr/bin/cmp "${SYSCONFDIR}/ssh_config" "${SYSCONFDIR}/defaults/${SYSCONFDIR}/ssh_config" >/dev/null 2>&1
688 if [ "${port_number}" != "22" ]
690 csih_inform "Updating ${SYSCONFDIR}/ssh_config file with requested port"
691 echo "Host localhost" >> ${SYSCONFDIR}/ssh_config
692 echo " Port ${port_number}" >> ${SYSCONFDIR}/ssh_config
696 # handle sshd_config (and privsep)
697 csih_install_config "${SYSCONFDIR}/sshd_config" "${SYSCONFDIR}/defaults" || let ++warning_cnt
698 if ! /usr/bin/cmp "${SYSCONFDIR}/sshd_config" "${SYSCONFDIR}/defaults/${SYSCONFDIR}/sshd_config" >/dev/null 2>&1
700 sshd_config_configured=yes
702 sshd_strictmodes || let warning_cnt+=$?
703 sshd_privsep || let warning_cnt+=$?
704 sshd_config_tweak || let warning_cnt+=$?
705 update_services_file || let warning_cnt+=$?
706 update_inetd_conf || let warning_cnt+=$?
707 install_service || let warning_cnt+=$?
710 if [ $warning_cnt -eq 0 ]
712 csih_inform "Host configuration finished. Have fun!"
714 csih_warning "Host configuration exited with ${warning_cnt} errors or warnings!"
715 csih_warning "Make sure that all problems reported are fixed,"
716 csih_warning "then re-run ssh-host-config."