From 8dcc7c5ef45cf5032dca7a308ffe17d3935e62d5 Mon Sep 17 00:00:00 2001 From: Colin Watson Date: Sat, 27 Feb 2010 14:05:10 +0000 Subject: Convert to source format 3.0 (quilt). --- debian/patches/authorized-keys-man-symlink.patch | 12 + debian/patches/banner-noslash.patch | 13 + debian/patches/config-guess-sub.patch | 381 +++ debian/patches/debian-banner.patch | 91 + debian/patches/debian-config.patch | 124 + debian/patches/doc-connection-sharing.patch | 16 + debian/patches/doc-hash-tab-completion.patch | 14 + debian/patches/epfnosupport.patch | 17 + debian/patches/gnome-ssh-askpass2-icon.patch | 13 + debian/patches/gnome-ssh-askpass2-link.patch | 16 + debian/patches/gssapi-autoconf.patch | 24 + debian/patches/gssapi.patch | 2975 +++++++++++++++++++++ debian/patches/helpful-wait-terminate.patch | 13 + debian/patches/keepalive-extensions.patch | 108 + debian/patches/keyfile-debug.patch | 18 + debian/patches/lintian-symlink-pickiness.patch | 16 + debian/patches/no-constraint-fallback.patch | 43 + debian/patches/no-openssl-version-check.patch | 21 + debian/patches/old-gssapi.patch | 141 + debian/patches/oom-adjust.patch | 212 ++ debian/patches/openbsd-docs.patch | 107 + debian/patches/package-versioning.patch | 41 + debian/patches/quieter-signals.patch | 17 + debian/patches/scp-quoting.patch | 23 + debian/patches/selinux-autoconf.patch | 33 + debian/patches/selinux-fix-chroot-directory.patch | 26 + debian/patches/selinux-role.patch | 283 ++ debian/patches/series | 59 + debian/patches/shell-path.patch | 22 + debian/patches/ssh-argv0.patch | 12 + debian/patches/ssh-copy-id-status-check.patch | 13 + debian/patches/ssh-copy-id-trailing-colons.patch | 17 + debian/patches/ssh-vulnkey.patch | 1376 ++++++++++ debian/patches/ssh1-keepalive.patch | 60 + debian/patches/sshd-ignore-sighup.patch | 12 + debian/patches/syslog-level-silent.patch | 176 ++ debian/patches/user-group-modes.patch | 72 + 37 files changed, 6617 insertions(+) create mode 100644 debian/patches/authorized-keys-man-symlink.patch create mode 100644 debian/patches/banner-noslash.patch create mode 100644 debian/patches/config-guess-sub.patch create mode 100644 debian/patches/debian-banner.patch create mode 100644 debian/patches/debian-config.patch create mode 100644 debian/patches/doc-connection-sharing.patch create mode 100644 debian/patches/doc-hash-tab-completion.patch create mode 100644 debian/patches/epfnosupport.patch create mode 100644 debian/patches/gnome-ssh-askpass2-icon.patch create mode 100644 debian/patches/gnome-ssh-askpass2-link.patch create mode 100644 debian/patches/gssapi-autoconf.patch create mode 100644 debian/patches/gssapi.patch create mode 100644 debian/patches/helpful-wait-terminate.patch create mode 100644 debian/patches/keepalive-extensions.patch create mode 100644 debian/patches/keyfile-debug.patch create mode 100644 debian/patches/lintian-symlink-pickiness.patch create mode 100644 debian/patches/no-constraint-fallback.patch create mode 100644 debian/patches/no-openssl-version-check.patch create mode 100644 debian/patches/old-gssapi.patch create mode 100644 debian/patches/oom-adjust.patch create mode 100644 debian/patches/openbsd-docs.patch create mode 100644 debian/patches/package-versioning.patch create mode 100644 debian/patches/quieter-signals.patch create mode 100644 debian/patches/scp-quoting.patch create mode 100644 debian/patches/selinux-autoconf.patch create mode 100644 debian/patches/selinux-fix-chroot-directory.patch create mode 100644 debian/patches/selinux-role.patch create mode 100644 debian/patches/series create mode 100644 debian/patches/shell-path.patch create mode 100644 debian/patches/ssh-argv0.patch create mode 100644 debian/patches/ssh-copy-id-status-check.patch create mode 100644 debian/patches/ssh-copy-id-trailing-colons.patch create mode 100644 debian/patches/ssh-vulnkey.patch create mode 100644 debian/patches/ssh1-keepalive.patch create mode 100644 debian/patches/sshd-ignore-sighup.patch create mode 100644 debian/patches/syslog-level-silent.patch create mode 100644 debian/patches/user-group-modes.patch (limited to 'debian/patches') diff --git a/debian/patches/authorized-keys-man-symlink.patch b/debian/patches/authorized-keys-man-symlink.patch new file mode 100644 index 000000000..1e8aab147 --- /dev/null +++ b/debian/patches/authorized-keys-man-symlink.patch @@ -0,0 +1,12 @@ +Index: b/Makefile.in +=================================================================== +--- a/Makefile.in ++++ b/Makefile.in +@@ -284,6 +284,7 @@ + $(INSTALL) -m 644 sshd_config.5.out $(DESTDIR)$(mandir)/$(mansubdir)5/sshd_config.5 + $(INSTALL) -m 644 ssh_config.5.out $(DESTDIR)$(mandir)/$(mansubdir)5/ssh_config.5 + $(INSTALL) -m 644 sshd.8.out $(DESTDIR)$(mandir)/$(mansubdir)8/sshd.8 ++ ln -s ../$(mansubdir)8/sshd.8 $(DESTDIR)$(mandir)/$(mansubdir)5/authorized_keys.5 + if [ ! -z "$(INSTALL_SSH_RAND_HELPER)" ]; then \ + $(INSTALL) -m 644 ssh-rand-helper.8.out $(DESTDIR)$(mandir)/$(mansubdir)8/ssh-rand-helper.8 ; \ + fi diff --git a/debian/patches/banner-noslash.patch b/debian/patches/banner-noslash.patch new file mode 100644 index 000000000..8002791bd --- /dev/null +++ b/debian/patches/banner-noslash.patch @@ -0,0 +1,13 @@ +Index: b/sshconnect2.c +=================================================================== +--- a/sshconnect2.c ++++ b/sshconnect2.c +@@ -477,7 +477,7 @@ + if (len > 65536) + len = 65536; + msg = xmalloc(len * 4 + 1); /* max expansion from strnvis() */ +- strnvis(msg, raw, len * 4 + 1, VIS_SAFE|VIS_OCTAL); ++ strnvis(msg, raw, len * 4 + 1, VIS_SAFE|VIS_OCTAL|VIS_NOSLASH); + fprintf(stderr, "%s", msg); + xfree(msg); + } diff --git a/debian/patches/config-guess-sub.patch b/debian/patches/config-guess-sub.patch new file mode 100644 index 000000000..d5c016b87 --- /dev/null +++ b/debian/patches/config-guess-sub.patch @@ -0,0 +1,381 @@ +Index: b/config.guess +=================================================================== +--- a/config.guess ++++ b/config.guess +@@ -1,10 +1,10 @@ + #! /bin/sh + # Attempt to guess a canonical system name. + # Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, +-# 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 ++# 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 + # Free Software Foundation, Inc. + +-timestamp='2008-04-14' ++timestamp='2009-06-10' + + # This file is free software; you can redistribute it and/or modify it + # under the terms of the GNU General Public License as published by +@@ -170,7 +170,7 @@ + arm*|i386|m68k|ns32k|sh3*|sparc|vax) + eval $set_cc_for_build + if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ +- | grep __ELF__ >/dev/null ++ | grep -q __ELF__ + then + # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout). + # Return netbsd for either. FIX? +@@ -324,6 +324,9 @@ + case `/usr/bin/uname -p` in + sparc) echo sparc-icl-nx7; exit ;; + esac ;; ++ s390x:SunOS:*:*) ++ echo ${UNAME_MACHINE}-ibm-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` ++ exit ;; + sun4H:SunOS:5.*:*) + echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; +@@ -331,7 +334,20 @@ + echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*) +- echo i386-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` ++ eval $set_cc_for_build ++ SUN_ARCH="i386" ++ # If there is a compiler, see if it is configured for 64-bit objects. ++ # Note that the Sun cc does not turn __LP64__ into 1 like gcc does. ++ # This test works for both compilers. ++ if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then ++ if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \ ++ (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ ++ grep IS_64BIT_ARCH >/dev/null ++ then ++ SUN_ARCH="x86_64" ++ fi ++ fi ++ echo ${SUN_ARCH}-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + sun4*:SunOS:6*:*) + # According to config.sub, this is the proper way to canonicalize +@@ -640,7 +656,7 @@ + # => hppa64-hp-hpux11.23 + + if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | +- grep __LP64__ >/dev/null ++ grep -q __LP64__ + then + HP_ARCH="hppa2.0w" + else +@@ -796,7 +812,7 @@ + x86) + echo i586-pc-interix${UNAME_RELEASE} + exit ;; +- EM64T | authenticamd) ++ EM64T | authenticamd | genuineintel) + echo x86_64-unknown-interix${UNAME_RELEASE} + exit ;; + IA64) +@@ -806,6 +822,9 @@ + [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*) + echo i${UNAME_MACHINE}-pc-mks + exit ;; ++ 8664:Windows_NT:*) ++ echo x86_64-pc-mks ++ exit ;; + i*:Windows_NT*:* | Pentium*:Windows_NT*:*) + # How do we know it's Interix rather than the generic POSIX subsystem? + # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we +@@ -866,40 +885,17 @@ + m68*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; +- mips:Linux:*:*) ++ mips:Linux:*:* | mips64:Linux:*:*) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #undef CPU +- #undef mips +- #undef mipsel ++ #undef ${UNAME_MACHINE} ++ #undef ${UNAME_MACHINE}el + #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) +- CPU=mipsel ++ CPU=${UNAME_MACHINE}el + #else + #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) +- CPU=mips +- #else +- CPU= +- #endif +- #endif +-EOF +- eval "`$CC_FOR_BUILD -E $dummy.c 2>/dev/null | sed -n ' +- /^CPU/{ +- s: ::g +- p +- }'`" +- test x"${CPU}" != x && { echo "${CPU}-unknown-linux-gnu"; exit; } +- ;; +- mips64:Linux:*:*) +- eval $set_cc_for_build +- sed 's/^ //' << EOF >$dummy.c +- #undef CPU +- #undef mips64 +- #undef mips64el +- #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) +- CPU=mips64el +- #else +- #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) +- CPU=mips64 ++ CPU=${UNAME_MACHINE} + #else + CPU= + #endif +@@ -931,10 +927,13 @@ + EV67) UNAME_MACHINE=alphaev67 ;; + EV68*) UNAME_MACHINE=alphaev68 ;; + esac +- objdump --private-headers /bin/sh | grep ld.so.1 >/dev/null ++ objdump --private-headers /bin/sh | grep -q ld.so.1 + if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi + echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC} + exit ;; ++ padre:Linux:*:*) ++ echo sparc-unknown-linux-gnu ++ exit ;; + parisc:Linux:*:* | hppa:Linux:*:*) + # Look for CPU level + case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in +@@ -982,14 +981,6 @@ + elf32-i386) + TENTATIVE="${UNAME_MACHINE}-pc-linux-gnu" + ;; +- a.out-i386-linux) +- echo "${UNAME_MACHINE}-pc-linux-gnuaout" +- exit ;; +- "") +- # Either a pre-BFD a.out linker (linux-gnuoldld) or +- # one that does not give us useful --help. +- echo "${UNAME_MACHINE}-pc-linux-gnuoldld" +- exit ;; + esac + # Determine whether the default compiler is a.out or elf + eval $set_cc_for_build +@@ -1055,7 +1046,7 @@ + i*86:syllable:*:*) + echo ${UNAME_MACHINE}-pc-syllable + exit ;; +- i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.0*:*) ++ i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*) + echo i386-unknown-lynxos${UNAME_RELEASE} + exit ;; + i*86:*DOS:*:*) +@@ -1099,8 +1090,11 @@ + pc:*:*:*) + # Left here for compatibility: + # uname -m prints for DJGPP always 'pc', but it prints nothing about +- # the processor, so we play safe by assuming i386. +- echo i386-pc-msdosdjgpp ++ # the processor, so we play safe by assuming i586. ++ # Note: whatever this is, it MUST be the same as what config.sub ++ # prints for the "djgpp" host, or else GDB configury will decide that ++ # this is a cross-build. ++ echo i586-pc-msdosdjgpp + exit ;; + Intel:Mach:3*:*) + echo i386-pc-mach3 +@@ -1138,6 +1132,16 @@ + 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && { echo i486-ncr-sysv4; exit; } ;; ++ NCR*:*:4.2:* | MPRAS*:*:4.2:*) ++ OS_REL='.3' ++ test -r /etc/.relid \ ++ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` ++ /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ ++ && { echo i486-ncr-sysv4.3${OS_REL}; exit; } ++ /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ ++ && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ++ /bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \ ++ && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; + m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*) + echo m68k-unknown-lynxos${UNAME_RELEASE} + exit ;; +@@ -1150,7 +1154,7 @@ + rs6000:LynxOS:2.*:*) + echo rs6000-unknown-lynxos${UNAME_RELEASE} + exit ;; +- PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.0*:*) ++ PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*) + echo powerpc-unknown-lynxos${UNAME_RELEASE} + exit ;; + SM[BE]S:UNIX_SV:*:*) +@@ -1324,6 +1328,9 @@ + i*86:rdos:*:*) + echo ${UNAME_MACHINE}-pc-rdos + exit ;; ++ i*86:AROS:*:*) ++ echo ${UNAME_MACHINE}-pc-aros ++ exit ;; + esac + + #echo '(No uname command or uname output not recognized.)' 1>&2 +Index: b/config.sub +=================================================================== +--- a/config.sub ++++ b/config.sub +@@ -1,10 +1,10 @@ + #! /bin/sh + # Configuration validation subroutine script. + # Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, +-# 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 ++# 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 + # Free Software Foundation, Inc. + +-timestamp='2008-06-16' ++timestamp='2009-06-11' + + # This file is (in principle) common to ALL GNU software. + # The presence of a machine in this file suggests that SOME GNU software +@@ -122,6 +122,7 @@ + case $maybe_os in + nto-qnx* | linux-gnu* | linux-dietlibc | linux-newlib* | linux-uclibc* | \ + uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | knetbsd*-gnu* | netbsd*-gnu* | \ ++ kopensolaris*-gnu* | \ + storm-chaos* | os2-emx* | rtmk-nova*) + os=-$maybe_os + basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'` +@@ -152,6 +153,9 @@ + os= + basic_machine=$1 + ;; ++ -bluegene*) ++ os=-cnk ++ ;; + -sim | -cisco | -oki | -wec | -winbond) + os= + basic_machine=$1 +@@ -249,6 +253,7 @@ + | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \ + | i370 | i860 | i960 | ia64 \ + | ip2k | iq2000 \ ++ | lm32 \ + | m32c | m32r | m32rle | m68000 | m68k | m88k \ + | maxq | mb | microblaze | mcore | mep | metag \ + | mips | mipsbe | mipseb | mipsel | mipsle \ +@@ -270,6 +275,7 @@ + | mipsisa64sr71k | mipsisa64sr71kel \ + | mipstx39 | mipstx39el \ + | mn10200 | mn10300 \ ++ | moxie \ + | mt \ + | msp430 \ + | nios | nios2 \ +@@ -279,7 +285,7 @@ + | powerpc | powerpc64 | powerpc64le | powerpcle | ppcbe \ + | pyramid \ + | score \ +- | sh | sh[1234] | sh[24]a | sh[23]e | sh[34]eb | sheb | shbe | shle | sh[1234]le | sh3ele \ ++ | sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[34]eb | sheb | shbe | shle | sh[1234]le | sh3ele \ + | sh64 | sh64le \ + | sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \ + | sparcv8 | sparcv9 | sparcv9b | sparcv9v \ +@@ -288,7 +294,7 @@ + | v850 | v850e \ + | we32k \ + | x86 | xc16x | xscale | xscalee[bl] | xstormy16 | xtensa \ +- | z8k) ++ | z8k | z80) + basic_machine=$basic_machine-unknown + ;; + m6811 | m68hc11 | m6812 | m68hc12) +@@ -331,6 +337,7 @@ + | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \ + | i*86-* | i860-* | i960-* | ia64-* \ + | ip2k-* | iq2000-* \ ++ | lm32-* \ + | m32c-* | m32r-* | m32rle-* \ + | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \ + | m88110-* | m88k-* | maxq-* | mcore-* | metag-* \ +@@ -362,7 +369,7 @@ + | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* | ppcbe-* \ + | pyramid-* \ + | romp-* | rs6000-* \ +- | sh-* | sh[1234]-* | sh[24]a-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \ ++ | sh-* | sh[1234]-* | sh[24]a-* | sh[24]aeb-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \ + | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \ + | sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \ + | sparclite-* \ +@@ -375,7 +382,7 @@ + | x86-* | x86_64-* | xc16x-* | xps100-* | xscale-* | xscalee[bl]-* \ + | xstormy16-* | xtensa*-* \ + | ymp-* \ +- | z8k-*) ++ | z8k-* | z80-*) + ;; + # Recognize the basic CPU types without company name, with glob match. + xtensa*) +@@ -443,6 +450,10 @@ + basic_machine=m68k-apollo + os=-bsd + ;; ++ aros) ++ basic_machine=i386-pc ++ os=-aros ++ ;; + aux) + basic_machine=m68k-apple + os=-aux +@@ -459,6 +470,10 @@ + basic_machine=bfin-`echo $basic_machine | sed 's/^[^-]*-//'` + os=-linux + ;; ++ bluegene*) ++ basic_machine=powerpc-ibm ++ os=-cnk ++ ;; + c90) + basic_machine=c90-cray + os=-unicos +@@ -1140,6 +1155,10 @@ + basic_machine=z8k-unknown + os=-sim + ;; ++ z80-*-coff) ++ basic_machine=z80-unknown ++ os=-sim ++ ;; + none) + basic_machine=none-none + os=-none +@@ -1178,7 +1197,7 @@ + we32k) + basic_machine=we32k-att + ;; +- sh[1234] | sh[24]a | sh[34]eb | sh[1234]le | sh[23]ele) ++ sh[1234] | sh[24]a | sh[24]aeb | sh[34]eb | sh[1234]le | sh[23]ele) + basic_machine=sh-unknown + ;; + sparc | sparcv8 | sparcv9 | sparcv9b | sparcv9v) +@@ -1248,10 +1267,11 @@ + # Each alternative MUST END IN A *, to match a version number. + # -sysv* is not here because it comes later, after sysvr4. + -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \ +- | -*vms* | -sco* | -esix* | -isc* | -aix* | -sunos | -sunos[34]*\ ++ | -*vms* | -sco* | -esix* | -isc* | -aix* | -cnk* | -sunos | -sunos[34]*\ + | -hpux* | -unos* | -osf* | -luna* | -dgux* | -solaris* | -sym* \ ++ | -kopensolaris* \ + | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \ +- | -aos* \ ++ | -aos* | -aros* \ + | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \ + | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \ + | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* \ +@@ -1600,7 +1620,7 @@ + -sunos*) + vendor=sun + ;; +- -aix*) ++ -cnk*|-aix*) + vendor=ibm + ;; + -beos*) diff --git a/debian/patches/debian-banner.patch b/debian/patches/debian-banner.patch new file mode 100644 index 000000000..814294e26 --- /dev/null +++ b/debian/patches/debian-banner.patch @@ -0,0 +1,91 @@ +Index: b/servconf.c +=================================================================== +--- a/servconf.c ++++ b/servconf.c +@@ -132,6 +132,7 @@ + options->adm_forced_command = NULL; + options->chroot_directory = NULL; + options->zero_knowledge_password_authentication = -1; ++ options->debian_banner = -1; + } + + void +@@ -273,6 +274,8 @@ + options->permit_tun = SSH_TUNMODE_NO; + if (options->zero_knowledge_password_authentication == -1) + options->zero_knowledge_password_authentication = 0; ++ if (options->debian_banner == -1) ++ options->debian_banner = 1; + + /* Turn privilege separation on by default */ + if (use_privsep == -1) +@@ -320,6 +323,7 @@ + sMatch, sPermitOpen, sForceCommand, sChrootDirectory, + sUsePrivilegeSeparation, sAllowAgentForwarding, + sZeroKnowledgePasswordAuthentication, ++ sDebianBanner, + sDeprecated, sUnsupported + } ServerOpCodes; + +@@ -449,6 +453,7 @@ + { "permitopen", sPermitOpen, SSHCFG_ALL }, + { "forcecommand", sForceCommand, SSHCFG_ALL }, + { "chrootdirectory", sChrootDirectory, SSHCFG_ALL }, ++ { "debianbanner", sDebianBanner, SSHCFG_GLOBAL }, + { NULL, sBadOption, 0 } + }; + +@@ -1335,6 +1340,10 @@ + *charptr = xstrdup(arg); + break; + ++ case sDebianBanner: ++ intptr = &options->debian_banner; ++ goto parse_int; ++ + case sDeprecated: + logit("%s line %d: Deprecated option %s", + filename, linenum, arg); +Index: b/servconf.h +=================================================================== +--- a/servconf.h ++++ b/servconf.h +@@ -154,6 +154,8 @@ + + int num_permitted_opens; + ++ int debian_banner; ++ + char *chroot_directory; + } ServerOptions; + +Index: b/sshd.c +=================================================================== +--- a/sshd.c ++++ b/sshd.c +@@ -426,7 +426,8 @@ + minor = PROTOCOL_MINOR_1; + } + snprintf(buf, sizeof buf, "SSH-%d.%d-%.100s%s", major, minor, +- SSH_RELEASE, newline); ++ options.debian_banner ? SSH_RELEASE : SSH_RELEASE_MINIMUM, ++ newline); + server_version_string = xstrdup(buf); + + /* Send our protocol version identification. */ +Index: b/sshd_config.5 +=================================================================== +--- a/sshd_config.5 ++++ b/sshd_config.5 +@@ -295,6 +295,11 @@ + .Dq no . + The default is + .Dq delayed . ++.It Cm DebianBanner ++Specifies whether the distribution-specified extra version suffix is ++included during initial protocol handshake. ++The default is ++.Dq yes . + .It Cm DenyGroups + This keyword can be followed by a list of group name patterns, separated + by spaces. diff --git a/debian/patches/debian-config.patch b/debian/patches/debian-config.patch new file mode 100644 index 000000000..5aa0035c8 --- /dev/null +++ b/debian/patches/debian-config.patch @@ -0,0 +1,124 @@ +Index: b/readconf.c +=================================================================== +--- a/readconf.c ++++ b/readconf.c +@@ -1150,7 +1150,7 @@ + if (options->forward_x11 == -1) + options->forward_x11 = 0; + if (options->forward_x11_trusted == -1) +- options->forward_x11_trusted = 0; ++ options->forward_x11_trusted = 1; + if (options->exit_on_forward_failure == -1) + options->exit_on_forward_failure = 0; + if (options->xauth_location == NULL) +Index: b/ssh_config +=================================================================== +--- a/ssh_config ++++ b/ssh_config +@@ -17,9 +17,10 @@ + # list of available options, their meanings and defaults, please see the + # ssh_config(5) man page. + +-# Host * ++Host * + # ForwardAgent no + # ForwardX11 no ++# ForwardX11Trusted yes + # RhostsRSAAuthentication no + # RSAAuthentication yes + # PasswordAuthentication yes +@@ -46,3 +47,7 @@ + # TunnelDevice any:any + # PermitLocalCommand no + # VisualHostKey no ++ SendEnv LANG LC_* ++ HashKnownHosts yes ++ GSSAPIAuthentication yes ++ GSSAPIDelegateCredentials no +Index: b/ssh_config.5 +=================================================================== +--- a/ssh_config.5 ++++ b/ssh_config.5 +@@ -72,6 +72,22 @@ + host-specific declarations should be given near the beginning of the + file, and general defaults at the end. + .Pp ++Note that the Debian ++.Ic openssh-client ++package sets several options as standard in ++.Pa /etc/ssh/ssh_config ++which are not the default in ++.Xr ssh 1 : ++.Pp ++.Bl -bullet -offset indent -compact ++.It ++.Cm SendEnv No LANG LC_* ++.It ++.Cm HashKnownHosts No yes ++.It ++.Cm GSSAPIAuthentication No yes ++.El ++.Pp + The configuration file has the following format: + .Pp + Empty lines and lines starting with +@@ -452,7 +468,8 @@ + Remote clients will be refused access after this time. + .Pp + The default is +-.Dq no . ++.Dq yes ++(Debian-specific). + .Pp + See the X11 SECURITY extension specification for full details on + the restrictions imposed on untrusted clients. +Index: b/sshd_config +=================================================================== +--- a/sshd_config ++++ b/sshd_config +@@ -38,6 +38,7 @@ + # Authentication: + + #LoginGraceTime 2m ++# See /usr/share/doc/openssh-server/README.Debian.gz. + #PermitRootLogin yes + #StrictModes yes + #MaxAuthTries 6 +Index: b/sshd_config.5 +=================================================================== +--- a/sshd_config.5 ++++ b/sshd_config.5 +@@ -58,6 +58,33 @@ + .Pq \&" + in order to represent arguments containing spaces. + .Pp ++Note that the Debian ++.Ic openssh-server ++package sets several options as standard in ++.Pa /etc/ssh/sshd_config ++which are not the default in ++.Xr sshd 8 . ++The exact list depends on whether the package was installed fresh or ++upgraded from various possible previous versions, but includes at least the ++following: ++.Pp ++.Bl -bullet -offset indent -compact ++.It ++.Cm Protocol No 2 ++.It ++.Cm ChallengeResponseAuthentication No no ++.It ++.Cm X11Forwarding No yes ++.It ++.Cm PrintMotd No no ++.It ++.Cm AcceptEnv No LANG LC_* ++.It ++.Cm Subsystem No sftp /usr/lib/openssh/sftp-server ++.It ++.Cm UsePAM No yes ++.El ++.Pp + The possible + keywords and their meanings are as follows (note that + keywords are case-insensitive and arguments are case-sensitive): diff --git a/debian/patches/doc-connection-sharing.patch b/debian/patches/doc-connection-sharing.patch new file mode 100644 index 000000000..69a11c590 --- /dev/null +++ b/debian/patches/doc-connection-sharing.patch @@ -0,0 +1,16 @@ +Index: b/ssh.1 +=================================================================== +--- a/ssh.1 ++++ b/ssh.1 +@@ -559,7 +559,10 @@ + the listen port will be dynamically allocated on the server and reported + to the client at run time. + .It Fl S Ar ctl_path +-Specifies the location of a control socket for connection sharing. ++Specifies the location of a control socket for connection sharing, ++or the string ++.Dq none ++to disable connection sharing. + Refer to the description of + .Cm ControlPath + and diff --git a/debian/patches/doc-hash-tab-completion.patch b/debian/patches/doc-hash-tab-completion.patch new file mode 100644 index 000000000..c878b213e --- /dev/null +++ b/debian/patches/doc-hash-tab-completion.patch @@ -0,0 +1,14 @@ +Index: b/ssh_config.5 +=================================================================== +--- a/ssh_config.5 ++++ b/ssh_config.5 +@@ -531,6 +531,9 @@ + will not be converted automatically, + but may be manually hashed using + .Xr ssh-keygen 1 . ++Use of this option may break facilities such as tab-completion that rely ++on being able to read unhashed host names from ++.Pa ~/.ssh/known_hosts . + .It Cm HostbasedAuthentication + Specifies whether to try rhosts based authentication with public key + authentication. diff --git a/debian/patches/epfnosupport.patch b/debian/patches/epfnosupport.patch new file mode 100644 index 000000000..aceb71ff8 --- /dev/null +++ b/debian/patches/epfnosupport.patch @@ -0,0 +1,17 @@ +Index: b/channels.c +=================================================================== +--- a/channels.c ++++ b/channels.c +@@ -3098,7 +3098,11 @@ + sock = socket(ai->ai_family, ai->ai_socktype, + ai->ai_protocol); + if (sock < 0) { +- if ((errno != EINVAL) && (errno != EAFNOSUPPORT)) { ++ if ((errno != EINVAL) && (errno != EAFNOSUPPORT) ++#ifdef EPFNOSUPPORT ++ && (errno != EPFNOSUPPORT) ++#endif ++ ) { + error("socket: %.100s", strerror(errno)); + freeaddrinfo(aitop); + return -1; diff --git a/debian/patches/gnome-ssh-askpass2-icon.patch b/debian/patches/gnome-ssh-askpass2-icon.patch new file mode 100644 index 000000000..aaf804841 --- /dev/null +++ b/debian/patches/gnome-ssh-askpass2-icon.patch @@ -0,0 +1,13 @@ +Index: b/contrib/gnome-ssh-askpass2.c +=================================================================== +--- a/contrib/gnome-ssh-askpass2.c ++++ b/contrib/gnome-ssh-askpass2.c +@@ -207,6 +207,8 @@ + + gtk_init(&argc, &argv); + ++ gtk_window_set_default_icon_from_file ("/usr/share/pixmaps/ssh-askpass-gnome.png", NULL); ++ + if (argc > 1) { + message = g_strjoinv(" ", argv + 1); + } else { diff --git a/debian/patches/gnome-ssh-askpass2-link.patch b/debian/patches/gnome-ssh-askpass2-link.patch new file mode 100644 index 000000000..6e1a341b0 --- /dev/null +++ b/debian/patches/gnome-ssh-askpass2-link.patch @@ -0,0 +1,16 @@ +Index: b/contrib/Makefile +=================================================================== +--- a/contrib/Makefile ++++ b/contrib/Makefile +@@ -7,9 +7,9 @@ + `gnome-config --libs gnome gnomeui` + + gnome-ssh-askpass2: gnome-ssh-askpass2.c +- $(CC) `pkg-config --cflags gtk+-2.0` \ ++ $(CC) `pkg-config --cflags gtk+-2.0 x11` \ + gnome-ssh-askpass2.c -o gnome-ssh-askpass2 \ +- `pkg-config --libs gtk+-2.0` ++ `pkg-config --libs gtk+-2.0 x11` + + clean: + rm -f *.o gnome-ssh-askpass1 gnome-ssh-askpass2 gnome-ssh-askpass diff --git a/debian/patches/gssapi-autoconf.patch b/debian/patches/gssapi-autoconf.patch new file mode 100644 index 000000000..1e7949116 --- /dev/null +++ b/debian/patches/gssapi-autoconf.patch @@ -0,0 +1,24 @@ +Index: b/config.h.in +=================================================================== +--- a/config.h.in ++++ b/config.h.in +@@ -1372,6 +1372,9 @@ + /* Use btmp to log bad logins */ + #undef USE_BTMP + ++/* platform uses an in-memory credentials cache */ ++#undef USE_CCAPI ++ + /* Use libedit for sftp */ + #undef USE_LIBEDIT + +@@ -1390,6 +1393,9 @@ + /* Define if you want smartcard support using sectok */ + #undef USE_SECTOK + ++/* platform has the Security Authorization Session API */ ++#undef USE_SECURITY_SESSION_API ++ + /* Define if you have Solaris process contracts */ + #undef USE_SOLARIS_PROCESS_CONTRACTS + diff --git a/debian/patches/gssapi.patch b/debian/patches/gssapi.patch new file mode 100644 index 000000000..a60a8b4e1 --- /dev/null +++ b/debian/patches/gssapi.patch @@ -0,0 +1,2975 @@ +Index: b/ChangeLog.gssapi +=================================================================== +--- /dev/null ++++ b/ChangeLog.gssapi +@@ -0,0 +1,103 @@ ++20100124 ++ - [ sshconnect2.c ] ++ Adapt to deal with additional element in Authmethod structure. Thanks to ++ Colin Wilson ++ - [ clientloop.c ] ++ Protect credentials updated code with suitable #ifdefs. Thanks to Colin ++ Wilson ++ ++20090615 ++ - [ gss-genr.c gss-serv.c kexgssc.c kexgsss.c monitor.c sshconnect2.c ++ sshd.c ] ++ Fix issues identified by Greg Hudson following a code review ++ Check return value of gss_indicate_mechs ++ Protect GSSAPI calls in monitor, so they can only be used if enabled ++ Check return values of bignum functions in key exchange ++ Use BN_clear_free to clear other side's DH value ++ Make ssh_gssapi_id_kex more robust ++ Only configure kex table pointers if GSSAPI is enabled ++ Don't leak mechanism list, or gss mechanism list ++ Cast data.length before printing ++ If serverkey isn't provided, use an empty string, rather than NULL ++ ++20090201 ++ - [ gss-genr.c gss-serv.c kex.h kexgssc.c readconf.c readconf.h ssh-gss.h ++ ssh_config.5 sshconnet2.c ] ++ Add support for the GSSAPIClientIdentity option, which allows the user ++ to specify which GSSAPI identity to use to contact a given server ++ ++20080404 ++ - [ gss-serv.c ] ++ Add code to actually implement GSSAPIStrictAcceptCheck, which had somehow ++ been omitted from a previous version of this patch. Reported by Borislav ++ Stoichkov ++ ++20070317 ++ - [ gss-serv-krb5.c ] ++ Remove C99ism, where new_ccname was being declared in the middle of a ++ function ++ ++20061220 ++ - [ servconf.c ] ++ Make default for GSSAPIStrictAcceptorCheck be Yes, to match previous, and ++ documented, behaviour. Reported by Dan Watson. ++ ++20060910 ++ - [ gss-genr.c kexgssc.c kexgsss.c kex.h monitor.c sshconnect2.c sshd.c ++ ssh-gss.h ] ++ add support for gss-group14-sha1 key exchange mechanisms ++ - [ gss-serv.c servconf.c servconf.h sshd_config sshd_config.5 ] ++ Add GSSAPIStrictAcceptorCheck option to allow the disabling of ++ acceptor principal checking on multi-homed machines. ++ ++ - [ sshd_config ssh_config ] ++ Add settings for GSSAPIKeyExchange and GSSAPITrustDNS to the sample ++ configuration files ++ - [ kexgss.c kegsss.c sshconnect2.c sshd.c ] ++ Code cleanup. Replace strlen/xmalloc/snprintf sequences with xasprintf() ++ Limit length of error messages displayed by client ++ ++20060909 ++ - [ gss-genr.c gss-serv.c ] ++ move ssh_gssapi_acquire_cred() and ssh_gssapi_server_ctx to be server ++ only, where they belong ++ ++ ++20060829 ++ - [ gss-serv-krb5.c ] ++ Fix CCAPI credentials cache name when creating KRB5CCNAME environment ++ variable ++ ++20060828 ++ - [ gss-genr.c ] ++ Avoid Heimdal context freeing problem ++ ++ ++20060818 ++ - [ gss-genr.c ssh-gss.h sshconnect2.c ] ++ Make sure that SPENGO is disabled ++ ++ ++20060421 ++ - [ gssgenr.c, sshconnect2.c ] ++ a few type changes (signed versus unsigned, int versus size_t) to ++ fix compiler errors/warnings ++ (from jbasney AT ncsa.uiuc.edu) ++ - [ kexgssc.c, sshconnect2.c ] ++ fix uninitialized variable warnings ++ (from jbasney AT ncsa.uiuc.edu) ++ - [ gssgenr.c ] ++ pass oid to gss_display_status (helpful when using GSSAPI mechglue) ++ (from jbasney AT ncsa.uiuc.edu) ++ ++ - [ gss-serv-krb5.c ] ++ #ifdef HAVE_GSSAPI_KRB5 should be #ifdef HAVE_GSSAPI_KRB5_H ++ (from jbasney AT ncsa.uiuc.edu) ++ ++ - [ readconf.c, readconf.h, ssh_config.5, sshconnect2.c ++ add client-side GssapiKeyExchange option ++ (from jbasney AT ncsa.uiuc.edu) ++ - [ sshconnect2.c ] ++ add support for GssapiTrustDns option for gssapi-with-mic ++ (from jbasney AT ncsa.uiuc.edu) ++ +Index: b/Makefile.in +=================================================================== +--- a/Makefile.in ++++ b/Makefile.in +@@ -71,7 +71,8 @@ + atomicio.o key.o dispatch.o kex.o mac.o uidswap.o uuencode.o misc.o \ + monitor_fdpass.o rijndael.o ssh-dss.o ssh-rsa.o dh.o kexdh.o \ + kexgex.o kexdhc.o kexgexc.o scard.o msg.o progressmeter.o dns.o \ +- entropy.o scard-opensc.o gss-genr.o umac.o jpake.o schnorr.o ++ entropy.o scard-opensc.o gss-genr.o umac.o jpake.o schnorr.o \ ++ kexgssc.o + + SSHOBJS= ssh.o readconf.o clientloop.o sshtty.o \ + sshconnect.o sshconnect1.o sshconnect2.o mux.o \ +@@ -85,7 +86,7 @@ + auth2-none.o auth2-passwd.o auth2-pubkey.o auth2-jpake.o \ + monitor_mm.o monitor.o monitor_wrap.o kexdhs.o kexgexs.o \ + auth-krb5.o \ +- auth2-gss.o gss-serv.o gss-serv-krb5.o \ ++ auth2-gss.o gss-serv.o gss-serv-krb5.o kexgsss.o\ + loginrec.o auth-pam.o auth-shadow.o auth-sia.o md5crypt.o \ + audit.o audit-bsm.o platform.o sftp-server.o sftp-common.o \ + roaming_common.o +Index: b/auth-krb5.c +=================================================================== +--- a/auth-krb5.c ++++ b/auth-krb5.c +@@ -166,8 +166,13 @@ + + len = strlen(authctxt->krb5_ticket_file) + 6; + authctxt->krb5_ccname = xmalloc(len); ++#ifdef USE_CCAPI ++ snprintf(authctxt->krb5_ccname, len, "API:%s", ++ authctxt->krb5_ticket_file); ++#else + snprintf(authctxt->krb5_ccname, len, "FILE:%s", + authctxt->krb5_ticket_file); ++#endif + + #ifdef USE_PAM + if (options.use_pam) +@@ -219,15 +224,22 @@ + #ifndef HEIMDAL + krb5_error_code + ssh_krb5_cc_gen(krb5_context ctx, krb5_ccache *ccache) { +- int tmpfd, ret; ++ int ret; + char ccname[40]; + mode_t old_umask; ++#ifdef USE_CCAPI ++ char cctemplate[] = "API:krb5cc_%d"; ++#else ++ char cctemplate[] = "FILE:/tmp/krb5cc_%d_XXXXXXXXXX"; ++ int tmpfd; ++#endif + + ret = snprintf(ccname, sizeof(ccname), +- "FILE:/tmp/krb5cc_%d_XXXXXXXXXX", geteuid()); ++ cctemplate, geteuid()); + if (ret < 0 || (size_t)ret >= sizeof(ccname)) + return ENOMEM; + ++#ifndef USE_CCAPI + old_umask = umask(0177); + tmpfd = mkstemp(ccname + strlen("FILE:")); + umask(old_umask); +@@ -242,6 +254,7 @@ + return errno; + } + close(tmpfd); ++#endif + + return (krb5_cc_resolve(ctx, ccname, ccache)); + } +Index: b/auth.h +=================================================================== +--- a/auth.h ++++ b/auth.h +@@ -53,6 +53,7 @@ + int valid; /* user exists and is allowed to login */ + int attempt; + int failures; ++ int server_caused_failure; + int force_pwchange; + char *user; /* username sent by the client */ + char *service; +Index: b/auth2-gss.c +=================================================================== +--- a/auth2-gss.c ++++ b/auth2-gss.c +@@ -1,7 +1,7 @@ + /* $OpenBSD: auth2-gss.c,v 1.16 2007/10/29 00:52:45 dtucker Exp $ */ + + /* +- * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved. ++ * Copyright (c) 2001-2007 Simon Wilkinson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions +@@ -52,6 +52,40 @@ + static void input_gssapi_exchange_complete(int type, u_int32_t plen, void *ctxt); + static void input_gssapi_errtok(int, u_int32_t, void *); + ++/* ++ * The 'gssapi_keyex' userauth mechanism. ++ */ ++static int ++userauth_gsskeyex(Authctxt *authctxt) ++{ ++ int authenticated = 0; ++ Buffer b; ++ gss_buffer_desc mic, gssbuf; ++ u_int len; ++ ++ mic.value = packet_get_string(&len); ++ mic.length = len; ++ ++ packet_check_eom(); ++ ++ ssh_gssapi_buildmic(&b, authctxt->user, authctxt->service, ++ "gssapi-keyex"); ++ ++ gssbuf.value = buffer_ptr(&b); ++ gssbuf.length = buffer_len(&b); ++ ++ /* gss_kex_context is NULL with privsep, so we can't check it here */ ++ if (!GSS_ERROR(PRIVSEP(ssh_gssapi_checkmic(gss_kex_context, ++ &gssbuf, &mic)))) ++ authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user, ++ authctxt->pw)); ++ ++ buffer_free(&b); ++ xfree(mic.value); ++ ++ return (authenticated); ++} ++ + /* + * We only support those mechanisms that we know about (ie ones that we know + * how to check local user kuserok and the like) +@@ -102,6 +136,7 @@ + + if (!present) { + xfree(doid); ++ authctxt->server_caused_failure = 1; + return (0); + } + +@@ -109,6 +144,7 @@ + if (ctxt != NULL) + ssh_gssapi_delete_ctx(&ctxt); + xfree(doid); ++ authctxt->server_caused_failure = 1; + return (0); + } + +@@ -242,7 +278,8 @@ + + packet_check_eom(); + +- authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user)); ++ authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user, ++ authctxt->pw)); + + authctxt->postponed = 0; + dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL); +@@ -277,7 +314,8 @@ + gssbuf.length = buffer_len(&b); + + if (!GSS_ERROR(PRIVSEP(ssh_gssapi_checkmic(gssctxt, &gssbuf, &mic)))) +- authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user)); ++ authenticated = ++ PRIVSEP(ssh_gssapi_userok(authctxt->user, authctxt->pw)); + else + logit("GSSAPI MIC check failed"); + +@@ -292,6 +330,12 @@ + userauth_finish(authctxt, authenticated, "gssapi-with-mic"); + } + ++Authmethod method_gsskeyex = { ++ "gssapi-keyex", ++ userauth_gsskeyex, ++ &options.gss_authentication ++}; ++ + Authmethod method_gssapi = { + "gssapi-with-mic", + userauth_gssapi, +Index: b/auth2.c +=================================================================== +--- a/auth2.c ++++ b/auth2.c +@@ -69,6 +69,7 @@ + extern Authmethod method_kbdint; + extern Authmethod method_hostbased; + #ifdef GSSAPI ++extern Authmethod method_gsskeyex; + extern Authmethod method_gssapi; + #endif + #ifdef JPAKE +@@ -79,6 +80,7 @@ + &method_none, + &method_pubkey, + #ifdef GSSAPI ++ &method_gsskeyex, + &method_gssapi, + #endif + #ifdef JPAKE +@@ -274,6 +276,7 @@ + #endif + + authctxt->postponed = 0; ++ authctxt->server_caused_failure = 0; + + /* try to authenticate user */ + m = authmethod_lookup(method); +@@ -346,7 +349,8 @@ + } else { + + /* Allow initial try of "none" auth without failure penalty */ +- if (authctxt->attempt > 1 || strcmp(method, "none") != 0) ++ if (!authctxt->server_caused_failure && ++ (authctxt->attempt > 1 || strcmp(method, "none") != 0)) + authctxt->failures++; + if (authctxt->failures >= options.max_authtries) { + #ifdef SSH_AUDIT_EVENTS +Index: b/clientloop.c +=================================================================== +--- a/clientloop.c ++++ b/clientloop.c +@@ -111,6 +111,10 @@ + #include "msg.h" + #include "roaming.h" + ++#ifdef GSSAPI ++#include "ssh-gss.h" ++#endif ++ + /* import options */ + extern Options options; + +@@ -1430,6 +1434,15 @@ + /* Do channel operations unless rekeying in progress. */ + if (!rekeying) { + channel_after_select(readset, writeset); ++ ++#ifdef GSSAPI ++ if (options.gss_renewal_rekey && ++ ssh_gssapi_credentials_updated(GSS_C_NO_CONTEXT)) { ++ debug("credentials updated - forcing rekey"); ++ need_rekeying = 1; ++ } ++#endif ++ + if (need_rekeying || packet_need_rekeying()) { + debug("need rekeying"); + xxx_kex->done = 0; +Index: b/configure.ac +=================================================================== +--- a/configure.ac ++++ b/configure.ac +@@ -477,6 +477,30 @@ + [Use tunnel device compatibility to OpenBSD]) + AC_DEFINE(SSH_TUN_PREPEND_AF, 1, + [Prepend the address family to IP tunnel traffic]) ++ AC_MSG_CHECKING(if we have the Security Authorization Session API) ++ AC_TRY_COMPILE([#include ], ++ [SessionCreate(0, 0);], ++ [ac_cv_use_security_session_api="yes" ++ AC_DEFINE(USE_SECURITY_SESSION_API, 1, ++ [platform has the Security Authorization Session API]) ++ LIBS="$LIBS -framework Security" ++ AC_MSG_RESULT(yes)], ++ [ac_cv_use_security_session_api="no" ++ AC_MSG_RESULT(no)]) ++ AC_MSG_CHECKING(if we have an in-memory credentials cache) ++ AC_TRY_COMPILE( ++ [#include ], ++ [cc_context_t c; ++ (void) cc_initialize (&c, 0, NULL, NULL);], ++ [AC_DEFINE(USE_CCAPI, 1, ++ [platform uses an in-memory credentials cache]) ++ LIBS="$LIBS -framework Security" ++ AC_MSG_RESULT(yes) ++ if test "x$ac_cv_use_security_session_api" = "xno"; then ++ AC_MSG_ERROR(*** Need a security framework to use the credentials cache API ***) ++ fi], ++ [AC_MSG_RESULT(no)] ++ ) + m4_pattern_allow(AU_IPv) + AC_CHECK_DECL(AU_IPv4, [], + AC_DEFINE(AU_IPv4, 0, [System only supports IPv4 audit records]) +Index: b/gss-genr.c +=================================================================== +--- a/gss-genr.c ++++ b/gss-genr.c +@@ -1,7 +1,7 @@ + /* $OpenBSD: gss-genr.c,v 1.20 2009/06/22 05:39:28 dtucker Exp $ */ + + /* +- * Copyright (c) 2001-2007 Simon Wilkinson. All rights reserved. ++ * Copyright (c) 2001-2009 Simon Wilkinson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions +@@ -39,12 +39,167 @@ + #include "buffer.h" + #include "log.h" + #include "ssh2.h" ++#include "cipher.h" ++#include "key.h" ++#include "kex.h" ++#include + + #include "ssh-gss.h" + + extern u_char *session_id2; + extern u_int session_id2_len; + ++typedef struct { ++ char *encoded; ++ gss_OID oid; ++} ssh_gss_kex_mapping; ++ ++/* ++ * XXX - It would be nice to find a more elegant way of handling the ++ * XXX passing of the key exchange context to the userauth routines ++ */ ++ ++Gssctxt *gss_kex_context = NULL; ++ ++static ssh_gss_kex_mapping *gss_enc2oid = NULL; ++ ++int ++ssh_gssapi_oid_table_ok() { ++ return (gss_enc2oid != NULL); ++} ++ ++/* ++ * Return a list of the gss-group1-sha1 mechanisms supported by this program ++ * ++ * We test mechanisms to ensure that we can use them, to avoid starting ++ * a key exchange with a bad mechanism ++ */ ++ ++char * ++ssh_gssapi_client_mechanisms(const char *host, const char *client) { ++ gss_OID_set gss_supported; ++ OM_uint32 min_status; ++ ++ if (GSS_ERROR(gss_indicate_mechs(&min_status, &gss_supported))) ++ return NULL; ++ ++ return(ssh_gssapi_kex_mechs(gss_supported, ssh_gssapi_check_mechanism, ++ host, client)); ++} ++ ++char * ++ssh_gssapi_kex_mechs(gss_OID_set gss_supported, ssh_gssapi_check_fn *check, ++ const char *host, const char *client) { ++ Buffer buf; ++ size_t i; ++ int oidpos, enclen; ++ char *mechs, *encoded; ++ u_char digest[EVP_MAX_MD_SIZE]; ++ char deroid[2]; ++ const EVP_MD *evp_md = EVP_md5(); ++ EVP_MD_CTX md; ++ ++ if (gss_enc2oid != NULL) { ++ for (i = 0; gss_enc2oid[i].encoded != NULL; i++) ++ xfree(gss_enc2oid[i].encoded); ++ xfree(gss_enc2oid); ++ } ++ ++ gss_enc2oid = xmalloc(sizeof(ssh_gss_kex_mapping) * ++ (gss_supported->count + 1)); ++ ++ buffer_init(&buf); ++ ++ oidpos = 0; ++ for (i = 0; i < gss_supported->count; i++) { ++ if (gss_supported->elements[i].length < 128 && ++ (*check)(NULL, &(gss_supported->elements[i]), host, client)) { ++ ++ deroid[0] = SSH_GSS_OIDTYPE; ++ deroid[1] = gss_supported->elements[i].length; ++ ++ EVP_DigestInit(&md, evp_md); ++ EVP_DigestUpdate(&md, deroid, 2); ++ EVP_DigestUpdate(&md, ++ gss_supported->elements[i].elements, ++ gss_supported->elements[i].length); ++ EVP_DigestFinal(&md, digest, NULL); ++ ++ encoded = xmalloc(EVP_MD_size(evp_md) * 2); ++ enclen = __b64_ntop(digest, EVP_MD_size(evp_md), ++ encoded, EVP_MD_size(evp_md) * 2); ++ ++ if (oidpos != 0) ++ buffer_put_char(&buf, ','); ++ ++ buffer_append(&buf, KEX_GSS_GEX_SHA1_ID, ++ sizeof(KEX_GSS_GEX_SHA1_ID) - 1); ++ buffer_append(&buf, encoded, enclen); ++ buffer_put_char(&buf, ','); ++ buffer_append(&buf, KEX_GSS_GRP1_SHA1_ID, ++ sizeof(KEX_GSS_GRP1_SHA1_ID) - 1); ++ buffer_append(&buf, encoded, enclen); ++ buffer_put_char(&buf, ','); ++ buffer_append(&buf, KEX_GSS_GRP14_SHA1_ID, ++ sizeof(KEX_GSS_GRP14_SHA1_ID) - 1); ++ buffer_append(&buf, encoded, enclen); ++ ++ gss_enc2oid[oidpos].oid = &(gss_supported->elements[i]); ++ gss_enc2oid[oidpos].encoded = encoded; ++ oidpos++; ++ } ++ } ++ gss_enc2oid[oidpos].oid = NULL; ++ gss_enc2oid[oidpos].encoded = NULL; ++ ++ buffer_put_char(&buf, '\0'); ++ ++ mechs = xmalloc(buffer_len(&buf)); ++ buffer_get(&buf, mechs, buffer_len(&buf)); ++ buffer_free(&buf); ++ ++ if (strlen(mechs) == 0) { ++ xfree(mechs); ++ mechs = NULL; ++ } ++ ++ return (mechs); ++} ++ ++gss_OID ++ssh_gssapi_id_kex(Gssctxt *ctx, char *name, int kex_type) { ++ int i = 0; ++ ++ switch (kex_type) { ++ case KEX_GSS_GRP1_SHA1: ++ if (strlen(name) < sizeof(KEX_GSS_GRP1_SHA1_ID)) ++ return GSS_C_NO_OID; ++ name += sizeof(KEX_GSS_GRP1_SHA1_ID) - 1; ++ break; ++ case KEX_GSS_GRP14_SHA1: ++ if (strlen(name) < sizeof(KEX_GSS_GRP14_SHA1_ID)) ++ return GSS_C_NO_OID; ++ name += sizeof(KEX_GSS_GRP14_SHA1_ID) - 1; ++ break; ++ case KEX_GSS_GEX_SHA1: ++ if (strlen(name) < sizeof(KEX_GSS_GEX_SHA1_ID)) ++ return GSS_C_NO_OID; ++ name += sizeof(KEX_GSS_GEX_SHA1_ID) - 1; ++ break; ++ default: ++ return GSS_C_NO_OID; ++ } ++ ++ while (gss_enc2oid[i].encoded != NULL && ++ strcmp(name, gss_enc2oid[i].encoded) != 0) ++ i++; ++ ++ if (gss_enc2oid[i].oid != NULL && ctx != NULL) ++ ssh_gssapi_set_oid(ctx, gss_enc2oid[i].oid); ++ ++ return gss_enc2oid[i].oid; ++} ++ + /* Check that the OID in a data stream matches that in the context */ + int + ssh_gssapi_check_oid(Gssctxt *ctx, void *data, size_t len) +@@ -197,7 +352,7 @@ + } + + ctx->major = gss_init_sec_context(&ctx->minor, +- GSS_C_NO_CREDENTIAL, &ctx->context, ctx->name, ctx->oid, ++ ctx->client_creds, &ctx->context, ctx->name, ctx->oid, + GSS_C_MUTUAL_FLAG | GSS_C_INTEG_FLAG | deleg_flag, + 0, NULL, recv_tok, NULL, send_tok, flags, NULL); + +@@ -227,8 +382,42 @@ + } + + OM_uint32 ++ssh_gssapi_client_identity(Gssctxt *ctx, const char *name) ++{ ++ gss_buffer_desc gssbuf; ++ gss_name_t gssname; ++ OM_uint32 status; ++ gss_OID_set oidset; ++ ++ gssbuf.value = (void *) name; ++ gssbuf.length = strlen(gssbuf.value); ++ ++ gss_create_empty_oid_set(&status, &oidset); ++ gss_add_oid_set_member(&status, ctx->oid, &oidset); ++ ++ ctx->major = gss_import_name(&ctx->minor, &gssbuf, ++ GSS_C_NT_USER_NAME, &gssname); ++ ++ if (!ctx->major) ++ ctx->major = gss_acquire_cred(&ctx->minor, ++ gssname, 0, oidset, GSS_C_INITIATE, ++ &ctx->client_creds, NULL, NULL); ++ ++ gss_release_name(&status, &gssname); ++ gss_release_oid_set(&status, &oidset); ++ ++ if (ctx->major) ++ ssh_gssapi_error(ctx); ++ ++ return(ctx->major); ++} ++ ++OM_uint32 + ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_t buffer, gss_buffer_t hash) + { ++ if (ctx == NULL) ++ return -1; ++ + if ((ctx->major = gss_get_mic(&ctx->minor, ctx->context, + GSS_C_QOP_DEFAULT, buffer, hash))) + ssh_gssapi_error(ctx); +@@ -236,6 +425,19 @@ + return (ctx->major); + } + ++/* Priviledged when used by server */ ++OM_uint32 ++ssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic) ++{ ++ if (ctx == NULL) ++ return -1; ++ ++ ctx->major = gss_verify_mic(&ctx->minor, ctx->context, ++ gssbuf, gssmic, NULL); ++ ++ return (ctx->major); ++} ++ + void + ssh_gssapi_buildmic(Buffer *b, const char *user, const char *service, + const char *context) +@@ -249,11 +451,16 @@ + } + + int +-ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host) ++ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host, ++ const char *client) + { + gss_buffer_desc token = GSS_C_EMPTY_BUFFER; + OM_uint32 major, minor; + gss_OID_desc spnego_oid = {6, (void *)"\x2B\x06\x01\x05\x05\x02"}; ++ Gssctxt *intctx = NULL; ++ ++ if (ctx == NULL) ++ ctx = &intctx; + + /* RFC 4462 says we MUST NOT do SPNEGO */ + if (oid->length == spnego_oid.length && +@@ -263,6 +470,10 @@ + ssh_gssapi_build_ctx(ctx); + ssh_gssapi_set_oid(*ctx, oid); + major = ssh_gssapi_import_name(*ctx, host); ++ ++ if (!GSS_ERROR(major) && client) ++ major = ssh_gssapi_client_identity(*ctx, client); ++ + if (!GSS_ERROR(major)) { + major = ssh_gssapi_init_ctx(*ctx, 0, GSS_C_NO_BUFFER, &token, + NULL); +@@ -272,10 +483,67 @@ + GSS_C_NO_BUFFER); + } + +- if (GSS_ERROR(major)) ++ if (GSS_ERROR(major) || intctx != NULL) + ssh_gssapi_delete_ctx(ctx); + + return (!GSS_ERROR(major)); + } + ++int ++ssh_gssapi_credentials_updated(Gssctxt *ctxt) { ++ static gss_name_t saved_name = GSS_C_NO_NAME; ++ static OM_uint32 saved_lifetime = 0; ++ static gss_OID saved_mech = GSS_C_NO_OID; ++ static gss_name_t name; ++ static OM_uint32 last_call = 0; ++ OM_uint32 lifetime, now, major, minor; ++ int equal; ++ gss_cred_usage_t usage = GSS_C_INITIATE; ++ ++ now = time(NULL); ++ ++ if (ctxt) { ++ debug("Rekey has happened - updating saved versions"); ++ ++ if (saved_name != GSS_C_NO_NAME) ++ gss_release_name(&minor, &saved_name); ++ ++ major = gss_inquire_cred(&minor, GSS_C_NO_CREDENTIAL, ++ &saved_name, &saved_lifetime, NULL, NULL); ++ ++ if (!GSS_ERROR(major)) { ++ saved_mech = ctxt->oid; ++ saved_lifetime+= now; ++ } else { ++ /* Handle the error */ ++ } ++ return 0; ++ } ++ ++ if (now - last_call < 10) ++ return 0; ++ ++ last_call = now; ++ ++ if (saved_mech == GSS_C_NO_OID) ++ return 0; ++ ++ major = gss_inquire_cred(&minor, GSS_C_NO_CREDENTIAL, ++ &name, &lifetime, NULL, NULL); ++ if (major == GSS_S_CREDENTIALS_EXPIRED) ++ return 0; ++ else if (GSS_ERROR(major)) ++ return 0; ++ ++ major = gss_compare_name(&minor, saved_name, name, &equal); ++ gss_release_name(&minor, &name); ++ if (GSS_ERROR(major)) ++ return 0; ++ ++ if (equal && (saved_lifetime < lifetime + now - 10)) ++ return 1; ++ ++ return 0; ++} ++ + #endif /* GSSAPI */ +Index: b/gss-serv-krb5.c +=================================================================== +--- a/gss-serv-krb5.c ++++ b/gss-serv-krb5.c +@@ -1,7 +1,7 @@ + /* $OpenBSD: gss-serv-krb5.c,v 1.7 2006/08/03 03:34:42 deraadt Exp $ */ + + /* +- * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved. ++ * Copyright (c) 2001-2007 Simon Wilkinson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions +@@ -120,6 +120,7 @@ + krb5_principal princ; + OM_uint32 maj_status, min_status; + int len; ++ const char *new_ccname; + + if (client->creds == NULL) { + debug("No credentials stored"); +@@ -168,11 +169,16 @@ + return; + } + +- client->store.filename = xstrdup(krb5_cc_get_name(krb_context, ccache)); ++ new_ccname = krb5_cc_get_name(krb_context, ccache); ++ + client->store.envvar = "KRB5CCNAME"; +- len = strlen(client->store.filename) + 6; +- client->store.envval = xmalloc(len); +- snprintf(client->store.envval, len, "FILE:%s", client->store.filename); ++#ifdef USE_CCAPI ++ xasprintf(&client->store.envval, "API:%s", new_ccname); ++ client->store.filename = NULL; ++#else ++ xasprintf(&client->store.envval, "FILE:%s", new_ccname); ++ client->store.filename = xstrdup(new_ccname); ++#endif + + #ifdef USE_PAM + if (options.use_pam) +@@ -184,6 +190,71 @@ + return; + } + ++int ++ssh_gssapi_krb5_updatecreds(ssh_gssapi_ccache *store, ++ ssh_gssapi_client *client) ++{ ++ krb5_ccache ccache = NULL; ++ krb5_principal principal = NULL; ++ char *name = NULL; ++ krb5_error_code problem; ++ OM_uint32 maj_status, min_status; ++ ++ if ((problem = krb5_cc_resolve(krb_context, store->envval, &ccache))) { ++ logit("krb5_cc_resolve(): %.100s", ++ krb5_get_err_text(krb_context, problem)); ++ return 0; ++ } ++ ++ /* Find out who the principal in this cache is */ ++ if ((problem = krb5_cc_get_principal(krb_context, ccache, ++ &principal))) { ++ logit("krb5_cc_get_principal(): %.100s", ++ krb5_get_err_text(krb_context, problem)); ++ krb5_cc_close(krb_context, ccache); ++ return 0; ++ } ++ ++ if ((problem = krb5_unparse_name(krb_context, principal, &name))) { ++ logit("krb5_unparse_name(): %.100s", ++ krb5_get_err_text(krb_context, problem)); ++ krb5_free_principal(krb_context, principal); ++ krb5_cc_close(krb_context, ccache); ++ return 0; ++ } ++ ++ ++ if (strcmp(name,client->exportedname.value)!=0) { ++ debug("Name in local credentials cache differs. Not storing"); ++ krb5_free_principal(krb_context, principal); ++ krb5_cc_close(krb_context, ccache); ++ krb5_free_unparsed_name(krb_context, name); ++ return 0; ++ } ++ krb5_free_unparsed_name(krb_context, name); ++ ++ /* Name matches, so lets get on with it! */ ++ ++ if ((problem = krb5_cc_initialize(krb_context, ccache, principal))) { ++ logit("krb5_cc_initialize(): %.100s", ++ krb5_get_err_text(krb_context, problem)); ++ krb5_free_principal(krb_context, principal); ++ krb5_cc_close(krb_context, ccache); ++ return 0; ++ } ++ ++ krb5_free_principal(krb_context, principal); ++ ++ if ((maj_status = gss_krb5_copy_ccache(&min_status, client->creds, ++ ccache))) { ++ logit("gss_krb5_copy_ccache() failed. Sorry!"); ++ krb5_cc_close(krb_context, ccache); ++ return 0; ++ } ++ ++ return 1; ++} ++ + ssh_gssapi_mech gssapi_kerberos_mech = { + "toWM5Slw5Ew8Mqkay+al2g==", + "Kerberos", +@@ -191,7 +262,8 @@ + NULL, + &ssh_gssapi_krb5_userok, + NULL, +- &ssh_gssapi_krb5_storecreds ++ &ssh_gssapi_krb5_storecreds, ++ &ssh_gssapi_krb5_updatecreds + }; + + #endif /* KRB5 */ +Index: b/gss-serv.c +=================================================================== +--- a/gss-serv.c ++++ b/gss-serv.c +@@ -1,7 +1,7 @@ + /* $OpenBSD: gss-serv.c,v 1.22 2008/05/08 12:02:23 djm Exp $ */ + + /* +- * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved. ++ * Copyright (c) 2001-2009 Simon Wilkinson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions +@@ -45,15 +45,20 @@ + #include "channels.h" + #include "session.h" + #include "misc.h" ++#include "servconf.h" ++#include "uidswap.h" + + #include "ssh-gss.h" ++#include "monitor_wrap.h" ++ ++extern ServerOptions options; + + static ssh_gssapi_client gssapi_client = + { GSS_C_EMPTY_BUFFER, GSS_C_EMPTY_BUFFER, +- GSS_C_NO_CREDENTIAL, NULL, {NULL, NULL, NULL}}; ++ GSS_C_NO_CREDENTIAL, GSS_C_NO_NAME, NULL, {NULL, NULL, NULL}, 0, 0}; + + ssh_gssapi_mech gssapi_null_mech = +- { NULL, NULL, {0, NULL}, NULL, NULL, NULL, NULL}; ++ { NULL, NULL, {0, NULL}, NULL, NULL, NULL, NULL, NULL}; + + #ifdef KRB5 + extern ssh_gssapi_mech gssapi_kerberos_mech; +@@ -81,25 +86,32 @@ + char lname[MAXHOSTNAMELEN]; + gss_OID_set oidset; + +- gss_create_empty_oid_set(&status, &oidset); +- gss_add_oid_set_member(&status, ctx->oid, &oidset); ++ if (options.gss_strict_acceptor) { ++ gss_create_empty_oid_set(&status, &oidset); ++ gss_add_oid_set_member(&status, ctx->oid, &oidset); ++ ++ if (gethostname(lname, MAXHOSTNAMELEN)) { ++ gss_release_oid_set(&status, &oidset); ++ return (-1); ++ } + +- if (gethostname(lname, MAXHOSTNAMELEN)) { +- gss_release_oid_set(&status, &oidset); +- return (-1); +- } ++ if (GSS_ERROR(ssh_gssapi_import_name(ctx, lname))) { ++ gss_release_oid_set(&status, &oidset); ++ return (ctx->major); ++ } ++ ++ if ((ctx->major = gss_acquire_cred(&ctx->minor, ++ ctx->name, 0, oidset, GSS_C_ACCEPT, &ctx->creds, ++ NULL, NULL))) ++ ssh_gssapi_error(ctx); + +- if (GSS_ERROR(ssh_gssapi_import_name(ctx, lname))) { + gss_release_oid_set(&status, &oidset); + return (ctx->major); ++ } else { ++ ctx->name = GSS_C_NO_NAME; ++ ctx->creds = GSS_C_NO_CREDENTIAL; + } +- +- if ((ctx->major = gss_acquire_cred(&ctx->minor, +- ctx->name, 0, oidset, GSS_C_ACCEPT, &ctx->creds, NULL, NULL))) +- ssh_gssapi_error(ctx); +- +- gss_release_oid_set(&status, &oidset); +- return (ctx->major); ++ return GSS_S_COMPLETE; + } + + /* Privileged */ +@@ -114,6 +126,29 @@ + } + + /* Unprivileged */ ++char * ++ssh_gssapi_server_mechanisms() { ++ gss_OID_set supported; ++ ++ ssh_gssapi_supported_oids(&supported); ++ return (ssh_gssapi_kex_mechs(supported, &ssh_gssapi_server_check_mech, ++ NULL, NULL)); ++} ++ ++/* Unprivileged */ ++int ++ssh_gssapi_server_check_mech(Gssctxt **dum, gss_OID oid, const char *data, ++ const char *dummy) { ++ Gssctxt *ctx = NULL; ++ int res; ++ ++ res = !GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctx, oid))); ++ ssh_gssapi_delete_ctx(&ctx); ++ ++ return (res); ++} ++ ++/* Unprivileged */ + void + ssh_gssapi_supported_oids(gss_OID_set *oidset) + { +@@ -123,7 +158,9 @@ + gss_OID_set supported; + + gss_create_empty_oid_set(&min_status, oidset); +- gss_indicate_mechs(&min_status, &supported); ++ ++ if (GSS_ERROR(gss_indicate_mechs(&min_status, &supported))) ++ return; + + while (supported_mechs[i]->name != NULL) { + if (GSS_ERROR(gss_test_oid_set_member(&min_status, +@@ -247,8 +284,48 @@ + ssh_gssapi_getclient(Gssctxt *ctx, ssh_gssapi_client *client) + { + int i = 0; ++ int equal = 0; ++ gss_name_t new_name = GSS_C_NO_NAME; ++ gss_buffer_desc ename = GSS_C_EMPTY_BUFFER; ++ ++ if (options.gss_store_rekey && client->used && ctx->client_creds) { ++ if (client->mech->oid.length != ctx->oid->length || ++ (memcmp(client->mech->oid.elements, ++ ctx->oid->elements, ctx->oid->length) !=0)) { ++ debug("Rekeyed credentials have different mechanism"); ++ return GSS_S_COMPLETE; ++ } ++ ++ if ((ctx->major = gss_inquire_cred_by_mech(&ctx->minor, ++ ctx->client_creds, ctx->oid, &new_name, ++ NULL, NULL, NULL))) { ++ ssh_gssapi_error(ctx); ++ return (ctx->major); ++ } ++ ++ ctx->major = gss_compare_name(&ctx->minor, client->name, ++ new_name, &equal); + +- gss_buffer_desc ename; ++ if (GSS_ERROR(ctx->major)) { ++ ssh_gssapi_error(ctx); ++ return (ctx->major); ++ } ++ ++ if (!equal) { ++ debug("Rekeyed credentials have different name"); ++ return GSS_S_COMPLETE; ++ } ++ ++ debug("Marking rekeyed credentials for export"); ++ ++ gss_release_name(&ctx->minor, &client->name); ++ gss_release_cred(&ctx->minor, &client->creds); ++ client->name = new_name; ++ client->creds = ctx->client_creds; ++ ctx->client_creds = GSS_C_NO_CREDENTIAL; ++ client->updated = 1; ++ return GSS_S_COMPLETE; ++ } + + client->mech = NULL; + +@@ -263,6 +340,13 @@ + if (client->mech == NULL) + return GSS_S_FAILURE; + ++ if (ctx->client_creds && ++ (ctx->major = gss_inquire_cred_by_mech(&ctx->minor, ++ ctx->client_creds, ctx->oid, &client->name, NULL, NULL, NULL))) { ++ ssh_gssapi_error(ctx); ++ return (ctx->major); ++ } ++ + if ((ctx->major = gss_display_name(&ctx->minor, ctx->client, + &client->displayname, NULL))) { + ssh_gssapi_error(ctx); +@@ -280,6 +364,8 @@ + return (ctx->major); + } + ++ gss_release_buffer(&ctx->minor, &ename); ++ + /* We can't copy this structure, so we just move the pointer to it */ + client->creds = ctx->client_creds; + ctx->client_creds = GSS_C_NO_CREDENTIAL; +@@ -327,7 +413,7 @@ + + /* Privileged */ + int +-ssh_gssapi_userok(char *user) ++ssh_gssapi_userok(char *user, struct passwd *pw) + { + OM_uint32 lmin; + +@@ -337,9 +423,11 @@ + return 0; + } + if (gssapi_client.mech && gssapi_client.mech->userok) +- if ((*gssapi_client.mech->userok)(&gssapi_client, user)) ++ if ((*gssapi_client.mech->userok)(&gssapi_client, user)) { ++ gssapi_client.used = 1; ++ gssapi_client.store.owner = pw; + return 1; +- else { ++ } else { + /* Destroy delegated credentials if userok fails */ + gss_release_buffer(&lmin, &gssapi_client.displayname); + gss_release_buffer(&lmin, &gssapi_client.exportedname); +@@ -352,14 +440,90 @@ + return (0); + } + +-/* Privileged */ +-OM_uint32 +-ssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic) ++/* These bits are only used for rekeying. The unpriviledged child is running ++ * as the user, the monitor is root. ++ * ++ * In the child, we want to : ++ * *) Ask the monitor to store our credentials into the store we specify ++ * *) If it succeeds, maybe do a PAM update ++ */ ++ ++/* Stuff for PAM */ ++ ++#ifdef USE_PAM ++static int ssh_gssapi_simple_conv(int n, const struct pam_message **msg, ++ struct pam_response **resp, void *data) + { +- ctx->major = gss_verify_mic(&ctx->minor, ctx->context, +- gssbuf, gssmic, NULL); ++ return (PAM_CONV_ERR); ++} ++#endif + +- return (ctx->major); ++void ++ssh_gssapi_rekey_creds() { ++ int ok; ++ int ret; ++#ifdef USE_PAM ++ pam_handle_t *pamh = NULL; ++ struct pam_conv pamconv = {ssh_gssapi_simple_conv, NULL}; ++ char *envstr; ++#endif ++ ++ if (gssapi_client.store.filename == NULL && ++ gssapi_client.store.envval == NULL && ++ gssapi_client.store.envvar == NULL) ++ return; ++ ++ ok = PRIVSEP(ssh_gssapi_update_creds(&gssapi_client.store)); ++ ++ if (!ok) ++ return; ++ ++ debug("Rekeyed credentials stored successfully"); ++ ++ /* Actually managing to play with the ssh pam stack from here will ++ * be next to impossible. In any case, we may want different options ++ * for rekeying. So, use our own :) ++ */ ++#ifdef USE_PAM ++ if (!use_privsep) { ++ debug("Not even going to try and do PAM with privsep disabled"); ++ return; ++ } ++ ++ ret = pam_start("sshd-rekey", gssapi_client.store.owner->pw_name, ++ &pamconv, &pamh); ++ if (ret) ++ return; ++ ++ xasprintf(&envstr, "%s=%s", gssapi_client.store.envvar, ++ gssapi_client.store.envval); ++ ++ ret = pam_putenv(pamh, envstr); ++ if (!ret) ++ pam_setcred(pamh, PAM_REINITIALIZE_CRED); ++ pam_end(pamh, PAM_SUCCESS); ++#endif ++} ++ ++int ++ssh_gssapi_update_creds(ssh_gssapi_ccache *store) { ++ int ok = 0; ++ ++ /* Check we've got credentials to store */ ++ if (!gssapi_client.updated) ++ return 0; ++ ++ gssapi_client.updated = 0; ++ ++ temporarily_use_uid(gssapi_client.store.owner); ++ if (gssapi_client.mech && gssapi_client.mech->updatecreds) ++ ok = (*gssapi_client.mech->updatecreds)(store, &gssapi_client); ++ else ++ debug("No update function for this mechanism"); ++ ++ restore_uid(); ++ ++ return ok; + } + + #endif +Index: b/kex.c +=================================================================== +--- a/kex.c ++++ b/kex.c +@@ -49,6 +49,10 @@ + #include "dispatch.h" + #include "monitor.h" + ++#ifdef GSSAPI ++#include "ssh-gss.h" ++#endif ++ + #if OPENSSL_VERSION_NUMBER >= 0x00907000L + # if defined(HAVE_EVP_SHA256) + # define evp_ssh_sha256 EVP_sha256 +@@ -325,6 +329,20 @@ + k->kex_type = KEX_DH_GEX_SHA256; + k->evp_md = evp_ssh_sha256(); + #endif ++#ifdef GSSAPI ++ } else if (strncmp(k->name, KEX_GSS_GEX_SHA1_ID, ++ sizeof(KEX_GSS_GEX_SHA1_ID) - 1) == 0) { ++ k->kex_type = KEX_GSS_GEX_SHA1; ++ k->evp_md = EVP_sha1(); ++ } else if (strncmp(k->name, KEX_GSS_GRP1_SHA1_ID, ++ sizeof(KEX_GSS_GRP1_SHA1_ID) - 1) == 0) { ++ k->kex_type = KEX_GSS_GRP1_SHA1; ++ k->evp_md = EVP_sha1(); ++ } else if (strncmp(k->name, KEX_GSS_GRP14_SHA1_ID, ++ sizeof(KEX_GSS_GRP14_SHA1_ID) - 1) == 0) { ++ k->kex_type = KEX_GSS_GRP14_SHA1; ++ k->evp_md = EVP_sha1(); ++#endif + } else + fatal("bad kex alg %s", k->name); + } +Index: b/kex.h +=================================================================== +--- a/kex.h ++++ b/kex.h +@@ -66,6 +66,9 @@ + KEX_DH_GRP14_SHA1, + KEX_DH_GEX_SHA1, + KEX_DH_GEX_SHA256, ++ KEX_GSS_GRP1_SHA1, ++ KEX_GSS_GRP14_SHA1, ++ KEX_GSS_GEX_SHA1, + KEX_MAX + }; + +@@ -121,6 +124,12 @@ + sig_atomic_t done; + int flags; + const EVP_MD *evp_md; ++#ifdef GSSAPI ++ int gss_deleg_creds; ++ int gss_trust_dns; ++ char *gss_host; ++ char *gss_client; ++#endif + char *client_version_string; + char *server_version_string; + int (*verify_host_key)(Key *); +@@ -143,6 +152,11 @@ + void kexgex_client(Kex *); + void kexgex_server(Kex *); + ++#ifdef GSSAPI ++void kexgss_client(Kex *); ++void kexgss_server(Kex *); ++#endif ++ + void + kex_dh_hash(char *, char *, char *, int, char *, int, u_char *, int, + BIGNUM *, BIGNUM *, BIGNUM *, u_char **, u_int *); +Index: b/kexgssc.c +=================================================================== +--- /dev/null ++++ b/kexgssc.c +@@ -0,0 +1,334 @@ ++/* ++ * Copyright (c) 2001-2009 Simon Wilkinson. All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES ++ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. ++ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, ++ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT ++ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ++ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ++ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ++ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF ++ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++#include "includes.h" ++ ++#ifdef GSSAPI ++ ++#include "includes.h" ++ ++#include ++#include ++ ++#include ++ ++#include "xmalloc.h" ++#include "buffer.h" ++#include "ssh2.h" ++#include "key.h" ++#include "cipher.h" ++#include "kex.h" ++#include "log.h" ++#include "packet.h" ++#include "dh.h" ++ ++#include "ssh-gss.h" ++ ++void ++kexgss_client(Kex *kex) { ++ gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER; ++ gss_buffer_desc recv_tok, gssbuf, msg_tok, *token_ptr; ++ Gssctxt *ctxt; ++ OM_uint32 maj_status, min_status, ret_flags; ++ u_int klen, kout, slen = 0, hashlen, strlen; ++ DH *dh; ++ BIGNUM *dh_server_pub = NULL; ++ BIGNUM *shared_secret = NULL; ++ BIGNUM *p = NULL; ++ BIGNUM *g = NULL; ++ u_char *kbuf, *hash; ++ u_char *serverhostkey = NULL; ++ u_char *empty = ""; ++ char *msg; ++ char *lang; ++ int type = 0; ++ int first = 1; ++ int nbits = 0, min = DH_GRP_MIN, max = DH_GRP_MAX; ++ ++ /* Initialise our GSSAPI world */ ++ ssh_gssapi_build_ctx(&ctxt); ++ if (ssh_gssapi_id_kex(ctxt, kex->name, kex->kex_type) ++ == GSS_C_NO_OID) ++ fatal("Couldn't identify host exchange"); ++ ++ if (ssh_gssapi_import_name(ctxt, kex->gss_host)) ++ fatal("Couldn't import hostname"); ++ ++ if (kex->gss_client && ++ ssh_gssapi_client_identity(ctxt, kex->gss_client)) ++ fatal("Couldn't acquire client credentials"); ++ ++ switch (kex->kex_type) { ++ case KEX_GSS_GRP1_SHA1: ++ dh = dh_new_group1(); ++ break; ++ case KEX_GSS_GRP14_SHA1: ++ dh = dh_new_group14(); ++ break; ++ case KEX_GSS_GEX_SHA1: ++ debug("Doing group exchange\n"); ++ nbits = dh_estimate(kex->we_need * 8); ++ packet_start(SSH2_MSG_KEXGSS_GROUPREQ); ++ packet_put_int(min); ++ packet_put_int(nbits); ++ packet_put_int(max); ++ ++ packet_send(); ++ ++ packet_read_expect(SSH2_MSG_KEXGSS_GROUP); ++ ++ if ((p = BN_new()) == NULL) ++ fatal("BN_new() failed"); ++ packet_get_bignum2(p); ++ if ((g = BN_new()) == NULL) ++ fatal("BN_new() failed"); ++ packet_get_bignum2(g); ++ packet_check_eom(); ++ ++ if (BN_num_bits(p) < min || BN_num_bits(p) > max) ++ fatal("GSSGRP_GEX group out of range: %d !< %d !< %d", ++ min, BN_num_bits(p), max); ++ ++ dh = dh_new_group(g, p); ++ break; ++ default: ++ fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type); ++ } ++ ++ /* Step 1 - e is dh->pub_key */ ++ dh_gen_key(dh, kex->we_need * 8); ++ ++ /* This is f, we initialise it now to make life easier */ ++ dh_server_pub = BN_new(); ++ if (dh_server_pub == NULL) ++ fatal("dh_server_pub == NULL"); ++ ++ token_ptr = GSS_C_NO_BUFFER; ++ ++ do { ++ debug("Calling gss_init_sec_context"); ++ ++ maj_status = ssh_gssapi_init_ctx(ctxt, ++ kex->gss_deleg_creds, token_ptr, &send_tok, ++ &ret_flags); ++ ++ if (GSS_ERROR(maj_status)) { ++ if (send_tok.length != 0) { ++ packet_start(SSH2_MSG_KEXGSS_CONTINUE); ++ packet_put_string(send_tok.value, ++ send_tok.length); ++ } ++ fatal("gss_init_context failed"); ++ } ++ ++ /* If we've got an old receive buffer get rid of it */ ++ if (token_ptr != GSS_C_NO_BUFFER) ++ xfree(recv_tok.value); ++ ++ if (maj_status == GSS_S_COMPLETE) { ++ /* If mutual state flag is not true, kex fails */ ++ if (!(ret_flags & GSS_C_MUTUAL_FLAG)) ++ fatal("Mutual authentication failed"); ++ ++ /* If integ avail flag is not true kex fails */ ++ if (!(ret_flags & GSS_C_INTEG_FLAG)) ++ fatal("Integrity check failed"); ++ } ++ ++ /* ++ * If we have data to send, then the last message that we ++ * received cannot have been a 'complete'. ++ */ ++ if (send_tok.length != 0) { ++ if (first) { ++ packet_start(SSH2_MSG_KEXGSS_INIT); ++ packet_put_string(send_tok.value, ++ send_tok.length); ++ packet_put_bignum2(dh->pub_key); ++ first = 0; ++ } else { ++ packet_start(SSH2_MSG_KEXGSS_CONTINUE); ++ packet_put_string(send_tok.value, ++ send_tok.length); ++ } ++ packet_send(); ++ gss_release_buffer(&min_status, &send_tok); ++ ++ /* If we've sent them data, they should reply */ ++ do { ++ type = packet_read(); ++ if (type == SSH2_MSG_KEXGSS_HOSTKEY) { ++ debug("Received KEXGSS_HOSTKEY"); ++ if (serverhostkey) ++ fatal("Server host key received more than once"); ++ serverhostkey = ++ packet_get_string(&slen); ++ } ++ } while (type == SSH2_MSG_KEXGSS_HOSTKEY); ++ ++ switch (type) { ++ case SSH2_MSG_KEXGSS_CONTINUE: ++ debug("Received GSSAPI_CONTINUE"); ++ if (maj_status == GSS_S_COMPLETE) ++ fatal("GSSAPI Continue received from server when complete"); ++ recv_tok.value = packet_get_string(&strlen); ++ recv_tok.length = strlen; ++ break; ++ case SSH2_MSG_KEXGSS_COMPLETE: ++ debug("Received GSSAPI_COMPLETE"); ++ packet_get_bignum2(dh_server_pub); ++ msg_tok.value = packet_get_string(&strlen); ++ msg_tok.length = strlen; ++ ++ /* Is there a token included? */ ++ if (packet_get_char()) { ++ recv_tok.value= ++ packet_get_string(&strlen); ++ recv_tok.length = strlen; ++ /* If we're already complete - protocol error */ ++ if (maj_status == GSS_S_COMPLETE) ++ packet_disconnect("Protocol error: received token when complete"); ++ } else { ++ /* No token included */ ++ if (maj_status != GSS_S_COMPLETE) ++ packet_disconnect("Protocol error: did not receive final token"); ++ } ++ break; ++ case SSH2_MSG_KEXGSS_ERROR: ++ debug("Received Error"); ++ maj_status = packet_get_int(); ++ min_status = packet_get_int(); ++ msg = packet_get_string(NULL); ++ lang = packet_get_string(NULL); ++ fatal("GSSAPI Error: \n%.400s",msg); ++ default: ++ packet_disconnect("Protocol error: didn't expect packet type %d", ++ type); ++ } ++ token_ptr = &recv_tok; ++ } else { ++ /* No data, and not complete */ ++ if (maj_status != GSS_S_COMPLETE) ++ fatal("Not complete, and no token output"); ++ } ++ } while (maj_status & GSS_S_CONTINUE_NEEDED); ++ ++ /* ++ * We _must_ have received a COMPLETE message in reply from the ++ * server, which will have set dh_server_pub and msg_tok ++ */ ++ ++ if (type != SSH2_MSG_KEXGSS_COMPLETE) ++ fatal("Didn't receive a SSH2_MSG_KEXGSS_COMPLETE when I expected it"); ++ ++ /* Check f in range [1, p-1] */ ++ if (!dh_pub_is_valid(dh, dh_server_pub)) ++ packet_disconnect("bad server public DH value"); ++ ++ /* compute K=f^x mod p */ ++ klen = DH_size(dh); ++ kbuf = xmalloc(klen); ++ kout = DH_compute_key(kbuf, dh_server_pub, dh); ++ if (kout < 0) ++ fatal("DH_compute_key: failed"); ++ ++ shared_secret = BN_new(); ++ if (shared_secret == NULL) ++ fatal("kexgss_client: BN_new failed"); ++ ++ if (BN_bin2bn(kbuf, kout, shared_secret) == NULL) ++ fatal("kexdh_client: BN_bin2bn failed"); ++ ++ memset(kbuf, 0, klen); ++ xfree(kbuf); ++ ++ switch (kex->kex_type) { ++ case KEX_GSS_GRP1_SHA1: ++ case KEX_GSS_GRP14_SHA1: ++ kex_dh_hash( kex->client_version_string, ++ kex->server_version_string, ++ buffer_ptr(&kex->my), buffer_len(&kex->my), ++ buffer_ptr(&kex->peer), buffer_len(&kex->peer), ++ (serverhostkey ? serverhostkey : empty), slen, ++ dh->pub_key, /* e */ ++ dh_server_pub, /* f */ ++ shared_secret, /* K */ ++ &hash, &hashlen ++ ); ++ break; ++ case KEX_GSS_GEX_SHA1: ++ kexgex_hash( ++ kex->evp_md, ++ kex->client_version_string, ++ kex->server_version_string, ++ buffer_ptr(&kex->my), buffer_len(&kex->my), ++ buffer_ptr(&kex->peer), buffer_len(&kex->peer), ++ (serverhostkey ? serverhostkey : empty), slen, ++ min, nbits, max, ++ dh->p, dh->g, ++ dh->pub_key, ++ dh_server_pub, ++ shared_secret, ++ &hash, &hashlen ++ ); ++ break; ++ default: ++ fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type); ++ } ++ ++ gssbuf.value = hash; ++ gssbuf.length = hashlen; ++ ++ /* Verify that the hash matches the MIC we just got. */ ++ if (GSS_ERROR(ssh_gssapi_checkmic(ctxt, &gssbuf, &msg_tok))) ++ packet_disconnect("Hash's MIC didn't verify"); ++ ++ xfree(msg_tok.value); ++ ++ DH_free(dh); ++ if (serverhostkey) ++ xfree(serverhostkey); ++ BN_clear_free(dh_server_pub); ++ ++ /* save session id */ ++ if (kex->session_id == NULL) { ++ kex->session_id_len = hashlen; ++ kex->session_id = xmalloc(kex->session_id_len); ++ memcpy(kex->session_id, hash, kex->session_id_len); ++ } ++ ++ if (kex->gss_deleg_creds) ++ ssh_gssapi_credentials_updated(ctxt); ++ ++ if (gss_kex_context == NULL) ++ gss_kex_context = ctxt; ++ else ++ ssh_gssapi_delete_ctx(&ctxt); ++ ++ kex_derive_keys(kex, hash, hashlen, shared_secret); ++ BN_clear_free(shared_secret); ++ kex_finish(kex); ++} ++ ++#endif /* GSSAPI */ +Index: b/kexgsss.c +=================================================================== +--- /dev/null ++++ b/kexgsss.c +@@ -0,0 +1,288 @@ ++/* ++ * Copyright (c) 2001-2009 Simon Wilkinson. All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES ++ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. ++ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, ++ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT ++ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ++ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ++ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ++ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF ++ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++#include "includes.h" ++ ++#ifdef GSSAPI ++ ++#include ++ ++#include ++#include ++ ++#include "xmalloc.h" ++#include "buffer.h" ++#include "ssh2.h" ++#include "key.h" ++#include "cipher.h" ++#include "kex.h" ++#include "log.h" ++#include "packet.h" ++#include "dh.h" ++#include "ssh-gss.h" ++#include "monitor_wrap.h" ++#include "servconf.h" ++ ++extern ServerOptions options; ++ ++void ++kexgss_server(Kex *kex) ++{ ++ OM_uint32 maj_status, min_status; ++ ++ /* ++ * Some GSSAPI implementations use the input value of ret_flags (an ++ * output variable) as a means of triggering mechanism specific ++ * features. Initializing it to zero avoids inadvertently ++ * activating this non-standard behaviour. ++ */ ++ ++ OM_uint32 ret_flags = 0; ++ gss_buffer_desc gssbuf, recv_tok, msg_tok; ++ gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER; ++ Gssctxt *ctxt = NULL; ++ u_int slen, klen, kout, hashlen; ++ u_char *kbuf, *hash; ++ DH *dh; ++ int min = -1, max = -1, nbits = -1; ++ BIGNUM *shared_secret = NULL; ++ BIGNUM *dh_client_pub = NULL; ++ int type = 0; ++ gss_OID oid; ++ char *mechs; ++ ++ /* Initialise GSSAPI */ ++ ++ /* If we're rekeying, privsep means that some of the private structures ++ * in the GSSAPI code are no longer available. This kludges them back ++ * into life ++ */ ++ if (!ssh_gssapi_oid_table_ok()) ++ if ((mechs = ssh_gssapi_server_mechanisms())) ++ xfree(mechs); ++ ++ debug2("%s: Identifying %s", __func__, kex->name); ++ oid = ssh_gssapi_id_kex(NULL, kex->name, kex->kex_type); ++ if (oid == GSS_C_NO_OID) ++ fatal("Unknown gssapi mechanism"); ++ ++ debug2("%s: Acquiring credentials", __func__); ++ ++ if (GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctxt, oid)))) ++ fatal("Unable to acquire credentials for the server"); ++ ++ switch (kex->kex_type) { ++ case KEX_GSS_GRP1_SHA1: ++ dh = dh_new_group1(); ++ break; ++ case KEX_GSS_GRP14_SHA1: ++ dh = dh_new_group14(); ++ break; ++ case KEX_GSS_GEX_SHA1: ++ debug("Doing group exchange"); ++ packet_read_expect(SSH2_MSG_KEXGSS_GROUPREQ); ++ min = packet_get_int(); ++ nbits = packet_get_int(); ++ max = packet_get_int(); ++ min = MAX(DH_GRP_MIN, min); ++ max = MIN(DH_GRP_MAX, max); ++ packet_check_eom(); ++ if (max < min || nbits < min || max < nbits) ++ fatal("GSS_GEX, bad parameters: %d !< %d !< %d", ++ min, nbits, max); ++ dh = PRIVSEP(choose_dh(min, nbits, max)); ++ if (dh == NULL) ++ packet_disconnect("Protocol error: no matching group found"); ++ ++ packet_start(SSH2_MSG_KEXGSS_GROUP); ++ packet_put_bignum2(dh->p); ++ packet_put_bignum2(dh->g); ++ packet_send(); ++ ++ packet_write_wait(); ++ break; ++ default: ++ fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type); ++ } ++ ++ dh_gen_key(dh, kex->we_need * 8); ++ ++ do { ++ debug("Wait SSH2_MSG_GSSAPI_INIT"); ++ type = packet_read(); ++ switch(type) { ++ case SSH2_MSG_KEXGSS_INIT: ++ if (dh_client_pub != NULL) ++ fatal("Received KEXGSS_INIT after initialising"); ++ recv_tok.value = packet_get_string(&slen); ++ recv_tok.length = slen; ++ ++ if ((dh_client_pub = BN_new()) == NULL) ++ fatal("dh_client_pub == NULL"); ++ ++ packet_get_bignum2(dh_client_pub); ++ ++ /* Send SSH_MSG_KEXGSS_HOSTKEY here, if we want */ ++ break; ++ case SSH2_MSG_KEXGSS_CONTINUE: ++ recv_tok.value = packet_get_string(&slen); ++ recv_tok.length = slen; ++ break; ++ default: ++ packet_disconnect( ++ "Protocol error: didn't expect packet type %d", ++ type); ++ } ++ ++ maj_status = PRIVSEP(ssh_gssapi_accept_ctx(ctxt, &recv_tok, ++ &send_tok, &ret_flags)); ++ ++ xfree(recv_tok.value); ++ ++ if (maj_status != GSS_S_COMPLETE && send_tok.length == 0) ++ fatal("Zero length token output when incomplete"); ++ ++ if (dh_client_pub == NULL) ++ fatal("No client public key"); ++ ++ if (maj_status & GSS_S_CONTINUE_NEEDED) { ++ debug("Sending GSSAPI_CONTINUE"); ++ packet_start(SSH2_MSG_KEXGSS_CONTINUE); ++ packet_put_string(send_tok.value, send_tok.length); ++ packet_send(); ++ gss_release_buffer(&min_status, &send_tok); ++ } ++ } while (maj_status & GSS_S_CONTINUE_NEEDED); ++ ++ if (GSS_ERROR(maj_status)) { ++ if (send_tok.length > 0) { ++ packet_start(SSH2_MSG_KEXGSS_CONTINUE); ++ packet_put_string(send_tok.value, send_tok.length); ++ packet_send(); ++ } ++ fatal("accept_ctx died"); ++ } ++ ++ if (!(ret_flags & GSS_C_MUTUAL_FLAG)) ++ fatal("Mutual Authentication flag wasn't set"); ++ ++ if (!(ret_flags & GSS_C_INTEG_FLAG)) ++ fatal("Integrity flag wasn't set"); ++ ++ if (!dh_pub_is_valid(dh, dh_client_pub)) ++ packet_disconnect("bad client public DH value"); ++ ++ klen = DH_size(dh); ++ kbuf = xmalloc(klen); ++ kout = DH_compute_key(kbuf, dh_client_pub, dh); ++ if (kout < 0) ++ fatal("DH_compute_key: failed"); ++ ++ shared_secret = BN_new(); ++ if (shared_secret == NULL) ++ fatal("kexgss_server: BN_new failed"); ++ ++ if (BN_bin2bn(kbuf, kout, shared_secret) == NULL) ++ fatal("kexgss_server: BN_bin2bn failed"); ++ ++ memset(kbuf, 0, klen); ++ xfree(kbuf); ++ ++ switch (kex->kex_type) { ++ case KEX_GSS_GRP1_SHA1: ++ case KEX_GSS_GRP14_SHA1: ++ kex_dh_hash( ++ kex->client_version_string, kex->server_version_string, ++ buffer_ptr(&kex->peer), buffer_len(&kex->peer), ++ buffer_ptr(&kex->my), buffer_len(&kex->my), ++ NULL, 0, /* Change this if we start sending host keys */ ++ dh_client_pub, dh->pub_key, shared_secret, ++ &hash, &hashlen ++ ); ++ break; ++ case KEX_GSS_GEX_SHA1: ++ kexgex_hash( ++ kex->evp_md, ++ kex->client_version_string, kex->server_version_string, ++ buffer_ptr(&kex->peer), buffer_len(&kex->peer), ++ buffer_ptr(&kex->my), buffer_len(&kex->my), ++ NULL, 0, ++ min, nbits, max, ++ dh->p, dh->g, ++ dh_client_pub, ++ dh->pub_key, ++ shared_secret, ++ &hash, &hashlen ++ ); ++ break; ++ default: ++ fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type); ++ } ++ ++ BN_clear_free(dh_client_pub); ++ ++ if (kex->session_id == NULL) { ++ kex->session_id_len = hashlen; ++ kex->session_id = xmalloc(kex->session_id_len); ++ memcpy(kex->session_id, hash, kex->session_id_len); ++ } ++ ++ gssbuf.value = hash; ++ gssbuf.length = hashlen; ++ ++ if (GSS_ERROR(PRIVSEP(ssh_gssapi_sign(ctxt,&gssbuf,&msg_tok)))) ++ fatal("Couldn't get MIC"); ++ ++ packet_start(SSH2_MSG_KEXGSS_COMPLETE); ++ packet_put_bignum2(dh->pub_key); ++ packet_put_string(msg_tok.value,msg_tok.length); ++ ++ if (send_tok.length != 0) { ++ packet_put_char(1); /* true */ ++ packet_put_string(send_tok.value, send_tok.length); ++ } else { ++ packet_put_char(0); /* false */ ++ } ++ packet_send(); ++ ++ gss_release_buffer(&min_status, &send_tok); ++ gss_release_buffer(&min_status, &msg_tok); ++ ++ if (gss_kex_context == NULL) ++ gss_kex_context = ctxt; ++ else ++ ssh_gssapi_delete_ctx(&ctxt); ++ ++ DH_free(dh); ++ ++ kex_derive_keys(kex, hash, hashlen, shared_secret); ++ BN_clear_free(shared_secret); ++ kex_finish(kex); ++ ++ /* If this was a rekey, then save out any delegated credentials we ++ * just exchanged. */ ++ if (options.gss_store_rekey) ++ ssh_gssapi_rekey_creds(); ++} ++#endif /* GSSAPI */ +Index: b/key.c +=================================================================== +--- a/key.c ++++ b/key.c +@@ -764,6 +764,8 @@ + return KEY_RSA; + } else if (strcmp(name, "ssh-dss") == 0) { + return KEY_DSA; ++ } else if (strcmp(name, "null") == 0) { ++ return KEY_NULL; + } + debug2("key_type_from_name: unknown key type '%s'", name); + return KEY_UNSPEC; +Index: b/key.h +=================================================================== +--- a/key.h ++++ b/key.h +@@ -34,6 +34,7 @@ + KEY_RSA1, + KEY_RSA, + KEY_DSA, ++ KEY_NULL, + KEY_UNSPEC + }; + enum fp_type { +Index: b/monitor.c +=================================================================== +--- a/monitor.c ++++ b/monitor.c +@@ -172,6 +172,8 @@ + int mm_answer_gss_accept_ctx(int, Buffer *); + int mm_answer_gss_userok(int, Buffer *); + int mm_answer_gss_checkmic(int, Buffer *); ++int mm_answer_gss_sign(int, Buffer *); ++int mm_answer_gss_updatecreds(int, Buffer *); + #endif + + #ifdef SSH_AUDIT_EVENTS +@@ -241,6 +243,7 @@ + {MONITOR_REQ_GSSSTEP, MON_ISAUTH, mm_answer_gss_accept_ctx}, + {MONITOR_REQ_GSSUSEROK, MON_AUTH, mm_answer_gss_userok}, + {MONITOR_REQ_GSSCHECKMIC, MON_ISAUTH, mm_answer_gss_checkmic}, ++ {MONITOR_REQ_GSSSIGN, MON_ONCE, mm_answer_gss_sign}, + #endif + #ifdef JPAKE + {MONITOR_REQ_JPAKE_GET_PWDATA, MON_ONCE, mm_answer_jpake_get_pwdata}, +@@ -253,6 +256,12 @@ + }; + + struct mon_table mon_dispatch_postauth20[] = { ++#ifdef GSSAPI ++ {MONITOR_REQ_GSSSETUP, 0, mm_answer_gss_setup_ctx}, ++ {MONITOR_REQ_GSSSTEP, 0, mm_answer_gss_accept_ctx}, ++ {MONITOR_REQ_GSSSIGN, 0, mm_answer_gss_sign}, ++ {MONITOR_REQ_GSSUPCREDS, 0, mm_answer_gss_updatecreds}, ++#endif + {MONITOR_REQ_MODULI, 0, mm_answer_moduli}, + {MONITOR_REQ_SIGN, 0, mm_answer_sign}, + {MONITOR_REQ_PTY, 0, mm_answer_pty}, +@@ -357,6 +366,10 @@ + /* Permit requests for moduli and signatures */ + monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1); + monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1); ++#ifdef GSSAPI ++ /* and for the GSSAPI key exchange */ ++ monitor_permit(mon_dispatch, MONITOR_REQ_GSSSETUP, 1); ++#endif + } else { + mon_dispatch = mon_dispatch_proto15; + +@@ -443,6 +456,10 @@ + monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1); + monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1); + monitor_permit(mon_dispatch, MONITOR_REQ_TERM, 1); ++#ifdef GSSAPI ++ /* and for the GSSAPI key exchange */ ++ monitor_permit(mon_dispatch, MONITOR_REQ_GSSSETUP, 1); ++#endif + } else { + mon_dispatch = mon_dispatch_postauth15; + monitor_permit(mon_dispatch, MONITOR_REQ_TERM, 1); +@@ -1706,6 +1723,13 @@ + kex->kex[KEX_DH_GRP14_SHA1] = kexdh_server; + kex->kex[KEX_DH_GEX_SHA1] = kexgex_server; + kex->kex[KEX_DH_GEX_SHA256] = kexgex_server; ++#ifdef GSSAPI ++ if (options.gss_keyex) { ++ kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_server; ++ kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_server; ++ kex->kex[KEX_GSS_GEX_SHA1] = kexgss_server; ++ } ++#endif + kex->server = 1; + kex->hostkey_type = buffer_get_int(m); + kex->kex_type = buffer_get_int(m); +@@ -1911,6 +1935,9 @@ + OM_uint32 major; + u_int len; + ++ if (!options.gss_authentication && !options.gss_keyex) ++ fatal("In GSSAPI monitor when GSSAPI is disabled"); ++ + goid.elements = buffer_get_string(m, &len); + goid.length = len; + +@@ -1938,6 +1965,9 @@ + OM_uint32 flags = 0; /* GSI needs this */ + u_int len; + ++ if (!options.gss_authentication && !options.gss_keyex) ++ fatal("In GSSAPI monitor when GSSAPI is disabled"); ++ + in.value = buffer_get_string(m, &len); + in.length = len; + major = ssh_gssapi_accept_ctx(gsscontext, &in, &out, &flags); +@@ -1955,6 +1985,7 @@ + monitor_permit(mon_dispatch, MONITOR_REQ_GSSSTEP, 0); + monitor_permit(mon_dispatch, MONITOR_REQ_GSSUSEROK, 1); + monitor_permit(mon_dispatch, MONITOR_REQ_GSSCHECKMIC, 1); ++ monitor_permit(mon_dispatch, MONITOR_REQ_GSSSIGN, 1); + } + return (0); + } +@@ -1966,6 +1997,9 @@ + OM_uint32 ret; + u_int len; + ++ if (!options.gss_authentication && !options.gss_keyex) ++ fatal("In GSSAPI monitor when GSSAPI is disabled"); ++ + gssbuf.value = buffer_get_string(m, &len); + gssbuf.length = len; + mic.value = buffer_get_string(m, &len); +@@ -1992,7 +2026,11 @@ + { + int authenticated; + +- authenticated = authctxt->valid && ssh_gssapi_userok(authctxt->user); ++ if (!options.gss_authentication && !options.gss_keyex) ++ fatal("In GSSAPI monitor when GSSAPI is disabled"); ++ ++ authenticated = authctxt->valid && ++ ssh_gssapi_userok(authctxt->user, authctxt->pw); + + buffer_clear(m); + buffer_put_int(m, authenticated); +@@ -2005,6 +2043,74 @@ + /* Monitor loop will terminate if authenticated */ + return (authenticated); + } ++ ++int ++mm_answer_gss_sign(int socket, Buffer *m) ++{ ++ gss_buffer_desc data; ++ gss_buffer_desc hash = GSS_C_EMPTY_BUFFER; ++ OM_uint32 major, minor; ++ u_int len; ++ ++ if (!options.gss_authentication && !options.gss_keyex) ++ fatal("In GSSAPI monitor when GSSAPI is disabled"); ++ ++ data.value = buffer_get_string(m, &len); ++ data.length = len; ++ if (data.length != 20) ++ fatal("%s: data length incorrect: %d", __func__, ++ (int) data.length); ++ ++ /* Save the session ID on the first time around */ ++ if (session_id2_len == 0) { ++ session_id2_len = data.length; ++ session_id2 = xmalloc(session_id2_len); ++ memcpy(session_id2, data.value, session_id2_len); ++ } ++ major = ssh_gssapi_sign(gsscontext, &data, &hash); ++ ++ xfree(data.value); ++ ++ buffer_clear(m); ++ buffer_put_int(m, major); ++ buffer_put_string(m, hash.value, hash.length); ++ ++ mm_request_send(socket, MONITOR_ANS_GSSSIGN, m); ++ ++ gss_release_buffer(&minor, &hash); ++ ++ /* Turn on getpwnam permissions */ ++ monitor_permit(mon_dispatch, MONITOR_REQ_PWNAM, 1); ++ ++ /* And credential updating, for when rekeying */ ++ monitor_permit(mon_dispatch, MONITOR_REQ_GSSUPCREDS, 1); ++ ++ return (0); ++} ++ ++int ++mm_answer_gss_updatecreds(int socket, Buffer *m) { ++ ssh_gssapi_ccache store; ++ int ok; ++ ++ store.filename = buffer_get_string(m, NULL); ++ store.envvar = buffer_get_string(m, NULL); ++ store.envval = buffer_get_string(m, NULL); ++ ++ ok = ssh_gssapi_update_creds(&store); ++ ++ xfree(store.filename); ++ xfree(store.envvar); ++ xfree(store.envval); ++ ++ buffer_clear(m); ++ buffer_put_int(m, ok); ++ ++ mm_request_send(socket, MONITOR_ANS_GSSUPCREDS, m); ++ ++ return(0); ++} ++ + #endif /* GSSAPI */ + + #ifdef JPAKE +Index: b/monitor.h +=================================================================== +--- a/monitor.h ++++ b/monitor.h +@@ -53,6 +53,8 @@ + MONITOR_REQ_GSSSTEP, MONITOR_ANS_GSSSTEP, + MONITOR_REQ_GSSUSEROK, MONITOR_ANS_GSSUSEROK, + MONITOR_REQ_GSSCHECKMIC, MONITOR_ANS_GSSCHECKMIC, ++ MONITOR_REQ_GSSSIGN, MONITOR_ANS_GSSSIGN, ++ MONITOR_REQ_GSSUPCREDS, MONITOR_ANS_GSSUPCREDS, + MONITOR_REQ_PAM_START, + MONITOR_REQ_PAM_ACCOUNT, MONITOR_ANS_PAM_ACCOUNT, + MONITOR_REQ_PAM_INIT_CTX, MONITOR_ANS_PAM_INIT_CTX, +Index: b/monitor_wrap.c +=================================================================== +--- a/monitor_wrap.c ++++ b/monitor_wrap.c +@@ -1248,7 +1248,7 @@ + } + + int +-mm_ssh_gssapi_userok(char *user) ++mm_ssh_gssapi_userok(char *user, struct passwd *pw) + { + Buffer m; + int authenticated = 0; +@@ -1265,6 +1265,51 @@ + debug3("%s: user %sauthenticated",__func__, authenticated ? "" : "not "); + return (authenticated); + } ++ ++OM_uint32 ++mm_ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_desc *data, gss_buffer_desc *hash) ++{ ++ Buffer m; ++ OM_uint32 major; ++ u_int len; ++ ++ buffer_init(&m); ++ buffer_put_string(&m, data->value, data->length); ++ ++ mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSSIGN, &m); ++ mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSSIGN, &m); ++ ++ major = buffer_get_int(&m); ++ hash->value = buffer_get_string(&m, &len); ++ hash->length = len; ++ ++ buffer_free(&m); ++ ++ return(major); ++} ++ ++int ++mm_ssh_gssapi_update_creds(ssh_gssapi_ccache *store) ++{ ++ Buffer m; ++ int ok; ++ ++ buffer_init(&m); ++ ++ buffer_put_cstring(&m, store->filename ? store->filename : ""); ++ buffer_put_cstring(&m, store->envvar ? store->envvar : ""); ++ buffer_put_cstring(&m, store->envval ? store->envval : ""); ++ ++ mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSUPCREDS, &m); ++ mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSUPCREDS, &m); ++ ++ ok = buffer_get_int(&m); ++ ++ buffer_free(&m); ++ ++ return (ok); ++} ++ + #endif /* GSSAPI */ + + #ifdef JPAKE +Index: b/monitor_wrap.h +=================================================================== +--- a/monitor_wrap.h ++++ b/monitor_wrap.h +@@ -57,8 +57,10 @@ + OM_uint32 mm_ssh_gssapi_server_ctx(Gssctxt **, gss_OID); + OM_uint32 mm_ssh_gssapi_accept_ctx(Gssctxt *, + gss_buffer_desc *, gss_buffer_desc *, OM_uint32 *); +-int mm_ssh_gssapi_userok(char *user); ++int mm_ssh_gssapi_userok(char *user, struct passwd *); + OM_uint32 mm_ssh_gssapi_checkmic(Gssctxt *, gss_buffer_t, gss_buffer_t); ++OM_uint32 mm_ssh_gssapi_sign(Gssctxt *, gss_buffer_t, gss_buffer_t); ++int mm_ssh_gssapi_update_creds(ssh_gssapi_ccache *); + #endif + + #ifdef USE_PAM +Index: b/readconf.c +=================================================================== +--- a/readconf.c ++++ b/readconf.c +@@ -127,6 +127,7 @@ + oClearAllForwardings, oNoHostAuthenticationForLocalhost, + oEnableSSHKeysign, oRekeyLimit, oVerifyHostKeyDNS, oConnectTimeout, + oAddressFamily, oGssAuthentication, oGssDelegateCreds, ++ oGssTrustDns, oGssKeyEx, oGssClientIdentity, oGssRenewalRekey, + oServerAliveInterval, oServerAliveCountMax, oIdentitiesOnly, + oSendEnv, oControlPath, oControlMaster, oHashKnownHosts, + oTunnel, oTunnelDevice, oLocalCommand, oPermitLocalCommand, +@@ -164,10 +165,18 @@ + { "afstokenpassing", oUnsupported }, + #if defined(GSSAPI) + { "gssapiauthentication", oGssAuthentication }, ++ { "gssapikeyexchange", oGssKeyEx }, + { "gssapidelegatecredentials", oGssDelegateCreds }, ++ { "gssapitrustdns", oGssTrustDns }, ++ { "gssapiclientidentity", oGssClientIdentity }, ++ { "gssapirenewalforcesrekey", oGssRenewalRekey }, + #else + { "gssapiauthentication", oUnsupported }, ++ { "gssapikeyexchange", oUnsupported }, + { "gssapidelegatecredentials", oUnsupported }, ++ { "gssapitrustdns", oUnsupported }, ++ { "gssapiclientidentity", oUnsupported }, ++ { "gssapirenewalforcesrekey", oUnsupported }, + #endif + { "fallbacktorsh", oDeprecated }, + { "usersh", oDeprecated }, +@@ -454,10 +463,26 @@ + intptr = &options->gss_authentication; + goto parse_flag; + ++ case oGssKeyEx: ++ intptr = &options->gss_keyex; ++ goto parse_flag; ++ + case oGssDelegateCreds: + intptr = &options->gss_deleg_creds; + goto parse_flag; + ++ case oGssTrustDns: ++ intptr = &options->gss_trust_dns; ++ goto parse_flag; ++ ++ case oGssClientIdentity: ++ charptr = &options->gss_client_identity; ++ goto parse_string; ++ ++ case oGssRenewalRekey: ++ intptr = &options->gss_renewal_rekey; ++ goto parse_flag; ++ + case oBatchMode: + intptr = &options->batch_mode; + goto parse_flag; +@@ -1013,7 +1038,11 @@ + options->pubkey_authentication = -1; + options->challenge_response_authentication = -1; + options->gss_authentication = -1; ++ options->gss_keyex = -1; + options->gss_deleg_creds = -1; ++ options->gss_trust_dns = -1; ++ options->gss_renewal_rekey = -1; ++ options->gss_client_identity = NULL; + options->password_authentication = -1; + options->kbd_interactive_authentication = -1; + options->kbd_interactive_devices = NULL; +@@ -1105,8 +1134,14 @@ + options->challenge_response_authentication = 1; + if (options->gss_authentication == -1) + options->gss_authentication = 0; ++ if (options->gss_keyex == -1) ++ options->gss_keyex = 0; + if (options->gss_deleg_creds == -1) + options->gss_deleg_creds = 0; ++ if (options->gss_trust_dns == -1) ++ options->gss_trust_dns = 0; ++ if (options->gss_renewal_rekey == -1) ++ options->gss_renewal_rekey = 0; + if (options->password_authentication == -1) + options->password_authentication = 1; + if (options->kbd_interactive_authentication == -1) +Index: b/readconf.h +=================================================================== +--- a/readconf.h ++++ b/readconf.h +@@ -44,7 +44,11 @@ + int challenge_response_authentication; + /* Try S/Key or TIS, authentication. */ + int gss_authentication; /* Try GSS authentication */ ++ int gss_keyex; /* Try GSS key exchange */ + int gss_deleg_creds; /* Delegate GSS credentials */ ++ int gss_trust_dns; /* Trust DNS for GSS canonicalization */ ++ int gss_renewal_rekey; /* Credential renewal forces rekey */ ++ char *gss_client_identity; /* Principal to initiate GSSAPI with */ + int password_authentication; /* Try password + * authentication. */ + int kbd_interactive_authentication; /* Try keyboard-interactive auth. */ +Index: b/servconf.c +=================================================================== +--- a/servconf.c ++++ b/servconf.c +@@ -92,7 +92,10 @@ + options->kerberos_ticket_cleanup = -1; + options->kerberos_get_afs_token = -1; + options->gss_authentication=-1; ++ options->gss_keyex = -1; + options->gss_cleanup_creds = -1; ++ options->gss_strict_acceptor = -1; ++ options->gss_store_rekey = -1; + options->password_authentication = -1; + options->kbd_interactive_authentication = -1; + options->challenge_response_authentication = -1; +@@ -210,8 +213,14 @@ + options->kerberos_get_afs_token = 0; + if (options->gss_authentication == -1) + options->gss_authentication = 0; ++ if (options->gss_keyex == -1) ++ options->gss_keyex = 0; + if (options->gss_cleanup_creds == -1) + options->gss_cleanup_creds = 1; ++ if (options->gss_strict_acceptor == -1) ++ options->gss_strict_acceptor = 1; ++ if (options->gss_store_rekey == -1) ++ options->gss_store_rekey = 0; + if (options->password_authentication == -1) + options->password_authentication = 1; + if (options->kbd_interactive_authentication == -1) +@@ -302,7 +311,9 @@ + sBanner, sUseDNS, sHostbasedAuthentication, + sHostbasedUsesNameFromPacketOnly, sClientAliveInterval, + sClientAliveCountMax, sAuthorizedKeysFile, sAuthorizedKeysFile2, +- sGssAuthentication, sGssCleanupCreds, sAcceptEnv, sPermitTunnel, ++ sGssAuthentication, sGssCleanupCreds, sGssStrictAcceptor, ++ sGssKeyEx, sGssStoreRekey, ++ sAcceptEnv, sPermitTunnel, + sMatch, sPermitOpen, sForceCommand, sChrootDirectory, + sUsePrivilegeSeparation, sAllowAgentForwarding, + sZeroKnowledgePasswordAuthentication, +@@ -364,9 +375,15 @@ + #ifdef GSSAPI + { "gssapiauthentication", sGssAuthentication, SSHCFG_ALL }, + { "gssapicleanupcredentials", sGssCleanupCreds, SSHCFG_GLOBAL }, ++ { "gssapistrictacceptorcheck", sGssStrictAcceptor, SSHCFG_GLOBAL }, ++ { "gssapikeyexchange", sGssKeyEx, SSHCFG_GLOBAL }, ++ { "gssapistorecredentialsonrekey", sGssStoreRekey, SSHCFG_GLOBAL }, + #else + { "gssapiauthentication", sUnsupported, SSHCFG_ALL }, + { "gssapicleanupcredentials", sUnsupported, SSHCFG_GLOBAL }, ++ { "gssapistrictacceptorcheck", sUnsupported, SSHCFG_GLOBAL }, ++ { "gssapikeyexchange", sUnsupported, SSHCFG_GLOBAL }, ++ { "gssapistorecredentialsonrekey", sUnsupported, SSHCFG_GLOBAL }, + #endif + { "passwordauthentication", sPasswordAuthentication, SSHCFG_ALL }, + { "kbdinteractiveauthentication", sKbdInteractiveAuthentication, SSHCFG_ALL }, +@@ -891,10 +908,22 @@ + intptr = &options->gss_authentication; + goto parse_flag; + ++ case sGssKeyEx: ++ intptr = &options->gss_keyex; ++ goto parse_flag; ++ + case sGssCleanupCreds: + intptr = &options->gss_cleanup_creds; + goto parse_flag; + ++ case sGssStrictAcceptor: ++ intptr = &options->gss_strict_acceptor; ++ goto parse_flag; ++ ++ case sGssStoreRekey: ++ intptr = &options->gss_store_rekey; ++ goto parse_flag; ++ + case sPasswordAuthentication: + intptr = &options->password_authentication; + goto parse_flag; +Index: b/servconf.h +=================================================================== +--- a/servconf.h ++++ b/servconf.h +@@ -91,7 +91,10 @@ + int kerberos_get_afs_token; /* If true, try to get AFS token if + * authenticated with Kerberos. */ + int gss_authentication; /* If true, permit GSSAPI authentication */ ++ int gss_keyex; /* If true, permit GSSAPI key exchange */ + int gss_cleanup_creds; /* If true, destroy cred cache on logout */ ++ int gss_strict_acceptor; /* If true, restrict the GSSAPI acceptor name */ ++ int gss_store_rekey; + int password_authentication; /* If true, permit password + * authentication. */ + int kbd_interactive_authentication; /* If true, permit */ +Index: b/ssh-gss.h +=================================================================== +--- a/ssh-gss.h ++++ b/ssh-gss.h +@@ -1,6 +1,6 @@ + /* $OpenBSD: ssh-gss.h,v 1.10 2007/06/12 08:20:00 djm Exp $ */ + /* +- * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved. ++ * Copyright (c) 2001-2009 Simon Wilkinson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions +@@ -60,10 +60,22 @@ + + #define SSH_GSS_OIDTYPE 0x06 + ++#define SSH2_MSG_KEXGSS_INIT 30 ++#define SSH2_MSG_KEXGSS_CONTINUE 31 ++#define SSH2_MSG_KEXGSS_COMPLETE 32 ++#define SSH2_MSG_KEXGSS_HOSTKEY 33 ++#define SSH2_MSG_KEXGSS_ERROR 34 ++#define SSH2_MSG_KEXGSS_GROUPREQ 40 ++#define SSH2_MSG_KEXGSS_GROUP 41 ++#define KEX_GSS_GRP1_SHA1_ID "gss-group1-sha1-" ++#define KEX_GSS_GRP14_SHA1_ID "gss-group14-sha1-" ++#define KEX_GSS_GEX_SHA1_ID "gss-gex-sha1-" ++ + typedef struct { + char *filename; + char *envvar; + char *envval; ++ struct passwd *owner; + void *data; + } ssh_gssapi_ccache; + +@@ -71,8 +83,11 @@ + gss_buffer_desc displayname; + gss_buffer_desc exportedname; + gss_cred_id_t creds; ++ gss_name_t name; + struct ssh_gssapi_mech_struct *mech; + ssh_gssapi_ccache store; ++ int used; ++ int updated; + } ssh_gssapi_client; + + typedef struct ssh_gssapi_mech_struct { +@@ -83,6 +98,7 @@ + int (*userok) (ssh_gssapi_client *, char *); + int (*localname) (ssh_gssapi_client *, char **); + void (*storecreds) (ssh_gssapi_client *); ++ int (*updatecreds) (ssh_gssapi_ccache *, ssh_gssapi_client *); + } ssh_gssapi_mech; + + typedef struct { +@@ -93,10 +109,11 @@ + gss_OID oid; /* client */ + gss_cred_id_t creds; /* server */ + gss_name_t client; /* server */ +- gss_cred_id_t client_creds; /* server */ ++ gss_cred_id_t client_creds; /* both */ + } Gssctxt; + + extern ssh_gssapi_mech *supported_mechs[]; ++extern Gssctxt *gss_kex_context; + + int ssh_gssapi_check_oid(Gssctxt *, void *, size_t); + void ssh_gssapi_set_oid_data(Gssctxt *, void *, size_t); +@@ -116,16 +133,30 @@ + void ssh_gssapi_delete_ctx(Gssctxt **); + OM_uint32 ssh_gssapi_sign(Gssctxt *, gss_buffer_t, gss_buffer_t); + void ssh_gssapi_buildmic(Buffer *, const char *, const char *, const char *); +-int ssh_gssapi_check_mechanism(Gssctxt **, gss_OID, const char *); ++int ssh_gssapi_check_mechanism(Gssctxt **, gss_OID, const char *, const char *); ++OM_uint32 ssh_gssapi_client_identity(Gssctxt *, const char *); ++int ssh_gssapi_credentials_updated(Gssctxt *); + + /* In the server */ ++typedef int ssh_gssapi_check_fn(Gssctxt **, gss_OID, const char *, ++ const char *); ++char *ssh_gssapi_client_mechanisms(const char *, const char *); ++char *ssh_gssapi_kex_mechs(gss_OID_set, ssh_gssapi_check_fn *, const char *, ++ const char *); ++gss_OID ssh_gssapi_id_kex(Gssctxt *, char *, int); ++int ssh_gssapi_server_check_mech(Gssctxt **,gss_OID, const char *, ++ const char *); + OM_uint32 ssh_gssapi_server_ctx(Gssctxt **, gss_OID); +-int ssh_gssapi_userok(char *name); ++int ssh_gssapi_userok(char *name, struct passwd *); + OM_uint32 ssh_gssapi_checkmic(Gssctxt *, gss_buffer_t, gss_buffer_t); + void ssh_gssapi_do_child(char ***, u_int *); + void ssh_gssapi_cleanup_creds(void); + void ssh_gssapi_storecreds(void); + ++char *ssh_gssapi_server_mechanisms(void); ++int ssh_gssapi_oid_table_ok(); ++ ++int ssh_gssapi_update_creds(ssh_gssapi_ccache *store); + #endif /* GSSAPI */ + + #endif /* _SSH_GSS_H */ +Index: b/ssh_config +=================================================================== +--- a/ssh_config ++++ b/ssh_config +@@ -26,6 +26,8 @@ + # HostbasedAuthentication no + # GSSAPIAuthentication no + # GSSAPIDelegateCredentials no ++# GSSAPIKeyExchange no ++# GSSAPITrustDNS no + # BatchMode no + # CheckHostIP yes + # AddressFamily any +Index: b/ssh_config.5 +=================================================================== +--- a/ssh_config.5 ++++ b/ssh_config.5 +@@ -478,11 +478,38 @@ + The default is + .Dq no . + Note that this option applies to protocol version 2 only. ++.It Cm GSSAPIKeyExchange ++Specifies whether key exchange based on GSSAPI may be used. When using ++GSSAPI key exchange the server need not have a host key. ++The default is ++.Dq no . ++Note that this option applies to protocol version 2 only. ++.It Cm GSSAPIClientIdentity ++If set, specifies the GSSAPI client identity that ssh should use when ++connecting to the server. The default is unset, which means that the default ++identity will be used. + .It Cm GSSAPIDelegateCredentials + Forward (delegate) credentials to the server. + The default is + .Dq no . +-Note that this option applies to protocol version 2 only. ++Note that this option applies to protocol version 2 connections using GSSAPI. ++.It Cm GSSAPIRenewalForcesRekey ++If set to ++.Dq yes ++then renewal of the client's GSSAPI credentials will force the rekeying of the ++ssh connection. With a compatible server, this can delegate the renewed ++credentials to a session on the server. ++The default is ++.Dq no . ++.It Cm GSSAPITrustDns ++Set to ++.Dq yes to indicate that the DNS is trusted to securely canonicalize ++the name of the host being connected to. If ++.Dq no, the hostname entered on the ++command line will be passed untouched to the GSSAPI library. ++The default is ++.Dq no . ++This option only applies to protocol version 2 connections using GSSAPI. + .It Cm HashKnownHosts + Indicates that + .Xr ssh 1 +Index: b/sshconnect2.c +=================================================================== +--- a/sshconnect2.c ++++ b/sshconnect2.c +@@ -105,9 +105,34 @@ + { + Kex *kex; + ++#ifdef GSSAPI ++ char *orig = NULL, *gss = NULL; ++ char *gss_host = NULL; ++#endif ++ + xxx_host = host; + xxx_hostaddr = hostaddr; + ++#ifdef GSSAPI ++ if (options.gss_keyex) { ++ /* Add the GSSAPI mechanisms currently supported on this ++ * client to the key exchange algorithm proposal */ ++ orig = myproposal[PROPOSAL_KEX_ALGS]; ++ ++ if (options.gss_trust_dns) ++ gss_host = (char *)get_canonical_hostname(1); ++ else ++ gss_host = host; ++ ++ gss = ssh_gssapi_client_mechanisms(gss_host, options.gss_client_identity); ++ if (gss) { ++ debug("Offering GSSAPI proposal: %s", gss); ++ xasprintf(&myproposal[PROPOSAL_KEX_ALGS], ++ "%s,%s", gss, orig); ++ } ++ } ++#endif ++ + if (options.ciphers == (char *)-1) { + logit("No valid ciphers for protocol version 2 given, using defaults."); + options.ciphers = NULL; +@@ -135,6 +160,17 @@ + myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = + options.hostkeyalgorithms; + ++#ifdef GSSAPI ++ /* If we've got GSSAPI algorithms, then we also support the ++ * 'null' hostkey, as a last resort */ ++ if (options.gss_keyex && gss) { ++ orig = myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS]; ++ xasprintf(&myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS], ++ "%s,null", orig); ++ xfree(gss); ++ } ++#endif ++ + if (options.rekey_limit) + packet_set_rekey_limit((u_int32_t)options.rekey_limit); + +@@ -144,10 +180,26 @@ + kex->kex[KEX_DH_GRP14_SHA1] = kexdh_client; + kex->kex[KEX_DH_GEX_SHA1] = kexgex_client; + kex->kex[KEX_DH_GEX_SHA256] = kexgex_client; ++#ifdef GSSAPI ++ if (options.gss_keyex) { ++ kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_client; ++ kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_client; ++ kex->kex[KEX_GSS_GEX_SHA1] = kexgss_client; ++ } ++#endif + kex->client_version_string=client_version_string; + kex->server_version_string=server_version_string; + kex->verify_host_key=&verify_host_key_callback; + ++#ifdef GSSAPI ++ if (options.gss_keyex) { ++ kex->gss_deleg_creds = options.gss_deleg_creds; ++ kex->gss_trust_dns = options.gss_trust_dns; ++ kex->gss_client = options.gss_client_identity; ++ kex->gss_host = gss_host; ++ } ++#endif ++ + xxx_kex = kex; + + dispatch_run(DISPATCH_BLOCK, &kex->done, kex); +@@ -236,6 +288,7 @@ + void input_gssapi_hash(int type, u_int32_t, void *); + void input_gssapi_error(int, u_int32_t, void *); + void input_gssapi_errtok(int, u_int32_t, void *); ++int userauth_gsskeyex(Authctxt *authctxt); + #endif + + void userauth(Authctxt *, char *); +@@ -251,6 +304,11 @@ + + Authmethod authmethods[] = { + #ifdef GSSAPI ++ {"gssapi-keyex", ++ userauth_gsskeyex, ++ NULL, ++ &options.gss_authentication, ++ NULL}, + {"gssapi-with-mic", + userauth_gssapi, + NULL, +@@ -542,19 +600,29 @@ + static u_int mech = 0; + OM_uint32 min; + int ok = 0; ++ const char *gss_host; ++ ++ if (options.gss_trust_dns) ++ gss_host = get_canonical_hostname(1); ++ else ++ gss_host = authctxt->host; + + /* Try one GSSAPI method at a time, rather than sending them all at + * once. */ + + if (gss_supported == NULL) +- gss_indicate_mechs(&min, &gss_supported); ++ if (GSS_ERROR(gss_indicate_mechs(&min, &gss_supported))) { ++ gss_supported = NULL; ++ return 0; ++ } + + /* Check to see if the mechanism is usable before we offer it */ + while (mech < gss_supported->count && !ok) { + /* My DER encoding requires length<128 */ + if (gss_supported->elements[mech].length < 128 && + ssh_gssapi_check_mechanism(&gssctxt, +- &gss_supported->elements[mech], authctxt->host)) { ++ &gss_supported->elements[mech], gss_host, ++ options.gss_client_identity)) { + ok = 1; /* Mechanism works */ + } else { + mech++; +@@ -651,8 +719,8 @@ + { + Authctxt *authctxt = ctxt; + Gssctxt *gssctxt; +- int oidlen; +- char *oidv; ++ u_int oidlen; ++ u_char *oidv; + + if (authctxt == NULL) + fatal("input_gssapi_response: no authentication context"); +@@ -762,6 +830,48 @@ + xfree(msg); + xfree(lang); + } ++ ++int ++userauth_gsskeyex(Authctxt *authctxt) ++{ ++ Buffer b; ++ gss_buffer_desc gssbuf; ++ gss_buffer_desc mic = GSS_C_EMPTY_BUFFER; ++ OM_uint32 ms; ++ ++ static int attempt = 0; ++ if (attempt++ >= 1) ++ return (0); ++ ++ if (gss_kex_context == NULL) { ++ debug("No valid Key exchange context"); ++ return (0); ++ } ++ ++ ssh_gssapi_buildmic(&b, authctxt->server_user, authctxt->service, ++ "gssapi-keyex"); ++ ++ gssbuf.value = buffer_ptr(&b); ++ gssbuf.length = buffer_len(&b); ++ ++ if (GSS_ERROR(ssh_gssapi_sign(gss_kex_context, &gssbuf, &mic))) { ++ buffer_free(&b); ++ return (0); ++ } ++ ++ packet_start(SSH2_MSG_USERAUTH_REQUEST); ++ packet_put_cstring(authctxt->server_user); ++ packet_put_cstring(authctxt->service); ++ packet_put_cstring(authctxt->method->name); ++ packet_put_string(mic.value, mic.length); ++ packet_send(); ++ ++ buffer_free(&b); ++ gss_release_buffer(&ms, &mic); ++ ++ return (1); ++} ++ + #endif /* GSSAPI */ + + int +Index: b/sshd.c +=================================================================== +--- a/sshd.c ++++ b/sshd.c +@@ -120,6 +120,10 @@ + #include "roaming.h" + #include "version.h" + ++#ifdef USE_SECURITY_SESSION_API ++#include ++#endif ++ + #ifdef LIBWRAP + #include + #include +@@ -1531,10 +1535,13 @@ + logit("Disabling protocol version 1. Could not load host key"); + options.protocol &= ~SSH_PROTO_1; + } ++#ifndef GSSAPI ++ /* The GSSAPI key exchange can run without a host key */ + if ((options.protocol & SSH_PROTO_2) && !sensitive_data.have_ssh2_key) { + logit("Disabling protocol version 2. Could not load host key"); + options.protocol &= ~SSH_PROTO_2; + } ++#endif + if (!(options.protocol & (SSH_PROTO_1|SSH_PROTO_2))) { + logit("sshd: no hostkeys available -- exiting."); + exit(1); +@@ -1818,6 +1825,60 @@ + /* Log the connection. */ + verbose("Connection from %.500s port %d", remote_ip, remote_port); + ++#ifdef USE_SECURITY_SESSION_API ++ /* ++ * Create a new security session for use by the new user login if ++ * the current session is the root session or we are not launched ++ * by inetd (eg: debugging mode or server mode). We do not ++ * necessarily need to create a session if we are launched from ++ * inetd because Panther xinetd will create a session for us. ++ * ++ * The only case where this logic will fail is if there is an ++ * inetd running in a non-root session which is not creating ++ * new sessions for us. Then all the users will end up in the ++ * same session (bad). ++ * ++ * When the client exits, the session will be destroyed for us ++ * automatically. ++ * ++ * We must create the session before any credentials are stored ++ * (including AFS pags, which happens a few lines below). ++ */ ++ { ++ OSStatus err = 0; ++ SecuritySessionId sid = 0; ++ SessionAttributeBits sattrs = 0; ++ ++ err = SessionGetInfo(callerSecuritySession, &sid, &sattrs); ++ if (err) ++ error("SessionGetInfo() failed with error %.8X", ++ (unsigned) err); ++ else ++ debug("Current Session ID is %.8X / Session Attributes are %.8X", ++ (unsigned) sid, (unsigned) sattrs); ++ ++ if (inetd_flag && !(sattrs & sessionIsRoot)) ++ debug("Running in inetd mode in a non-root session... " ++ "assuming inetd created the session for us."); ++ else { ++ debug("Creating new security session..."); ++ err = SessionCreate(0, sessionHasTTY | sessionIsRemote); ++ if (err) ++ error("SessionCreate() failed with error %.8X", ++ (unsigned) err); ++ ++ err = SessionGetInfo(callerSecuritySession, &sid, ++ &sattrs); ++ if (err) ++ error("SessionGetInfo() failed with error %.8X", ++ (unsigned) err); ++ else ++ debug("New Session ID is %.8X / Session Attributes are %.8X", ++ (unsigned) sid, (unsigned) sattrs); ++ } ++ } ++#endif ++ + /* + * We don't want to listen forever unless the other side + * successfully authenticates itself. So we set up an alarm which is +@@ -2195,12 +2256,61 @@ + + myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = list_hostkey_types(); + ++#ifdef GSSAPI ++ { ++ char *orig; ++ char *gss = NULL; ++ char *newstr = NULL; ++ orig = myproposal[PROPOSAL_KEX_ALGS]; ++ ++ /* ++ * If we don't have a host key, then there's no point advertising ++ * the other key exchange algorithms ++ */ ++ ++ if (strlen(myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS]) == 0) ++ orig = NULL; ++ ++ if (options.gss_keyex) ++ gss = ssh_gssapi_server_mechanisms(); ++ else ++ gss = NULL; ++ ++ if (gss && orig) ++ xasprintf(&newstr, "%s,%s", gss, orig); ++ else if (gss) ++ newstr = gss; ++ else if (orig) ++ newstr = orig; ++ ++ /* ++ * If we've got GSSAPI mechanisms, then we've got the 'null' host ++ * key alg, but we can't tell people about it unless its the only ++ * host key algorithm we support ++ */ ++ if (gss && (strlen(myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS])) == 0) ++ myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = "null"; ++ ++ if (newstr) ++ myproposal[PROPOSAL_KEX_ALGS] = newstr; ++ else ++ fatal("No supported key exchange algorithms"); ++ } ++#endif ++ + /* start key exchange */ + kex = kex_setup(myproposal); + kex->kex[KEX_DH_GRP1_SHA1] = kexdh_server; + kex->kex[KEX_DH_GRP14_SHA1] = kexdh_server; + kex->kex[KEX_DH_GEX_SHA1] = kexgex_server; + kex->kex[KEX_DH_GEX_SHA256] = kexgex_server; ++#ifdef GSSAPI ++ if (options.gss_keyex) { ++ kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_server; ++ kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_server; ++ kex->kex[KEX_GSS_GEX_SHA1] = kexgss_server; ++ } ++#endif + kex->server = 1; + kex->client_version_string=client_version_string; + kex->server_version_string=server_version_string; +Index: b/sshd_config +=================================================================== +--- a/sshd_config ++++ b/sshd_config +@@ -73,6 +73,8 @@ + # GSSAPI options + #GSSAPIAuthentication no + #GSSAPICleanupCredentials yes ++#GSSAPIStrictAcceptorCheck yes ++#GSSAPIKeyExchange no + + # Set this to 'yes' to enable PAM authentication, account processing, + # and session processing. If this is enabled, PAM authentication will +Index: b/sshd_config.5 +=================================================================== +--- a/sshd_config.5 ++++ b/sshd_config.5 +@@ -379,12 +379,40 @@ + The default is + .Dq no . + Note that this option applies to protocol version 2 only. ++.It Cm GSSAPIKeyExchange ++Specifies whether key exchange based on GSSAPI is allowed. GSSAPI key exchange ++doesn't rely on ssh keys to verify host identity. ++The default is ++.Dq no . ++Note that this option applies to protocol version 2 only. + .It Cm GSSAPICleanupCredentials + Specifies whether to automatically destroy the user's credentials cache + on logout. + The default is + .Dq yes . + Note that this option applies to protocol version 2 only. ++.It Cm GSSAPIStrictAcceptorCheck ++Determines whether to be strict about the identity of the GSSAPI acceptor ++a client authenticates against. If ++.Dq yes ++then the client must authenticate against the ++.Pa host ++service on the current hostname. If ++.Dq no ++then the client may authenticate against any service key stored in the ++machine's default store. This facility is provided to assist with operation ++on multi homed machines. ++The default is ++.Dq yes . ++Note that this option applies only to protocol version 2 GSSAPI connections, ++and setting it to ++.Dq no ++may only work with recent Kerberos GSSAPI libraries. ++.It Cm GSSAPIStoreCredentialsOnRekey ++Controls whether the user's GSSAPI credentials should be updated following a ++successful connection rekeying. This option can be used to accepted renewed ++or updated credentials from a compatible client. The default is ++.Dq no . + .It Cm HostbasedAuthentication + Specifies whether rhosts or /etc/hosts.equiv authentication together + with successful public key client host authentication is allowed diff --git a/debian/patches/helpful-wait-terminate.patch b/debian/patches/helpful-wait-terminate.patch new file mode 100644 index 000000000..f4baa88ba --- /dev/null +++ b/debian/patches/helpful-wait-terminate.patch @@ -0,0 +1,13 @@ +Index: b/serverloop.c +=================================================================== +--- a/serverloop.c ++++ b/serverloop.c +@@ -680,7 +680,7 @@ + if (!channel_still_open()) + break; + if (!waiting_termination) { +- const char *s = "Waiting for forwarded connections to terminate...\r\n"; ++ const char *s = "Waiting for forwarded connections to terminate... (press ~& to background)\r\n"; + char *cp; + waiting_termination = 1; + buffer_append(&stderr_buffer, s, strlen(s)); diff --git a/debian/patches/keepalive-extensions.patch b/debian/patches/keepalive-extensions.patch new file mode 100644 index 000000000..cb9c2823c --- /dev/null +++ b/debian/patches/keepalive-extensions.patch @@ -0,0 +1,108 @@ +Index: b/readconf.c +=================================================================== +--- a/readconf.c ++++ b/readconf.c +@@ -133,6 +133,7 @@ + oSendEnv, oControlPath, oControlMaster, oHashKnownHosts, + oTunnel, oTunnelDevice, oLocalCommand, oPermitLocalCommand, + oVisualHostKey, oUseRoaming, oZeroKnowledgePasswordAuthentication, ++ oProtocolKeepAlives, oSetupTimeOut, + oDeprecated, oUnsupported + } OpCodes; + +@@ -246,6 +247,8 @@ + #else + { "zeroknowledgepasswordauthentication", oUnsupported }, + #endif ++ { "protocolkeepalives", oProtocolKeepAlives }, ++ { "setuptimeout", oSetupTimeOut }, + + { NULL, oBadOption } + }; +@@ -845,6 +848,8 @@ + goto parse_flag; + + case oServerAliveInterval: ++ case oProtocolKeepAlives: /* Debian-specific compatibility alias */ ++ case oSetupTimeOut: /* Debian-specific compatibility alias */ + intptr = &options->server_alive_interval; + goto parse_time; + +@@ -1233,8 +1238,13 @@ + options->rekey_limit = 0; + if (options->verify_host_key_dns == -1) + options->verify_host_key_dns = 0; +- if (options->server_alive_interval == -1) +- options->server_alive_interval = 0; ++ if (options->server_alive_interval == -1) { ++ /* in batch mode, default is 5mins */ ++ if (options->batch_mode == 1) ++ options->server_alive_interval = 300; ++ else ++ options->server_alive_interval = 0; ++ } + if (options->server_alive_count_max == -1) + options->server_alive_count_max = 3; + if (options->control_master == -1) +Index: b/ssh_config.5 +=================================================================== +--- a/ssh_config.5 ++++ b/ssh_config.5 +@@ -128,8 +128,12 @@ + If set to + .Dq yes , + passphrase/password querying will be disabled. ++In addition, the ++.Cm ServerAliveInterval ++option will be set to 300 seconds by default. + This option is useful in scripts and other batch jobs where no user +-is present to supply the password. ++is present to supply the password, ++and where it is desirable to detect a broken network swiftly. + The argument must be + .Dq yes + or +@@ -946,8 +950,15 @@ + will send a message through the encrypted + channel to request a response from the server. + The default +-is 0, indicating that these messages will not be sent to the server. ++is 0, indicating that these messages will not be sent to the server, ++or 300 if the ++.Cm BatchMode ++option is set. + This option applies to protocol version 2 only. ++.Cm ProtocolKeepAlives ++and ++.Cm SetupTimeOut ++are Debian-specific compatibility aliases for this option. + .It Cm SmartcardDevice + Specifies which smartcard device to use. + The argument to this keyword is the device +@@ -993,6 +1004,12 @@ + other side. + If they are sent, death of the connection or crash of one + of the machines will be properly noticed. ++This option only uses TCP keepalives (as opposed to using ssh level ++keepalives), so takes a long time to notice when the connection dies. ++As such, you probably want ++the ++.Cm ServerAliveInterval ++option as well. + However, this means that + connections will die if the route is down temporarily, and some people + find it annoying. +Index: b/sshd_config.5 +=================================================================== +--- a/sshd_config.5 ++++ b/sshd_config.5 +@@ -919,6 +919,9 @@ + .Pp + To disable TCP keepalive messages, the value should be set to + .Dq no . ++.Pp ++This option was formerly called ++.Cm KeepAlive . + .It Cm UseDNS + Specifies whether + .Xr sshd 8 diff --git a/debian/patches/keyfile-debug.patch b/debian/patches/keyfile-debug.patch new file mode 100644 index 000000000..7a46a78d7 --- /dev/null +++ b/debian/patches/keyfile-debug.patch @@ -0,0 +1,18 @@ +Index: b/auth.c +=================================================================== +--- a/auth.c ++++ b/auth.c +@@ -516,8 +516,12 @@ + * Open the file containing the authorized keys + * Fail quietly if file does not exist + */ +- if ((fd = open(file, O_RDONLY|O_NONBLOCK)) == -1) ++ if ((fd = open(file, O_RDONLY|O_NONBLOCK)) == -1) { ++ if (errno != ENOENT) ++ debug("Could not open keyfile '%s': %s", file, ++ strerror(errno)); + return NULL; ++ } + + if (fstat(fd, &st) < 0) { + close(fd); diff --git a/debian/patches/lintian-symlink-pickiness.patch b/debian/patches/lintian-symlink-pickiness.patch new file mode 100644 index 000000000..33acec411 --- /dev/null +++ b/debian/patches/lintian-symlink-pickiness.patch @@ -0,0 +1,16 @@ +Index: b/Makefile.in +=================================================================== +--- a/Makefile.in ++++ b/Makefile.in +@@ -293,9 +293,9 @@ + $(INSTALL) -m 644 ssh-keysign.8.out $(DESTDIR)$(mandir)/$(mansubdir)8/ssh-keysign.8 + $(INSTALL) -m 644 ssh-vulnkey.1.out $(DESTDIR)$(mandir)/$(mansubdir)1/ssh-vulnkey.1 + -rm -f $(DESTDIR)$(bindir)/slogin +- ln -s ./ssh$(EXEEXT) $(DESTDIR)$(bindir)/slogin ++ ln -s ssh$(EXEEXT) $(DESTDIR)$(bindir)/slogin + -rm -f $(DESTDIR)$(mandir)/$(mansubdir)1/slogin.1 +- ln -s ./ssh.1 $(DESTDIR)$(mandir)/$(mansubdir)1/slogin.1 ++ ln -s ssh.1 $(DESTDIR)$(mandir)/$(mansubdir)1/slogin.1 + + install-sysconf: + if [ ! -d $(DESTDIR)$(sysconfdir) ]; then \ diff --git a/debian/patches/no-constraint-fallback.patch b/debian/patches/no-constraint-fallback.patch new file mode 100644 index 000000000..139d1ec73 --- /dev/null +++ b/debian/patches/no-constraint-fallback.patch @@ -0,0 +1,43 @@ +Index: b/authfd.c +=================================================================== +--- a/authfd.c ++++ b/authfd.c +@@ -545,12 +545,6 @@ + return decode_reply(type); + } + +-int +-ssh_add_identity(AuthenticationConnection *auth, Key *key, const char *comment) +-{ +- return ssh_add_identity_constrained(auth, key, comment, 0, 0); +-} +- + /* + * Removes an identity from the authentication server. This call is not + * meant to be used by normal applications. +Index: b/authfd.h +=================================================================== +--- a/authfd.h ++++ b/authfd.h +@@ -75,7 +75,6 @@ + int ssh_get_num_identities(AuthenticationConnection *, int); + Key *ssh_get_first_identity(AuthenticationConnection *, char **, int); + Key *ssh_get_next_identity(AuthenticationConnection *, char **, int); +-int ssh_add_identity(AuthenticationConnection *, Key *, const char *); + int ssh_add_identity_constrained(AuthenticationConnection *, Key *, + const char *, u_int, u_int); + int ssh_remove_identity(AuthenticationConnection *, Key *); +Index: b/ssh-add.c +=================================================================== +--- a/ssh-add.c ++++ b/ssh-add.c +@@ -203,9 +203,6 @@ + if (confirm != 0) + fprintf(stderr, + "The user has to confirm each use of the key\n"); +- } else if (ssh_add_identity(ac, private, comment)) { +- fprintf(stderr, "Identity added: %s (%s)\n", filename, comment); +- ret = 0; + } else { + fprintf(stderr, "Could not add identity: %s\n", filename); + } diff --git a/debian/patches/no-openssl-version-check.patch b/debian/patches/no-openssl-version-check.patch new file mode 100644 index 000000000..9001f6565 --- /dev/null +++ b/debian/patches/no-openssl-version-check.patch @@ -0,0 +1,21 @@ +Index: b/entropy.c +=================================================================== +--- a/entropy.c ++++ b/entropy.c +@@ -151,6 +151,8 @@ + void + init_rng(void) + { ++#if defined (DISABLED_BY_DEBIAN) ++ /* drow: Is this check still too strict for Debian? */ + /* + * OpenSSL version numbers: MNNFFPPS: major minor fix patch status + * We match major, minor, fix and status (not patch) +@@ -158,6 +160,7 @@ + if ((SSLeay() ^ OPENSSL_VERSION_NUMBER) & ~0xff0L) + fatal("OpenSSL version mismatch. Built against %lx, you " + "have %lx", OPENSSL_VERSION_NUMBER, SSLeay()); ++#endif + + #ifndef OPENSSL_PRNG_ONLY + original_uid = getuid(); diff --git a/debian/patches/old-gssapi.patch b/debian/patches/old-gssapi.patch new file mode 100644 index 000000000..272654fd8 --- /dev/null +++ b/debian/patches/old-gssapi.patch @@ -0,0 +1,141 @@ +Index: b/servconf.c +=================================================================== +--- a/servconf.c ++++ b/servconf.c +@@ -375,16 +375,20 @@ + #ifdef GSSAPI + { "gssapiauthentication", sGssAuthentication, SSHCFG_ALL }, + { "gssapicleanupcredentials", sGssCleanupCreds, SSHCFG_GLOBAL }, ++ { "gssapicleanupcreds", sGssCleanupCreds, SSHCFG_GLOBAL }, + { "gssapistrictacceptorcheck", sGssStrictAcceptor, SSHCFG_GLOBAL }, + { "gssapikeyexchange", sGssKeyEx, SSHCFG_GLOBAL }, + { "gssapistorecredentialsonrekey", sGssStoreRekey, SSHCFG_GLOBAL }, + #else + { "gssapiauthentication", sUnsupported, SSHCFG_ALL }, + { "gssapicleanupcredentials", sUnsupported, SSHCFG_GLOBAL }, ++ { "gssapicleanupcreds", sUnsupported, SSHCFG_GLOBAL }, + { "gssapistrictacceptorcheck", sUnsupported, SSHCFG_GLOBAL }, + { "gssapikeyexchange", sUnsupported, SSHCFG_GLOBAL }, + { "gssapistorecredentialsonrekey", sUnsupported, SSHCFG_GLOBAL }, + #endif ++ { "gssusesessionccache", sUnsupported, SSHCFG_GLOBAL }, ++ { "gssapiusesessioncredcache", sUnsupported, SSHCFG_GLOBAL }, + { "passwordauthentication", sPasswordAuthentication, SSHCFG_ALL }, + { "kbdinteractiveauthentication", sKbdInteractiveAuthentication, SSHCFG_ALL }, + { "challengeresponseauthentication", sChallengeResponseAuthentication, SSHCFG_GLOBAL }, +@@ -1620,7 +1624,9 @@ + #endif + #ifdef GSSAPI + dump_cfg_fmtint(sGssAuthentication, o->gss_authentication); ++ dump_cfg_fmtint(sGssKeyEx, o->gss_keyex); + dump_cfg_fmtint(sGssCleanupCreds, o->gss_cleanup_creds); ++ dump_cfg_fmtint(sGssStrictAcceptor, o->gss_strict_acceptor); + #endif + #ifdef JPAKE + dump_cfg_fmtint(sZeroKnowledgePasswordAuthentication, +Index: b/sshconnect2.c +=================================================================== +--- a/sshconnect2.c ++++ b/sshconnect2.c +@@ -314,6 +314,11 @@ + NULL, + &options.gss_authentication, + NULL}, ++ {"gssapi", ++ userauth_gssapi, ++ NULL, ++ &options.gss_authentication, ++ NULL}, + #endif + {"hostbased", + userauth_hostbased, +@@ -601,6 +606,7 @@ + OM_uint32 min; + int ok = 0; + const char *gss_host; ++ int old_gssapi_method; + + if (options.gss_trust_dns) + gss_host = get_canonical_hostname(1); +@@ -639,13 +645,25 @@ + packet_put_cstring(authctxt->service); + packet_put_cstring(authctxt->method->name); + +- packet_put_int(1); ++ old_gssapi_method = !strcmp(authctxt->method->name, "gssapi"); ++ ++ /* Versions of Debian ssh-krb5 prior to 3.8.1p1-1 don't expect ++ * tagged OIDs. As such we include both tagged and untagged oids ++ * for the old gssapi method. ++ * We only include tagged oids for the new gssapi-with-mic method. ++ */ ++ packet_put_int(old_gssapi_method ? 2 : 1); + + packet_put_int((gss_supported->elements[mech].length) + 2); + packet_put_char(SSH_GSS_OIDTYPE); + packet_put_char(gss_supported->elements[mech].length); + packet_put_raw(gss_supported->elements[mech].elements, + gss_supported->elements[mech].length); ++ if (old_gssapi_method) { ++ packet_put_int(gss_supported->elements[mech].length); ++ packet_put_raw(gss_supported->elements[mech].elements, ++ gss_supported->elements[mech].length); ++ } + + packet_send(); + +@@ -685,8 +703,10 @@ + } + + if (status == GSS_S_COMPLETE) { ++ int old_gssapi_method = !strcmp(authctxt->method->name, ++ "gssapi"); + /* send either complete or MIC, depending on mechanism */ +- if (!(flags & GSS_C_INTEG_FLAG)) { ++ if (old_gssapi_method || !(flags & GSS_C_INTEG_FLAG)) { + packet_start(SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE); + packet_send(); + } else { +@@ -720,7 +740,7 @@ + Authctxt *authctxt = ctxt; + Gssctxt *gssctxt; + u_int oidlen; +- u_char *oidv; ++ u_char *oidv, *oidv_free; + + if (authctxt == NULL) + fatal("input_gssapi_response: no authentication context"); +@@ -728,22 +748,28 @@ + + /* Setup our OID */ + oidv = packet_get_string(&oidlen); ++ oidv_free = oidv; + + if (oidlen <= 2 || + oidv[0] != SSH_GSS_OIDTYPE || + oidv[1] != oidlen - 2) { +- xfree(oidv); + debug("Badly encoded mechanism OID received"); +- userauth(authctxt, NULL); +- return; ++ if (oidlen < 2) { ++ xfree(oidv_free); ++ userauth(authctxt, NULL); ++ return; ++ } ++ } else { ++ oidlen -= 2; ++ oidv += 2; + } + +- if (!ssh_gssapi_check_oid(gssctxt, oidv + 2, oidlen - 2)) ++ if (!ssh_gssapi_check_oid(gssctxt, oidv, oidlen)) + fatal("Server returned different OID than expected"); + + packet_check_eom(); + +- xfree(oidv); ++ xfree(oidv_free); + + if (GSS_ERROR(process_gssapi_token(ctxt, GSS_C_NO_BUFFER))) { + /* Start again with next method on list */ diff --git a/debian/patches/oom-adjust.patch b/debian/patches/oom-adjust.patch new file mode 100644 index 000000000..d01f1f3ad --- /dev/null +++ b/debian/patches/oom-adjust.patch @@ -0,0 +1,212 @@ +Index: b/config.h.in +=================================================================== +--- a/config.h.in ++++ b/config.h.in +@@ -1238,6 +1238,9 @@ + /* Define if X11 doesn't support AF_UNIX sockets on that system */ + #undef NO_X11_UNIX_SOCKETS + ++/* Adjust Linux out-of-memory killer */ ++#undef OOM_ADJUST ++ + /* Define if EVP_DigestUpdate returns void */ + #undef OPENSSL_EVP_DIGESTUPDATE_VOID + +Index: b/configure +=================================================================== +--- a/configure ++++ b/configure +@@ -8369,6 +8369,11 @@ + _ACEOF + + fi ++ ++cat >>confdefs.h <<\_ACEOF ++#define OOM_ADJUST 1 ++_ACEOF ++ + ;; + mips-sony-bsd|mips-sony-newsos4) + +Index: b/configure.ac +=================================================================== +--- a/configure.ac ++++ b/configure.ac +@@ -630,6 +630,7 @@ + AC_DEFINE(SSH_TUN_PREPEND_AF, 1, + [Prepend the address family to IP tunnel traffic]) + fi ++ AC_DEFINE(OOM_ADJUST, 1, [Adjust Linux out-of-memory killer]) + ;; + mips-sony-bsd|mips-sony-newsos4) + AC_DEFINE(NEED_SETPGRP, 1, [Need setpgrp to acquire controlling tty]) +Index: b/openbsd-compat/port-linux.c +=================================================================== +--- a/openbsd-compat/port-linux.c ++++ b/openbsd-compat/port-linux.c +@@ -18,7 +18,7 @@ + */ + + /* +- * Linux-specific portability code - just SELinux support at present ++ * Linux-specific portability code + */ + + #include "includes.h" +@@ -27,6 +27,15 @@ + #include + #include + ++#ifdef OOM_ADJUST ++#include ++#include ++#include ++#include ++#endif ++ ++#include "log.h" ++ + #ifdef WITH_SELINUX + #include "key.h" + #include "hostfile.h" +@@ -34,7 +43,6 @@ + #ifdef HAVE_GETSEUSERBYNAME + #include "xmalloc.h" + #endif +-#include "log.h" + #include "port-linux.h" + + #include +@@ -186,3 +194,47 @@ + debug3("%s: done", __func__); + } + #endif /* WITH_SELINUX */ ++ ++#ifdef OOM_ADJUST ++/* Get the out-of-memory adjustment file for the current process */ ++static int ++oom_adj_open(int oflag) ++{ ++ int fd = open("/proc/self/oom_adj", oflag); ++ if (fd < 0) ++ logit("error opening /proc/self/oom_adj: %s", strerror(errno)); ++ return fd; ++} ++ ++/* Get the current OOM adjustment */ ++int ++oom_adj_get(char *buf, size_t maxlen) ++{ ++ ssize_t n; ++ int fd = oom_adj_open(O_RDONLY); ++ if (fd < 0) ++ return -1; ++ n = read(fd, buf, maxlen); ++ if (n < 0) ++ logit("error reading /proc/self/oom_adj: %s", strerror(errno)); ++ else ++ buf[n] = '\0'; ++ close(fd); ++ return n < 0 ? -1 : 0; ++} ++ ++/* Set the current OOM adjustment */ ++int ++oom_adj_set(const char *buf) ++{ ++ ssize_t n; ++ int fd = oom_adj_open(O_WRONLY); ++ if (fd < 0) ++ return -1; ++ n = write(fd, buf, strlen(buf)); ++ if (n < 0) ++ logit("error writing /proc/self/oom_adj: %s", strerror(errno)); ++ close(fd); ++ return n < 0 ? -1 : 0; ++} ++#endif +Index: b/openbsd-compat/port-linux.h +=================================================================== +--- a/openbsd-compat/port-linux.h ++++ b/openbsd-compat/port-linux.h +@@ -25,4 +25,9 @@ + void ssh_selinux_setup_exec_context(char *); + #endif + ++#ifdef OOM_ADJUST ++int oom_adj_get(char *buf, size_t maxlen); ++int oom_adj_set(const char *buf); ++#endif ++ + #endif /* ! _PORT_LINUX_H */ +Index: b/sshd.c +=================================================================== +--- a/sshd.c ++++ b/sshd.c +@@ -254,6 +254,11 @@ + /* Unprivileged user */ + struct passwd *privsep_pw = NULL; + ++#ifdef OOM_ADJUST ++/* Linux out-of-memory killer adjustment */ ++static char oom_adj_save[8]; ++#endif ++ + /* Prototypes for various functions defined later in this file. */ + void destroy_sensitive_data(void); + void demote_sensitive_data(void); +@@ -908,6 +913,31 @@ + debug3("%s: done", __func__); + } + ++#ifdef OOM_ADJUST ++/* ++ * If requested in the environment, tell the Linux kernel's out-of-memory ++ * killer to avoid sshd. The old state will be restored when forking child ++ * processes. ++ */ ++static void ++oom_adjust_startup(void) ++{ ++ const char *oom_adj = getenv("SSHD_OOM_ADJUST"); ++ ++ if (!oom_adj || !*oom_adj) ++ return; ++ oom_adj_get(oom_adj_save, sizeof(oom_adj_save)); ++ oom_adj_set(oom_adj); ++} ++ ++static void ++oom_restore(void) ++{ ++ if (oom_adj_save[0]) ++ oom_adj_set(oom_adj_save); ++} ++#endif ++ + /* Accept a connection from inetd */ + static void + server_accept_inetd(int *sock_in, int *sock_out) +@@ -1670,6 +1700,11 @@ + /* ignore SIGPIPE */ + signal(SIGPIPE, SIG_IGN); + ++#ifdef OOM_ADJUST ++ /* Adjust out-of-memory killer */ ++ oom_adjust_startup(); ++#endif ++ + /* Get a connection, either from inetd or a listening TCP socket */ + if (inetd_flag) { + server_accept_inetd(&sock_in, &sock_out); +@@ -1708,6 +1743,10 @@ + /* This is the child processing a new connection. */ + setproctitle("%s", "[accepted]"); + ++#ifdef OOM_ADJUST ++ oom_restore(); ++#endif ++ + /* + * Create a new session and process group since the 4.4BSD + * setlogin() affects the entire process group. We don't diff --git a/debian/patches/openbsd-docs.patch b/debian/patches/openbsd-docs.patch new file mode 100644 index 000000000..851e6adfb --- /dev/null +++ b/debian/patches/openbsd-docs.patch @@ -0,0 +1,107 @@ +Index: b/moduli.5 +=================================================================== +--- a/moduli.5 ++++ b/moduli.5 +@@ -21,7 +21,7 @@ + .Nd Diffie Hellman moduli + .Sh DESCRIPTION + The +-.Pa /etc/moduli ++.Pa /etc/ssh/moduli + file contains prime numbers and generators for use by + .Xr sshd 8 + in the Diffie-Hellman Group Exchange key exchange method. +@@ -111,7 +111,7 @@ + Diffie Hellman output to sufficiently key the selected symmetric cipher. + .Xr sshd 8 + then randomly selects a modulus from +-.Fa /etc/moduli ++.Fa /etc/ssh/moduli + that best meets the size requirement. + .Pp + .Sh SEE ALSO +Index: b/ssh-keygen.1 +=================================================================== +--- a/ssh-keygen.1 ++++ b/ssh-keygen.1 +@@ -137,9 +137,7 @@ + .Pa ~/.ssh/id_dsa + or + .Pa ~/.ssh/id_rsa . +-Additionally, the system administrator may use this to generate host keys, +-as seen in +-.Pa /etc/rc . ++Additionally, the system administrator may use this to generate host keys. + .Pp + Normally this program generates the key and asks for a file in which + to store the private key. +@@ -282,9 +280,7 @@ + .It Fl q + Silence + .Nm ssh-keygen . +-Used by +-.Pa /etc/rc +-when creating a new key. ++Used by system administration scripts when creating a new key. + .It Fl R Ar hostname + Removes all keys belonging to + .Ar hostname +Index: b/ssh.1 +=================================================================== +--- a/ssh.1 ++++ b/ssh.1 +@@ -749,6 +749,10 @@ + .Sx HISTORY + section of + .Xr ssl 8 ++(on non-OpenBSD systems, see ++.nh ++http://www.openbsd.org/cgi\-bin/man.cgi?query=ssl&sektion=8#HISTORY) ++.hy + contains a brief discussion of the two algorithms. + .Pp + The file +Index: b/sshd.8 +=================================================================== +--- a/sshd.8 ++++ b/sshd.8 +@@ -69,7 +69,7 @@ + .Nm + listens for connections from clients. + It is normally started at boot from +-.Pa /etc/rc . ++.Pa /etc/init.d/ssh . + It forks a new + daemon for each incoming connection. + The forked daemons handle +@@ -781,7 +781,7 @@ + .Xr ssh 1 ) . + It should only be writable by root. + .Pp +-.It /etc/moduli ++.It /etc/ssh/moduli + Contains Diffie-Hellman groups used for the "Diffie-Hellman Group Exchange". + The file format is described in + .Xr moduli 5 . +@@ -877,7 +877,6 @@ + .Xr ssh-vulnkey 1 , + .Xr chroot 2 , + .Xr hosts_access 5 , +-.Xr login.conf 5 , + .Xr moduli 5 , + .Xr sshd_config 5 , + .Xr inetd 8 , +Index: b/sshd_config.5 +=================================================================== +--- a/sshd_config.5 ++++ b/sshd_config.5 +@@ -177,8 +177,7 @@ + By default, no banner is displayed. + .It Cm ChallengeResponseAuthentication + Specifies whether challenge-response authentication is allowed (e.g. via +-PAM or though authentication styles supported in +-.Xr login.conf 5 ) ++PAM). + The default is + .Dq yes . + .It Cm ChrootDirectory diff --git a/debian/patches/package-versioning.patch b/debian/patches/package-versioning.patch new file mode 100644 index 000000000..4c227f054 --- /dev/null +++ b/debian/patches/package-versioning.patch @@ -0,0 +1,41 @@ +Index: b/sshconnect.c +=================================================================== +--- a/sshconnect.c ++++ b/sshconnect.c +@@ -537,7 +537,7 @@ + snprintf(buf, sizeof buf, "SSH-%d.%d-%.100s%s", + compat20 ? PROTOCOL_MAJOR_2 : PROTOCOL_MAJOR_1, + compat20 ? PROTOCOL_MINOR_2 : minor1, +- SSH_VERSION, compat20 ? "\r\n" : "\n"); ++ SSH_RELEASE, compat20 ? "\r\n" : "\n"); + if (roaming_atomicio(vwrite, connection_out, buf, strlen(buf)) + != strlen(buf)) + fatal("write: %.100s", strerror(errno)); +Index: b/sshd.c +=================================================================== +--- a/sshd.c ++++ b/sshd.c +@@ -426,7 +426,7 @@ + minor = PROTOCOL_MINOR_1; + } + snprintf(buf, sizeof buf, "SSH-%d.%d-%.100s%s", major, minor, +- SSH_VERSION, newline); ++ SSH_RELEASE, newline); + server_version_string = xstrdup(buf); + + /* Send our protocol version identification. */ +Index: b/version.h +=================================================================== +--- a/version.h ++++ b/version.h +@@ -3,4 +3,9 @@ + #define SSH_VERSION "OpenSSH_5.3" + + #define SSH_PORTABLE "p1" +-#define SSH_RELEASE SSH_VERSION SSH_PORTABLE ++#define SSH_RELEASE_MINIMUM SSH_VERSION SSH_PORTABLE ++#ifdef SSH_EXTRAVERSION ++#define SSH_RELEASE SSH_RELEASE_MINIMUM " " SSH_EXTRAVERSION ++#else ++#define SSH_RELEASE SSH_RELEASE_MINIMUM ++#endif diff --git a/debian/patches/quieter-signals.patch b/debian/patches/quieter-signals.patch new file mode 100644 index 000000000..bf36b5498 --- /dev/null +++ b/debian/patches/quieter-signals.patch @@ -0,0 +1,17 @@ +Index: b/clientloop.c +=================================================================== +--- a/clientloop.c ++++ b/clientloop.c +@@ -1526,8 +1526,10 @@ + exit_status = 0; + } + +- if (received_signal) +- fatal("Killed by signal %d.", (int) received_signal); ++ if (received_signal) { ++ debug("Killed by signal %d.", (int) received_signal); ++ cleanup_exit((int) received_signal + 128); ++ } + + /* + * In interactive mode (with pseudo tty) display a message indicating diff --git a/debian/patches/scp-quoting.patch b/debian/patches/scp-quoting.patch new file mode 100644 index 000000000..80c1b2a24 --- /dev/null +++ b/debian/patches/scp-quoting.patch @@ -0,0 +1,23 @@ +Index: b/scp.c +=================================================================== +--- a/scp.c ++++ b/scp.c +@@ -168,8 +168,16 @@ + + if (verbose_mode) { + fprintf(stderr, "Executing:"); +- for (i = 0; i < a->num; i++) +- fprintf(stderr, " %s", a->list[i]); ++ for (i = 0; i < a->num; i++) { ++ if (i == 0) ++ fprintf(stderr, " %s", a->list[i]); ++ else ++ /* ++ * TODO: misbehaves if a->list[i] contains a ++ * single quote ++ */ ++ fprintf(stderr, " '%s'", a->list[i]); ++ } + fprintf(stderr, "\n"); + } + if ((pid = fork()) == -1) diff --git a/debian/patches/selinux-autoconf.patch b/debian/patches/selinux-autoconf.patch new file mode 100644 index 000000000..934f885c8 --- /dev/null +++ b/debian/patches/selinux-autoconf.patch @@ -0,0 +1,33 @@ +Index: b/configure +=================================================================== +--- a/configure ++++ b/configure +@@ -28011,6 +28011,8 @@ + $as_echo "$ac_cv_lib_selinux_setexeccon" >&6; } + if test $ac_cv_lib_selinux_setexeccon = yes; then + LIBSELINUX="-lselinux" ++ LIBS="$LIBS -lselinux" ++ + else + { { $as_echo "$as_me:$LINENO: error: SELinux support requires libselinux library" >&5 + $as_echo "$as_me: error: SELinux support requires libselinux library" >&2;} +Index: b/configure.ac +=================================================================== +--- a/configure.ac ++++ b/configure.ac +@@ -3422,9 +3422,12 @@ + AC_DEFINE(WITH_SELINUX,1,[Define if you want SELinux support.]) + SELINUX_MSG="yes" + AC_CHECK_HEADER([selinux/selinux.h], , +- AC_MSG_ERROR(SELinux support requires selinux.h header)) +- AC_CHECK_LIB(selinux, setexeccon, [ LIBSELINUX="-lselinux" ], +- AC_MSG_ERROR(SELinux support requires libselinux library)) ++ AC_MSG_ERROR(SELinux support requires selinux.h header)) ++ AC_CHECK_LIB(selinux, setexeccon, ++ [ LIBSELINUX="-lselinux" ++ LIBS="$LIBS -lselinux" ++ ], ++ AC_MSG_ERROR(SELinux support requires libselinux library)) + SSHDLIBS="$SSHDLIBS $LIBSELINUX" + AC_CHECK_FUNCS(getseuserbyname get_default_context_with_level) + LIBS="$save_LIBS" diff --git a/debian/patches/selinux-fix-chroot-directory.patch b/debian/patches/selinux-fix-chroot-directory.patch new file mode 100644 index 000000000..a69ded59b --- /dev/null +++ b/debian/patches/selinux-fix-chroot-directory.patch @@ -0,0 +1,26 @@ +Index: b/session.c +=================================================================== +--- a/session.c ++++ b/session.c +@@ -1522,6 +1522,10 @@ + # endif /* USE_LIBIAF */ + #endif + ++#ifdef WITH_SELINUX ++ ssh_selinux_setup_exec_context(pw->pw_name); ++#endif ++ + if (options.chroot_directory != NULL && + strcasecmp(options.chroot_directory, "none") != 0) { + tmp = tilde_expand_filename(options.chroot_directory, +@@ -1550,10 +1554,6 @@ + + if (getuid() != pw->pw_uid || geteuid() != pw->pw_uid) + fatal("Failed to set uids to %u.", (u_int) pw->pw_uid); +- +-#ifdef WITH_SELINUX +- ssh_selinux_setup_exec_context(pw->pw_name); +-#endif + } + + static void diff --git a/debian/patches/selinux-role.patch b/debian/patches/selinux-role.patch new file mode 100644 index 000000000..5e2a9ecb6 --- /dev/null +++ b/debian/patches/selinux-role.patch @@ -0,0 +1,283 @@ +Index: b/auth.h +=================================================================== +--- a/auth.h ++++ b/auth.h +@@ -59,6 +59,7 @@ + char *service; + struct passwd *pw; /* set if 'valid' */ + char *style; ++ char *role; + void *kbdintctxt; + void *jpake_ctx; + #ifdef BSD_AUTH +Index: b/auth1.c +=================================================================== +--- a/auth1.c ++++ b/auth1.c +@@ -383,7 +383,7 @@ + do_authentication(Authctxt *authctxt) + { + u_int ulen; +- char *user, *style = NULL; ++ char *user, *style = NULL, *role = NULL; + + /* Get the name of the user that we wish to log in as. */ + packet_read_expect(SSH_CMSG_USER); +@@ -392,11 +392,17 @@ + user = packet_get_string(&ulen); + packet_check_eom(); + ++ if ((role = strchr(user, '/')) != NULL) ++ *role++ = '\0'; ++ + if ((style = strchr(user, ':')) != NULL) + *style++ = '\0'; ++ else if (role && (style = strchr(role, ':')) != NULL) ++ *style++ = '\0'; + + authctxt->user = user; + authctxt->style = style; ++ authctxt->role = role; + + /* Verify that the user is a valid user. */ + if ((authctxt->pw = PRIVSEP(getpwnamallow(user))) != NULL) +Index: b/auth2.c +=================================================================== +--- a/auth2.c ++++ b/auth2.c +@@ -217,7 +217,7 @@ + { + Authctxt *authctxt = ctxt; + Authmethod *m = NULL; +- char *user, *service, *method, *style = NULL; ++ char *user, *service, *method, *style = NULL, *role = NULL; + int authenticated = 0; + + if (authctxt == NULL) +@@ -229,8 +229,13 @@ + debug("userauth-request for user %s service %s method %s", user, service, method); + debug("attempt %d failures %d", authctxt->attempt, authctxt->failures); + ++ if ((role = strchr(user, '/')) != NULL) ++ *role++ = 0; ++ + if ((style = strchr(user, ':')) != NULL) + *style++ = 0; ++ else if (role && (style = strchr(role, ':')) != NULL) ++ *style++ = '\0'; + + if (authctxt->attempt++ == 0) { + /* setup auth context */ +@@ -254,8 +259,9 @@ + use_privsep ? " [net]" : ""); + authctxt->service = xstrdup(service); + authctxt->style = style ? xstrdup(style) : NULL; ++ authctxt->role = role ? xstrdup(role) : NULL; + if (use_privsep) +- mm_inform_authserv(service, style); ++ mm_inform_authserv(service, style, role); + userauth_banner(); + } else if (strcmp(user, authctxt->user) != 0 || + strcmp(service, authctxt->service) != 0) { +Index: b/monitor.c +=================================================================== +--- a/monitor.c ++++ b/monitor.c +@@ -137,6 +137,7 @@ + int mm_answer_pwnamallow(int, Buffer *); + int mm_answer_auth2_read_banner(int, Buffer *); + int mm_answer_authserv(int, Buffer *); ++int mm_answer_authrole(int, Buffer *); + int mm_answer_authpassword(int, Buffer *); + int mm_answer_bsdauthquery(int, Buffer *); + int mm_answer_bsdauthrespond(int, Buffer *); +@@ -215,6 +216,7 @@ + {MONITOR_REQ_SIGN, MON_ONCE, mm_answer_sign}, + {MONITOR_REQ_PWNAM, MON_ONCE, mm_answer_pwnamallow}, + {MONITOR_REQ_AUTHSERV, MON_ONCE, mm_answer_authserv}, ++ {MONITOR_REQ_AUTHROLE, MON_ONCE, mm_answer_authrole}, + {MONITOR_REQ_AUTH2_READ_BANNER, MON_ONCE, mm_answer_auth2_read_banner}, + {MONITOR_REQ_AUTHPASSWORD, MON_AUTH, mm_answer_authpassword}, + #ifdef USE_PAM +@@ -699,6 +701,7 @@ + else { + /* Allow service/style information on the auth context */ + monitor_permit(mon_dispatch, MONITOR_REQ_AUTHSERV, 1); ++ monitor_permit(mon_dispatch, MONITOR_REQ_AUTHROLE, 1); + monitor_permit(mon_dispatch, MONITOR_REQ_AUTH2_READ_BANNER, 1); + } + +@@ -732,14 +735,37 @@ + + authctxt->service = buffer_get_string(m, NULL); + authctxt->style = buffer_get_string(m, NULL); +- debug3("%s: service=%s, style=%s", +- __func__, authctxt->service, authctxt->style); ++ authctxt->role = buffer_get_string(m, NULL); ++ debug3("%s: service=%s, style=%s, role=%s", ++ __func__, authctxt->service, authctxt->style, authctxt->role); + + if (strlen(authctxt->style) == 0) { + xfree(authctxt->style); + authctxt->style = NULL; + } + ++ if (strlen(authctxt->role) == 0) { ++ xfree(authctxt->role); ++ authctxt->role = NULL; ++ } ++ ++ return (0); ++} ++ ++int ++mm_answer_authrole(int sock, Buffer *m) ++{ ++ monitor_permit_authentications(1); ++ ++ authctxt->role = buffer_get_string(m, NULL); ++ debug3("%s: role=%s", ++ __func__, authctxt->role); ++ ++ if (strlen(authctxt->role) == 0) { ++ xfree(authctxt->role); ++ authctxt->role = NULL; ++ } ++ + return (0); + } + +Index: b/monitor.h +=================================================================== +--- a/monitor.h ++++ b/monitor.h +@@ -30,7 +30,7 @@ + + enum monitor_reqtype { + MONITOR_REQ_MODULI, MONITOR_ANS_MODULI, +- MONITOR_REQ_FREE, MONITOR_REQ_AUTHSERV, ++ MONITOR_REQ_FREE, MONITOR_REQ_AUTHSERV, MONITOR_REQ_AUTHROLE, + MONITOR_REQ_SIGN, MONITOR_ANS_SIGN, + MONITOR_REQ_PWNAM, MONITOR_ANS_PWNAM, + MONITOR_REQ_AUTH2_READ_BANNER, MONITOR_ANS_AUTH2_READ_BANNER, +Index: b/monitor_wrap.c +=================================================================== +--- a/monitor_wrap.c ++++ b/monitor_wrap.c +@@ -279,10 +279,10 @@ + return (banner); + } + +-/* Inform the privileged process about service and style */ ++/* Inform the privileged process about service, style, and role */ + + void +-mm_inform_authserv(char *service, char *style) ++mm_inform_authserv(char *service, char *style, char *role) + { + Buffer m; + +@@ -291,11 +291,29 @@ + buffer_init(&m); + buffer_put_cstring(&m, service); + buffer_put_cstring(&m, style ? style : ""); ++ buffer_put_cstring(&m, role ? role : ""); + + mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_AUTHSERV, &m); + + buffer_free(&m); + } ++ ++/* Inform the privileged process about role */ ++ ++void ++mm_inform_authrole(char *role) ++{ ++ Buffer m; ++ ++ debug3("%s entering", __func__); ++ ++ buffer_init(&m); ++ buffer_put_cstring(&m, role ? role : ""); ++ ++ mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_AUTHROLE, &m); ++ ++ buffer_free(&m); ++} + + /* Do the password authentication */ + int +Index: b/monitor_wrap.h +=================================================================== +--- a/monitor_wrap.h ++++ b/monitor_wrap.h +@@ -40,7 +40,8 @@ + int mm_is_monitor(void); + DH *mm_choose_dh(int, int, int); + int mm_key_sign(Key *, u_char **, u_int *, u_char *, u_int); +-void mm_inform_authserv(char *, char *); ++void mm_inform_authserv(char *, char *, char *); ++void mm_inform_authrole(char *); + struct passwd *mm_getpwnamallow(const char *); + char *mm_auth2_read_banner(void); + int mm_auth_password(struct Authctxt *, char *); +Index: b/openbsd-compat/port-linux.c +=================================================================== +--- a/openbsd-compat/port-linux.c ++++ b/openbsd-compat/port-linux.c +@@ -28,6 +28,12 @@ + #include + + #ifdef WITH_SELINUX ++#include "key.h" ++#include "hostfile.h" ++#include "auth.h" ++#ifdef HAVE_GETSEUSERBYNAME ++#include "xmalloc.h" ++#endif + #include "log.h" + #include "port-linux.h" + +@@ -35,6 +41,8 @@ + #include + #include + ++extern Authctxt *the_authctxt; ++ + /* Wrapper around is_selinux_enabled() to log its return value once only */ + int + ssh_selinux_enabled(void) +@@ -53,8 +61,8 @@ + static security_context_t + ssh_selinux_getctxbyname(char *pwname) + { +- security_context_t sc; +- char *sename = NULL, *lvl = NULL; ++ security_context_t sc = NULL; ++ char *sename = NULL, *role = NULL, *lvl = NULL; + int r; + + #ifdef HAVE_GETSEUSERBYNAME +@@ -64,11 +72,20 @@ + sename = pwname; + lvl = NULL; + #endif ++ if (the_authctxt) ++ role = the_authctxt->role; + + #ifdef HAVE_GET_DEFAULT_CONTEXT_WITH_LEVEL +- r = get_default_context_with_level(sename, lvl, NULL, &sc); ++ if (role != NULL && role[0]) ++ r = get_default_context_with_rolelevel(sename, role, lvl, NULL, ++ &sc); ++ else ++ r = get_default_context_with_level(sename, lvl, NULL, &sc); + #else +- r = get_default_context(sename, NULL, &sc); ++ if (role != NULL && role[0]) ++ r = get_default_context_with_role(sename, role, NULL, &sc); ++ else ++ r = get_default_context(sename, NULL, &sc); + #endif + + if (r != 0) { diff --git a/debian/patches/series b/debian/patches/series new file mode 100644 index 000000000..fa74a5ee6 --- /dev/null +++ b/debian/patches/series @@ -0,0 +1,59 @@ +# GSSAPI +gssapi.patch +gssapi-autoconf.patch +old-gssapi.patch + +# Autotools +config-guess-sub.patch + +# SELinux +selinux-role.patch +selinux-autoconf.patch +selinux-fix-chroot-directory.patch + +# Key blacklisting +ssh-vulnkey.patch + +# Keepalive handling +ssh1-keepalive.patch +keepalive-extensions.patch + +# Linux OOM handling +oom-adjust.patch + +# Message adjustments +syslog-level-silent.patch +quieter-signals.patch +helpful-wait-terminate.patch +banner-noslash.patch +keyfile-debug.patch + +# Miscellaneous bug fixes +gnome-ssh-askpass2-link.patch +user-group-modes.patch +epfnosupport.patch +scp-quoting.patch +shell-path.patch +ssh-copy-id-status-check.patch +ssh-copy-id-trailing-colons.patch +no-constraint-fallback.patch +sshd-ignore-sighup.patch + +# Versioning +package-versioning.patch +debian-banner.patch + +# File system layout +authorized-keys-man-symlink.patch +lintian-symlink-pickiness.patch + +# Documentation +openbsd-docs.patch +ssh-argv0.patch +doc-connection-sharing.patch +doc-hash-tab-completion.patch + +# Debian-specific configuration +no-openssl-version-check.patch +gnome-ssh-askpass2-icon.patch +debian-config.patch diff --git a/debian/patches/shell-path.patch b/debian/patches/shell-path.patch new file mode 100644 index 000000000..9501972b9 --- /dev/null +++ b/debian/patches/shell-path.patch @@ -0,0 +1,22 @@ +Index: b/sshconnect.c +=================================================================== +--- a/sshconnect.c ++++ b/sshconnect.c +@@ -139,7 +139,7 @@ + + /* Execute the proxy command. Note that we gave up any + extra privileges above. */ +- execv(argv[0], argv); ++ execvp(argv[0], argv); + perror(argv[0]); + exit(1); + } +@@ -1167,7 +1167,7 @@ + pid = fork(); + if (pid == 0) { + debug3("Executing %s -c \"%s\"", shell, args); +- execl(shell, shell, "-c", args, (char *)NULL); ++ execlp(shell, shell, "-c", args, (char *)NULL); + error("Couldn't execute %s -c \"%s\": %s", + shell, args, strerror(errno)); + _exit(1); diff --git a/debian/patches/ssh-argv0.patch b/debian/patches/ssh-argv0.patch new file mode 100644 index 000000000..0d5412374 --- /dev/null +++ b/debian/patches/ssh-argv0.patch @@ -0,0 +1,12 @@ +Index: b/ssh.1 +=================================================================== +--- a/ssh.1 ++++ b/ssh.1 +@@ -1405,6 +1405,7 @@ + .Xr sftp 1 , + .Xr ssh-add 1 , + .Xr ssh-agent 1 , ++.Xr ssh-argv0 1 , + .Xr ssh-keygen 1 , + .Xr ssh-keyscan 1 , + .Xr ssh-vulnkey 1 , diff --git a/debian/patches/ssh-copy-id-status-check.patch b/debian/patches/ssh-copy-id-status-check.patch new file mode 100644 index 000000000..4f7fb4dfb --- /dev/null +++ b/debian/patches/ssh-copy-id-status-check.patch @@ -0,0 +1,13 @@ +Index: b/contrib/ssh-copy-id +=================================================================== +--- a/contrib/ssh-copy-id ++++ b/contrib/ssh-copy-id +@@ -19,7 +19,7 @@ + shift # and this should leave $1 as the target name + fi + else +- if [ x$SSH_AUTH_SOCK != x ] ; then ++ if [ x$SSH_AUTH_SOCK != x ] && ssh-add -L >/dev/null 2>&1; then + GET_ID="$GET_ID ssh-add -L" + fi + fi diff --git a/debian/patches/ssh-copy-id-trailing-colons.patch b/debian/patches/ssh-copy-id-trailing-colons.patch new file mode 100644 index 000000000..624e8d5a7 --- /dev/null +++ b/debian/patches/ssh-copy-id-trailing-colons.patch @@ -0,0 +1,17 @@ +Index: b/contrib/ssh-copy-id +=================================================================== +--- a/contrib/ssh-copy-id ++++ b/contrib/ssh-copy-id +@@ -38,10 +38,10 @@ + exit 1 + fi + +-{ eval "$GET_ID" ; } | ssh $1 "umask 077; test -d .ssh || mkdir .ssh ; cat >> .ssh/authorized_keys" || exit 1 ++{ eval "$GET_ID" ; } | ssh ${1%:} "umask 077; test -d .ssh || mkdir .ssh ; cat >> .ssh/authorized_keys" || exit 1 + + cat <rsa->n), bits); + ++ if (reject_blacklisted_key(key, 0) == 1) ++ continue; ++ + /* We have found the desired key. */ + /* + * If our options do not allow this key to be used, +Index: b/auth.c +=================================================================== +--- a/auth.c ++++ b/auth.c +@@ -59,6 +59,7 @@ + #include "servconf.h" + #include "key.h" + #include "hostfile.h" ++#include "authfile.h" + #include "auth.h" + #include "auth-options.h" + #include "canohost.h" +@@ -398,6 +399,38 @@ + return host_status; + } + ++int ++reject_blacklisted_key(Key *key, int hostkey) ++{ ++ char *fp; ++ ++ if (blacklisted_key(key, &fp) != 1) ++ return 0; ++ ++ if (options.permit_blacklisted_keys) { ++ if (hostkey) ++ error("Host key %s blacklisted (see " ++ "ssh-vulnkey(1)); continuing anyway", fp); ++ else ++ logit("Public key %s from %s blacklisted (see " ++ "ssh-vulnkey(1)); continuing anyway", ++ fp, get_remote_ipaddr()); ++ xfree(fp); ++ } else { ++ if (hostkey) ++ error("Host key %s blacklisted (see " ++ "ssh-vulnkey(1))", fp); ++ else ++ logit("Public key %s from %s blacklisted (see " ++ "ssh-vulnkey(1))", ++ fp, get_remote_ipaddr()); ++ xfree(fp); ++ return 1; ++ } ++ ++ return 0; ++} ++ + + /* + * Check a given file for security. This is defined as all components +Index: b/auth.h +=================================================================== +--- a/auth.h ++++ b/auth.h +@@ -178,6 +178,8 @@ + check_key_in_hostfiles(struct passwd *, Key *, const char *, + const char *, const char *); + ++int reject_blacklisted_key(Key *, int); ++ + /* hostkey handling */ + Key *get_hostkey_by_index(int); + Key *get_hostkey_by_type(int); +Index: b/auth2-hostbased.c +=================================================================== +--- a/auth2-hostbased.c ++++ b/auth2-hostbased.c +@@ -145,6 +145,9 @@ + HostStatus host_status; + int len; + ++ if (reject_blacklisted_key(key, 0) == 1) ++ return 0; ++ + resolvedname = get_canonical_hostname(options.use_dns); + ipaddr = get_remote_ipaddr(); + +Index: b/auth2-pubkey.c +=================================================================== +--- a/auth2-pubkey.c ++++ b/auth2-pubkey.c +@@ -254,6 +254,9 @@ + int success; + char *file; + ++ if (reject_blacklisted_key(key, 0) == 1) ++ return 0; ++ + file = authorized_keys_file(pw); + success = user_key_allowed2(pw, key, file); + xfree(file); +Index: b/authfile.c +=================================================================== +--- a/authfile.c ++++ b/authfile.c +@@ -65,6 +65,7 @@ + #include "rsa.h" + #include "misc.h" + #include "atomicio.h" ++#include "pathnames.h" + + /* Version identification string for SSH v1 identity files. */ + static const char authfile_id_string[] = +@@ -677,3 +678,140 @@ + key_free(pub); + return NULL; + } ++ ++/* Scan a blacklist of known-vulnerable keys in blacklist_file. */ ++static int ++blacklisted_key_in_file(const Key *key, const char *blacklist_file, char **fp) ++{ ++ int fd = -1; ++ char *dgst_hex = NULL; ++ char *dgst_packed = NULL, *p; ++ int i; ++ size_t line_len; ++ struct stat st; ++ char buf[256]; ++ off_t start, lower, upper; ++ int ret = 0; ++ ++ debug("Checking blacklist file %s", blacklist_file); ++ fd = open(blacklist_file, O_RDONLY); ++ if (fd < 0) { ++ ret = -1; ++ goto out; ++ } ++ ++ dgst_hex = key_fingerprint(key, SSH_FP_MD5, SSH_FP_HEX); ++ /* Remove all colons */ ++ dgst_packed = xcalloc(1, strlen(dgst_hex) + 1); ++ for (i = 0, p = dgst_packed; dgst_hex[i]; i++) ++ if (dgst_hex[i] != ':') ++ *p++ = dgst_hex[i]; ++ /* Only compare least-significant 80 bits (to keep the blacklist ++ * size down) ++ */ ++ line_len = strlen(dgst_packed + 12); ++ if (line_len > 32) ++ goto out; ++ ++ /* Skip leading comments */ ++ start = 0; ++ for (;;) { ++ ssize_t r; ++ char *newline; ++ ++ r = atomicio(read, fd, buf, sizeof(buf)); ++ if (r <= 0) ++ goto out; ++ if (buf[0] != '#') ++ break; ++ ++ newline = memchr(buf, '\n', sizeof(buf)); ++ if (!newline) ++ goto out; ++ start += newline + 1 - buf; ++ if (lseek(fd, start, SEEK_SET) < 0) ++ goto out; ++ } ++ ++ /* Initialise binary search record numbers */ ++ if (fstat(fd, &st) < 0) ++ goto out; ++ lower = 0; ++ upper = (st.st_size - start) / (line_len + 1); ++ ++ while (lower != upper) { ++ off_t cur; ++ int cmp; ++ ++ cur = lower + (upper - lower) / 2; ++ ++ /* Read this line and compare to digest; this is ++ * overflow-safe since cur < max(off_t) / (line_len + 1) */ ++ if (lseek(fd, start + cur * (line_len + 1), SEEK_SET) < 0) ++ break; ++ if (atomicio(read, fd, buf, line_len) != line_len) ++ break; ++ cmp = memcmp(buf, dgst_packed + 12, line_len); ++ if (cmp < 0) { ++ if (cur == lower) ++ break; ++ lower = cur; ++ } else if (cmp > 0) { ++ if (cur == upper) ++ break; ++ upper = cur; ++ } else { ++ debug("Found %s in blacklist", dgst_hex); ++ ret = 1; ++ break; ++ } ++ } ++ ++out: ++ if (dgst_packed) ++ xfree(dgst_packed); ++ if (ret != 1 && dgst_hex) { ++ xfree(dgst_hex); ++ dgst_hex = NULL; ++ } ++ if (fp) ++ *fp = dgst_hex; ++ if (fd >= 0) ++ close(fd); ++ return ret; ++} ++ ++/* ++ * Scan blacklists of known-vulnerable keys. If a vulnerable key is found, ++ * its fingerprint is returned in *fp, unless fp is NULL. ++ */ ++int ++blacklisted_key(const Key *key, char **fp) ++{ ++ Key *public; ++ char *blacklist_file; ++ int ret, ret2; ++ ++ public = key_demote(key); ++ if (public->type == KEY_RSA1) ++ public->type = KEY_RSA; ++ ++ xasprintf(&blacklist_file, "%s.%s-%u", ++ _PATH_BLACKLIST, key_type(public), key_size(public)); ++ ret = blacklisted_key_in_file(public, blacklist_file, fp); ++ xfree(blacklist_file); ++ if (ret > 0) { ++ key_free(public); ++ return ret; ++ } ++ ++ xasprintf(&blacklist_file, "%s.%s-%u", ++ _PATH_BLACKLIST_CONFIG, key_type(public), key_size(public)); ++ ret2 = blacklisted_key_in_file(public, blacklist_file, fp); ++ xfree(blacklist_file); ++ if (ret2 > ret) ++ ret = ret2; ++ ++ key_free(public); ++ return ret; ++} +Index: b/authfile.h +=================================================================== +--- a/authfile.h ++++ b/authfile.h +@@ -23,4 +23,6 @@ + Key *key_load_private_pem(int, int, const char *, char **); + int key_perm_ok(int, const char *); + ++int blacklisted_key(const Key *key, char **fp); ++ + #endif +Index: b/pathnames.h +=================================================================== +--- a/pathnames.h ++++ b/pathnames.h +@@ -18,6 +18,10 @@ + #define SSHDIR ETCDIR "/ssh" + #endif + ++#ifndef _PATH_SSH_DATADIR ++#define _PATH_SSH_DATADIR "/usr/share/ssh" ++#endif ++ + #ifndef _PATH_SSH_PIDDIR + #define _PATH_SSH_PIDDIR "/var/run" + #endif +@@ -43,6 +47,9 @@ + /* Backwards compatibility */ + #define _PATH_DH_PRIMES SSHDIR "/primes" + ++#define _PATH_BLACKLIST _PATH_SSH_DATADIR "/blacklist" ++#define _PATH_BLACKLIST_CONFIG SSHDIR "/blacklist" ++ + #ifndef _PATH_SSH_PROGRAM + #define _PATH_SSH_PROGRAM "/usr/bin/ssh" + #endif +Index: b/readconf.c +=================================================================== +--- a/readconf.c ++++ b/readconf.c +@@ -123,6 +123,7 @@ + oGlobalKnownHostsFile2, oUserKnownHostsFile2, oPubkeyAuthentication, + oKbdInteractiveAuthentication, oKbdInteractiveDevices, oHostKeyAlias, + oDynamicForward, oPreferredAuthentications, oHostbasedAuthentication, ++ oUseBlacklistedKeys, + oHostKeyAlgorithms, oBindAddress, oSmartcardDevice, + oClearAllForwardings, oNoHostAuthenticationForLocalhost, + oEnableSSHKeysign, oRekeyLimit, oVerifyHostKeyDNS, oConnectTimeout, +@@ -152,6 +153,7 @@ + { "passwordauthentication", oPasswordAuthentication }, + { "kbdinteractiveauthentication", oKbdInteractiveAuthentication }, + { "kbdinteractivedevices", oKbdInteractiveDevices }, ++ { "useblacklistedkeys", oUseBlacklistedKeys }, + { "rsaauthentication", oRSAAuthentication }, + { "pubkeyauthentication", oPubkeyAuthentication }, + { "dsaauthentication", oPubkeyAuthentication }, /* alias */ +@@ -459,6 +461,10 @@ + intptr = &options->challenge_response_authentication; + goto parse_flag; + ++ case oUseBlacklistedKeys: ++ intptr = &options->use_blacklisted_keys; ++ goto parse_flag; ++ + case oGssAuthentication: + intptr = &options->gss_authentication; + goto parse_flag; +@@ -1048,6 +1054,7 @@ + options->kbd_interactive_devices = NULL; + options->rhosts_rsa_authentication = -1; + options->hostbased_authentication = -1; ++ options->use_blacklisted_keys = -1; + options->batch_mode = -1; + options->check_host_ip = -1; + options->strict_host_key_checking = -1; +@@ -1150,6 +1157,8 @@ + options->rhosts_rsa_authentication = 0; + if (options->hostbased_authentication == -1) + options->hostbased_authentication = 0; ++ if (options->use_blacklisted_keys == -1) ++ options->use_blacklisted_keys = 0; + if (options->batch_mode == -1) + options->batch_mode = 0; + if (options->check_host_ip == -1) +Index: b/readconf.h +=================================================================== +--- a/readconf.h ++++ b/readconf.h +@@ -54,6 +54,7 @@ + int kbd_interactive_authentication; /* Try keyboard-interactive auth. */ + char *kbd_interactive_devices; /* Keyboard-interactive auth devices. */ + int zero_knowledge_password_authentication; /* Try jpake */ ++ int use_blacklisted_keys; /* If true, send */ + int batch_mode; /* Batch mode: do not ask for passwords. */ + int check_host_ip; /* Also keep track of keys for IP address */ + int strict_host_key_checking; /* Strict host key checking. */ +Index: b/servconf.c +=================================================================== +--- a/servconf.c ++++ b/servconf.c +@@ -99,6 +99,7 @@ + options->password_authentication = -1; + options->kbd_interactive_authentication = -1; + options->challenge_response_authentication = -1; ++ options->permit_blacklisted_keys = -1; + options->permit_empty_passwd = -1; + options->permit_user_env = -1; + options->use_login = -1; +@@ -227,6 +228,8 @@ + options->kbd_interactive_authentication = 0; + if (options->challenge_response_authentication == -1) + options->challenge_response_authentication = 1; ++ if (options->permit_blacklisted_keys == -1) ++ options->permit_blacklisted_keys = 0; + if (options->permit_empty_passwd == -1) + options->permit_empty_passwd = 0; + if (options->permit_user_env == -1) +@@ -302,7 +305,7 @@ + sListenAddress, sAddressFamily, + sPrintMotd, sPrintLastLog, sIgnoreRhosts, + sX11Forwarding, sX11DisplayOffset, sX11UseLocalhost, +- sStrictModes, sEmptyPasswd, sTCPKeepAlive, ++ sStrictModes, sPermitBlacklistedKeys, sEmptyPasswd, sTCPKeepAlive, + sPermitUserEnvironment, sUseLogin, sAllowTcpForwarding, sCompression, + sAllowUsers, sDenyUsers, sAllowGroups, sDenyGroups, + sIgnoreUserKnownHosts, sCiphers, sMacs, sProtocol, sPidFile, +@@ -410,6 +413,7 @@ + { "x11uselocalhost", sX11UseLocalhost, SSHCFG_ALL }, + { "xauthlocation", sXAuthLocation, SSHCFG_GLOBAL }, + { "strictmodes", sStrictModes, SSHCFG_GLOBAL }, ++ { "permitblacklistedkeys", sPermitBlacklistedKeys, SSHCFG_GLOBAL }, + { "permitemptypasswords", sEmptyPasswd, SSHCFG_ALL }, + { "permituserenvironment", sPermitUserEnvironment, SSHCFG_GLOBAL }, + { "uselogin", sUseLogin, SSHCFG_GLOBAL }, +@@ -976,6 +980,10 @@ + intptr = &options->tcp_keep_alive; + goto parse_flag; + ++ case sPermitBlacklistedKeys: ++ intptr = &options->permit_blacklisted_keys; ++ goto parse_flag; ++ + case sEmptyPasswd: + intptr = &options->permit_empty_passwd; + goto parse_flag; +@@ -1643,6 +1651,7 @@ + dump_cfg_fmtint(sX11UseLocalhost, o->x11_use_localhost); + dump_cfg_fmtint(sStrictModes, o->strict_modes); + dump_cfg_fmtint(sTCPKeepAlive, o->tcp_keep_alive); ++ dump_cfg_fmtint(sPermitBlacklistedKeys, o->permit_blacklisted_keys); + dump_cfg_fmtint(sEmptyPasswd, o->permit_empty_passwd); + dump_cfg_fmtint(sPermitUserEnvironment, o->permit_user_env); + dump_cfg_fmtint(sUseLogin, o->use_login); +Index: b/servconf.h +=================================================================== +--- a/servconf.h ++++ b/servconf.h +@@ -101,6 +101,7 @@ + int challenge_response_authentication; + int zero_knowledge_password_authentication; + /* If true, permit jpake auth */ ++ int permit_blacklisted_keys; /* If true, permit */ + int permit_empty_passwd; /* If false, do not permit empty + * passwords. */ + int permit_user_env; /* If true, read ~/.ssh/environment */ +Index: b/ssh-add.1 +=================================================================== +--- a/ssh-add.1 ++++ b/ssh-add.1 +@@ -75,6 +75,10 @@ + .Nm + to work. + .Pp ++Any keys recorded in the blacklist of known-compromised keys (see ++.Xr ssh-vulnkey 1 ) ++will be refused. ++.Pp + The options are as follows: + .Bl -tag -width Ds + .It Fl c +@@ -174,6 +178,7 @@ + .Xr ssh 1 , + .Xr ssh-agent 1 , + .Xr ssh-keygen 1 , ++.Xr ssh-vulnkey 1 , + .Xr sshd 8 + .Sh AUTHORS + OpenSSH is a derivative of the original and free +Index: b/ssh-add.c +=================================================================== +--- a/ssh-add.c ++++ b/ssh-add.c +@@ -139,7 +139,7 @@ + add_file(AuthenticationConnection *ac, const char *filename) + { + Key *private; +- char *comment = NULL; ++ char *comment = NULL, *fp; + char msg[1024]; + int fd, perms_ok, ret = -1; + +@@ -184,6 +184,14 @@ + "Bad passphrase, try again for %.200s: ", comment); + } + } ++ if (blacklisted_key(private, &fp) == 1) { ++ fprintf(stderr, "Public key %s blacklisted (see " ++ "ssh-vulnkey(1)); refusing to add it\n", fp); ++ xfree(fp); ++ key_free(private); ++ xfree(comment); ++ return -1; ++ } + + if (ssh_add_identity_constrained(ac, private, comment, lifetime, + confirm)) { +Index: b/ssh-keygen.1 +=================================================================== +--- a/ssh-keygen.1 ++++ b/ssh-keygen.1 +@@ -451,6 +451,7 @@ + .Xr ssh 1 , + .Xr ssh-add 1 , + .Xr ssh-agent 1 , ++.Xr ssh-vulnkey 1 , + .Xr moduli 5 , + .Xr sshd 8 + .Rs +Index: b/ssh-vulnkey.1 +=================================================================== +--- /dev/null ++++ b/ssh-vulnkey.1 +@@ -0,0 +1,242 @@ ++.\" Copyright (c) 2008 Canonical Ltd. All rights reserved. ++.\" ++.\" Redistribution and use in source and binary forms, with or without ++.\" modification, are permitted provided that the following conditions ++.\" are met: ++.\" 1. Redistributions of source code must retain the above copyright ++.\" notice, this list of conditions and the following disclaimer. ++.\" 2. Redistributions in binary form must reproduce the above copyright ++.\" notice, this list of conditions and the following disclaimer in the ++.\" documentation and/or other materials provided with the distribution. ++.\" ++.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR ++.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES ++.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. ++.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, ++.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT ++.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ++.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ++.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ++.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF ++.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++.\" ++.Dd $Mdocdate: May 12 2008 $ ++.Dt SSH-VULNKEY 1 ++.Os ++.Sh NAME ++.Nm ssh-vulnkey ++.Nd check blacklist of compromised keys ++.Sh SYNOPSIS ++.Nm ++.Op Fl q | Fl v ++.Ar file ... ++.Nm ++.Fl a ++.Sh DESCRIPTION ++.Nm ++checks a key against a blacklist of compromised keys. ++.Pp ++A substantial number of keys are known to have been generated using a broken ++version of OpenSSL distributed by Debian which failed to seed its random ++number generator correctly. ++Keys generated using these OpenSSL versions should be assumed to be ++compromised. ++This tool may be useful in checking for such keys. ++.Pp ++Keys that are compromised cannot be repaired; replacements must be generated ++using ++.Xr ssh-keygen 1 . ++Make sure to update ++.Pa authorized_keys ++files on all systems where compromised keys were permitted to authenticate. ++.Pp ++The argument list will be interpreted as a list of paths to public key files ++or ++.Pa authorized_keys ++files. ++If no suitable file is found at a given path, ++.Nm ++will append ++.Pa .pub ++and retry, in case it was given a private key file. ++If no files are given as arguments, ++.Nm ++will check ++.Pa ~/.ssh/id_rsa , ++.Pa ~/.ssh/id_dsa , ++.Pa ~/.ssh/identity , ++.Pa ~/.ssh/authorized_keys ++and ++.Pa ~/.ssh/authorized_keys2 , ++as well as the system's host keys if readable. ++.Pp ++If ++.Dq - ++is given as an argument, ++.Nm ++will read from standard input. ++This can be used to process output from ++.Xr ssh-keyscan 1 , ++for example: ++.Pp ++.Dl $ ssh-keyscan -t rsa remote.example.org | ssh-vulnkey - ++.Pp ++Unless the ++.Cm PermitBlacklistedKeys ++option is used, ++.Xr sshd 8 ++will reject attempts to authenticate with keys in the compromised list. ++.Pp ++The output from ++.Nm ++looks like this: ++.Pp ++.Bd -literal -offset indent ++/etc/ssh/ssh_host_key:1: COMPROMISED: RSA1 2048 xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx root@host ++/home/user/.ssh/id_dsa:1: Not blacklisted: DSA 1024 xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx /home/user/.ssh/id_dsa.pub ++/home/user/.ssh/authorized_keys:3: Unknown (blacklist file not installed): RSA 1024 xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx user@host ++.Ed ++.Pp ++Each line is of the following format (any lines beginning with ++.Dq # ++should be ignored by scripts): ++.Pp ++.Dl Ar filename : Ns Ar line : Ar status : Ar type Ar size Ar fingerprint Ar comment ++.Pp ++It is important to distinguish between the possible values of ++.Ar status : ++.Pp ++.Bl -tag -width Ds ++.It COMPROMISED ++These keys are listed in a blacklist file, normally because their ++corresponding private keys are well-known. ++Replacements must be generated using ++.Xr ssh-keygen 1 . ++.It Not blacklisted ++A blacklist file exists for this key type and size, but this key is not ++listed in it. ++Unless there is some particular reason to believe otherwise, this key ++may be used safely. ++(Note that DSA keys used with the broken version of OpenSSL distributed ++by Debian may be compromised in the event that anyone captured a network ++trace, even if they were generated with a secure version of OpenSSL.) ++.It Unknown (blacklist file not installed) ++No blacklist file exists for this key type and size. ++You should find a suitable published blacklist and install it before ++deciding whether this key is safe to use. ++.El ++.Pp ++The options are as follows: ++.Bl -tag -width Ds ++.It Fl a ++Check keys of all users on the system. ++You will typically need to run ++.Nm ++as root to use this option. ++For each user, ++.Nm ++will check ++.Pa ~/.ssh/id_rsa , ++.Pa ~/.ssh/id_dsa , ++.Pa ~/.ssh/identity , ++.Pa ~/.ssh/authorized_keys ++and ++.Pa ~/.ssh/authorized_keys2 . ++It will also check the system's host keys. ++.It Fl q ++Quiet mode. ++Normally, ++.Nm ++outputs the fingerprint of each key scanned, with a description of its ++status. ++This option suppresses that output. ++.It Fl v ++Verbose mode. ++Normally, ++.Nm ++does not output anything for keys that are not listed in their corresponding ++blacklist file (although it still produces output for keys for which there ++is no blacklist file, since their status is unknown). ++This option causes ++.Nm ++to produce output for all keys. ++.El ++.Sh EXIT STATUS ++.Nm ++will exit zero if any of the given keys were in the compromised list, ++otherwise non-zero. ++.Sh BLACKLIST FILE FORMAT ++The blacklist file may start with comments, on lines starting with ++.Dq # . ++After these initial comments, it must follow a strict format: ++.Pp ++.Bl -bullet -offset indent -compact ++.It ++All the lines must be exactly the same length (20 characters followed by a ++newline) and must be in sorted order. ++.It ++Each line must consist of the lower-case hexadecimal MD5 key fingerprint, ++without colons, and with the first 12 characters removed (that is, the least ++significant 80 bits of the fingerprint). ++.El ++.Pp ++The key fingerprint may be generated using ++.Xr ssh-keygen 1 : ++.Pp ++.Dl $ ssh-keygen -l -f /path/to/key ++.Pp ++This strict format is necessary to allow the blacklist file to be checked ++quickly, using a binary-search algorithm. ++.Sh FILES ++.Bl -tag -width Ds ++.It Pa ~/.ssh/id_rsa ++If present, contains the protocol version 2 RSA authentication identity of ++the user. ++.It Pa ~/.ssh/id_dsa ++If present, contains the protocol version 2 DSA authentication identity of ++the user. ++.It Pa ~/.ssh/identity ++If present, contains the protocol version 1 RSA authentication identity of ++the user. ++.It Pa ~/.ssh/authorized_keys ++If present, lists the public keys (RSA/DSA) that can be used for logging in ++as this user. ++.It Pa ~/.ssh/authorized_keys2 ++Obsolete name for ++.Pa ~/.ssh/authorized_keys . ++This file may still be present on some old systems, but should not be ++created if it is missing. ++.It Pa /etc/ssh/ssh_host_rsa_key ++If present, contains the protocol version 2 RSA identity of the system. ++.It Pa /etc/ssh/ssh_host_dsa_key ++If present, contains the protocol version 2 DSA identity of the system. ++.It Pa /etc/ssh/ssh_host_key ++If present, contains the protocol version 1 RSA identity of the system. ++.It Pa /usr/share/ssh/blacklist. Ns Ar TYPE Ns Pa - Ns Ar LENGTH ++If present, lists the blacklisted keys of type ++.Ar TYPE ++.Pf ( Dq RSA ++or ++.Dq DSA ) ++and bit length ++.Ar LENGTH . ++The format of this file is described above. ++RSA1 keys are converted to RSA before being checked in the blacklist. ++Note that the fingerprints of RSA1 keys are computed differently, so you ++will not be able to find them in the blacklist by hand. ++.It Pa /etc/ssh/blacklist. Ns Ar TYPE Ns Pa - Ns Ar LENGTH ++Same as ++.Pa /usr/share/ssh/blacklist. Ns Ar TYPE Ns Pa - Ns Ar LENGTH , ++but may be edited by the system administrator to add new blacklist entries. ++.El ++.Sh SEE ALSO ++.Xr ssh-keygen 1 , ++.Xr sshd 8 ++.Sh AUTHORS ++.An -nosplit ++.An Colin Watson Aq cjwatson@ubuntu.com ++.Pp ++Florian Weimer suggested the option to check keys of all users, and the idea ++of processing ++.Xr ssh-keyscan 1 ++output. +Index: b/ssh-vulnkey.c +=================================================================== +--- /dev/null ++++ b/ssh-vulnkey.c +@@ -0,0 +1,388 @@ ++/* ++ * Copyright (c) 2008 Canonical Ltd. All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES ++ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. ++ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, ++ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT ++ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ++ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ++ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ++ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF ++ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++#include "includes.h" ++ ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include "xmalloc.h" ++#include "ssh.h" ++#include "log.h" ++#include "key.h" ++#include "authfile.h" ++#include "pathnames.h" ++#include "uidswap.h" ++#include "misc.h" ++ ++extern char *__progname; ++ ++/* Default files to check */ ++static char *default_host_files[] = { ++ _PATH_HOST_RSA_KEY_FILE, ++ _PATH_HOST_DSA_KEY_FILE, ++ _PATH_HOST_KEY_FILE, ++ NULL ++}; ++static char *default_files[] = { ++ _PATH_SSH_CLIENT_ID_RSA, ++ _PATH_SSH_CLIENT_ID_DSA, ++ _PATH_SSH_CLIENT_IDENTITY, ++ _PATH_SSH_USER_PERMITTED_KEYS, ++ _PATH_SSH_USER_PERMITTED_KEYS2, ++ NULL ++}; ++ ++static int verbosity = 0; ++ ++static int some_keys = 0; ++static int some_unknown = 0; ++static int some_compromised = 0; ++ ++static void ++usage(void) ++{ ++ fprintf(stderr, "usage: %s [-aqv] [file ...]\n", __progname); ++ fprintf(stderr, "Options:\n"); ++ fprintf(stderr, " -a Check keys of all users.\n"); ++ fprintf(stderr, " -q Quiet mode.\n"); ++ fprintf(stderr, " -v Verbose mode.\n"); ++ exit(1); ++} ++ ++void ++describe_key(const char *filename, u_long linenum, const char *msg, ++ const Key *key, const char *comment, int min_verbosity) ++{ ++ char *fp; ++ ++ fp = key_fingerprint(key, SSH_FP_MD5, SSH_FP_HEX); ++ if (verbosity >= min_verbosity) { ++ if (strchr(filename, ':')) ++ printf("\"%s\"", filename); ++ else ++ printf("%s", filename); ++ printf(":%lu: %s: %s %u %s %s\n", linenum, msg, ++ key_type(key), key_size(key), fp, comment); ++ } ++ xfree(fp); ++} ++ ++int ++do_key(const char *filename, u_long linenum, ++ const Key *key, const char *comment) ++{ ++ Key *public; ++ int blacklist_status; ++ int ret = 1; ++ ++ some_keys = 1; ++ ++ public = key_demote(key); ++ if (public->type == KEY_RSA1) ++ public->type = KEY_RSA; ++ ++ blacklist_status = blacklisted_key(public, NULL); ++ if (blacklist_status == -1) { ++ describe_key(filename, linenum, ++ "Unknown (blacklist file not installed)", key, comment, 0); ++ some_unknown = 1; ++ } else if (blacklist_status == 1) { ++ describe_key(filename, linenum, ++ "COMPROMISED", key, comment, 0); ++ some_compromised = 1; ++ ret = 0; ++ } else ++ describe_key(filename, linenum, ++ "Not blacklisted", key, comment, 1); ++ ++ key_free(public); ++ ++ return ret; ++} ++ ++int ++do_filename(const char *filename, int quiet_open) ++{ ++ FILE *f; ++ char line[SSH_MAX_PUBKEY_BYTES]; ++ char *cp; ++ u_long linenum = 0; ++ Key *key; ++ char *comment = NULL; ++ int found = 0, ret = 1; ++ ++ /* Copy much of key_load_public's logic here so that we can read ++ * several keys from a single file (e.g. authorized_keys). ++ */ ++ ++ if (strcmp(filename, "-") != 0) { ++ int save_errno; ++ f = fopen(filename, "r"); ++ save_errno = errno; ++ if (!f) { ++ char pubfile[MAXPATHLEN]; ++ if (strlcpy(pubfile, filename, sizeof pubfile) < ++ sizeof(pubfile) && ++ strlcat(pubfile, ".pub", sizeof pubfile) < ++ sizeof(pubfile)) ++ f = fopen(pubfile, "r"); ++ } ++ errno = save_errno; /* earlier errno is more useful */ ++ if (!f) { ++ if (!quiet_open) ++ perror(filename); ++ return -1; ++ } ++ if (verbosity > 0) ++ printf("# %s\n", filename); ++ } else ++ f = stdin; ++ while (read_keyfile_line(f, filename, line, sizeof(line), ++ &linenum) != -1) { ++ int i; ++ char *space; ++ int type; ++ char *end; ++ ++ /* Chop trailing newline. */ ++ i = strlen(line) - 1; ++ if (line[i] == '\n') ++ line[i] = '\0'; ++ ++ /* Skip leading whitespace, empty and comment lines. */ ++ for (cp = line; *cp == ' ' || *cp == '\t'; cp++) ++ ; ++ if (!*cp || *cp == '\n' || *cp == '#') ++ continue; ++ ++ /* Cope with ssh-keyscan output and options in ++ * authorized_keys files. ++ */ ++ space = strchr(cp, ' '); ++ if (!space) ++ continue; ++ *space = '\0'; ++ type = key_type_from_name(cp); ++ *space = ' '; ++ /* Leading number (RSA1) or valid type (RSA/DSA) indicates ++ * that we have no host name or options to skip. ++ */ ++ if ((strtol(cp, &end, 10) == 0 || *end != ' ') && ++ type == KEY_UNSPEC) { ++ int quoted = 0; ++ ++ for (; *cp && (quoted || (*cp != ' ' && *cp != '\t')); cp++) { ++ if (*cp == '\\' && cp[1] == '"') ++ cp++; /* Skip both */ ++ else if (*cp == '"') ++ quoted = !quoted; ++ } ++ /* Skip remaining whitespace. */ ++ for (; *cp == ' ' || *cp == '\t'; cp++) ++ ; ++ if (!*cp) ++ continue; ++ } ++ ++ /* Read and process the key itself. */ ++ key = key_new(KEY_RSA1); ++ if (key_read(key, &cp) == 1) { ++ while (*cp == ' ' || *cp == '\t') ++ cp++; ++ if (!do_key(filename, linenum, ++ key, *cp ? cp : filename)) ++ ret = 0; ++ found = 1; ++ } else { ++ key_free(key); ++ key = key_new(KEY_UNSPEC); ++ if (key_read(key, &cp) == 1) { ++ while (*cp == ' ' || *cp == '\t') ++ cp++; ++ if (!do_key(filename, linenum, ++ key, *cp ? cp : filename)) ++ ret = 0; ++ found = 1; ++ } ++ } ++ key_free(key); ++ } ++ if (f != stdin) ++ fclose(f); ++ ++ if (!found && filename) { ++ key = key_load_public(filename, &comment); ++ if (key) { ++ if (!do_key(filename, 1, key, comment)) ++ ret = 0; ++ found = 1; ++ } ++ if (comment) ++ xfree(comment); ++ } ++ ++ return ret; ++} ++ ++int ++do_host(int quiet_open) ++{ ++ int i; ++ struct stat st; ++ int ret = 1; ++ ++ for (i = 0; default_host_files[i]; i++) { ++ if (stat(default_host_files[i], &st) < 0 && errno == ENOENT) ++ continue; ++ if (!do_filename(default_host_files[i], quiet_open)) ++ ret = 0; ++ } ++ ++ return ret; ++} ++ ++int ++do_user(const char *dir) ++{ ++ int i; ++ char *file; ++ struct stat st; ++ int ret = 1; ++ ++ for (i = 0; default_files[i]; i++) { ++ xasprintf(&file, "%s/%s", dir, default_files[i]); ++ if (stat(file, &st) < 0 && errno == ENOENT) { ++ xfree(file); ++ continue; ++ } ++ if (!do_filename(file, 0)) ++ ret = 0; ++ xfree(file); ++ } ++ ++ return ret; ++} ++ ++int ++main(int argc, char **argv) ++{ ++ int opt, all_users = 0; ++ int ret = 1; ++ extern int optind; ++ ++ /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ ++ sanitise_stdfd(); ++ ++ __progname = ssh_get_progname(argv[0]); ++ ++ SSLeay_add_all_algorithms(); ++ log_init(argv[0], SYSLOG_LEVEL_INFO, SYSLOG_FACILITY_USER, 1); ++ ++ /* We don't need the RNG ourselves, but symbol references here allow ++ * ld to link us properly. ++ */ ++ init_rng(); ++ seed_rng(); ++ ++ while ((opt = getopt(argc, argv, "ahqv")) != -1) { ++ switch (opt) { ++ case 'a': ++ all_users = 1; ++ break; ++ case 'q': ++ verbosity--; ++ break; ++ case 'v': ++ verbosity++; ++ break; ++ case 'h': ++ default: ++ usage(); ++ } ++ } ++ ++ if (all_users) { ++ struct passwd *pw; ++ ++ if (!do_host(0)) ++ ret = 0; ++ ++ while ((pw = getpwent()) != NULL) { ++ if (pw->pw_dir) { ++ temporarily_use_uid(pw); ++ if (!do_user(pw->pw_dir)) ++ ret = 0; ++ restore_uid(); ++ } ++ } ++ } else if (optind == argc) { ++ struct passwd *pw; ++ ++ if (!do_host(1)) ++ ret = 0; ++ ++ if ((pw = getpwuid(geteuid())) == NULL) ++ fprintf(stderr, "No user found with uid %u\n", ++ (u_int)geteuid()); ++ else { ++ if (!do_user(pw->pw_dir)) ++ ret = 0; ++ } ++ } else { ++ while (optind < argc) ++ if (!do_filename(argv[optind++], 0)) ++ ret = 0; ++ } ++ ++ if (verbosity >= 0) { ++ if (some_unknown) { ++ printf("#\n"); ++ printf("# The status of some keys on your system is unknown.\n"); ++ printf("# You may need to install additional blacklist files.\n"); ++ } ++ if (some_compromised) { ++ printf("#\n"); ++ printf("# Some keys on your system have been compromised!\n"); ++ printf("# You must replace them using ssh-keygen(1).\n"); ++ } ++ if (some_unknown || some_compromised) { ++ printf("#\n"); ++ printf("# See the ssh-vulnkey(1) manual page for further advice.\n"); ++ } else if (some_keys && verbosity > 0) { ++ printf("#\n"); ++ printf("# No blacklisted keys!\n"); ++ } ++ } ++ ++ return ret; ++} +Index: b/ssh.1 +=================================================================== +--- a/ssh.1 ++++ b/ssh.1 +@@ -1396,6 +1396,7 @@ + .Xr ssh-agent 1 , + .Xr ssh-keygen 1 , + .Xr ssh-keyscan 1 , ++.Xr ssh-vulnkey 1 , + .Xr tun 4 , + .Xr hosts.equiv 5 , + .Xr ssh_config 5 , +Index: b/ssh.c +=================================================================== +--- a/ssh.c ++++ b/ssh.c +@@ -1229,7 +1229,7 @@ + static void + load_public_identity_files(void) + { +- char *filename, *cp, thishost[NI_MAXHOST]; ++ char *filename, *cp, thishost[NI_MAXHOST], *fp; + char *pwdir = NULL, *pwname = NULL; + int i = 0; + Key *public; +@@ -1276,6 +1276,22 @@ + public = key_load_public(filename, NULL); + debug("identity file %s type %d", filename, + public ? public->type : -1); ++ if (public && blacklisted_key(public, &fp) == 1) { ++ if (options.use_blacklisted_keys) ++ logit("Public key %s blacklisted (see " ++ "ssh-vulnkey(1)); continuing anyway", fp); ++ else ++ logit("Public key %s blacklisted (see " ++ "ssh-vulnkey(1)); refusing to send it", ++ fp); ++ xfree(fp); ++ if (!options.use_blacklisted_keys) { ++ key_free(public); ++ xfree(filename); ++ filename = NULL; ++ public = NULL; ++ } ++ } + xfree(options.identity_files[i]); + options.identity_files[i] = filename; + options.identity_keys[i] = public; +Index: b/ssh_config.5 +=================================================================== +--- a/ssh_config.5 ++++ b/ssh_config.5 +@@ -1041,6 +1041,23 @@ + .Dq any . + The default is + .Dq any:any . ++.It Cm UseBlacklistedKeys ++Specifies whether ++.Xr ssh 1 ++should use keys recorded in its blacklist of known-compromised keys (see ++.Xr ssh-vulnkey 1 ) ++for authentication. ++If ++.Dq yes , ++then attempts to use compromised keys for authentication will be logged but ++accepted. ++It is strongly recommended that this be used only to install new authorized ++keys on the remote system, and even then only with the utmost care. ++If ++.Dq no , ++then attempts to use compromised keys for authentication will be prevented. ++The default is ++.Dq no . + .It Cm UsePrivilegedPort + Specifies whether to use a privileged port for outgoing connections. + The argument must be +Index: b/sshconnect2.c +=================================================================== +--- a/sshconnect2.c ++++ b/sshconnect2.c +@@ -1418,6 +1418,8 @@ + + /* list of keys stored in the filesystem */ + for (i = 0; i < options.num_identity_files; i++) { ++ if (options.identity_files[i] == NULL) ++ continue; + key = options.identity_keys[i]; + if (key && key->type == KEY_RSA1) + continue; +@@ -1508,7 +1510,7 @@ + if (id->key && id->key->type != KEY_RSA1) { + debug("Offering public key: %s", id->filename); + sent = send_pubkey_test(authctxt, id); +- } else if (id->key == NULL) { ++ } else if (id->key == NULL && id->filename) { + debug("Trying private key: %s", id->filename); + id->key = load_identity_file(id->filename); + if (id->key != NULL) { +Index: b/sshd.8 +=================================================================== +--- a/sshd.8 ++++ b/sshd.8 +@@ -871,6 +871,7 @@ + .Xr ssh-agent 1 , + .Xr ssh-keygen 1 , + .Xr ssh-keyscan 1 , ++.Xr ssh-vulnkey 1 , + .Xr chroot 2 , + .Xr hosts_access 5 , + .Xr login.conf 5 , +Index: b/sshd.c +=================================================================== +--- a/sshd.c ++++ b/sshd.c +@@ -1518,6 +1518,11 @@ + sensitive_data.host_keys[i] = NULL; + continue; + } ++ if (reject_blacklisted_key(key, 1) == 1) { ++ key_free(key); ++ sensitive_data.host_keys[i] = NULL; ++ continue; ++ } + switch (key->type) { + case KEY_RSA1: + sensitive_data.ssh1_host_key = key; +Index: b/sshd_config.5 +=================================================================== +--- a/sshd_config.5 ++++ b/sshd_config.5 +@@ -685,6 +685,20 @@ + Specifies whether password authentication is allowed. + The default is + .Dq yes . ++.It Cm PermitBlacklistedKeys ++Specifies whether ++.Xr sshd 8 ++should allow keys recorded in its blacklist of known-compromised keys (see ++.Xr ssh-vulnkey 1 ) . ++If ++.Dq yes , ++then attempts to authenticate with compromised keys will be logged but ++accepted. ++If ++.Dq no , ++then attempts to authenticate with compromised keys will be rejected. ++The default is ++.Dq no . + .It Cm PermitEmptyPasswords + When password authentication is allowed, it specifies whether the + server allows login to accounts with empty password strings. diff --git a/debian/patches/ssh1-keepalive.patch b/debian/patches/ssh1-keepalive.patch new file mode 100644 index 000000000..37b8052eb --- /dev/null +++ b/debian/patches/ssh1-keepalive.patch @@ -0,0 +1,60 @@ +Index: b/clientloop.c +=================================================================== +--- a/clientloop.c ++++ b/clientloop.c +@@ -502,16 +502,21 @@ + static void + server_alive_check(void) + { +- if (packet_inc_alive_timeouts() > options.server_alive_count_max) { +- logit("Timeout, server not responding."); +- cleanup_exit(255); ++ if (compat20) { ++ if (packet_inc_alive_timeouts() > options.server_alive_count_max) { ++ logit("Timeout, server not responding."); ++ cleanup_exit(255); ++ } ++ packet_start(SSH2_MSG_GLOBAL_REQUEST); ++ packet_put_cstring("keepalive@openssh.com"); ++ packet_put_char(1); /* boolean: want reply */ ++ packet_send(); ++ /* Insert an empty placeholder to maintain ordering */ ++ client_register_global_confirm(NULL, NULL); ++ } else { ++ packet_send_ignore(0); ++ packet_send(); + } +- packet_start(SSH2_MSG_GLOBAL_REQUEST); +- packet_put_cstring("keepalive@openssh.com"); +- packet_put_char(1); /* boolean: want reply */ +- packet_send(); +- /* Insert an empty placeholder to maintain ordering */ +- client_register_global_confirm(NULL, NULL); + } + + /* +@@ -572,7 +577,7 @@ + * event pending. + */ + +- if (options.server_alive_interval == 0 || !compat20) ++ if (options.server_alive_interval == 0) + tvp = NULL; + else { + tv.tv_sec = options.server_alive_interval; +Index: b/ssh_config.5 +=================================================================== +--- a/ssh_config.5 ++++ b/ssh_config.5 +@@ -935,7 +935,10 @@ + .Cm ServerAliveCountMax + is left at the default, if the server becomes unresponsive, + ssh will disconnect after approximately 45 seconds. +-This option applies to protocol version 2 only. ++This option applies to protocol version 2 only; in protocol version ++1 there is no mechanism to request a response from the server to the ++server alive messages, so disconnection is the responsibility of the TCP ++stack. + .It Cm ServerAliveInterval + Sets a timeout interval in seconds after which if no data has been received + from the server, diff --git a/debian/patches/sshd-ignore-sighup.patch b/debian/patches/sshd-ignore-sighup.patch new file mode 100644 index 000000000..652b3fa66 --- /dev/null +++ b/debian/patches/sshd-ignore-sighup.patch @@ -0,0 +1,12 @@ +Index: b/sshd.c +=================================================================== +--- a/sshd.c ++++ b/sshd.c +@@ -318,6 +318,7 @@ + close_listen_socks(); + close_startup_pipes(); + alarm(0); /* alarm timer persists across exec */ ++ signal(SIGHUP, SIG_IGN); /* will be restored after exec */ + execv(saved_argv[0], saved_argv); + logit("RESTART FAILED: av[0]='%.100s', error: %.100s.", saved_argv[0], + strerror(errno)); diff --git a/debian/patches/syslog-level-silent.patch b/debian/patches/syslog-level-silent.patch new file mode 100644 index 000000000..492d61983 --- /dev/null +++ b/debian/patches/syslog-level-silent.patch @@ -0,0 +1,176 @@ +Index: b/clientloop.c +=================================================================== +--- a/clientloop.c ++++ b/clientloop.c +@@ -1533,7 +1533,7 @@ + * In interactive mode (with pseudo tty) display a message indicating + * that the connection has been closed. + */ +- if (have_pty && options.log_level != SYSLOG_LEVEL_QUIET) { ++ if (have_pty && options.log_level > SYSLOG_LEVEL_QUIET) { + snprintf(buf, sizeof buf, + "Connection to %.64s closed.\r\n", host); + buffer_append(&stderr_buffer, buf, strlen(buf)); +Index: b/log.c +=================================================================== +--- a/log.c ++++ b/log.c +@@ -90,6 +90,7 @@ + LogLevel val; + } log_levels[] = + { ++ { "SILENT", SYSLOG_LEVEL_SILENT }, + { "QUIET", SYSLOG_LEVEL_QUIET }, + { "FATAL", SYSLOG_LEVEL_FATAL }, + { "ERROR", SYSLOG_LEVEL_ERROR }, +@@ -244,6 +245,7 @@ + argv0 = av0; + + switch (level) { ++ case SYSLOG_LEVEL_SILENT: + case SYSLOG_LEVEL_QUIET: + case SYSLOG_LEVEL_FATAL: + case SYSLOG_LEVEL_ERROR: +Index: b/log.h +=================================================================== +--- a/log.h ++++ b/log.h +@@ -35,6 +35,7 @@ + } SyslogFacility; + + typedef enum { ++ SYSLOG_LEVEL_SILENT, + SYSLOG_LEVEL_QUIET, + SYSLOG_LEVEL_FATAL, + SYSLOG_LEVEL_ERROR, +Index: b/mux.c +=================================================================== +--- a/mux.c ++++ b/mux.c +@@ -721,7 +721,7 @@ + } else + debug2("Received exit status from master %d", exitval[0]); + +- if (tty_flag && options.log_level != SYSLOG_LEVEL_QUIET) ++ if (tty_flag && options.log_level > SYSLOG_LEVEL_QUIET) + fprintf(stderr, "Shared connection to %s closed.\r\n", host); + + exit(exitval[0]); +Index: b/sftp-server.8 +=================================================================== +--- a/sftp-server.8 ++++ b/sftp-server.8 +@@ -64,7 +64,7 @@ + Specifies which messages will be logged by + .Nm . + The possible values are: +-QUIET, FATAL, ERROR, INFO, VERBOSE, DEBUG, DEBUG1, DEBUG2, and DEBUG3. ++SILENT, QUIET, FATAL, ERROR, INFO, VERBOSE, DEBUG, DEBUG1, DEBUG2, and DEBUG3. + INFO and VERBOSE log transactions that + .Nm + performs on behalf of the client. +Index: b/ssh.1 +=================================================================== +--- a/ssh.1 ++++ b/ssh.1 +@@ -500,6 +500,11 @@ + .It Fl q + Quiet mode. + Causes most warning and diagnostic messages to be suppressed. ++Only fatal errors are displayed. ++If a second ++.Fl q ++is given then even fatal errors are suppressed, except for those produced ++due solely to bad arguments. + .It Fl R Xo + .Sm off + .Oo Ar bind_address : Oc +Index: b/ssh.c +=================================================================== +--- a/ssh.c ++++ b/ssh.c +@@ -389,7 +389,12 @@ + } + break; + case 'q': +- options.log_level = SYSLOG_LEVEL_QUIET; ++ if (options.log_level == SYSLOG_LEVEL_QUIET) { ++ options.log_level = SYSLOG_LEVEL_SILENT; ++ } ++ else if (options.log_level != SYSLOG_LEVEL_SILENT) { ++ options.log_level = SYSLOG_LEVEL_QUIET; ++ } + break; + case 'e': + if (optarg[0] == '^' && optarg[2] == 0 && +@@ -592,7 +597,7 @@ + tty_flag = 0; + /* Do not allocate a tty if stdin is not a tty. */ + if ((!isatty(fileno(stdin)) || stdin_null_flag) && !force_tty_flag) { +- if (tty_flag) ++ if (tty_flag && options.log_level > SYSLOG_LEVEL_QUIET) + logit("Pseudo-terminal will not be allocated because " + "stdin is not a terminal."); + tty_flag = 0; +Index: b/ssh_config.5 +=================================================================== +--- a/ssh_config.5 ++++ b/ssh_config.5 +@@ -685,7 +685,7 @@ + Gives the verbosity level that is used when logging messages from + .Xr ssh 1 . + The possible values are: +-QUIET, FATAL, ERROR, INFO, VERBOSE, DEBUG, DEBUG1, DEBUG2, and DEBUG3. ++SILENT, QUIET, FATAL, ERROR, INFO, VERBOSE, DEBUG, DEBUG1, DEBUG2, and DEBUG3. + The default is INFO. + DEBUG and DEBUG1 are equivalent. + DEBUG2 and DEBUG3 each specify higher levels of verbose output. +Index: b/sshd.8 +=================================================================== +--- a/sshd.8 ++++ b/sshd.8 +@@ -207,9 +207,12 @@ + option override command-line ports. + .It Fl q + Quiet mode. +-Nothing is sent to the system log. ++Only fatal errors are sent to the system log. + Normally the beginning, + authentication, and termination of each connection is logged. ++If a second ++.Fl q ++is given then nothing is sent to the system log. + .It Fl T + Extended test mode. + Check the validity of the configuration file, output the effective configuration +Index: b/sshd.c +=================================================================== +--- a/sshd.c ++++ b/sshd.c +@@ -1355,7 +1355,12 @@ + /* ignored */ + break; + case 'q': +- options.log_level = SYSLOG_LEVEL_QUIET; ++ if (options.log_level == SYSLOG_LEVEL_QUIET) { ++ options.log_level = SYSLOG_LEVEL_SILENT; ++ } ++ else if (options.log_level != SYSLOG_LEVEL_SILENT) { ++ options.log_level = SYSLOG_LEVEL_QUIET; ++ } + break; + case 'b': + options.server_key_bits = (int)strtonum(optarg, 256, +Index: b/sshd_config.5 +=================================================================== +--- a/sshd_config.5 ++++ b/sshd_config.5 +@@ -567,7 +567,7 @@ + Gives the verbosity level that is used when logging messages from + .Xr sshd 8 . + The possible values are: +-QUIET, FATAL, ERROR, INFO, VERBOSE, DEBUG, DEBUG1, DEBUG2, and DEBUG3. ++SILENT, QUIET, FATAL, ERROR, INFO, VERBOSE, DEBUG, DEBUG1, DEBUG2, and DEBUG3. + The default is INFO. + DEBUG and DEBUG1 are equivalent. + DEBUG2 and DEBUG3 each specify higher levels of debugging output. diff --git a/debian/patches/user-group-modes.patch b/debian/patches/user-group-modes.patch new file mode 100644 index 000000000..47bb458e6 --- /dev/null +++ b/debian/patches/user-group-modes.patch @@ -0,0 +1,72 @@ +Index: b/readconf.c +=================================================================== +--- a/readconf.c ++++ b/readconf.c +@@ -28,6 +28,8 @@ + #include + #include + #include ++#include ++#include + + #include "xmalloc.h" + #include "ssh.h" +@@ -998,11 +1000,30 @@ + + if (checkperm) { + struct stat sb; ++ int bad_modes = 0; + + if (fstat(fileno(f), &sb) == -1) + fatal("fstat %s: %s", filename, strerror(errno)); +- if (((sb.st_uid != 0 && sb.st_uid != getuid()) || +- (sb.st_mode & 022) != 0)) ++ if (sb.st_uid != 0 && sb.st_uid != getuid()) ++ bad_modes = 1; ++ if ((sb.st_mode & 020) != 0) { ++ /* If the file is group-writable, the group in ++ * question must have at most one member, namely the ++ * file's owner. ++ */ ++ struct passwd *pw = getpwuid(sb.st_uid); ++ struct group *gr = getgrgid(sb.st_gid); ++ if (!pw || !gr) ++ bad_modes = 1; ++ else if (gr->gr_mem[0]) { ++ if (strcmp(pw->pw_name, gr->gr_mem[0]) || ++ gr->gr_mem[1]) ++ bad_modes = 1; ++ } ++ } ++ if ((sb.st_mode & 002) != 0) ++ bad_modes = 1; ++ if (bad_modes) + fatal("Bad owner or permissions on %s", filename); + } + +Index: b/ssh.1 +=================================================================== +--- a/ssh.1 ++++ b/ssh.1 +@@ -1299,6 +1299,8 @@ + .Xr ssh_config 5 . + Because of the potential for abuse, this file must have strict permissions: + read/write for the user, and not accessible by others. ++It may be group-writable provided that the group in question contains only ++the user. + .Pp + .It ~/.ssh/environment + Contains additional definitions for environment variables; see +Index: b/ssh_config.5 +=================================================================== +--- a/ssh_config.5 ++++ b/ssh_config.5 +@@ -1194,6 +1194,8 @@ + This file is used by the SSH client. + Because of the potential for abuse, this file must have strict permissions: + read/write for the user, and not accessible by others. ++It may be group-writable provided that the group in question contains only ++the user. + .It Pa /etc/ssh/ssh_config + Systemwide configuration file. + This file provides defaults for those -- cgit v1.2.3