Created
October 12, 2014 04:33
-
-
Save akerl/a347acb4b923a673bfc1 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff -urp --new-file openssh-6.7p1/Makefile.in openssh-6.7p1.patched/Makefile.in | |
--- openssh-6.7p1/Makefile.in 2014-08-29 23:23:07.000000000 -0700 | |
+++ openssh-6.7p1.patched/Makefile.in 2014-10-11 21:18:36.000000000 -0700 | |
@@ -59,6 +59,7 @@ SED=@SED@ | |
ENT=@ENT@ | |
XAUTH_PATH=@XAUTH_PATH@ | |
LDFLAGS=-L. -Lopenbsd-compat/ @LDFLAGS@ | |
+KEYCHAIN_LDFLAGS=@KEYCHAIN_LDFLAGS@ | |
EXEEXT=@EXEEXT@ | |
MANFMT=@MANFMT@ | |
@@ -108,6 +109,8 @@ SSHDOBJS=sshd.o auth-rhosts.o auth-passw | |
sandbox-null.o sandbox-rlimit.o sandbox-systrace.o sandbox-darwin.o \ | |
sandbox-seccomp-filter.o sandbox-capsicum.o | |
+KEYCHAINOBJS=keychain.o | |
+ | |
MANPAGES = moduli.5.out scp.1.out ssh-add.1.out ssh-agent.1.out ssh-keygen.1.out ssh-keyscan.1.out ssh.1.out sshd.8.out sftp-server.8.out sftp.1.out ssh-keysign.8.out ssh-pkcs11-helper.8.out sshd_config.5.out ssh_config.5.out | |
MANPAGES_IN = moduli.5 scp.1 ssh-add.1 ssh-agent.1 ssh-keygen.1 ssh-keyscan.1 ssh.1 sshd.8 sftp-server.8 sftp.1 ssh-keysign.8 ssh-pkcs11-helper.8 sshd_config.5 ssh_config.5 | |
MANTYPE = @MANTYPE@ | |
@@ -143,6 +146,7 @@ all: $(CONFIGFILES) $(MANPAGES) $(TARGET | |
$(LIBSSH_OBJS): Makefile.in config.h | |
$(SSHOBJS): Makefile.in config.h | |
$(SSHDOBJS): Makefile.in config.h | |
+$(KEYCHAINOBJS): Makefile.in config.h | |
.c.o: | |
$(CC) $(CFLAGS) $(CPPFLAGS) -c $< -o $@ | |
@@ -156,8 +160,8 @@ libssh.a: $(LIBSSH_OBJS) | |
$(AR) rv $@ $(LIBSSH_OBJS) | |
$(RANLIB) $@ | |
-ssh$(EXEEXT): $(LIBCOMPAT) libssh.a $(SSHOBJS) | |
- $(LD) -o $@ $(SSHOBJS) $(LDFLAGS) -lssh -lopenbsd-compat $(SSHLIBS) $(LIBS) $(GSSLIBS) | |
+ssh$(EXEEXT): $(LIBCOMPAT) libssh.a $(SSHOBJS) $(KEYCHAINOBJS) | |
+ $(LD) -o $@ $(SSHOBJS) $(KEYCHAINOBJS) $(LDFLAGS) $(KEYCHAIN_LDFLAGS) -lssh -lopenbsd-compat $(SSHLIBS) $(LIBS) $(GSSLIBS) | |
sshd$(EXEEXT): libssh.a $(LIBCOMPAT) $(SSHDOBJS) | |
$(LD) -o $@ $(SSHDOBJS) $(LDFLAGS) -lssh -lopenbsd-compat $(SSHDLIBS) $(LIBS) $(GSSLIBS) $(K5LIBS) | |
@@ -165,11 +169,11 @@ sshd$(EXEEXT): libssh.a $(LIBCOMPAT) $(S | |
scp$(EXEEXT): $(LIBCOMPAT) libssh.a scp.o progressmeter.o | |
$(LD) -o $@ scp.o progressmeter.o bufaux.o $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS) | |
-ssh-add$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-add.o | |
- $(LD) -o $@ ssh-add.o $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS) | |
+ssh-add$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-add.o $(KEYCHAINOBJS) | |
+ $(LD) -o $@ ssh-add.o $(KEYCHAINOBJS) $(LDFLAGS) $(KEYCHAIN_LDFLAGS) -lssh -lopenbsd-compat $(LIBS) | |
-ssh-agent$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-agent.o ssh-pkcs11-client.o | |
- $(LD) -o $@ ssh-agent.o ssh-pkcs11-client.o $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS) | |
+ssh-agent$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-agent.o ssh-pkcs11-client.o $(KEYCHAINOBJS) | |
+ $(LD) -o $@ ssh-agent.o ssh-pkcs11-client.o $(KEYCHAINOBJS) $(LDFLAGS) $(KEYCHAIN_LDFLAGS) -lssh -lopenbsd-compat $(LIBS) | |
ssh-keygen$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-keygen.o | |
$(LD) -o $@ ssh-keygen.o $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS) | |
@@ -293,7 +297,7 @@ install-files: | |
$(INSTALL) -m 0755 $(STRIP_OPT) ssh-keygen$(EXEEXT) $(DESTDIR)$(bindir)/ssh-keygen$(EXEEXT) | |
$(INSTALL) -m 0755 $(STRIP_OPT) ssh-keyscan$(EXEEXT) $(DESTDIR)$(bindir)/ssh-keyscan$(EXEEXT) | |
$(INSTALL) -m 0755 $(STRIP_OPT) sshd$(EXEEXT) $(DESTDIR)$(sbindir)/sshd$(EXEEXT) | |
- $(INSTALL) -m 4711 $(STRIP_OPT) ssh-keysign$(EXEEXT) $(DESTDIR)$(SSH_KEYSIGN)$(EXEEXT) | |
+ $(INSTALL) -m 0711 $(STRIP_OPT) ssh-keysign$(EXEEXT) $(DESTDIR)$(SSH_KEYSIGN)$(EXEEXT) | |
$(INSTALL) -m 0755 $(STRIP_OPT) ssh-pkcs11-helper$(EXEEXT) $(DESTDIR)$(SSH_PKCS11_HELPER)$(EXEEXT) | |
$(INSTALL) -m 0755 $(STRIP_OPT) sftp$(EXEEXT) $(DESTDIR)$(bindir)/sftp$(EXEEXT) | |
$(INSTALL) -m 0755 $(STRIP_OPT) sftp-server$(EXEEXT) $(DESTDIR)$(SFTP_SERVER)$(EXEEXT) | |
diff -urp --new-file openssh-6.7p1/Makefile.in.orig openssh-6.7p1.patched/Makefile.in.orig | |
--- openssh-6.7p1/Makefile.in.orig 1969-12-31 16:00:00.000000000 -0800 | |
+++ openssh-6.7p1.patched/Makefile.in.orig 2014-08-29 23:23:07.000000000 -0700 | |
@@ -0,0 +1,546 @@ | |
+# $Id: Makefile.in,v 1.365 2014/08/30 06:23:07 djm Exp $ | |
+ | |
+# uncomment if you run a non bourne compatable shell. Ie. csh | |
+#SHELL = @SH@ | |
+ | |
+AUTORECONF=autoreconf | |
+ | |
+prefix=@prefix@ | |
+exec_prefix=@exec_prefix@ | |
+bindir=@bindir@ | |
+sbindir=@sbindir@ | |
+libexecdir=@libexecdir@ | |
+datadir=@datadir@ | |
+datarootdir=@datarootdir@ | |
+mandir=@mandir@ | |
+mansubdir=@mansubdir@ | |
+sysconfdir=@sysconfdir@ | |
+piddir=@piddir@ | |
+srcdir=@srcdir@ | |
+top_srcdir=@top_srcdir@ | |
+ | |
+DESTDIR= | |
+VPATH=@srcdir@ | |
+SSH_PROGRAM=@bindir@/ssh | |
+ASKPASS_PROGRAM=$(libexecdir)/ssh-askpass | |
+SFTP_SERVER=$(libexecdir)/sftp-server | |
+SSH_KEYSIGN=$(libexecdir)/ssh-keysign | |
+SSH_PKCS11_HELPER=$(libexecdir)/ssh-pkcs11-helper | |
+PRIVSEP_PATH=@PRIVSEP_PATH@ | |
+SSH_PRIVSEP_USER=@SSH_PRIVSEP_USER@ | |
+STRIP_OPT=@STRIP_OPT@ | |
+TEST_SHELL=@TEST_SHELL@ | |
+ | |
+PATHS= -DSSHDIR=\"$(sysconfdir)\" \ | |
+ -D_PATH_SSH_PROGRAM=\"$(SSH_PROGRAM)\" \ | |
+ -D_PATH_SSH_ASKPASS_DEFAULT=\"$(ASKPASS_PROGRAM)\" \ | |
+ -D_PATH_SFTP_SERVER=\"$(SFTP_SERVER)\" \ | |
+ -D_PATH_SSH_KEY_SIGN=\"$(SSH_KEYSIGN)\" \ | |
+ -D_PATH_SSH_PKCS11_HELPER=\"$(SSH_PKCS11_HELPER)\" \ | |
+ -D_PATH_SSH_PIDDIR=\"$(piddir)\" \ | |
+ -D_PATH_PRIVSEP_CHROOT_DIR=\"$(PRIVSEP_PATH)\" | |
+ | |
+CC=@CC@ | |
+LD=@LD@ | |
+CFLAGS=@CFLAGS@ | |
+CPPFLAGS=-I. -I$(srcdir) @CPPFLAGS@ $(PATHS) @DEFS@ | |
+LIBS=@LIBS@ | |
+K5LIBS=@K5LIBS@ | |
+GSSLIBS=@GSSLIBS@ | |
+SSHLIBS=@SSHLIBS@ | |
+SSHDLIBS=@SSHDLIBS@ | |
+LIBEDIT=@LIBEDIT@ | |
+AR=@AR@ | |
+AWK=@AWK@ | |
+RANLIB=@RANLIB@ | |
+INSTALL=@INSTALL@ | |
+PERL=@PERL@ | |
+SED=@SED@ | |
+ENT=@ENT@ | |
+XAUTH_PATH=@XAUTH_PATH@ | |
+LDFLAGS=-L. -Lopenbsd-compat/ @LDFLAGS@ | |
+EXEEXT=@EXEEXT@ | |
+MANFMT=@MANFMT@ | |
+ | |
+TARGETS=ssh$(EXEEXT) sshd$(EXEEXT) ssh-add$(EXEEXT) ssh-keygen$(EXEEXT) ssh-keyscan${EXEEXT} ssh-keysign${EXEEXT} ssh-pkcs11-helper$(EXEEXT) ssh-agent$(EXEEXT) scp$(EXEEXT) sftp-server$(EXEEXT) sftp$(EXEEXT) | |
+ | |
+LIBOPENSSH_OBJS=\ | |
+ ssherr.o \ | |
+ sshbuf.o \ | |
+ sshkey.o \ | |
+ sshbuf-getput-basic.o \ | |
+ sshbuf-misc.o \ | |
+ sshbuf-getput-crypto.o | |
+ | |
+LIBSSH_OBJS=${LIBOPENSSH_OBJS} \ | |
+ authfd.o authfile.o bufaux.o bufbn.o buffer.o \ | |
+ canohost.o channels.o cipher.o cipher-aes.o \ | |
+ cipher-bf1.o cipher-ctr.o cipher-3des1.o cleanup.o \ | |
+ compat.o compress.o crc32.o deattack.o fatal.o hostfile.o \ | |
+ log.o match.o md-sha256.o moduli.o nchan.o packet.o \ | |
+ readpass.o rsa.o ttymodes.o xmalloc.o addrmatch.o \ | |
+ 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-ecdsa.o ssh-rsa.o dh.o \ | |
+ kexdh.o kexgex.o kexdhc.o kexgexc.o bufec.o kexecdh.o kexecdhc.o \ | |
+ msg.o progressmeter.o dns.o entropy.o gss-genr.o umac.o umac128.o \ | |
+ ssh-pkcs11.o krl.o smult_curve25519_ref.o \ | |
+ kexc25519.o kexc25519c.o poly1305.o chacha.o cipher-chachapoly.o \ | |
+ ssh-ed25519.o digest-openssl.o hmac.o \ | |
+ sc25519.o ge25519.o fe25519.o ed25519.o verify.o hash.o blocks.o | |
+ | |
+SSHOBJS= ssh.o readconf.o clientloop.o sshtty.o \ | |
+ sshconnect.o sshconnect1.o sshconnect2.o mux.o \ | |
+ roaming_common.o roaming_client.o | |
+ | |
+SSHDOBJS=sshd.o auth-rhosts.o auth-passwd.o auth-rsa.o auth-rh-rsa.o \ | |
+ audit.o audit-bsm.o audit-linux.o platform.o \ | |
+ sshpty.o sshlogin.o servconf.o serverloop.o \ | |
+ auth.o auth1.o auth2.o auth-options.o session.o \ | |
+ auth-chall.o auth2-chall.o groupaccess.o \ | |
+ auth-skey.o auth-bsdauth.o auth2-hostbased.o auth2-kbdint.o \ | |
+ auth2-none.o auth2-passwd.o auth2-pubkey.o \ | |
+ monitor_mm.o monitor.o monitor_wrap.o kexdhs.o kexgexs.o kexecdhs.o \ | |
+ kexc25519s.o auth-krb5.o \ | |
+ auth2-gss.o gss-serv.o gss-serv-krb5.o \ | |
+ loginrec.o auth-pam.o auth-shadow.o auth-sia.o md5crypt.o \ | |
+ sftp-server.o sftp-common.o \ | |
+ roaming_common.o roaming_serv.o \ | |
+ sandbox-null.o sandbox-rlimit.o sandbox-systrace.o sandbox-darwin.o \ | |
+ sandbox-seccomp-filter.o sandbox-capsicum.o | |
+ | |
+MANPAGES = moduli.5.out scp.1.out ssh-add.1.out ssh-agent.1.out ssh-keygen.1.out ssh-keyscan.1.out ssh.1.out sshd.8.out sftp-server.8.out sftp.1.out ssh-keysign.8.out ssh-pkcs11-helper.8.out sshd_config.5.out ssh_config.5.out | |
+MANPAGES_IN = moduli.5 scp.1 ssh-add.1 ssh-agent.1 ssh-keygen.1 ssh-keyscan.1 ssh.1 sshd.8 sftp-server.8 sftp.1 ssh-keysign.8 ssh-pkcs11-helper.8 sshd_config.5 ssh_config.5 | |
+MANTYPE = @MANTYPE@ | |
+ | |
+CONFIGFILES=sshd_config.out ssh_config.out moduli.out | |
+CONFIGFILES_IN=sshd_config ssh_config moduli | |
+ | |
+PATHSUBS = \ | |
+ -e 's|/etc/ssh/ssh_config|$(sysconfdir)/ssh_config|g' \ | |
+ -e 's|/etc/ssh/ssh_known_hosts|$(sysconfdir)/ssh_known_hosts|g' \ | |
+ -e 's|/etc/ssh/sshd_config|$(sysconfdir)/sshd_config|g' \ | |
+ -e 's|/usr/libexec|$(libexecdir)|g' \ | |
+ -e 's|/etc/shosts.equiv|$(sysconfdir)/shosts.equiv|g' \ | |
+ -e 's|/etc/ssh/ssh_host_key|$(sysconfdir)/ssh_host_key|g' \ | |
+ -e 's|/etc/ssh/ssh_host_ecdsa_key|$(sysconfdir)/ssh_host_ecdsa_key|g' \ | |
+ -e 's|/etc/ssh/ssh_host_dsa_key|$(sysconfdir)/ssh_host_dsa_key|g' \ | |
+ -e 's|/etc/ssh/ssh_host_rsa_key|$(sysconfdir)/ssh_host_rsa_key|g' \ | |
+ -e 's|/etc/ssh/ssh_host_ed25519_key|$(sysconfdir)/ssh_host_ed25519_key|g' \ | |
+ -e 's|/var/run/sshd.pid|$(piddir)/sshd.pid|g' \ | |
+ -e 's|/etc/moduli|$(sysconfdir)/moduli|g' \ | |
+ -e 's|/etc/ssh/moduli|$(sysconfdir)/moduli|g' \ | |
+ -e 's|/etc/ssh/sshrc|$(sysconfdir)/sshrc|g' \ | |
+ -e 's|/usr/X11R6/bin/xauth|$(XAUTH_PATH)|g' \ | |
+ -e 's|/var/empty|$(PRIVSEP_PATH)|g' \ | |
+ -e 's|/usr/bin:/bin:/usr/sbin:/sbin|@user_path@|g' | |
+ | |
+FIXPATHSCMD = $(SED) $(PATHSUBS) | |
+FIXALGORITHMSCMD= $(SHELL) $(srcdir)/fixalgorithms $(SED) \ | |
+ @UNSUPPORTED_ALGORITHMS@ | |
+ | |
+all: $(CONFIGFILES) $(MANPAGES) $(TARGETS) | |
+ | |
+$(LIBSSH_OBJS): Makefile.in config.h | |
+$(SSHOBJS): Makefile.in config.h | |
+$(SSHDOBJS): Makefile.in config.h | |
+ | |
+.c.o: | |
+ $(CC) $(CFLAGS) $(CPPFLAGS) -c $< -o $@ | |
+ | |
+LIBCOMPAT=openbsd-compat/libopenbsd-compat.a | |
+$(LIBCOMPAT): always | |
+ (cd openbsd-compat && $(MAKE)) | |
+always: | |
+ | |
+libssh.a: $(LIBSSH_OBJS) | |
+ $(AR) rv $@ $(LIBSSH_OBJS) | |
+ $(RANLIB) $@ | |
+ | |
+ssh$(EXEEXT): $(LIBCOMPAT) libssh.a $(SSHOBJS) | |
+ $(LD) -o $@ $(SSHOBJS) $(LDFLAGS) -lssh -lopenbsd-compat $(SSHLIBS) $(LIBS) $(GSSLIBS) | |
+ | |
+sshd$(EXEEXT): libssh.a $(LIBCOMPAT) $(SSHDOBJS) | |
+ $(LD) -o $@ $(SSHDOBJS) $(LDFLAGS) -lssh -lopenbsd-compat $(SSHDLIBS) $(LIBS) $(GSSLIBS) $(K5LIBS) | |
+ | |
+scp$(EXEEXT): $(LIBCOMPAT) libssh.a scp.o progressmeter.o | |
+ $(LD) -o $@ scp.o progressmeter.o bufaux.o $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS) | |
+ | |
+ssh-add$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-add.o | |
+ $(LD) -o $@ ssh-add.o $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS) | |
+ | |
+ssh-agent$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-agent.o ssh-pkcs11-client.o | |
+ $(LD) -o $@ ssh-agent.o ssh-pkcs11-client.o $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS) | |
+ | |
+ssh-keygen$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-keygen.o | |
+ $(LD) -o $@ ssh-keygen.o $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS) | |
+ | |
+ssh-keysign$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-keysign.o roaming_dummy.o readconf.o | |
+ $(LD) -o $@ ssh-keysign.o readconf.o roaming_dummy.o $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS) | |
+ | |
+ssh-pkcs11-helper$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-pkcs11-helper.o ssh-pkcs11.o | |
+ $(LD) -o $@ ssh-pkcs11-helper.o ssh-pkcs11.o $(LDFLAGS) -lssh -lopenbsd-compat -lssh -lopenbsd-compat $(LIBS) | |
+ | |
+ssh-keyscan$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-keyscan.o roaming_dummy.o | |
+ $(LD) -o $@ ssh-keyscan.o roaming_dummy.o $(LDFLAGS) -lssh -lopenbsd-compat -lssh $(LIBS) | |
+ | |
+sftp-server$(EXEEXT): $(LIBCOMPAT) libssh.a sftp.o sftp-common.o sftp-server.o sftp-server-main.o | |
+ $(LD) -o $@ sftp-server.o sftp-common.o sftp-server-main.o $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS) | |
+ | |
+sftp$(EXEEXT): $(LIBCOMPAT) libssh.a sftp.o sftp-client.o sftp-common.o sftp-glob.o progressmeter.o | |
+ $(LD) -o $@ progressmeter.o sftp.o sftp-client.o sftp-common.o sftp-glob.o $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS) $(LIBEDIT) | |
+ | |
+# test driver for the loginrec code - not built by default | |
+logintest: logintest.o $(LIBCOMPAT) libssh.a loginrec.o | |
+ $(LD) -o $@ logintest.o $(LDFLAGS) loginrec.o -lopenbsd-compat -lssh $(LIBS) | |
+ | |
+$(MANPAGES): $(MANPAGES_IN) | |
+ if test "$(MANTYPE)" = "cat"; then \ | |
+ manpage=$(srcdir)/`echo $@ | sed 's/\.[1-9]\.out$$/\.0/'`; \ | |
+ else \ | |
+ manpage=$(srcdir)/`echo $@ | sed 's/\.out$$//'`; \ | |
+ fi; \ | |
+ if test "$(MANTYPE)" = "man"; then \ | |
+ $(FIXPATHSCMD) $${manpage} | $(FIXALGORITHMSCMD) | \ | |
+ $(AWK) -f $(srcdir)/mdoc2man.awk > $@; \ | |
+ else \ | |
+ $(FIXPATHSCMD) $${manpage} | $(FIXALGORITHMSCMD) > $@; \ | |
+ fi | |
+ | |
+$(CONFIGFILES): $(CONFIGFILES_IN) | |
+ conffile=`echo $@ | sed 's/.out$$//'`; \ | |
+ $(FIXPATHSCMD) $(srcdir)/$${conffile} > $@ | |
+ | |
+# fake rule to stop make trying to compile moduli.o into a binary "moduli.o" | |
+moduli: | |
+ echo | |
+ | |
+# special case target for umac128 | |
+umac128.o: umac.c | |
+ $(CC) $(CFLAGS) $(CPPFLAGS) -o umac128.o -c $(srcdir)/umac.c \ | |
+ -DUMAC_OUTPUT_LEN=16 -Dumac_new=umac128_new \ | |
+ -Dumac_update=umac128_update -Dumac_final=umac128_final \ | |
+ -Dumac_delete=umac128_delete | |
+ | |
+clean: regressclean | |
+ rm -f *.o *.a $(TARGETS) logintest config.cache config.log | |
+ rm -f *.out core survey | |
+ rm -f regress/unittests/test_helper/*.a | |
+ rm -f regress/unittests/test_helper/*.o | |
+ rm -f regress/unittests/sshbuf/*.o | |
+ rm -f regress/unittests/sshbuf/test_sshbuf | |
+ rm -f regress/unittests/sshkey/*.o | |
+ rm -f regress/unittests/sshkey/test_sshkey | |
+ (cd openbsd-compat && $(MAKE) clean) | |
+ | |
+distclean: regressclean | |
+ rm -f *.o *.a $(TARGETS) logintest config.cache config.log | |
+ rm -f *.out core opensshd.init openssh.xml | |
+ rm -f Makefile buildpkg.sh config.h config.status | |
+ rm -f survey.sh openbsd-compat/regress/Makefile *~ | |
+ rm -rf autom4te.cache | |
+ rm -f regress/unittests/test_helper/*.a | |
+ rm -f regress/unittests/test_helper/*.o | |
+ rm -f regress/unittests/sshbuf/*.o | |
+ rm -f regress/unittests/sshbuf/test_sshbuf | |
+ rm -f regress/unittests/sshkey/*.o | |
+ rm -f regress/unittests/sshkey/test_sshkey | |
+ (cd openbsd-compat && $(MAKE) distclean) | |
+ if test -d pkg ; then \ | |
+ rm -fr pkg ; \ | |
+ fi | |
+ | |
+veryclean: distclean | |
+ rm -f configure config.h.in *.0 | |
+ | |
+cleandir: veryclean | |
+ | |
+mrproper: veryclean | |
+ | |
+realclean: veryclean | |
+ | |
+catman-do: | |
+ @for f in $(MANPAGES_IN) ; do \ | |
+ base=`echo $$f | sed 's/\..*$$//'` ; \ | |
+ echo "$$f -> $$base.0" ; \ | |
+ $(MANFMT) $$f | cat -v | sed -e 's/.\^H//g' \ | |
+ >$$base.0 ; \ | |
+ done | |
+ | |
+distprep: catman-do | |
+ $(AUTORECONF) | |
+ -rm -rf autom4te.cache | |
+ | |
+install: $(CONFIGFILES) $(MANPAGES) $(TARGETS) install-files install-sysconf host-key check-config | |
+install-nokeys: $(CONFIGFILES) $(MANPAGES) $(TARGETS) install-files install-sysconf | |
+install-nosysconf: $(CONFIGFILES) $(MANPAGES) $(TARGETS) install-files | |
+ | |
+check-config: | |
+ -$(DESTDIR)$(sbindir)/sshd -t -f $(DESTDIR)$(sysconfdir)/sshd_config | |
+ | |
+install-files: | |
+ $(srcdir)/mkinstalldirs $(DESTDIR)$(bindir) | |
+ $(srcdir)/mkinstalldirs $(DESTDIR)$(sbindir) | |
+ $(srcdir)/mkinstalldirs $(DESTDIR)$(mandir) | |
+ $(srcdir)/mkinstalldirs $(DESTDIR)$(mandir)/$(mansubdir)1 | |
+ $(srcdir)/mkinstalldirs $(DESTDIR)$(mandir)/$(mansubdir)5 | |
+ $(srcdir)/mkinstalldirs $(DESTDIR)$(mandir)/$(mansubdir)8 | |
+ $(srcdir)/mkinstalldirs $(DESTDIR)$(libexecdir) | |
+ (umask 022 ; $(srcdir)/mkinstalldirs $(DESTDIR)$(PRIVSEP_PATH)) | |
+ $(INSTALL) -m 0755 $(STRIP_OPT) ssh$(EXEEXT) $(DESTDIR)$(bindir)/ssh$(EXEEXT) | |
+ $(INSTALL) -m 0755 $(STRIP_OPT) scp$(EXEEXT) $(DESTDIR)$(bindir)/scp$(EXEEXT) | |
+ $(INSTALL) -m 0755 $(STRIP_OPT) ssh-add$(EXEEXT) $(DESTDIR)$(bindir)/ssh-add$(EXEEXT) | |
+ $(INSTALL) -m 0755 $(STRIP_OPT) ssh-agent$(EXEEXT) $(DESTDIR)$(bindir)/ssh-agent$(EXEEXT) | |
+ $(INSTALL) -m 0755 $(STRIP_OPT) ssh-keygen$(EXEEXT) $(DESTDIR)$(bindir)/ssh-keygen$(EXEEXT) | |
+ $(INSTALL) -m 0755 $(STRIP_OPT) ssh-keyscan$(EXEEXT) $(DESTDIR)$(bindir)/ssh-keyscan$(EXEEXT) | |
+ $(INSTALL) -m 0755 $(STRIP_OPT) sshd$(EXEEXT) $(DESTDIR)$(sbindir)/sshd$(EXEEXT) | |
+ $(INSTALL) -m 4711 $(STRIP_OPT) ssh-keysign$(EXEEXT) $(DESTDIR)$(SSH_KEYSIGN)$(EXEEXT) | |
+ $(INSTALL) -m 0755 $(STRIP_OPT) ssh-pkcs11-helper$(EXEEXT) $(DESTDIR)$(SSH_PKCS11_HELPER)$(EXEEXT) | |
+ $(INSTALL) -m 0755 $(STRIP_OPT) sftp$(EXEEXT) $(DESTDIR)$(bindir)/sftp$(EXEEXT) | |
+ $(INSTALL) -m 0755 $(STRIP_OPT) sftp-server$(EXEEXT) $(DESTDIR)$(SFTP_SERVER)$(EXEEXT) | |
+ $(INSTALL) -m 644 ssh.1.out $(DESTDIR)$(mandir)/$(mansubdir)1/ssh.1 | |
+ $(INSTALL) -m 644 scp.1.out $(DESTDIR)$(mandir)/$(mansubdir)1/scp.1 | |
+ $(INSTALL) -m 644 ssh-add.1.out $(DESTDIR)$(mandir)/$(mansubdir)1/ssh-add.1 | |
+ $(INSTALL) -m 644 ssh-agent.1.out $(DESTDIR)$(mandir)/$(mansubdir)1/ssh-agent.1 | |
+ $(INSTALL) -m 644 ssh-keygen.1.out $(DESTDIR)$(mandir)/$(mansubdir)1/ssh-keygen.1 | |
+ $(INSTALL) -m 644 ssh-keyscan.1.out $(DESTDIR)$(mandir)/$(mansubdir)1/ssh-keyscan.1 | |
+ $(INSTALL) -m 644 moduli.5.out $(DESTDIR)$(mandir)/$(mansubdir)5/moduli.5 | |
+ $(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 | |
+ $(INSTALL) -m 644 sftp.1.out $(DESTDIR)$(mandir)/$(mansubdir)1/sftp.1 | |
+ $(INSTALL) -m 644 sftp-server.8.out $(DESTDIR)$(mandir)/$(mansubdir)8/sftp-server.8 | |
+ $(INSTALL) -m 644 ssh-keysign.8.out $(DESTDIR)$(mandir)/$(mansubdir)8/ssh-keysign.8 | |
+ $(INSTALL) -m 644 ssh-pkcs11-helper.8.out $(DESTDIR)$(mandir)/$(mansubdir)8/ssh-pkcs11-helper.8 | |
+ -rm -f $(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 | |
+ | |
+install-sysconf: | |
+ if [ ! -d $(DESTDIR)$(sysconfdir) ]; then \ | |
+ $(srcdir)/mkinstalldirs $(DESTDIR)$(sysconfdir); \ | |
+ fi | |
+ @if [ ! -f $(DESTDIR)$(sysconfdir)/ssh_config ]; then \ | |
+ $(INSTALL) -m 644 ssh_config.out $(DESTDIR)$(sysconfdir)/ssh_config; \ | |
+ else \ | |
+ echo "$(DESTDIR)$(sysconfdir)/ssh_config already exists, install will not overwrite"; \ | |
+ fi | |
+ @if [ ! -f $(DESTDIR)$(sysconfdir)/sshd_config ]; then \ | |
+ $(INSTALL) -m 644 sshd_config.out $(DESTDIR)$(sysconfdir)/sshd_config; \ | |
+ else \ | |
+ echo "$(DESTDIR)$(sysconfdir)/sshd_config already exists, install will not overwrite"; \ | |
+ fi | |
+ @if [ ! -f $(DESTDIR)$(sysconfdir)/moduli ]; then \ | |
+ if [ -f $(DESTDIR)$(sysconfdir)/primes ]; then \ | |
+ echo "moving $(DESTDIR)$(sysconfdir)/primes to $(DESTDIR)$(sysconfdir)/moduli"; \ | |
+ mv "$(DESTDIR)$(sysconfdir)/primes" "$(DESTDIR)$(sysconfdir)/moduli"; \ | |
+ else \ | |
+ $(INSTALL) -m 644 moduli.out $(DESTDIR)$(sysconfdir)/moduli; \ | |
+ fi ; \ | |
+ else \ | |
+ echo "$(DESTDIR)$(sysconfdir)/moduli already exists, install will not overwrite"; \ | |
+ fi | |
+ | |
+host-key: ssh-keygen$(EXEEXT) | |
+ @if [ -z "$(DESTDIR)" ] ; then \ | |
+ if [ -f "$(sysconfdir)/ssh_host_key" ] ; then \ | |
+ echo "$(sysconfdir)/ssh_host_key already exists, skipping." ; \ | |
+ else \ | |
+ ./ssh-keygen -t rsa1 -f $(sysconfdir)/ssh_host_key -N "" ; \ | |
+ fi ; \ | |
+ if [ -f $(sysconfdir)/ssh_host_dsa_key ] ; then \ | |
+ echo "$(sysconfdir)/ssh_host_dsa_key already exists, skipping." ; \ | |
+ else \ | |
+ ./ssh-keygen -t dsa -f $(sysconfdir)/ssh_host_dsa_key -N "" ; \ | |
+ fi ; \ | |
+ if [ -f $(sysconfdir)/ssh_host_rsa_key ] ; then \ | |
+ echo "$(sysconfdir)/ssh_host_rsa_key already exists, skipping." ; \ | |
+ else \ | |
+ ./ssh-keygen -t rsa -f $(sysconfdir)/ssh_host_rsa_key -N "" ; \ | |
+ fi ; \ | |
+ if [ -f $(sysconfdir)/ssh_host_ed25519_key ] ; then \ | |
+ echo "$(sysconfdir)/ssh_host_ed25519_key already exists, skipping." ; \ | |
+ else \ | |
+ ./ssh-keygen -t ed25519 -f $(sysconfdir)/ssh_host_ed25519_key -N "" ; \ | |
+ fi ; \ | |
+ if [ -z "@COMMENT_OUT_ECC@" ] ; then \ | |
+ if [ -f $(sysconfdir)/ssh_host_ecdsa_key ] ; then \ | |
+ echo "$(sysconfdir)/ssh_host_ecdsa_key already exists, skipping." ; \ | |
+ else \ | |
+ ./ssh-keygen -t ecdsa -f $(sysconfdir)/ssh_host_ecdsa_key -N "" ; \ | |
+ fi ; \ | |
+ fi ; \ | |
+ fi ; | |
+ | |
+host-key-force: ssh-keygen$(EXEEXT) | |
+ ./ssh-keygen -t rsa1 -f $(DESTDIR)$(sysconfdir)/ssh_host_key -N "" | |
+ ./ssh-keygen -t dsa -f $(DESTDIR)$(sysconfdir)/ssh_host_dsa_key -N "" | |
+ ./ssh-keygen -t rsa -f $(DESTDIR)$(sysconfdir)/ssh_host_rsa_key -N "" | |
+ ./ssh-keygen -t ed25519 -f $(DESTDIR)$(sysconfdir)/ssh_host_ed25519_key -N "" | |
+ test -z "@COMMENT_OUT_ECC@" && ./ssh-keygen -t ecdsa -f $(DESTDIR)$(sysconfdir)/ssh_host_ecdsa_key -N "" | |
+ | |
+uninstallall: uninstall | |
+ -rm -f $(DESTDIR)$(sysconfdir)/ssh_config | |
+ -rm -f $(DESTDIR)$(sysconfdir)/sshd_config | |
+ -rmdir $(DESTDIR)$(sysconfdir) | |
+ -rmdir $(DESTDIR)$(bindir) | |
+ -rmdir $(DESTDIR)$(sbindir) | |
+ -rmdir $(DESTDIR)$(mandir)/$(mansubdir)1 | |
+ -rmdir $(DESTDIR)$(mandir)/$(mansubdir)8 | |
+ -rmdir $(DESTDIR)$(mandir) | |
+ -rmdir $(DESTDIR)$(libexecdir) | |
+ | |
+uninstall: | |
+ -rm -f $(DESTDIR)$(bindir)/slogin | |
+ -rm -f $(DESTDIR)$(bindir)/ssh$(EXEEXT) | |
+ -rm -f $(DESTDIR)$(bindir)/scp$(EXEEXT) | |
+ -rm -f $(DESTDIR)$(bindir)/ssh-add$(EXEEXT) | |
+ -rm -f $(DESTDIR)$(bindir)/ssh-agent$(EXEEXT) | |
+ -rm -f $(DESTDIR)$(bindir)/ssh-keygen$(EXEEXT) | |
+ -rm -f $(DESTDIR)$(bindir)/ssh-keyscan$(EXEEXT) | |
+ -rm -f $(DESTDIR)$(bindir)/sftp$(EXEEXT) | |
+ -rm -f $(DESTDIR)$(sbindir)/sshd$(EXEEXT) | |
+ -rm -r $(DESTDIR)$(SFTP_SERVER)$(EXEEXT) | |
+ -rm -f $(DESTDIR)$(SSH_KEYSIGN)$(EXEEXT) | |
+ -rm -f $(DESTDIR)$(SSH_PKCS11_HELPER)$(EXEEXT) | |
+ -rm -f $(DESTDIR)$(mandir)/$(mansubdir)1/ssh.1 | |
+ -rm -f $(DESTDIR)$(mandir)/$(mansubdir)1/scp.1 | |
+ -rm -f $(DESTDIR)$(mandir)/$(mansubdir)1/ssh-add.1 | |
+ -rm -f $(DESTDIR)$(mandir)/$(mansubdir)1/ssh-agent.1 | |
+ -rm -f $(DESTDIR)$(mandir)/$(mansubdir)1/ssh-keygen.1 | |
+ -rm -f $(DESTDIR)$(mandir)/$(mansubdir)1/sftp.1 | |
+ -rm -f $(DESTDIR)$(mandir)/$(mansubdir)1/ssh-keyscan.1 | |
+ -rm -f $(DESTDIR)$(mandir)/$(mansubdir)8/sshd.8 | |
+ -rm -f $(DESTDIR)$(mandir)/$(mansubdir)8/sftp-server.8 | |
+ -rm -f $(DESTDIR)$(mandir)/$(mansubdir)8/ssh-keysign.8 | |
+ -rm -f $(DESTDIR)$(mandir)/$(mansubdir)8/ssh-pkcs11-helper.8 | |
+ -rm -f $(DESTDIR)$(mandir)/$(mansubdir)1/slogin.1 | |
+ | |
+regress-prep: | |
+ [ -d `pwd`/regress ] || mkdir -p `pwd`/regress | |
+ [ -d `pwd`/regress/unittests ] || mkdir -p `pwd`/regress/unittests | |
+ [ -d `pwd`/regress/unittests/test_helper ] || \ | |
+ mkdir -p `pwd`/regress/unittests/test_helper | |
+ [ -d `pwd`/regress/unittests/sshbuf ] || \ | |
+ mkdir -p `pwd`/regress/unittests/sshbuf | |
+ [ -d `pwd`/regress/unittests/sshkey ] || \ | |
+ mkdir -p `pwd`/regress/unittests/sshkey | |
+ [ -f `pwd`/regress/Makefile ] || \ | |
+ ln -s `cd $(srcdir) && pwd`/regress/Makefile `pwd`/regress/Makefile | |
+ | |
+regress/modpipe$(EXEEXT): $(srcdir)/regress/modpipe.c | |
+ $(CC) $(CFLAGS) $(CPPFLAGS) -o $@ $? \ | |
+ $(LDFLAGS) -lssh -lopenbsd-compat -lssh -lopenbsd-compat $(LIBS) | |
+ | |
+regress/setuid-allowed$(EXEEXT): $(srcdir)/regress/setuid-allowed.c | |
+ $(CC) $(CFLAGS) $(CPPFLAGS) -o $@ $? \ | |
+ $(LDFLAGS) -lssh -lopenbsd-compat -lssh -lopenbsd-compat $(LIBS) | |
+ | |
+UNITTESTS_TEST_HELPER_OBJS=\ | |
+ regress/unittests/test_helper/test_helper.o \ | |
+ regress/unittests/test_helper/fuzz.o | |
+ | |
+regress/unittests/test_helper/libtest_helper.a: ${UNITTESTS_TEST_HELPER_OBJS} | |
+ $(AR) rv $@ $(UNITTESTS_TEST_HELPER_OBJS) | |
+ $(RANLIB) $@ | |
+ | |
+UNITTESTS_TEST_SSHBUF_OBJS=\ | |
+ regress/unittests/sshbuf/tests.o \ | |
+ regress/unittests/sshbuf/test_sshbuf.o \ | |
+ regress/unittests/sshbuf/test_sshbuf_getput_basic.o \ | |
+ regress/unittests/sshbuf/test_sshbuf_getput_crypto.o \ | |
+ regress/unittests/sshbuf/test_sshbuf_misc.o \ | |
+ regress/unittests/sshbuf/test_sshbuf_fuzz.o \ | |
+ regress/unittests/sshbuf/test_sshbuf_getput_fuzz.o \ | |
+ regress/unittests/sshbuf/test_sshbuf_fixed.o | |
+ | |
+regress/unittests/sshbuf/test_sshbuf$(EXEEXT): ${UNITTESTS_TEST_SSHBUF_OBJS} \ | |
+ regress/unittests/test_helper/libtest_helper.a libssh.a | |
+ $(LD) -o $@ $(LDFLAGS) $(UNITTESTS_TEST_SSHBUF_OBJS) \ | |
+ regress/unittests/test_helper/libtest_helper.a \ | |
+ -lssh -lopenbsd-compat -lssh -lopenbsd-compat $(LIBS) | |
+ | |
+UNITTESTS_TEST_SSHKEY_OBJS=\ | |
+ regress/unittests/sshkey/test_fuzz.o \ | |
+ regress/unittests/sshkey/tests.o \ | |
+ regress/unittests/sshkey/common.o \ | |
+ regress/unittests/sshkey/test_file.o \ | |
+ regress/unittests/sshkey/test_sshkey.o | |
+ | |
+regress/unittests/sshkey/test_sshkey$(EXEEXT): ${UNITTESTS_TEST_SSHKEY_OBJS} \ | |
+ regress/unittests/test_helper/libtest_helper.a libssh.a | |
+ $(LD) -o $@ $(LDFLAGS) $(UNITTESTS_TEST_SSHKEY_OBJS) \ | |
+ regress/unittests/test_helper/libtest_helper.a \ | |
+ -lssh -lopenbsd-compat -lssh -lopenbsd-compat $(LIBS) | |
+ | |
+REGRESS_BINARIES=\ | |
+ regress/modpipe$(EXEEXT) \ | |
+ regress/setuid-allowed$(EXEEXT) \ | |
+ regress/unittests/sshbuf/test_sshbuf$(EXEEXT) \ | |
+ regress/unittests/sshkey/test_sshkey$(EXEEXT) | |
+ | |
+tests interop-tests t-exec: regress-prep $(TARGETS) $(REGRESS_BINARIES) | |
+ BUILDDIR=`pwd`; \ | |
+ TEST_SSH_SCP="$${BUILDDIR}/scp"; \ | |
+ TEST_SSH_SSH="$${BUILDDIR}/ssh"; \ | |
+ TEST_SSH_SSHD="$${BUILDDIR}/sshd"; \ | |
+ TEST_SSH_SSHAGENT="$${BUILDDIR}/ssh-agent"; \ | |
+ TEST_SSH_SSHADD="$${BUILDDIR}/ssh-add"; \ | |
+ TEST_SSH_SSHKEYGEN="$${BUILDDIR}/ssh-keygen"; \ | |
+ TEST_SSH_SSHPKCS11HELPER="$${BUILDDIR}/ssh-pkcs11-helper"; \ | |
+ TEST_SSH_SSHKEYSCAN="$${BUILDDIR}/ssh-keyscan"; \ | |
+ TEST_SSH_SFTP="$${BUILDDIR}/sftp"; \ | |
+ TEST_SSH_SFTPSERVER="$${BUILDDIR}/sftp-server"; \ | |
+ TEST_SSH_PLINK="plink"; \ | |
+ TEST_SSH_PUTTYGEN="puttygen"; \ | |
+ TEST_SSH_CONCH="conch"; \ | |
+ TEST_SSH_IPV6="@TEST_SSH_IPV6@" ; \ | |
+ TEST_SSH_ECC="@TEST_SSH_ECC@" ; \ | |
+ cd $(srcdir)/regress || exit $$?; \ | |
+ $(MAKE) \ | |
+ .OBJDIR="$${BUILDDIR}/regress" \ | |
+ .CURDIR="`pwd`" \ | |
+ BUILDDIR="$${BUILDDIR}" \ | |
+ OBJ="$${BUILDDIR}/regress/" \ | |
+ PATH="$${BUILDDIR}:$${PATH}" \ | |
+ TEST_ENV=MALLOC_OPTIONS="@TEST_MALLOC_OPTIONS@" \ | |
+ TEST_SSH_SCP="$${TEST_SSH_SCP}" \ | |
+ TEST_SSH_SSH="$${TEST_SSH_SSH}" \ | |
+ TEST_SSH_SSHD="$${TEST_SSH_SSHD}" \ | |
+ TEST_SSH_SSHAGENT="$${TEST_SSH_SSHAGENT}" \ | |
+ TEST_SSH_SSHADD="$${TEST_SSH_SSHADD}" \ | |
+ TEST_SSH_SSHKEYGEN="$${TEST_SSH_SSHKEYGEN}" \ | |
+ TEST_SSH_SSHPKCS11HELPER="$${TEST_SSH_SSHPKCS11HELPER}" \ | |
+ TEST_SSH_SSHKEYSCAN="$${TEST_SSH_SSHKEYSCAN}" \ | |
+ TEST_SSH_SFTP="$${TEST_SSH_SFTP}" \ | |
+ TEST_SSH_SFTPSERVER="$${TEST_SSH_SFTPSERVER}" \ | |
+ TEST_SSH_PLINK="$${TEST_SSH_PLINK}" \ | |
+ TEST_SSH_PUTTYGEN="$${TEST_SSH_PUTTYGEN}" \ | |
+ TEST_SSH_CONCH="$${TEST_SSH_CONCH}" \ | |
+ TEST_SSH_IPV6="$${TEST_SSH_IPV6}" \ | |
+ TEST_SSH_ECC="$${TEST_SSH_ECC}" \ | |
+ TEST_SHELL="${TEST_SHELL}" \ | |
+ EXEEXT="$(EXEEXT)" \ | |
+ $@ && echo all tests passed | |
+ | |
+compat-tests: $(LIBCOMPAT) | |
+ (cd openbsd-compat/regress && $(MAKE)) | |
+ | |
+regressclean: | |
+ if [ -f regress/Makefile ] && [ -r regress/Makefile ]; then \ | |
+ (cd regress && $(MAKE) clean) \ | |
+ fi | |
+ | |
+survey: survey.sh ssh | |
+ @$(SHELL) ./survey.sh > survey | |
+ @echo 'The survey results have been placed in the file "survey" in the' | |
+ @echo 'current directory. Please review the file then send with' | |
+ @echo '"make send-survey".' | |
+ | |
+send-survey: survey | |
+ mail portable-survey@mindrot.org <survey | |
+ | |
+package: $(CONFIGFILES) $(MANPAGES) $(TARGETS) | |
+ if [ "@MAKE_PACKAGE_SUPPORTED@" = yes ]; then \ | |
+ sh buildpkg.sh; \ | |
+ fi | |
diff -urp --new-file openssh-6.7p1/audit-bsm.c openssh-6.7p1.patched/audit-bsm.c | |
--- openssh-6.7p1/audit-bsm.c 2012-02-23 15:40:43.000000000 -0800 | |
+++ openssh-6.7p1.patched/audit-bsm.c 2014-10-11 21:18:36.000000000 -0700 | |
@@ -263,7 +263,12 @@ bsm_audit_record(int typ, char *string, | |
pid_t pid = getpid(); | |
AuditInfoTermID tid = ssh_bsm_tid; | |
- if (the_authctxt != NULL && the_authctxt->valid) { | |
+ if (the_authctxt == NULL) { | |
+ error("BSM audit: audit record internal error (NULL ctxt)"); | |
+ abort(); | |
+ } | |
+ | |
+ if (the_authctxt->valid) { | |
uid = the_authctxt->pw->pw_uid; | |
gid = the_authctxt->pw->pw_gid; | |
} | |
diff -urp --new-file openssh-6.7p1/auth-pam.c openssh-6.7p1.patched/auth-pam.c | |
--- openssh-6.7p1/auth-pam.c 2013-12-18 16:31:45.000000000 -0800 | |
+++ openssh-6.7p1.patched/auth-pam.c 2014-10-11 21:18:36.000000000 -0700 | |
@@ -793,10 +793,11 @@ sshpam_query(void *ctx, char **name, cha | |
free(msg); | |
return (0); | |
} | |
- error("PAM: %s for %s%.100s from %.100s", msg, | |
+ error("PAM: %s for %s%.100s from %.100s via %s", msg, | |
sshpam_authctxt->valid ? "" : "illegal user ", | |
sshpam_authctxt->user, | |
- get_remote_name_or_ip(utmp_len, options.use_dns)); | |
+ get_remote_name_or_ip(utmp_len, options.use_dns), | |
+ get_local_ipaddr(packet_get_connection_in())); | |
/* FALLTHROUGH */ | |
default: | |
*num = 0; | |
diff -urp --new-file openssh-6.7p1/auth.c openssh-6.7p1.patched/auth.c | |
--- openssh-6.7p1/auth.c 2014-07-17 21:11:25.000000000 -0700 | |
+++ openssh-6.7p1.patched/auth.c 2014-10-11 21:18:36.000000000 -0700 | |
@@ -211,7 +211,7 @@ allowed_user(struct passwd * pw) | |
} | |
if (options.num_deny_groups > 0 || options.num_allow_groups > 0) { | |
/* Get the user's group access list (primary and supplementary) */ | |
- if (ga_init(pw->pw_name, pw->pw_gid) == 0) { | |
+ if (ga_init(pw) == 0) { | |
logit("User %.100s from %.100s not allowed because " | |
"not in any group", pw->pw_name, hostname); | |
return 0; | |
diff -urp --new-file openssh-6.7p1/authfd.c openssh-6.7p1.patched/authfd.c | |
--- openssh-6.7p1/authfd.c 2014-05-14 21:24:10.000000000 -0700 | |
+++ openssh-6.7p1.patched/authfd.c 2014-10-11 21:18:36.000000000 -0700 | |
@@ -650,6 +650,29 @@ ssh_remove_all_identities(Authentication | |
return decode_reply(type); | |
} | |
+/* | |
+ * Adds identities using passphrases stored in the keychain. This call is not | |
+ * meant to be used by normal applications. | |
+ */ | |
+ | |
+int | |
+ssh_add_from_keychain(AuthenticationConnection *auth) | |
+{ | |
+ Buffer msg; | |
+ int type; | |
+ | |
+ buffer_init(&msg); | |
+ buffer_put_char(&msg, SSH_AGENTC_ADD_FROM_KEYCHAIN); | |
+ | |
+ if (ssh_request_reply(auth, &msg, &msg) == 0) { | |
+ buffer_free(&msg); | |
+ return 0; | |
+ } | |
+ type = buffer_get_char(&msg); | |
+ buffer_free(&msg); | |
+ return decode_reply(type); | |
+} | |
+ | |
int | |
decode_reply(int type) | |
{ | |
diff -urp --new-file openssh-6.7p1/authfd.c.orig openssh-6.7p1.patched/authfd.c.orig | |
--- openssh-6.7p1/authfd.c.orig 1969-12-31 16:00:00.000000000 -0800 | |
+++ openssh-6.7p1.patched/authfd.c.orig 2014-05-14 21:24:10.000000000 -0700 | |
@@ -0,0 +1,669 @@ | |
+/* $OpenBSD: authfd.c,v 1.93 2014/04/29 18:01:49 markus Exp $ */ | |
+/* | |
+ * Author: Tatu Ylonen <ylo@cs.hut.fi> | |
+ * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | |
+ * All rights reserved | |
+ * Functions for connecting the local authentication agent. | |
+ * | |
+ * As far as I am concerned, the code I have written for this software | |
+ * can be used freely for any purpose. Any derived versions of this | |
+ * software must be clearly marked as such, and if the derived work is | |
+ * incompatible with the protocol description in the RFC file, it must be | |
+ * called by a name other than "ssh" or "Secure Shell". | |
+ * | |
+ * SSH2 implementation, | |
+ * Copyright (c) 2000 Markus Friedl. 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 <sys/types.h> | |
+#include <sys/un.h> | |
+#include <sys/socket.h> | |
+ | |
+#include <fcntl.h> | |
+#include <stdlib.h> | |
+#include <signal.h> | |
+#include <stdarg.h> | |
+#include <string.h> | |
+#include <unistd.h> | |
+ | |
+#include "xmalloc.h" | |
+#include "ssh.h" | |
+#include "rsa.h" | |
+#include "buffer.h" | |
+#include "key.h" | |
+#include "authfd.h" | |
+#include "cipher.h" | |
+#include "kex.h" | |
+#include "compat.h" | |
+#include "log.h" | |
+#include "atomicio.h" | |
+#include "misc.h" | |
+ | |
+static int agent_present = 0; | |
+ | |
+/* helper */ | |
+int decode_reply(int type); | |
+ | |
+/* macro to check for "agent failure" message */ | |
+#define agent_failed(x) \ | |
+ ((x == SSH_AGENT_FAILURE) || (x == SSH_COM_AGENT2_FAILURE) || \ | |
+ (x == SSH2_AGENT_FAILURE)) | |
+ | |
+int | |
+ssh_agent_present(void) | |
+{ | |
+ int authfd; | |
+ | |
+ if (agent_present) | |
+ return 1; | |
+ if ((authfd = ssh_get_authentication_socket()) == -1) | |
+ return 0; | |
+ else { | |
+ ssh_close_authentication_socket(authfd); | |
+ return 1; | |
+ } | |
+} | |
+ | |
+/* Returns the number of the authentication fd, or -1 if there is none. */ | |
+ | |
+int | |
+ssh_get_authentication_socket(void) | |
+{ | |
+ const char *authsocket; | |
+ int sock; | |
+ struct sockaddr_un sunaddr; | |
+ | |
+ authsocket = getenv(SSH_AUTHSOCKET_ENV_NAME); | |
+ if (!authsocket) | |
+ return -1; | |
+ | |
+ memset(&sunaddr, 0, sizeof(sunaddr)); | |
+ sunaddr.sun_family = AF_UNIX; | |
+ strlcpy(sunaddr.sun_path, authsocket, sizeof(sunaddr.sun_path)); | |
+ | |
+ sock = socket(AF_UNIX, SOCK_STREAM, 0); | |
+ if (sock < 0) | |
+ return -1; | |
+ | |
+ /* close on exec */ | |
+ if (fcntl(sock, F_SETFD, FD_CLOEXEC) == -1) { | |
+ close(sock); | |
+ return -1; | |
+ } | |
+ if (connect(sock, (struct sockaddr *)&sunaddr, sizeof sunaddr) < 0) { | |
+ close(sock); | |
+ return -1; | |
+ } | |
+ agent_present = 1; | |
+ return sock; | |
+} | |
+ | |
+static int | |
+ssh_request_reply(AuthenticationConnection *auth, Buffer *request, Buffer *reply) | |
+{ | |
+ u_int l, len; | |
+ char buf[1024]; | |
+ | |
+ /* Get the length of the message, and format it in the buffer. */ | |
+ len = buffer_len(request); | |
+ put_u32(buf, len); | |
+ | |
+ /* Send the length and then the packet to the agent. */ | |
+ if (atomicio(vwrite, auth->fd, buf, 4) != 4 || | |
+ atomicio(vwrite, auth->fd, buffer_ptr(request), | |
+ buffer_len(request)) != buffer_len(request)) { | |
+ error("Error writing to authentication socket."); | |
+ return 0; | |
+ } | |
+ /* | |
+ * Wait for response from the agent. First read the length of the | |
+ * response packet. | |
+ */ | |
+ if (atomicio(read, auth->fd, buf, 4) != 4) { | |
+ error("Error reading response length from authentication socket."); | |
+ return 0; | |
+ } | |
+ | |
+ /* Extract the length, and check it for sanity. */ | |
+ len = get_u32(buf); | |
+ if (len > 256 * 1024) | |
+ fatal("Authentication response too long: %u", len); | |
+ | |
+ /* Read the rest of the response in to the buffer. */ | |
+ buffer_clear(reply); | |
+ while (len > 0) { | |
+ l = len; | |
+ if (l > sizeof(buf)) | |
+ l = sizeof(buf); | |
+ if (atomicio(read, auth->fd, buf, l) != l) { | |
+ error("Error reading response from authentication socket."); | |
+ return 0; | |
+ } | |
+ buffer_append(reply, buf, l); | |
+ len -= l; | |
+ } | |
+ return 1; | |
+} | |
+ | |
+/* | |
+ * Closes the agent socket if it should be closed (depends on how it was | |
+ * obtained). The argument must have been returned by | |
+ * ssh_get_authentication_socket(). | |
+ */ | |
+ | |
+void | |
+ssh_close_authentication_socket(int sock) | |
+{ | |
+ if (getenv(SSH_AUTHSOCKET_ENV_NAME)) | |
+ close(sock); | |
+} | |
+ | |
+/* | |
+ * Opens and connects a private socket for communication with the | |
+ * authentication agent. Returns the file descriptor (which must be | |
+ * shut down and closed by the caller when no longer needed). | |
+ * Returns NULL if an error occurred and the connection could not be | |
+ * opened. | |
+ */ | |
+ | |
+AuthenticationConnection * | |
+ssh_get_authentication_connection(void) | |
+{ | |
+ AuthenticationConnection *auth; | |
+ int sock; | |
+ | |
+ sock = ssh_get_authentication_socket(); | |
+ | |
+ /* | |
+ * Fail if we couldn't obtain a connection. This happens if we | |
+ * exited due to a timeout. | |
+ */ | |
+ if (sock < 0) | |
+ return NULL; | |
+ | |
+ auth = xcalloc(1, sizeof(*auth)); | |
+ auth->fd = sock; | |
+ buffer_init(&auth->identities); | |
+ auth->howmany = 0; | |
+ | |
+ return auth; | |
+} | |
+ | |
+/* | |
+ * Closes the connection to the authentication agent and frees any associated | |
+ * memory. | |
+ */ | |
+ | |
+void | |
+ssh_close_authentication_connection(AuthenticationConnection *auth) | |
+{ | |
+ buffer_free(&auth->identities); | |
+ close(auth->fd); | |
+ free(auth); | |
+} | |
+ | |
+/* Lock/unlock agent */ | |
+int | |
+ssh_lock_agent(AuthenticationConnection *auth, int lock, const char *password) | |
+{ | |
+ int type; | |
+ Buffer msg; | |
+ | |
+ buffer_init(&msg); | |
+ buffer_put_char(&msg, lock ? SSH_AGENTC_LOCK : SSH_AGENTC_UNLOCK); | |
+ buffer_put_cstring(&msg, password); | |
+ | |
+ if (ssh_request_reply(auth, &msg, &msg) == 0) { | |
+ buffer_free(&msg); | |
+ return 0; | |
+ } | |
+ type = buffer_get_char(&msg); | |
+ buffer_free(&msg); | |
+ return decode_reply(type); | |
+} | |
+ | |
+/* | |
+ * Returns the first authentication identity held by the agent. | |
+ */ | |
+ | |
+int | |
+ssh_get_num_identities(AuthenticationConnection *auth, int version) | |
+{ | |
+ int type, code1 = 0, code2 = 0; | |
+ Buffer request; | |
+ | |
+ switch (version) { | |
+ case 1: | |
+ code1 = SSH_AGENTC_REQUEST_RSA_IDENTITIES; | |
+ code2 = SSH_AGENT_RSA_IDENTITIES_ANSWER; | |
+ break; | |
+ case 2: | |
+ code1 = SSH2_AGENTC_REQUEST_IDENTITIES; | |
+ code2 = SSH2_AGENT_IDENTITIES_ANSWER; | |
+ break; | |
+ default: | |
+ return 0; | |
+ } | |
+ | |
+ /* | |
+ * Send a message to the agent requesting for a list of the | |
+ * identities it can represent. | |
+ */ | |
+ buffer_init(&request); | |
+ buffer_put_char(&request, code1); | |
+ | |
+ buffer_clear(&auth->identities); | |
+ if (ssh_request_reply(auth, &request, &auth->identities) == 0) { | |
+ buffer_free(&request); | |
+ return 0; | |
+ } | |
+ buffer_free(&request); | |
+ | |
+ /* Get message type, and verify that we got a proper answer. */ | |
+ type = buffer_get_char(&auth->identities); | |
+ if (agent_failed(type)) { | |
+ return 0; | |
+ } else if (type != code2) { | |
+ fatal("Bad authentication reply message type: %d", type); | |
+ } | |
+ | |
+ /* Get the number of entries in the response and check it for sanity. */ | |
+ auth->howmany = buffer_get_int(&auth->identities); | |
+ if ((u_int)auth->howmany > 1024) | |
+ fatal("Too many identities in authentication reply: %d", | |
+ auth->howmany); | |
+ | |
+ return auth->howmany; | |
+} | |
+ | |
+Key * | |
+ssh_get_first_identity(AuthenticationConnection *auth, char **comment, int version) | |
+{ | |
+ /* get number of identities and return the first entry (if any). */ | |
+ if (ssh_get_num_identities(auth, version) > 0) | |
+ return ssh_get_next_identity(auth, comment, version); | |
+ return NULL; | |
+} | |
+ | |
+Key * | |
+ssh_get_next_identity(AuthenticationConnection *auth, char **comment, int version) | |
+{ | |
+#ifdef WITH_SSH1 | |
+ int keybits; | |
+ u_int bits; | |
+#endif | |
+ u_char *blob; | |
+ u_int blen; | |
+ Key *key = NULL; | |
+ | |
+ /* Return failure if no more entries. */ | |
+ if (auth->howmany <= 0) | |
+ return NULL; | |
+ | |
+ /* | |
+ * Get the next entry from the packet. These will abort with a fatal | |
+ * error if the packet is too short or contains corrupt data. | |
+ */ | |
+ switch (version) { | |
+#ifdef WITH_SSH1 | |
+ case 1: | |
+ key = key_new(KEY_RSA1); | |
+ bits = buffer_get_int(&auth->identities); | |
+ buffer_get_bignum(&auth->identities, key->rsa->e); | |
+ buffer_get_bignum(&auth->identities, key->rsa->n); | |
+ *comment = buffer_get_string(&auth->identities, NULL); | |
+ keybits = BN_num_bits(key->rsa->n); | |
+ if (keybits < 0 || bits != (u_int)keybits) | |
+ logit("Warning: identity keysize mismatch: actual %d, announced %u", | |
+ BN_num_bits(key->rsa->n), bits); | |
+ break; | |
+#endif | |
+ case 2: | |
+ blob = buffer_get_string(&auth->identities, &blen); | |
+ *comment = buffer_get_string(&auth->identities, NULL); | |
+ key = key_from_blob(blob, blen); | |
+ free(blob); | |
+ break; | |
+ default: | |
+ return NULL; | |
+ } | |
+ /* Decrement the number of remaining entries. */ | |
+ auth->howmany--; | |
+ return key; | |
+} | |
+ | |
+/* | |
+ * Generates a random challenge, sends it to the agent, and waits for | |
+ * response from the agent. Returns true (non-zero) if the agent gave the | |
+ * correct answer, zero otherwise. Response type selects the style of | |
+ * response desired, with 0 corresponding to protocol version 1.0 (no longer | |
+ * supported) and 1 corresponding to protocol version 1.1. | |
+ */ | |
+ | |
+#ifdef WITH_SSH1 | |
+int | |
+ssh_decrypt_challenge(AuthenticationConnection *auth, | |
+ Key* key, BIGNUM *challenge, | |
+ u_char session_id[16], | |
+ u_int response_type, | |
+ u_char response[16]) | |
+{ | |
+ Buffer buffer; | |
+ int success = 0; | |
+ int i; | |
+ int type; | |
+ | |
+ if (key->type != KEY_RSA1) | |
+ return 0; | |
+ if (response_type == 0) { | |
+ logit("Compatibility with ssh protocol version 1.0 no longer supported."); | |
+ return 0; | |
+ } | |
+ buffer_init(&buffer); | |
+ buffer_put_char(&buffer, SSH_AGENTC_RSA_CHALLENGE); | |
+ buffer_put_int(&buffer, BN_num_bits(key->rsa->n)); | |
+ buffer_put_bignum(&buffer, key->rsa->e); | |
+ buffer_put_bignum(&buffer, key->rsa->n); | |
+ buffer_put_bignum(&buffer, challenge); | |
+ buffer_append(&buffer, session_id, 16); | |
+ buffer_put_int(&buffer, response_type); | |
+ | |
+ if (ssh_request_reply(auth, &buffer, &buffer) == 0) { | |
+ buffer_free(&buffer); | |
+ return 0; | |
+ } | |
+ type = buffer_get_char(&buffer); | |
+ | |
+ if (agent_failed(type)) { | |
+ logit("Agent admitted failure to authenticate using the key."); | |
+ } else if (type != SSH_AGENT_RSA_RESPONSE) { | |
+ fatal("Bad authentication response: %d", type); | |
+ } else { | |
+ success = 1; | |
+ /* | |
+ * Get the response from the packet. This will abort with a | |
+ * fatal error if the packet is corrupt. | |
+ */ | |
+ for (i = 0; i < 16; i++) | |
+ response[i] = (u_char)buffer_get_char(&buffer); | |
+ } | |
+ buffer_free(&buffer); | |
+ return success; | |
+} | |
+#endif | |
+ | |
+/* ask agent to sign data, returns -1 on error, 0 on success */ | |
+int | |
+ssh_agent_sign(AuthenticationConnection *auth, | |
+ Key *key, | |
+ u_char **sigp, u_int *lenp, | |
+ u_char *data, u_int datalen) | |
+{ | |
+ extern int datafellows; | |
+ Buffer msg; | |
+ u_char *blob; | |
+ u_int blen; | |
+ int type, flags = 0; | |
+ int ret = -1; | |
+ | |
+ if (key_to_blob(key, &blob, &blen) == 0) | |
+ return -1; | |
+ | |
+ if (datafellows & SSH_BUG_SIGBLOB) | |
+ flags = SSH_AGENT_OLD_SIGNATURE; | |
+ | |
+ buffer_init(&msg); | |
+ buffer_put_char(&msg, SSH2_AGENTC_SIGN_REQUEST); | |
+ buffer_put_string(&msg, blob, blen); | |
+ buffer_put_string(&msg, data, datalen); | |
+ buffer_put_int(&msg, flags); | |
+ free(blob); | |
+ | |
+ if (ssh_request_reply(auth, &msg, &msg) == 0) { | |
+ buffer_free(&msg); | |
+ return -1; | |
+ } | |
+ type = buffer_get_char(&msg); | |
+ if (agent_failed(type)) { | |
+ logit("Agent admitted failure to sign using the key."); | |
+ } else if (type != SSH2_AGENT_SIGN_RESPONSE) { | |
+ fatal("Bad authentication response: %d", type); | |
+ } else { | |
+ ret = 0; | |
+ *sigp = buffer_get_string(&msg, lenp); | |
+ } | |
+ buffer_free(&msg); | |
+ return ret; | |
+} | |
+ | |
+/* Encode key for a message to the agent. */ | |
+ | |
+#ifdef WITH_SSH1 | |
+static void | |
+ssh_encode_identity_rsa1(Buffer *b, RSA *key, const char *comment) | |
+{ | |
+ buffer_put_int(b, BN_num_bits(key->n)); | |
+ buffer_put_bignum(b, key->n); | |
+ buffer_put_bignum(b, key->e); | |
+ buffer_put_bignum(b, key->d); | |
+ /* To keep within the protocol: p < q for ssh. in SSL p > q */ | |
+ buffer_put_bignum(b, key->iqmp); /* ssh key->u */ | |
+ buffer_put_bignum(b, key->q); /* ssh key->p, SSL key->q */ | |
+ buffer_put_bignum(b, key->p); /* ssh key->q, SSL key->p */ | |
+ buffer_put_cstring(b, comment); | |
+} | |
+#endif | |
+ | |
+static void | |
+ssh_encode_identity_ssh2(Buffer *b, Key *key, const char *comment) | |
+{ | |
+ key_private_serialize(key, b); | |
+ buffer_put_cstring(b, comment); | |
+} | |
+ | |
+/* | |
+ * Adds an identity to the authentication server. This call is not meant to | |
+ * be used by normal applications. | |
+ */ | |
+ | |
+int | |
+ssh_add_identity_constrained(AuthenticationConnection *auth, Key *key, | |
+ const char *comment, u_int life, u_int confirm) | |
+{ | |
+ Buffer msg; | |
+ int type, constrained = (life || confirm); | |
+ | |
+ buffer_init(&msg); | |
+ | |
+ switch (key->type) { | |
+#ifdef WITH_SSH1 | |
+ case KEY_RSA1: | |
+ type = constrained ? | |
+ SSH_AGENTC_ADD_RSA_ID_CONSTRAINED : | |
+ SSH_AGENTC_ADD_RSA_IDENTITY; | |
+ buffer_put_char(&msg, type); | |
+ ssh_encode_identity_rsa1(&msg, key->rsa, comment); | |
+ break; | |
+#endif | |
+#ifdef WITH_OPENSSL | |
+ case KEY_RSA: | |
+ case KEY_RSA_CERT: | |
+ case KEY_RSA_CERT_V00: | |
+ case KEY_DSA: | |
+ case KEY_DSA_CERT: | |
+ case KEY_DSA_CERT_V00: | |
+ case KEY_ECDSA: | |
+ case KEY_ECDSA_CERT: | |
+#endif | |
+ case KEY_ED25519: | |
+ case KEY_ED25519_CERT: | |
+ type = constrained ? | |
+ SSH2_AGENTC_ADD_ID_CONSTRAINED : | |
+ SSH2_AGENTC_ADD_IDENTITY; | |
+ buffer_put_char(&msg, type); | |
+ ssh_encode_identity_ssh2(&msg, key, comment); | |
+ break; | |
+ default: | |
+ buffer_free(&msg); | |
+ return 0; | |
+ } | |
+ if (constrained) { | |
+ if (life != 0) { | |
+ buffer_put_char(&msg, SSH_AGENT_CONSTRAIN_LIFETIME); | |
+ buffer_put_int(&msg, life); | |
+ } | |
+ if (confirm != 0) | |
+ buffer_put_char(&msg, SSH_AGENT_CONSTRAIN_CONFIRM); | |
+ } | |
+ if (ssh_request_reply(auth, &msg, &msg) == 0) { | |
+ buffer_free(&msg); | |
+ return 0; | |
+ } | |
+ type = buffer_get_char(&msg); | |
+ buffer_free(&msg); | |
+ return decode_reply(type); | |
+} | |
+ | |
+/* | |
+ * Removes an identity from the authentication server. This call is not | |
+ * meant to be used by normal applications. | |
+ */ | |
+ | |
+int | |
+ssh_remove_identity(AuthenticationConnection *auth, Key *key) | |
+{ | |
+ Buffer msg; | |
+ int type; | |
+ u_char *blob; | |
+ u_int blen; | |
+ | |
+ buffer_init(&msg); | |
+ | |
+#ifdef WITH_SSH1 | |
+ if (key->type == KEY_RSA1) { | |
+ buffer_put_char(&msg, SSH_AGENTC_REMOVE_RSA_IDENTITY); | |
+ buffer_put_int(&msg, BN_num_bits(key->rsa->n)); | |
+ buffer_put_bignum(&msg, key->rsa->e); | |
+ buffer_put_bignum(&msg, key->rsa->n); | |
+ } else | |
+#endif | |
+ if (key->type != KEY_UNSPEC) { | |
+ key_to_blob(key, &blob, &blen); | |
+ buffer_put_char(&msg, SSH2_AGENTC_REMOVE_IDENTITY); | |
+ buffer_put_string(&msg, blob, blen); | |
+ free(blob); | |
+ } else { | |
+ buffer_free(&msg); | |
+ return 0; | |
+ } | |
+ if (ssh_request_reply(auth, &msg, &msg) == 0) { | |
+ buffer_free(&msg); | |
+ return 0; | |
+ } | |
+ type = buffer_get_char(&msg); | |
+ buffer_free(&msg); | |
+ return decode_reply(type); | |
+} | |
+ | |
+int | |
+ssh_update_card(AuthenticationConnection *auth, int add, | |
+ const char *reader_id, const char *pin, u_int life, u_int confirm) | |
+{ | |
+ Buffer msg; | |
+ int type, constrained = (life || confirm); | |
+ | |
+ if (add) { | |
+ type = constrained ? | |
+ SSH_AGENTC_ADD_SMARTCARD_KEY_CONSTRAINED : | |
+ SSH_AGENTC_ADD_SMARTCARD_KEY; | |
+ } else | |
+ type = SSH_AGENTC_REMOVE_SMARTCARD_KEY; | |
+ | |
+ buffer_init(&msg); | |
+ buffer_put_char(&msg, type); | |
+ buffer_put_cstring(&msg, reader_id); | |
+ buffer_put_cstring(&msg, pin); | |
+ | |
+ if (constrained) { | |
+ if (life != 0) { | |
+ buffer_put_char(&msg, SSH_AGENT_CONSTRAIN_LIFETIME); | |
+ buffer_put_int(&msg, life); | |
+ } | |
+ if (confirm != 0) | |
+ buffer_put_char(&msg, SSH_AGENT_CONSTRAIN_CONFIRM); | |
+ } | |
+ | |
+ if (ssh_request_reply(auth, &msg, &msg) == 0) { | |
+ buffer_free(&msg); | |
+ return 0; | |
+ } | |
+ type = buffer_get_char(&msg); | |
+ buffer_free(&msg); | |
+ return decode_reply(type); | |
+} | |
+ | |
+/* | |
+ * Removes all identities from the agent. This call is not meant to be used | |
+ * by normal applications. | |
+ */ | |
+ | |
+int | |
+ssh_remove_all_identities(AuthenticationConnection *auth, int version) | |
+{ | |
+ Buffer msg; | |
+ int type; | |
+ int code = (version==1) ? | |
+ SSH_AGENTC_REMOVE_ALL_RSA_IDENTITIES : | |
+ SSH2_AGENTC_REMOVE_ALL_IDENTITIES; | |
+ | |
+ buffer_init(&msg); | |
+ buffer_put_char(&msg, code); | |
+ | |
+ if (ssh_request_reply(auth, &msg, &msg) == 0) { | |
+ buffer_free(&msg); | |
+ return 0; | |
+ } | |
+ type = buffer_get_char(&msg); | |
+ buffer_free(&msg); | |
+ return decode_reply(type); | |
+} | |
+ | |
+int | |
+decode_reply(int type) | |
+{ | |
+ switch (type) { | |
+ case SSH_AGENT_FAILURE: | |
+ case SSH_COM_AGENT2_FAILURE: | |
+ case SSH2_AGENT_FAILURE: | |
+ logit("SSH_AGENT_FAILURE"); | |
+ return 0; | |
+ case SSH_AGENT_SUCCESS: | |
+ return 1; | |
+ default: | |
+ fatal("Bad response from authentication agent: %d", type); | |
+ } | |
+ /* NOTREACHED */ | |
+ return 0; | |
+} | |
diff -urp --new-file openssh-6.7p1/authfd.h openssh-6.7p1.patched/authfd.h | |
--- openssh-6.7p1/authfd.h 2009-10-06 14:47:02.000000000 -0700 | |
+++ openssh-6.7p1.patched/authfd.h 2014-10-11 21:18:36.000000000 -0700 | |
@@ -49,6 +49,9 @@ | |
#define SSH2_AGENTC_ADD_ID_CONSTRAINED 25 | |
#define SSH_AGENTC_ADD_SMARTCARD_KEY_CONSTRAINED 26 | |
+/* keychain */ | |
+#define SSH_AGENTC_ADD_FROM_KEYCHAIN 27 | |
+ | |
#define SSH_AGENT_CONSTRAIN_LIFETIME 1 | |
#define SSH_AGENT_CONSTRAIN_CONFIRM 2 | |
diff -urp --new-file openssh-6.7p1/config.h.in openssh-6.7p1.patched/config.h.in | |
--- openssh-6.7p1/config.h.in 2014-10-05 20:39:24.000000000 -0700 | |
+++ openssh-6.7p1.patched/config.h.in 2014-10-11 21:18:36.000000000 -0700 | |
@@ -81,6 +81,18 @@ | |
/* FreeBSD strnvis argument order is swapped compared to OpenBSD */ | |
#undef BROKEN_STRNVIS | |
+/* platform uses an in-memory credentials cache */ | |
+#undef USE_CCAPI | |
+ | |
+/* platform has a Security Authorization Session API */ | |
+#undef USE_SECURITY_SESSION_API | |
+ | |
+/* Define to 1 if you have the `copyfile' function. */ | |
+#undef HAVE_COPYFILE | |
+ | |
+/* Define to 1 if you have the <copyfile.h> header file. */ | |
+#undef HAVE_COPYFILE_H | |
+ | |
/* tcgetattr with ICANON may hang */ | |
#undef BROKEN_TCGETATTR_ICANON | |
diff -urp --new-file openssh-6.7p1/configure.ac openssh-6.7p1.patched/configure.ac | |
--- openssh-6.7p1/configure.ac 2014-08-26 13:32:01.000000000 -0700 | |
+++ openssh-6.7p1.patched/configure.ac 2014-10-11 21:18:36.000000000 -0700 | |
@@ -4766,10 +4766,40 @@ AC_CHECK_MEMBER([struct utmp.ut_line], [ | |
#endif | |
]) | |
+dnl Keychain support | |
+AC_ARG_WITH(keychain, | |
+ [ --with-keychain=apple Use Mac OS X Keychain], | |
+ [ | |
+ case "$withval" in | |
+ apple|no) | |
+ KEYCHAIN=$withval | |
+ ;; | |
+ *) | |
+ AC_MSG_ERROR(invalid keychain type: $withval) | |
+ ;; | |
+ esac | |
+ ] | |
+) | |
+if test ! -z "$KEYCHAIN" -a "$KEYCHAIN" != "no"; then | |
+ case "$KEYCHAIN" in | |
+ apple) | |
+ AC_CHECK_HEADERS(Security/Security.h, [ | |
+ CPPFLAGS="$CPPFLAGS -D__APPLE_KEYCHAIN__" | |
+ KEYCHAIN_LDFLAGS="-framework Security -framework CoreFoundation" | |
+ AC_SUBST(KEYCHAIN_LDFLAGS) | |
+ ], | |
+ AC_MSG_WARN([Security framework not found. Disabling Mac OS X Keychain support.])) | |
+ ;; | |
+ esac | |
+fi | |
+ | |
dnl Adding -Werror to CFLAGS early prevents configure tests from running. | |
dnl Add now. | |
CFLAGS="$CFLAGS $werror_flags" | |
+AC_CHECK_FUNCS(copyfile) | |
+AC_CHECK_HEADERS(copyfile.h) | |
+ | |
if test "x$ac_cv_func_getaddrinfo" != "xyes" ; then | |
TEST_SSH_IPV6=no | |
else | |
diff -urp --new-file openssh-6.7p1/configure.ac.orig openssh-6.7p1.patched/configure.ac.orig | |
--- openssh-6.7p1/configure.ac.orig 1969-12-31 16:00:00.000000000 -0800 | |
+++ openssh-6.7p1.patched/configure.ac.orig 2014-08-26 13:32:01.000000000 -0700 | |
@@ -0,0 +1,4885 @@ | |
+# $Id: configure.ac,v 1.583 2014/08/26 20:32:01 djm Exp $ | |
+# | |
+# Copyright (c) 1999-2004 Damien Miller | |
+# | |
+# Permission to use, copy, modify, and distribute this software for any | |
+# purpose with or without fee is hereby granted, provided that the above | |
+# copyright notice and this permission notice appear in all copies. | |
+# | |
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
+ | |
+AC_INIT([OpenSSH], [Portable], [openssh-unix-dev@mindrot.org]) | |
+AC_REVISION($Revision: 1.583 $) | |
+AC_CONFIG_SRCDIR([ssh.c]) | |
+AC_LANG([C]) | |
+ | |
+AC_CONFIG_HEADER([config.h]) | |
+AC_PROG_CC | |
+AC_CANONICAL_HOST | |
+AC_C_BIGENDIAN | |
+ | |
+# Checks for programs. | |
+AC_PROG_AWK | |
+AC_PROG_CPP | |
+AC_PROG_RANLIB | |
+AC_PROG_INSTALL | |
+AC_PROG_EGREP | |
+AC_PATH_PROG([AR], [ar]) | |
+AC_PATH_PROG([CAT], [cat]) | |
+AC_PATH_PROG([KILL], [kill]) | |
+AC_PATH_PROGS([PERL], [perl5 perl]) | |
+AC_PATH_PROG([SED], [sed]) | |
+AC_SUBST([PERL]) | |
+AC_PATH_PROG([ENT], [ent]) | |
+AC_SUBST([ENT]) | |
+AC_PATH_PROG([TEST_MINUS_S_SH], [bash]) | |
+AC_PATH_PROG([TEST_MINUS_S_SH], [ksh]) | |
+AC_PATH_PROG([TEST_MINUS_S_SH], [sh]) | |
+AC_PATH_PROG([SH], [sh]) | |
+AC_PATH_PROG([GROFF], [groff]) | |
+AC_PATH_PROG([NROFF], [nroff]) | |
+AC_PATH_PROG([MANDOC], [mandoc]) | |
+AC_SUBST([TEST_SHELL], [sh]) | |
+ | |
+dnl select manpage formatter | |
+if test "x$MANDOC" != "x" ; then | |
+ MANFMT="$MANDOC" | |
+elif test "x$NROFF" != "x" ; then | |
+ MANFMT="$NROFF -mandoc" | |
+elif test "x$GROFF" != "x" ; then | |
+ MANFMT="$GROFF -mandoc -Tascii" | |
+else | |
+ AC_MSG_WARN([no manpage formatted found]) | |
+ MANFMT="false" | |
+fi | |
+AC_SUBST([MANFMT]) | |
+ | |
+dnl for buildpkg.sh | |
+AC_PATH_PROG([PATH_GROUPADD_PROG], [groupadd], [groupadd], | |
+ [/usr/sbin${PATH_SEPARATOR}/etc]) | |
+AC_PATH_PROG([PATH_USERADD_PROG], [useradd], [useradd], | |
+ [/usr/sbin${PATH_SEPARATOR}/etc]) | |
+AC_CHECK_PROG([MAKE_PACKAGE_SUPPORTED], [pkgmk], [yes], [no]) | |
+if test -x /sbin/sh; then | |
+ AC_SUBST([STARTUP_SCRIPT_SHELL], [/sbin/sh]) | |
+else | |
+ AC_SUBST([STARTUP_SCRIPT_SHELL], [/bin/sh]) | |
+fi | |
+ | |
+# System features | |
+AC_SYS_LARGEFILE | |
+ | |
+if test -z "$AR" ; then | |
+ AC_MSG_ERROR([*** 'ar' missing, please install or fix your \$PATH ***]) | |
+fi | |
+ | |
+# Use LOGIN_PROGRAM from environment if possible | |
+if test ! -z "$LOGIN_PROGRAM" ; then | |
+ AC_DEFINE_UNQUOTED([LOGIN_PROGRAM_FALLBACK], ["$LOGIN_PROGRAM"], | |
+ [If your header files don't define LOGIN_PROGRAM, | |
+ then use this (detected) from environment and PATH]) | |
+else | |
+ # Search for login | |
+ AC_PATH_PROG([LOGIN_PROGRAM_FALLBACK], [login]) | |
+ if test ! -z "$LOGIN_PROGRAM_FALLBACK" ; then | |
+ AC_DEFINE_UNQUOTED([LOGIN_PROGRAM_FALLBACK], ["$LOGIN_PROGRAM_FALLBACK"]) | |
+ fi | |
+fi | |
+ | |
+AC_PATH_PROG([PATH_PASSWD_PROG], [passwd]) | |
+if test ! -z "$PATH_PASSWD_PROG" ; then | |
+ AC_DEFINE_UNQUOTED([_PATH_PASSWD_PROG], ["$PATH_PASSWD_PROG"], | |
+ [Full path of your "passwd" program]) | |
+fi | |
+ | |
+if test -z "$LD" ; then | |
+ LD=$CC | |
+fi | |
+AC_SUBST([LD]) | |
+ | |
+AC_C_INLINE | |
+ | |
+AC_CHECK_DECL([LLONG_MAX], [have_llong_max=1], , [#include <limits.h>]) | |
+AC_CHECK_DECL([SYSTR_POLICY_KILL], [have_systr_policy_kill=1], , [ | |
+ #include <sys/types.h> | |
+ #include <sys/param.h> | |
+ #include <dev/systrace.h> | |
+]) | |
+AC_CHECK_DECL([RLIMIT_NPROC], | |
+ [AC_DEFINE([HAVE_RLIMIT_NPROC], [], [sys/resource.h has RLIMIT_NPROC])], , [ | |
+ #include <sys/types.h> | |
+ #include <sys/resource.h> | |
+]) | |
+AC_CHECK_DECL([PR_SET_NO_NEW_PRIVS], [have_linux_no_new_privs=1], , [ | |
+ #include <sys/types.h> | |
+ #include <linux/prctl.h> | |
+]) | |
+ | |
+use_stack_protector=1 | |
+use_toolchain_hardening=1 | |
+AC_ARG_WITH([stackprotect], | |
+ [ --without-stackprotect Don't use compiler's stack protection], [ | |
+ if test "x$withval" = "xno"; then | |
+ use_stack_protector=0 | |
+ fi ]) | |
+AC_ARG_WITH([hardening], | |
+ [ --without-hardening Don't use toolchain hardening flags], [ | |
+ if test "x$withval" = "xno"; then | |
+ use_toolchain_hardening=0 | |
+ fi ]) | |
+ | |
+# We use -Werror for the tests only so that we catch warnings like "this is | |
+# on by default" for things like -fPIE. | |
+AC_MSG_CHECKING([if $CC supports -Werror]) | |
+saved_CFLAGS="$CFLAGS" | |
+CFLAGS="$CFLAGS -Werror" | |
+AC_COMPILE_IFELSE([AC_LANG_SOURCE([[int main(void) { return 0; }]])], | |
+ [ AC_MSG_RESULT([yes]) | |
+ WERROR="-Werror"], | |
+ [ AC_MSG_RESULT([no]) | |
+ WERROR="" ] | |
+) | |
+CFLAGS="$saved_CFLAGS" | |
+ | |
+if test "$GCC" = "yes" || test "$GCC" = "egcs"; then | |
+ OSSH_CHECK_CFLAG_COMPILE([-Qunused-arguments]) | |
+ OSSH_CHECK_CFLAG_COMPILE([-Wunknown-warning-option]) | |
+ OSSH_CHECK_CFLAG_COMPILE([-Wall]) | |
+ OSSH_CHECK_CFLAG_COMPILE([-Wpointer-arith]) | |
+ OSSH_CHECK_CFLAG_COMPILE([-Wuninitialized]) | |
+ OSSH_CHECK_CFLAG_COMPILE([-Wsign-compare]) | |
+ OSSH_CHECK_CFLAG_COMPILE([-Wformat-security]) | |
+ OSSH_CHECK_CFLAG_COMPILE([-Wsizeof-pointer-memaccess]) | |
+ OSSH_CHECK_CFLAG_COMPILE([-Wpointer-sign], [-Wno-pointer-sign]) | |
+ OSSH_CHECK_CFLAG_COMPILE([-Wunused-result], [-Wno-unused-result]) | |
+ OSSH_CHECK_CFLAG_COMPILE([-fno-strict-aliasing]) | |
+ OSSH_CHECK_CFLAG_COMPILE([-D_FORTIFY_SOURCE=2]) | |
+ if test "x$use_toolchain_hardening" = "x1"; then | |
+ OSSH_CHECK_LDFLAG_LINK([-Wl,-z,relro]) | |
+ OSSH_CHECK_LDFLAG_LINK([-Wl,-z,now]) | |
+ OSSH_CHECK_LDFLAG_LINK([-Wl,-z,noexecstack]) | |
+ # NB. -ftrapv expects certain support functions to be present in | |
+ # the compiler library (libgcc or similar) to detect integer operations | |
+ # that can overflow. We must check that the result of enabling it | |
+ # actually links. The test program compiled/linked includes a number | |
+ # of integer operations that should exercise this. | |
+ OSSH_CHECK_CFLAG_LINK([-ftrapv]) | |
+ fi | |
+ AC_MSG_CHECKING([gcc version]) | |
+ GCC_VER=`$CC -v 2>&1 | $AWK '/gcc version /{print $3}'` | |
+ case $GCC_VER in | |
+ 1.*) no_attrib_nonnull=1 ;; | |
+ 2.8* | 2.9*) | |
+ no_attrib_nonnull=1 | |
+ ;; | |
+ 2.*) no_attrib_nonnull=1 ;; | |
+ *) ;; | |
+ esac | |
+ AC_MSG_RESULT([$GCC_VER]) | |
+ | |
+ AC_MSG_CHECKING([if $CC accepts -fno-builtin-memset]) | |
+ saved_CFLAGS="$CFLAGS" | |
+ CFLAGS="$CFLAGS -fno-builtin-memset" | |
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([[ #include <string.h> ]], | |
+ [[ char b[10]; memset(b, 0, sizeof(b)); ]])], | |
+ [ AC_MSG_RESULT([yes]) ], | |
+ [ AC_MSG_RESULT([no]) | |
+ CFLAGS="$saved_CFLAGS" ] | |
+ ) | |
+ | |
+ # -fstack-protector-all doesn't always work for some GCC versions | |
+ # and/or platforms, so we test if we can. If it's not supported | |
+ # on a given platform gcc will emit a warning so we use -Werror. | |
+ if test "x$use_stack_protector" = "x1"; then | |
+ for t in -fstack-protector-strong -fstack-protector-all \ | |
+ -fstack-protector; do | |
+ AC_MSG_CHECKING([if $CC supports $t]) | |
+ saved_CFLAGS="$CFLAGS" | |
+ saved_LDFLAGS="$LDFLAGS" | |
+ CFLAGS="$CFLAGS $t -Werror" | |
+ LDFLAGS="$LDFLAGS $t -Werror" | |
+ AC_LINK_IFELSE( | |
+ [AC_LANG_PROGRAM([[ #include <stdio.h> ]], | |
+ [[ | |
+ char x[256]; | |
+ snprintf(x, sizeof(x), "XXX"); | |
+ ]])], | |
+ [ AC_MSG_RESULT([yes]) | |
+ CFLAGS="$saved_CFLAGS $t" | |
+ LDFLAGS="$saved_LDFLAGS $t" | |
+ AC_MSG_CHECKING([if $t works]) | |
+ AC_RUN_IFELSE( | |
+ [AC_LANG_PROGRAM([[ #include <stdio.h> ]], | |
+ [[ | |
+ char x[256]; | |
+ snprintf(x, sizeof(x), "XXX"); | |
+ ]])], | |
+ [ AC_MSG_RESULT([yes]) | |
+ break ], | |
+ [ AC_MSG_RESULT([no]) ], | |
+ [ AC_MSG_WARN([cross compiling: cannot test]) | |
+ break ] | |
+ ) | |
+ ], | |
+ [ AC_MSG_RESULT([no]) ] | |
+ ) | |
+ CFLAGS="$saved_CFLAGS" | |
+ LDFLAGS="$saved_LDFLAGS" | |
+ done | |
+ fi | |
+ | |
+ if test -z "$have_llong_max"; then | |
+ # retry LLONG_MAX with -std=gnu99, needed on some Linuxes | |
+ unset ac_cv_have_decl_LLONG_MAX | |
+ saved_CFLAGS="$CFLAGS" | |
+ CFLAGS="$CFLAGS -std=gnu99" | |
+ AC_CHECK_DECL([LLONG_MAX], | |
+ [have_llong_max=1], | |
+ [CFLAGS="$saved_CFLAGS"], | |
+ [#include <limits.h>] | |
+ ) | |
+ fi | |
+fi | |
+ | |
+AC_MSG_CHECKING([if compiler allows __attribute__ on return types]) | |
+AC_COMPILE_IFELSE( | |
+ [AC_LANG_PROGRAM([[ | |
+#include <stdlib.h> | |
+__attribute__((__unused__)) static void foo(void){return;}]], | |
+ [[ exit(0); ]])], | |
+ [ AC_MSG_RESULT([yes]) ], | |
+ [ AC_MSG_RESULT([no]) | |
+ AC_DEFINE(NO_ATTRIBUTE_ON_RETURN_TYPE, 1, | |
+ [compiler does not accept __attribute__ on return types]) ] | |
+) | |
+ | |
+if test "x$no_attrib_nonnull" != "x1" ; then | |
+ AC_DEFINE([HAVE_ATTRIBUTE__NONNULL__], [1], [Have attribute nonnull]) | |
+fi | |
+ | |
+AC_ARG_WITH([rpath], | |
+ [ --without-rpath Disable auto-added -R linker paths], | |
+ [ | |
+ if test "x$withval" = "xno" ; then | |
+ need_dash_r="" | |
+ fi | |
+ if test "x$withval" = "xyes" ; then | |
+ need_dash_r=1 | |
+ fi | |
+ ] | |
+) | |
+ | |
+# Allow user to specify flags | |
+AC_ARG_WITH([cflags], | |
+ [ --with-cflags Specify additional flags to pass to compiler], | |
+ [ | |
+ if test -n "$withval" && test "x$withval" != "xno" && \ | |
+ test "x${withval}" != "xyes"; then | |
+ CFLAGS="$CFLAGS $withval" | |
+ fi | |
+ ] | |
+) | |
+AC_ARG_WITH([cppflags], | |
+ [ --with-cppflags Specify additional flags to pass to preprocessor] , | |
+ [ | |
+ if test -n "$withval" && test "x$withval" != "xno" && \ | |
+ test "x${withval}" != "xyes"; then | |
+ CPPFLAGS="$CPPFLAGS $withval" | |
+ fi | |
+ ] | |
+) | |
+AC_ARG_WITH([ldflags], | |
+ [ --with-ldflags Specify additional flags to pass to linker], | |
+ [ | |
+ if test -n "$withval" && test "x$withval" != "xno" && \ | |
+ test "x${withval}" != "xyes"; then | |
+ LDFLAGS="$LDFLAGS $withval" | |
+ fi | |
+ ] | |
+) | |
+AC_ARG_WITH([libs], | |
+ [ --with-libs Specify additional libraries to link with], | |
+ [ | |
+ if test -n "$withval" && test "x$withval" != "xno" && \ | |
+ test "x${withval}" != "xyes"; then | |
+ LIBS="$LIBS $withval" | |
+ fi | |
+ ] | |
+) | |
+AC_ARG_WITH([Werror], | |
+ [ --with-Werror Build main code with -Werror], | |
+ [ | |
+ if test -n "$withval" && test "x$withval" != "xno"; then | |
+ werror_flags="-Werror" | |
+ if test "x${withval}" != "xyes"; then | |
+ werror_flags="$withval" | |
+ fi | |
+ fi | |
+ ] | |
+) | |
+ | |
+AC_CHECK_HEADERS([ \ | |
+ blf.h \ | |
+ bstring.h \ | |
+ crypt.h \ | |
+ crypto/sha2.h \ | |
+ dirent.h \ | |
+ endian.h \ | |
+ elf.h \ | |
+ features.h \ | |
+ fcntl.h \ | |
+ floatingpoint.h \ | |
+ getopt.h \ | |
+ glob.h \ | |
+ ia.h \ | |
+ iaf.h \ | |
+ inttypes.h \ | |
+ limits.h \ | |
+ locale.h \ | |
+ login.h \ | |
+ maillock.h \ | |
+ ndir.h \ | |
+ net/if_tun.h \ | |
+ netdb.h \ | |
+ netgroup.h \ | |
+ pam/pam_appl.h \ | |
+ paths.h \ | |
+ poll.h \ | |
+ pty.h \ | |
+ readpassphrase.h \ | |
+ rpc/types.h \ | |
+ security/pam_appl.h \ | |
+ sha2.h \ | |
+ shadow.h \ | |
+ stddef.h \ | |
+ stdint.h \ | |
+ string.h \ | |
+ strings.h \ | |
+ sys/audit.h \ | |
+ sys/bitypes.h \ | |
+ sys/bsdtty.h \ | |
+ sys/capability.h \ | |
+ sys/cdefs.h \ | |
+ sys/dir.h \ | |
+ sys/mman.h \ | |
+ sys/ndir.h \ | |
+ sys/poll.h \ | |
+ sys/prctl.h \ | |
+ sys/pstat.h \ | |
+ sys/select.h \ | |
+ sys/stat.h \ | |
+ sys/stream.h \ | |
+ sys/stropts.h \ | |
+ sys/strtio.h \ | |
+ sys/statvfs.h \ | |
+ sys/sysmacros.h \ | |
+ sys/time.h \ | |
+ sys/timers.h \ | |
+ time.h \ | |
+ tmpdir.h \ | |
+ ttyent.h \ | |
+ ucred.h \ | |
+ unistd.h \ | |
+ usersec.h \ | |
+ util.h \ | |
+ utime.h \ | |
+ utmp.h \ | |
+ utmpx.h \ | |
+ vis.h \ | |
+]) | |
+ | |
+# lastlog.h requires sys/time.h to be included first on Solaris | |
+AC_CHECK_HEADERS([lastlog.h], [], [], [ | |
+#ifdef HAVE_SYS_TIME_H | |
+# include <sys/time.h> | |
+#endif | |
+]) | |
+ | |
+# sys/ptms.h requires sys/stream.h to be included first on Solaris | |
+AC_CHECK_HEADERS([sys/ptms.h], [], [], [ | |
+#ifdef HAVE_SYS_STREAM_H | |
+# include <sys/stream.h> | |
+#endif | |
+]) | |
+ | |
+# login_cap.h requires sys/types.h on NetBSD | |
+AC_CHECK_HEADERS([login_cap.h], [], [], [ | |
+#include <sys/types.h> | |
+]) | |
+ | |
+# older BSDs need sys/param.h before sys/mount.h | |
+AC_CHECK_HEADERS([sys/mount.h], [], [], [ | |
+#include <sys/param.h> | |
+]) | |
+ | |
+# Android requires sys/socket.h to be included before sys/un.h | |
+AC_CHECK_HEADERS([sys/un.h], [], [], [ | |
+#include <sys/types.h> | |
+#include <sys/socket.h> | |
+]) | |
+ | |
+# Messages for features tested for in target-specific section | |
+SIA_MSG="no" | |
+SPC_MSG="no" | |
+SP_MSG="no" | |
+ | |
+# Check for some target-specific stuff | |
+case "$host" in | |
+*-*-aix*) | |
+ # Some versions of VAC won't allow macro redefinitions at | |
+ # -qlanglevel=ansi, and autoconf 2.60 sometimes insists on using that | |
+ # particularly with older versions of vac or xlc. | |
+ # It also throws errors about null macro argments, but these are | |
+ # not fatal. | |
+ AC_MSG_CHECKING([if compiler allows macro redefinitions]) | |
+ AC_COMPILE_IFELSE( | |
+ [AC_LANG_PROGRAM([[ | |
+#define testmacro foo | |
+#define testmacro bar]], | |
+ [[ exit(0); ]])], | |
+ [ AC_MSG_RESULT([yes]) ], | |
+ [ AC_MSG_RESULT([no]) | |
+ CC="`echo $CC | sed 's/-qlanglvl\=ansi//g'`" | |
+ LD="`echo $LD | sed 's/-qlanglvl\=ansi//g'`" | |
+ CFLAGS="`echo $CFLAGS | sed 's/-qlanglvl\=ansi//g'`" | |
+ CPPFLAGS="`echo $CPPFLAGS | sed 's/-qlanglvl\=ansi//g'`" | |
+ ] | |
+ ) | |
+ | |
+ AC_MSG_CHECKING([how to specify blibpath for linker ($LD)]) | |
+ if (test -z "$blibpath"); then | |
+ blibpath="/usr/lib:/lib" | |
+ fi | |
+ saved_LDFLAGS="$LDFLAGS" | |
+ if test "$GCC" = "yes"; then | |
+ flags="-Wl,-blibpath: -Wl,-rpath, -blibpath:" | |
+ else | |
+ flags="-blibpath: -Wl,-blibpath: -Wl,-rpath," | |
+ fi | |
+ for tryflags in $flags ;do | |
+ if (test -z "$blibflags"); then | |
+ LDFLAGS="$saved_LDFLAGS $tryflags$blibpath" | |
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([[]], [[]])], | |
+ [blibflags=$tryflags], []) | |
+ fi | |
+ done | |
+ if (test -z "$blibflags"); then | |
+ AC_MSG_RESULT([not found]) | |
+ AC_MSG_ERROR([*** must be able to specify blibpath on AIX - check config.log]) | |
+ else | |
+ AC_MSG_RESULT([$blibflags]) | |
+ fi | |
+ LDFLAGS="$saved_LDFLAGS" | |
+ dnl Check for authenticate. Might be in libs.a on older AIXes | |
+ AC_CHECK_FUNC([authenticate], [AC_DEFINE([WITH_AIXAUTHENTICATE], [1], | |
+ [Define if you want to enable AIX4's authenticate function])], | |
+ [AC_CHECK_LIB([s], [authenticate], | |
+ [ AC_DEFINE([WITH_AIXAUTHENTICATE]) | |
+ LIBS="$LIBS -ls" | |
+ ]) | |
+ ]) | |
+ dnl Check for various auth function declarations in headers. | |
+ AC_CHECK_DECLS([authenticate, loginrestrictions, loginsuccess, | |
+ passwdexpired, setauthdb], , , [#include <usersec.h>]) | |
+ dnl Check if loginfailed is declared and takes 4 arguments (AIX >= 5.2) | |
+ AC_CHECK_DECLS([loginfailed], | |
+ [AC_MSG_CHECKING([if loginfailed takes 4 arguments]) | |
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include <usersec.h> ]], | |
+ [[ (void)loginfailed("user","host","tty",0); ]])], | |
+ [AC_MSG_RESULT([yes]) | |
+ AC_DEFINE([AIX_LOGINFAILED_4ARG], [1], | |
+ [Define if your AIX loginfailed() function | |
+ takes 4 arguments (AIX >= 5.2)])], [AC_MSG_RESULT([no]) | |
+ ])], | |
+ [], | |
+ [#include <usersec.h>] | |
+ ) | |
+ AC_CHECK_FUNCS([getgrset setauthdb]) | |
+ AC_CHECK_DECL([F_CLOSEM], | |
+ AC_DEFINE([HAVE_FCNTL_CLOSEM], [1], [Use F_CLOSEM fcntl for closefrom]), | |
+ [], | |
+ [ #include <limits.h> | |
+ #include <fcntl.h> ] | |
+ ) | |
+ check_for_aix_broken_getaddrinfo=1 | |
+ AC_DEFINE([BROKEN_REALPATH], [1], [Define if you have a broken realpath.]) | |
+ AC_DEFINE([SETEUID_BREAKS_SETUID], [1], | |
+ [Define if your platform breaks doing a seteuid before a setuid]) | |
+ AC_DEFINE([BROKEN_SETREUID], [1], [Define if your setreuid() is broken]) | |
+ AC_DEFINE([BROKEN_SETREGID], [1], [Define if your setregid() is broken]) | |
+ dnl AIX handles lastlog as part of its login message | |
+ AC_DEFINE([DISABLE_LASTLOG], [1], [Define if you don't want to use lastlog]) | |
+ AC_DEFINE([LOGIN_NEEDS_UTMPX], [1], | |
+ [Some systems need a utmpx entry for /bin/login to work]) | |
+ AC_DEFINE([SPT_TYPE], [SPT_REUSEARGV], | |
+ [Define to a Set Process Title type if your system is | |
+ supported by bsd-setproctitle.c]) | |
+ AC_DEFINE([SSHPAM_CHAUTHTOK_NEEDS_RUID], [1], | |
+ [AIX 5.2 and 5.3 (and presumably newer) require this]) | |
+ AC_DEFINE([PTY_ZEROREAD], [1], [read(1) can return 0 for a non-closed fd]) | |
+ AC_DEFINE([PLATFORM_SYS_DIR_UID], 2, [System dirs owned by bin (uid 2)]) | |
+ ;; | |
+*-*-android*) | |
+ AC_DEFINE([DISABLE_UTMP], [1], [Define if you don't want to use utmp]) | |
+ AC_DEFINE([DISABLE_WTMP], [1], [Define if you don't want to use wtmp]) | |
+ ;; | |
+*-*-cygwin*) | |
+ check_for_libcrypt_later=1 | |
+ LIBS="$LIBS /usr/lib/textreadmode.o" | |
+ AC_DEFINE([HAVE_CYGWIN], [1], [Define if you are on Cygwin]) | |
+ AC_DEFINE([USE_PIPES], [1], [Use PIPES instead of a socketpair()]) | |
+ AC_DEFINE([DISABLE_SHADOW], [1], | |
+ [Define if you want to disable shadow passwords]) | |
+ AC_DEFINE([NO_X11_UNIX_SOCKETS], [1], | |
+ [Define if X11 doesn't support AF_UNIX sockets on that system]) | |
+ AC_DEFINE([NO_IPPORT_RESERVED_CONCEPT], [1], | |
+ [Define if the concept of ports only accessible to | |
+ superusers isn't known]) | |
+ AC_DEFINE([DISABLE_FD_PASSING], [1], | |
+ [Define if your platform needs to skip post auth | |
+ file descriptor passing]) | |
+ AC_DEFINE([SSH_IOBUFSZ], [65535], [Windows is sensitive to read buffer size]) | |
+ AC_DEFINE([FILESYSTEM_NO_BACKSLASH], [1], [File names may not contain backslash characters]) | |
+ # Cygwin defines optargs, optargs as declspec(dllimport) for historical | |
+ # reasons which cause compile warnings, so we disable those warnings. | |
+ OSSH_CHECK_CFLAG_COMPILE([-Wno-attributes]) | |
+ ;; | |
+*-*-dgux*) | |
+ AC_DEFINE([IP_TOS_IS_BROKEN], [1], | |
+ [Define if your system choked on IP TOS setting]) | |
+ AC_DEFINE([SETEUID_BREAKS_SETUID]) | |
+ AC_DEFINE([BROKEN_SETREUID]) | |
+ AC_DEFINE([BROKEN_SETREGID]) | |
+ ;; | |
+*-*-darwin*) | |
+ use_pie=auto | |
+ AC_MSG_CHECKING([if we have working getaddrinfo]) | |
+ AC_RUN_IFELSE([AC_LANG_SOURCE([[ #include <mach-o/dyld.h> | |
+main() { if (NSVersionOfRunTimeLibrary("System") >= (60 << 16)) | |
+ exit(0); | |
+ else | |
+ exit(1); | |
+} | |
+ ]])], | |
+ [AC_MSG_RESULT([working])], | |
+ [AC_MSG_RESULT([buggy]) | |
+ AC_DEFINE([BROKEN_GETADDRINFO], [1], | |
+ [getaddrinfo is broken (if present)]) | |
+ ], | |
+ [AC_MSG_RESULT([assume it is working])]) | |
+ AC_DEFINE([SETEUID_BREAKS_SETUID]) | |
+ AC_DEFINE([BROKEN_SETREUID]) | |
+ AC_DEFINE([BROKEN_SETREGID]) | |
+ AC_DEFINE([BROKEN_GLOB], [1], [OS X glob does not do what we expect]) | |
+ AC_DEFINE_UNQUOTED([BIND_8_COMPAT], [1], | |
+ [Define if your resolver libs need this for getrrsetbyname]) | |
+ AC_DEFINE([SSH_TUN_FREEBSD], [1], [Open tunnel devices the FreeBSD way]) | |
+ AC_DEFINE([SSH_TUN_COMPAT_AF], [1], | |
+ [Use tunnel device compatibility to OpenBSD]) | |
+ AC_DEFINE([SSH_TUN_PREPEND_AF], [1], | |
+ [Prepend the address family to IP tunnel traffic]) | |
+ m4_pattern_allow([AU_IPv]) | |
+ AC_CHECK_DECL([AU_IPv4], [], | |
+ AC_DEFINE([AU_IPv4], [0], [System only supports IPv4 audit records]) | |
+ [#include <bsm/audit.h>] | |
+ AC_DEFINE([LASTLOG_WRITE_PUTUTXLINE], [1], | |
+ [Define if pututxline updates lastlog too]) | |
+ ) | |
+ AC_DEFINE([SPT_TYPE], [SPT_REUSEARGV], | |
+ [Define to a Set Process Title type if your system is | |
+ supported by bsd-setproctitle.c]) | |
+ AC_CHECK_FUNCS([sandbox_init]) | |
+ AC_CHECK_HEADERS([sandbox.h]) | |
+ ;; | |
+*-*-dragonfly*) | |
+ SSHDLIBS="$SSHDLIBS -lcrypt" | |
+ TEST_MALLOC_OPTIONS="AFGJPRX" | |
+ ;; | |
+*-*-haiku*) | |
+ LIBS="$LIBS -lbsd " | |
+ AC_CHECK_LIB([network], [socket]) | |
+ AC_DEFINE([HAVE_U_INT64_T]) | |
+ MANTYPE=man | |
+ ;; | |
+*-*-hpux*) | |
+ # first we define all of the options common to all HP-UX releases | |
+ CPPFLAGS="$CPPFLAGS -D_HPUX_SOURCE -D_XOPEN_SOURCE -D_XOPEN_SOURCE_EXTENDED=1" | |
+ IPADDR_IN_DISPLAY=yes | |
+ AC_DEFINE([USE_PIPES]) | |
+ AC_DEFINE([LOGIN_NO_ENDOPT], [1], | |
+ [Define if your login program cannot handle end of options ("--")]) | |
+ AC_DEFINE([LOGIN_NEEDS_UTMPX]) | |
+ AC_DEFINE([LOCKED_PASSWD_STRING], ["*"], | |
+ [String used in /etc/passwd to denote locked account]) | |
+ AC_DEFINE([SPT_TYPE], [SPT_PSTAT]) | |
+ AC_DEFINE([PLATFORM_SYS_DIR_UID], 2, [System dirs owned by bin (uid 2)]) | |
+ maildir="/var/mail" | |
+ LIBS="$LIBS -lsec" | |
+ AC_CHECK_LIB([xnet], [t_error], , | |
+ [AC_MSG_ERROR([*** -lxnet needed on HP-UX - check config.log ***])]) | |
+ | |
+ # next, we define all of the options specific to major releases | |
+ case "$host" in | |
+ *-*-hpux10*) | |
+ if test -z "$GCC"; then | |
+ CFLAGS="$CFLAGS -Ae" | |
+ fi | |
+ ;; | |
+ *-*-hpux11*) | |
+ AC_DEFINE([PAM_SUN_CODEBASE], [1], | |
+ [Define if you are using Solaris-derived PAM which | |
+ passes pam_messages to the conversation function | |
+ with an extra level of indirection]) | |
+ AC_DEFINE([DISABLE_UTMP], [1], | |
+ [Define if you don't want to use utmp]) | |
+ AC_DEFINE([USE_BTMP], [1], [Use btmp to log bad logins]) | |
+ check_for_hpux_broken_getaddrinfo=1 | |
+ check_for_conflicting_getspnam=1 | |
+ ;; | |
+ esac | |
+ | |
+ # lastly, we define options specific to minor releases | |
+ case "$host" in | |
+ *-*-hpux10.26) | |
+ AC_DEFINE([HAVE_SECUREWARE], [1], | |
+ [Define if you have SecureWare-based | |
+ protected password database]) | |
+ disable_ptmx_check=yes | |
+ LIBS="$LIBS -lsecpw" | |
+ ;; | |
+ esac | |
+ ;; | |
+*-*-irix5*) | |
+ PATH="$PATH:/usr/etc" | |
+ AC_DEFINE([BROKEN_INET_NTOA], [1], | |
+ [Define if you system's inet_ntoa is busted | |
+ (e.g. Irix gcc issue)]) | |
+ AC_DEFINE([SETEUID_BREAKS_SETUID]) | |
+ AC_DEFINE([BROKEN_SETREUID]) | |
+ AC_DEFINE([BROKEN_SETREGID]) | |
+ AC_DEFINE([WITH_ABBREV_NO_TTY], [1], | |
+ [Define if you shouldn't strip 'tty' from your | |
+ ttyname in [uw]tmp]) | |
+ AC_DEFINE([LOCKED_PASSWD_STRING], ["*LK*"]) | |
+ ;; | |
+*-*-irix6*) | |
+ PATH="$PATH:/usr/etc" | |
+ AC_DEFINE([WITH_IRIX_ARRAY], [1], | |
+ [Define if you have/want arrays | |
+ (cluster-wide session managment, not C arrays)]) | |
+ AC_DEFINE([WITH_IRIX_PROJECT], [1], | |
+ [Define if you want IRIX project management]) | |
+ AC_DEFINE([WITH_IRIX_AUDIT], [1], | |
+ [Define if you want IRIX audit trails]) | |
+ AC_CHECK_FUNC([jlimit_startjob], [AC_DEFINE([WITH_IRIX_JOBS], [1], | |
+ [Define if you want IRIX kernel jobs])]) | |
+ AC_DEFINE([BROKEN_INET_NTOA]) | |
+ AC_DEFINE([SETEUID_BREAKS_SETUID]) | |
+ AC_DEFINE([BROKEN_SETREUID]) | |
+ AC_DEFINE([BROKEN_SETREGID]) | |
+ AC_DEFINE([BROKEN_UPDWTMPX], [1], [updwtmpx is broken (if present)]) | |
+ AC_DEFINE([WITH_ABBREV_NO_TTY]) | |
+ AC_DEFINE([LOCKED_PASSWD_STRING], ["*LK*"]) | |
+ ;; | |
+*-*-k*bsd*-gnu | *-*-kopensolaris*-gnu) | |
+ check_for_libcrypt_later=1 | |
+ AC_DEFINE([PAM_TTY_KLUDGE]) | |
+ AC_DEFINE([LOCKED_PASSWD_PREFIX], ["!"]) | |
+ AC_DEFINE([SPT_TYPE], [SPT_REUSEARGV]) | |
+ AC_DEFINE([_PATH_BTMP], ["/var/log/btmp"], [log for bad login attempts]) | |
+ AC_DEFINE([USE_BTMP], [1], [Use btmp to log bad logins]) | |
+ ;; | |
+*-*-linux*) | |
+ no_dev_ptmx=1 | |
+ use_pie=auto | |
+ check_for_libcrypt_later=1 | |
+ check_for_openpty_ctty_bug=1 | |
+ AC_DEFINE([PAM_TTY_KLUDGE], [1], | |
+ [Work around problematic Linux PAM modules handling of PAM_TTY]) | |
+ AC_DEFINE([LOCKED_PASSWD_PREFIX], ["!"], | |
+ [String used in /etc/passwd to denote locked account]) | |
+ AC_DEFINE([SPT_TYPE], [SPT_REUSEARGV]) | |
+ AC_DEFINE([LINK_OPNOTSUPP_ERRNO], [EPERM], | |
+ [Define to whatever link() returns for "not supported" | |
+ if it doesn't return EOPNOTSUPP.]) | |
+ AC_DEFINE([_PATH_BTMP], ["/var/log/btmp"], [log for bad login attempts]) | |
+ AC_DEFINE([USE_BTMP]) | |
+ AC_DEFINE([LINUX_OOM_ADJUST], [1], [Adjust Linux out-of-memory killer]) | |
+ inet6_default_4in6=yes | |
+ case `uname -r` in | |
+ 1.*|2.0.*) | |
+ AC_DEFINE([BROKEN_CMSG_TYPE], [1], | |
+ [Define if cmsg_type is not passed correctly]) | |
+ ;; | |
+ esac | |
+ # tun(4) forwarding compat code | |
+ AC_CHECK_HEADERS([linux/if_tun.h]) | |
+ if test "x$ac_cv_header_linux_if_tun_h" = "xyes" ; then | |
+ AC_DEFINE([SSH_TUN_LINUX], [1], | |
+ [Open tunnel devices the Linux tun/tap way]) | |
+ AC_DEFINE([SSH_TUN_COMPAT_AF], [1], | |
+ [Use tunnel device compatibility to OpenBSD]) | |
+ AC_DEFINE([SSH_TUN_PREPEND_AF], [1], | |
+ [Prepend the address family to IP tunnel traffic]) | |
+ fi | |
+ AC_CHECK_HEADERS([linux/seccomp.h linux/filter.h linux/audit.h], [], | |
+ [], [#include <linux/types.h>]) | |
+ AC_CHECK_FUNCS([prctl]) | |
+ AC_MSG_CHECKING([for seccomp architecture]) | |
+ seccomp_audit_arch= | |
+ case "$host" in | |
+ x86_64-*) | |
+ seccomp_audit_arch=AUDIT_ARCH_X86_64 | |
+ ;; | |
+ i*86-*) | |
+ seccomp_audit_arch=AUDIT_ARCH_I386 | |
+ ;; | |
+ arm*-*) | |
+ seccomp_audit_arch=AUDIT_ARCH_ARM | |
+ ;; | |
+ esac | |
+ if test "x$seccomp_audit_arch" != "x" ; then | |
+ AC_MSG_RESULT(["$seccomp_audit_arch"]) | |
+ AC_DEFINE_UNQUOTED([SECCOMP_AUDIT_ARCH], [$seccomp_audit_arch], | |
+ [Specify the system call convention in use]) | |
+ else | |
+ AC_MSG_RESULT([architecture not supported]) | |
+ fi | |
+ ;; | |
+mips-sony-bsd|mips-sony-newsos4) | |
+ AC_DEFINE([NEED_SETPGRP], [1], [Need setpgrp to acquire controlling tty]) | |
+ SONY=1 | |
+ ;; | |
+*-*-netbsd*) | |
+ check_for_libcrypt_before=1 | |
+ if test "x$withval" != "xno" ; then | |
+ need_dash_r=1 | |
+ fi | |
+ AC_DEFINE([SSH_TUN_FREEBSD], [1], [Open tunnel devices the FreeBSD way]) | |
+ AC_CHECK_HEADER([net/if_tap.h], , | |
+ AC_DEFINE([SSH_TUN_NO_L2], [1], [No layer 2 tunnel support])) | |
+ AC_DEFINE([SSH_TUN_PREPEND_AF], [1], | |
+ [Prepend the address family to IP tunnel traffic]) | |
+ TEST_MALLOC_OPTIONS="AJRX" | |
+ AC_DEFINE([BROKEN_STRNVIS], [1], | |
+ [NetBSD strnvis argument order is swapped compared to OpenBSD]) | |
+ AC_DEFINE([BROKEN_READ_COMPARISON], [1], | |
+ [NetBSD read function is sometimes redirected, breaking atomicio comparisons against it]) | |
+ ;; | |
+*-*-freebsd*) | |
+ check_for_libcrypt_later=1 | |
+ AC_DEFINE([LOCKED_PASSWD_PREFIX], ["*LOCKED*"], [Account locked with pw(1)]) | |
+ AC_DEFINE([SSH_TUN_FREEBSD], [1], [Open tunnel devices the FreeBSD way]) | |
+ AC_CHECK_HEADER([net/if_tap.h], , | |
+ AC_DEFINE([SSH_TUN_NO_L2], [1], [No layer 2 tunnel support])) | |
+ AC_DEFINE([BROKEN_GLOB], [1], [FreeBSD glob does not do what we need]) | |
+ AC_DEFINE([BROKEN_STRNVIS], [1], | |
+ [FreeBSD strnvis argument order is swapped compared to OpenBSD]) | |
+ TEST_MALLOC_OPTIONS="AJRX" | |
+ # Preauth crypto occasionally uses file descriptors for crypto offload | |
+ # and will crash if they cannot be opened. | |
+ AC_DEFINE([SANDBOX_SKIP_RLIMIT_NOFILE], [1], | |
+ [define if setrlimit RLIMIT_NOFILE breaks things]) | |
+ ;; | |
+*-*-bsdi*) | |
+ AC_DEFINE([SETEUID_BREAKS_SETUID]) | |
+ AC_DEFINE([BROKEN_SETREUID]) | |
+ AC_DEFINE([BROKEN_SETREGID]) | |
+ ;; | |
+*-next-*) | |
+ conf_lastlog_location="/usr/adm/lastlog" | |
+ conf_utmp_location=/etc/utmp | |
+ conf_wtmp_location=/usr/adm/wtmp | |
+ maildir=/usr/spool/mail | |
+ AC_DEFINE([HAVE_NEXT], [1], [Define if you are on NeXT]) | |
+ AC_DEFINE([BROKEN_REALPATH]) | |
+ AC_DEFINE([USE_PIPES]) | |
+ AC_DEFINE([BROKEN_SAVED_UIDS], [1], [Needed for NeXT]) | |
+ ;; | |
+*-*-openbsd*) | |
+ use_pie=auto | |
+ AC_DEFINE([HAVE_ATTRIBUTE__SENTINEL__], [1], [OpenBSD's gcc has sentinel]) | |
+ AC_DEFINE([HAVE_ATTRIBUTE__BOUNDED__], [1], [OpenBSD's gcc has bounded]) | |
+ AC_DEFINE([SSH_TUN_OPENBSD], [1], [Open tunnel devices the OpenBSD way]) | |
+ AC_DEFINE([SYSLOG_R_SAFE_IN_SIGHAND], [1], | |
+ [syslog_r function is safe to use in in a signal handler]) | |
+ TEST_MALLOC_OPTIONS="AFGJPRX" | |
+ ;; | |
+*-*-solaris*) | |
+ if test "x$withval" != "xno" ; then | |
+ need_dash_r=1 | |
+ fi | |
+ AC_DEFINE([PAM_SUN_CODEBASE]) | |
+ AC_DEFINE([LOGIN_NEEDS_UTMPX]) | |
+ AC_DEFINE([LOGIN_NEEDS_TERM], [1], | |
+ [Some versions of /bin/login need the TERM supplied | |
+ on the commandline]) | |
+ AC_DEFINE([PAM_TTY_KLUDGE]) | |
+ AC_DEFINE([SSHPAM_CHAUTHTOK_NEEDS_RUID], [1], | |
+ [Define if pam_chauthtok wants real uid set | |
+ to the unpriv'ed user]) | |
+ AC_DEFINE([LOCKED_PASSWD_STRING], ["*LK*"]) | |
+ # Pushing STREAMS modules will cause sshd to acquire a controlling tty. | |
+ AC_DEFINE([SSHD_ACQUIRES_CTTY], [1], | |
+ [Define if sshd somehow reacquires a controlling TTY | |
+ after setsid()]) | |
+ AC_DEFINE([PASSWD_NEEDS_USERNAME], [1], [must supply username to passwd | |
+ in case the name is longer than 8 chars]) | |
+ AC_DEFINE([BROKEN_TCGETATTR_ICANON], [1], [tcgetattr with ICANON may hang]) | |
+ external_path_file=/etc/default/login | |
+ # hardwire lastlog location (can't detect it on some versions) | |
+ conf_lastlog_location="/var/adm/lastlog" | |
+ AC_MSG_CHECKING([for obsolete utmp and wtmp in solaris2.x]) | |
+ sol2ver=`echo "$host"| sed -e 's/.*[[0-9]]\.//'` | |
+ if test "$sol2ver" -ge 8; then | |
+ AC_MSG_RESULT([yes]) | |
+ AC_DEFINE([DISABLE_UTMP]) | |
+ AC_DEFINE([DISABLE_WTMP], [1], | |
+ [Define if you don't want to use wtmp]) | |
+ else | |
+ AC_MSG_RESULT([no]) | |
+ fi | |
+ AC_ARG_WITH([solaris-contracts], | |
+ [ --with-solaris-contracts Enable Solaris process contracts (experimental)], | |
+ [ | |
+ AC_CHECK_LIB([contract], [ct_tmpl_activate], | |
+ [ AC_DEFINE([USE_SOLARIS_PROCESS_CONTRACTS], [1], | |
+ [Define if you have Solaris process contracts]) | |
+ SSHDLIBS="$SSHDLIBS -lcontract" | |
+ SPC_MSG="yes" ], ) | |
+ ], | |
+ ) | |
+ AC_ARG_WITH([solaris-projects], | |
+ [ --with-solaris-projects Enable Solaris projects (experimental)], | |
+ [ | |
+ AC_CHECK_LIB([project], [setproject], | |
+ [ AC_DEFINE([USE_SOLARIS_PROJECTS], [1], | |
+ [Define if you have Solaris projects]) | |
+ SSHDLIBS="$SSHDLIBS -lproject" | |
+ SP_MSG="yes" ], ) | |
+ ], | |
+ ) | |
+ TEST_SHELL=$SHELL # let configure find us a capable shell | |
+ ;; | |
+*-*-sunos4*) | |
+ CPPFLAGS="$CPPFLAGS -DSUNOS4" | |
+ AC_CHECK_FUNCS([getpwanam]) | |
+ AC_DEFINE([PAM_SUN_CODEBASE]) | |
+ conf_utmp_location=/etc/utmp | |
+ conf_wtmp_location=/var/adm/wtmp | |
+ conf_lastlog_location=/var/adm/lastlog | |
+ AC_DEFINE([USE_PIPES]) | |
+ ;; | |
+*-ncr-sysv*) | |
+ LIBS="$LIBS -lc89" | |
+ AC_DEFINE([USE_PIPES]) | |
+ AC_DEFINE([SSHD_ACQUIRES_CTTY]) | |
+ AC_DEFINE([SETEUID_BREAKS_SETUID]) | |
+ AC_DEFINE([BROKEN_SETREUID]) | |
+ AC_DEFINE([BROKEN_SETREGID]) | |
+ ;; | |
+*-sni-sysv*) | |
+ # /usr/ucblib MUST NOT be searched on ReliantUNIX | |
+ AC_CHECK_LIB([dl], [dlsym], ,) | |
+ # -lresolv needs to be at the end of LIBS or DNS lookups break | |
+ AC_CHECK_LIB([resolv], [res_query], [ LIBS="$LIBS -lresolv" ]) | |
+ IPADDR_IN_DISPLAY=yes | |
+ AC_DEFINE([USE_PIPES]) | |
+ AC_DEFINE([IP_TOS_IS_BROKEN]) | |
+ AC_DEFINE([SETEUID_BREAKS_SETUID]) | |
+ AC_DEFINE([BROKEN_SETREUID]) | |
+ AC_DEFINE([BROKEN_SETREGID]) | |
+ AC_DEFINE([SSHD_ACQUIRES_CTTY]) | |
+ external_path_file=/etc/default/login | |
+ # /usr/ucblib/libucb.a no longer needed on ReliantUNIX | |
+ # Attention: always take care to bind libsocket and libnsl before libc, | |
+ # otherwise you will find lots of "SIOCGPGRP errno 22" on syslog | |
+ ;; | |
+# UnixWare 1.x, UnixWare 2.x, and others based on code from Univel. | |
+*-*-sysv4.2*) | |
+ AC_DEFINE([USE_PIPES]) | |
+ AC_DEFINE([SETEUID_BREAKS_SETUID]) | |
+ AC_DEFINE([BROKEN_SETREUID]) | |
+ AC_DEFINE([BROKEN_SETREGID]) | |
+ AC_DEFINE([PASSWD_NEEDS_USERNAME], [1], [must supply username to passwd]) | |
+ AC_DEFINE([LOCKED_PASSWD_STRING], ["*LK*"]) | |
+ TEST_SHELL=$SHELL # let configure find us a capable shell | |
+ ;; | |
+# UnixWare 7.x, OpenUNIX 8 | |
+*-*-sysv5*) | |
+ CPPFLAGS="$CPPFLAGS -Dvsnprintf=_xvsnprintf -Dsnprintf=_xsnprintf" | |
+ AC_DEFINE([UNIXWARE_LONG_PASSWORDS], [1], [Support passwords > 8 chars]) | |
+ AC_DEFINE([USE_PIPES]) | |
+ AC_DEFINE([SETEUID_BREAKS_SETUID]) | |
+ AC_DEFINE([BROKEN_GETADDRINFO]) | |
+ AC_DEFINE([BROKEN_SETREUID]) | |
+ AC_DEFINE([BROKEN_SETREGID]) | |
+ AC_DEFINE([PASSWD_NEEDS_USERNAME]) | |
+ TEST_SHELL=$SHELL # let configure find us a capable shell | |
+ case "$host" in | |
+ *-*-sysv5SCO_SV*) # SCO OpenServer 6.x | |
+ maildir=/var/spool/mail | |
+ AC_DEFINE([BROKEN_LIBIAF], [1], | |
+ [ia_uinfo routines not supported by OS yet]) | |
+ AC_DEFINE([BROKEN_UPDWTMPX]) | |
+ AC_CHECK_LIB([prot], [getluid], [ LIBS="$LIBS -lprot" | |
+ AC_CHECK_FUNCS([getluid setluid], , , [-lprot]) | |
+ AC_DEFINE([HAVE_SECUREWARE]) | |
+ AC_DEFINE([DISABLE_SHADOW]) | |
+ ], , ) | |
+ ;; | |
+ *) AC_DEFINE([LOCKED_PASSWD_STRING], ["*LK*"]) | |
+ check_for_libcrypt_later=1 | |
+ ;; | |
+ esac | |
+ ;; | |
+*-*-sysv*) | |
+ ;; | |
+# SCO UNIX and OEM versions of SCO UNIX | |
+*-*-sco3.2v4*) | |
+ AC_MSG_ERROR("This Platform is no longer supported.") | |
+ ;; | |
+# SCO OpenServer 5.x | |
+*-*-sco3.2v5*) | |
+ if test -z "$GCC"; then | |
+ CFLAGS="$CFLAGS -belf" | |
+ fi | |
+ LIBS="$LIBS -lprot -lx -ltinfo -lm" | |
+ no_dev_ptmx=1 | |
+ AC_DEFINE([USE_PIPES]) | |
+ AC_DEFINE([HAVE_SECUREWARE]) | |
+ AC_DEFINE([DISABLE_SHADOW]) | |
+ AC_DEFINE([DISABLE_FD_PASSING]) | |
+ AC_DEFINE([SETEUID_BREAKS_SETUID]) | |
+ AC_DEFINE([BROKEN_GETADDRINFO]) | |
+ AC_DEFINE([BROKEN_SETREUID]) | |
+ AC_DEFINE([BROKEN_SETREGID]) | |
+ AC_DEFINE([WITH_ABBREV_NO_TTY]) | |
+ AC_DEFINE([BROKEN_UPDWTMPX]) | |
+ AC_DEFINE([PASSWD_NEEDS_USERNAME]) | |
+ AC_CHECK_FUNCS([getluid setluid]) | |
+ MANTYPE=man | |
+ TEST_SHELL=$SHELL # let configure find us a capable shell | |
+ SKIP_DISABLE_LASTLOG_DEFINE=yes | |
+ ;; | |
+*-*-unicosmk*) | |
+ AC_DEFINE([NO_SSH_LASTLOG], [1], | |
+ [Define if you don't want to use lastlog in session.c]) | |
+ AC_DEFINE([SETEUID_BREAKS_SETUID]) | |
+ AC_DEFINE([BROKEN_SETREUID]) | |
+ AC_DEFINE([BROKEN_SETREGID]) | |
+ AC_DEFINE([USE_PIPES]) | |
+ AC_DEFINE([DISABLE_FD_PASSING]) | |
+ LDFLAGS="$LDFLAGS" | |
+ LIBS="$LIBS -lgen -lrsc -lshare -luex -lacm" | |
+ MANTYPE=cat | |
+ ;; | |
+*-*-unicosmp*) | |
+ AC_DEFINE([SETEUID_BREAKS_SETUID]) | |
+ AC_DEFINE([BROKEN_SETREUID]) | |
+ AC_DEFINE([BROKEN_SETREGID]) | |
+ AC_DEFINE([WITH_ABBREV_NO_TTY]) | |
+ AC_DEFINE([USE_PIPES]) | |
+ AC_DEFINE([DISABLE_FD_PASSING]) | |
+ LDFLAGS="$LDFLAGS" | |
+ LIBS="$LIBS -lgen -lacid -ldb" | |
+ MANTYPE=cat | |
+ ;; | |
+*-*-unicos*) | |
+ AC_DEFINE([SETEUID_BREAKS_SETUID]) | |
+ AC_DEFINE([BROKEN_SETREUID]) | |
+ AC_DEFINE([BROKEN_SETREGID]) | |
+ AC_DEFINE([USE_PIPES]) | |
+ AC_DEFINE([DISABLE_FD_PASSING]) | |
+ AC_DEFINE([NO_SSH_LASTLOG]) | |
+ LDFLAGS="$LDFLAGS -Wl,-Dmsglevel=334:fatal" | |
+ LIBS="$LIBS -lgen -lrsc -lshare -luex -lacm" | |
+ MANTYPE=cat | |
+ ;; | |
+*-dec-osf*) | |
+ AC_MSG_CHECKING([for Digital Unix SIA]) | |
+ no_osfsia="" | |
+ AC_ARG_WITH([osfsia], | |
+ [ --with-osfsia Enable Digital Unix SIA], | |
+ [ | |
+ if test "x$withval" = "xno" ; then | |
+ AC_MSG_RESULT([disabled]) | |
+ no_osfsia=1 | |
+ fi | |
+ ], | |
+ ) | |
+ if test -z "$no_osfsia" ; then | |
+ if test -f /etc/sia/matrix.conf; then | |
+ AC_MSG_RESULT([yes]) | |
+ AC_DEFINE([HAVE_OSF_SIA], [1], | |
+ [Define if you have Digital Unix Security | |
+ Integration Architecture]) | |
+ AC_DEFINE([DISABLE_LOGIN], [1], | |
+ [Define if you don't want to use your | |
+ system's login() call]) | |
+ AC_DEFINE([DISABLE_FD_PASSING]) | |
+ LIBS="$LIBS -lsecurity -ldb -lm -laud" | |
+ SIA_MSG="yes" | |
+ else | |
+ AC_MSG_RESULT([no]) | |
+ AC_DEFINE([LOCKED_PASSWD_SUBSTR], ["Nologin"], | |
+ [String used in /etc/passwd to denote locked account]) | |
+ fi | |
+ fi | |
+ AC_DEFINE([BROKEN_GETADDRINFO]) | |
+ AC_DEFINE([SETEUID_BREAKS_SETUID]) | |
+ AC_DEFINE([BROKEN_SETREUID]) | |
+ AC_DEFINE([BROKEN_SETREGID]) | |
+ AC_DEFINE([BROKEN_READV_COMPARISON], [1], [Can't do comparisons on readv]) | |
+ ;; | |
+ | |
+*-*-nto-qnx*) | |
+ AC_DEFINE([USE_PIPES]) | |
+ AC_DEFINE([NO_X11_UNIX_SOCKETS]) | |
+ AC_DEFINE([DISABLE_LASTLOG]) | |
+ AC_DEFINE([SSHD_ACQUIRES_CTTY]) | |
+ AC_DEFINE([BROKEN_SHADOW_EXPIRE], [1], [QNX shadow support is broken]) | |
+ enable_etc_default_login=no # has incompatible /etc/default/login | |
+ case "$host" in | |
+ *-*-nto-qnx6*) | |
+ AC_DEFINE([DISABLE_FD_PASSING]) | |
+ ;; | |
+ esac | |
+ ;; | |
+ | |
+*-*-ultrix*) | |
+ AC_DEFINE([BROKEN_GETGROUPS], [1], [getgroups(0,NULL) will return -1]) | |
+ AC_DEFINE([BROKEN_MMAP], [1], [Ultrix mmap can't map files]) | |
+ AC_DEFINE([NEED_SETPGRP]) | |
+ AC_DEFINE([HAVE_SYS_SYSLOG_H], [1], [Force use of sys/syslog.h on Ultrix]) | |
+ ;; | |
+ | |
+*-*-lynxos) | |
+ CFLAGS="$CFLAGS -D__NO_INCLUDE_WARN__" | |
+ AC_DEFINE([BROKEN_SETVBUF], [1], [LynxOS has broken setvbuf() implementation]) | |
+ ;; | |
+esac | |
+ | |
+AC_MSG_CHECKING([compiler and flags for sanity]) | |
+AC_RUN_IFELSE([AC_LANG_PROGRAM([[ #include <stdio.h> ]], [[ exit(0); ]])], | |
+ [ AC_MSG_RESULT([yes]) ], | |
+ [ | |
+ AC_MSG_RESULT([no]) | |
+ AC_MSG_ERROR([*** compiler cannot create working executables, check config.log ***]) | |
+ ], | |
+ [ AC_MSG_WARN([cross compiling: not checking compiler sanity]) ] | |
+) | |
+ | |
+dnl Checks for header files. | |
+# Checks for libraries. | |
+AC_CHECK_FUNC([yp_match], , [AC_CHECK_LIB([nsl], [yp_match])]) | |
+AC_CHECK_FUNC([setsockopt], , [AC_CHECK_LIB([socket], [setsockopt])]) | |
+ | |
+dnl IRIX and Solaris 2.5.1 have dirname() in libgen | |
+AC_CHECK_FUNCS([dirname], [AC_CHECK_HEADERS([libgen.h])] , [ | |
+ AC_CHECK_LIB([gen], [dirname], [ | |
+ AC_CACHE_CHECK([for broken dirname], | |
+ ac_cv_have_broken_dirname, [ | |
+ save_LIBS="$LIBS" | |
+ LIBS="$LIBS -lgen" | |
+ AC_RUN_IFELSE( | |
+ [AC_LANG_SOURCE([[ | |
+#include <libgen.h> | |
+#include <string.h> | |
+ | |
+int main(int argc, char **argv) { | |
+ char *s, buf[32]; | |
+ | |
+ strncpy(buf,"/etc", 32); | |
+ s = dirname(buf); | |
+ if (!s || strncmp(s, "/", 32) != 0) { | |
+ exit(1); | |
+ } else { | |
+ exit(0); | |
+ } | |
+} | |
+ ]])], | |
+ [ ac_cv_have_broken_dirname="no" ], | |
+ [ ac_cv_have_broken_dirname="yes" ], | |
+ [ ac_cv_have_broken_dirname="no" ], | |
+ ) | |
+ LIBS="$save_LIBS" | |
+ ]) | |
+ if test "x$ac_cv_have_broken_dirname" = "xno" ; then | |
+ LIBS="$LIBS -lgen" | |
+ AC_DEFINE([HAVE_DIRNAME]) | |
+ AC_CHECK_HEADERS([libgen.h]) | |
+ fi | |
+ ]) | |
+]) | |
+ | |
+AC_CHECK_FUNC([getspnam], , | |
+ [AC_CHECK_LIB([gen], [getspnam], [LIBS="$LIBS -lgen"])]) | |
+AC_SEARCH_LIBS([basename], [gen], [AC_DEFINE([HAVE_BASENAME], [1], | |
+ [Define if you have the basename function.])]) | |
+ | |
+dnl zlib is required | |
+AC_ARG_WITH([zlib], | |
+ [ --with-zlib=PATH Use zlib in PATH], | |
+ [ if test "x$withval" = "xno" ; then | |
+ AC_MSG_ERROR([*** zlib is required ***]) | |
+ elif test "x$withval" != "xyes"; then | |
+ if test -d "$withval/lib"; then | |
+ if test -n "${need_dash_r}"; then | |
+ LDFLAGS="-L${withval}/lib -R${withval}/lib ${LDFLAGS}" | |
+ else | |
+ LDFLAGS="-L${withval}/lib ${LDFLAGS}" | |
+ fi | |
+ else | |
+ if test -n "${need_dash_r}"; then | |
+ LDFLAGS="-L${withval} -R${withval} ${LDFLAGS}" | |
+ else | |
+ LDFLAGS="-L${withval} ${LDFLAGS}" | |
+ fi | |
+ fi | |
+ if test -d "$withval/include"; then | |
+ CPPFLAGS="-I${withval}/include ${CPPFLAGS}" | |
+ else | |
+ CPPFLAGS="-I${withval} ${CPPFLAGS}" | |
+ fi | |
+ fi ] | |
+) | |
+ | |
+AC_CHECK_HEADER([zlib.h], ,[AC_MSG_ERROR([*** zlib.h missing - please install first or check config.log ***])]) | |
+AC_CHECK_LIB([z], [deflate], , | |
+ [ | |
+ saved_CPPFLAGS="$CPPFLAGS" | |
+ saved_LDFLAGS="$LDFLAGS" | |
+ save_LIBS="$LIBS" | |
+ dnl Check default zlib install dir | |
+ if test -n "${need_dash_r}"; then | |
+ LDFLAGS="-L/usr/local/lib -R/usr/local/lib ${saved_LDFLAGS}" | |
+ else | |
+ LDFLAGS="-L/usr/local/lib ${saved_LDFLAGS}" | |
+ fi | |
+ CPPFLAGS="-I/usr/local/include ${saved_CPPFLAGS}" | |
+ LIBS="$LIBS -lz" | |
+ AC_TRY_LINK_FUNC([deflate], [AC_DEFINE([HAVE_LIBZ])], | |
+ [ | |
+ AC_MSG_ERROR([*** zlib missing - please install first or check config.log ***]) | |
+ ] | |
+ ) | |
+ ] | |
+) | |
+ | |
+AC_ARG_WITH([zlib-version-check], | |
+ [ --without-zlib-version-check Disable zlib version check], | |
+ [ if test "x$withval" = "xno" ; then | |
+ zlib_check_nonfatal=1 | |
+ fi | |
+ ] | |
+) | |
+ | |
+AC_MSG_CHECKING([for possibly buggy zlib]) | |
+AC_RUN_IFELSE([AC_LANG_PROGRAM([[ | |
+#include <stdio.h> | |
+#include <stdlib.h> | |
+#include <zlib.h> | |
+ ]], | |
+ [[ | |
+ int a=0, b=0, c=0, d=0, n, v; | |
+ n = sscanf(ZLIB_VERSION, "%d.%d.%d.%d", &a, &b, &c, &d); | |
+ if (n != 3 && n != 4) | |
+ exit(1); | |
+ v = a*1000000 + b*10000 + c*100 + d; | |
+ fprintf(stderr, "found zlib version %s (%d)\n", ZLIB_VERSION, v); | |
+ | |
+ /* 1.1.4 is OK */ | |
+ if (a == 1 && b == 1 && c >= 4) | |
+ exit(0); | |
+ | |
+ /* 1.2.3 and up are OK */ | |
+ if (v >= 1020300) | |
+ exit(0); | |
+ | |
+ exit(2); | |
+ ]])], | |
+ AC_MSG_RESULT([no]), | |
+ [ AC_MSG_RESULT([yes]) | |
+ if test -z "$zlib_check_nonfatal" ; then | |
+ AC_MSG_ERROR([*** zlib too old - check config.log *** | |
+Your reported zlib version has known security problems. It's possible your | |
+vendor has fixed these problems without changing the version number. If you | |
+are sure this is the case, you can disable the check by running | |
+"./configure --without-zlib-version-check". | |
+If you are in doubt, upgrade zlib to version 1.2.3 or greater. | |
+See http://www.gzip.org/zlib/ for details.]) | |
+ else | |
+ AC_MSG_WARN([zlib version may have security problems]) | |
+ fi | |
+ ], | |
+ [ AC_MSG_WARN([cross compiling: not checking zlib version]) ] | |
+) | |
+ | |
+dnl UnixWare 2.x | |
+AC_CHECK_FUNC([strcasecmp], | |
+ [], [ AC_CHECK_LIB([resolv], [strcasecmp], [LIBS="$LIBS -lresolv"]) ] | |
+) | |
+AC_CHECK_FUNCS([utimes], | |
+ [], [ AC_CHECK_LIB([c89], [utimes], [AC_DEFINE([HAVE_UTIMES]) | |
+ LIBS="$LIBS -lc89"]) ] | |
+) | |
+ | |
+dnl Checks for libutil functions | |
+AC_CHECK_HEADERS([bsd/libutil.h libutil.h]) | |
+AC_SEARCH_LIBS([fmt_scaled], [util bsd]) | |
+AC_SEARCH_LIBS([scan_scaled], [util bsd]) | |
+AC_SEARCH_LIBS([login], [util bsd]) | |
+AC_SEARCH_LIBS([logout], [util bsd]) | |
+AC_SEARCH_LIBS([logwtmp], [util bsd]) | |
+AC_SEARCH_LIBS([openpty], [util bsd]) | |
+AC_SEARCH_LIBS([updwtmp], [util bsd]) | |
+AC_CHECK_FUNCS([fmt_scaled scan_scaled login logout openpty updwtmp logwtmp]) | |
+ | |
+# On some platforms, inet_ntop may be found in libresolv or libnsl. | |
+AC_SEARCH_LIBS([inet_ntop], [resolv nsl]) | |
+ | |
+AC_FUNC_STRFTIME | |
+ | |
+# Check for ALTDIRFUNC glob() extension | |
+AC_MSG_CHECKING([for GLOB_ALTDIRFUNC support]) | |
+AC_EGREP_CPP([FOUNDIT], | |
+ [ | |
+ #include <glob.h> | |
+ #ifdef GLOB_ALTDIRFUNC | |
+ FOUNDIT | |
+ #endif | |
+ ], | |
+ [ | |
+ AC_DEFINE([GLOB_HAS_ALTDIRFUNC], [1], | |
+ [Define if your system glob() function has | |
+ the GLOB_ALTDIRFUNC extension]) | |
+ AC_MSG_RESULT([yes]) | |
+ ], | |
+ [ | |
+ AC_MSG_RESULT([no]) | |
+ ] | |
+) | |
+ | |
+# Check for g.gl_matchc glob() extension | |
+AC_MSG_CHECKING([for gl_matchc field in glob_t]) | |
+AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include <glob.h> ]], | |
+ [[ glob_t g; g.gl_matchc = 1; ]])], | |
+ [ | |
+ AC_DEFINE([GLOB_HAS_GL_MATCHC], [1], | |
+ [Define if your system glob() function has | |
+ gl_matchc options in glob_t]) | |
+ AC_MSG_RESULT([yes]) | |
+ ], [ | |
+ AC_MSG_RESULT([no]) | |
+]) | |
+ | |
+# Check for g.gl_statv glob() extension | |
+AC_MSG_CHECKING([for gl_statv and GLOB_KEEPSTAT extensions for glob]) | |
+AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include <glob.h> ]], [[ | |
+#ifndef GLOB_KEEPSTAT | |
+#error "glob does not support GLOB_KEEPSTAT extension" | |
+#endif | |
+glob_t g; | |
+g.gl_statv = NULL; | |
+]])], | |
+ [ | |
+ AC_DEFINE([GLOB_HAS_GL_STATV], [1], | |
+ [Define if your system glob() function has | |
+ gl_statv options in glob_t]) | |
+ AC_MSG_RESULT([yes]) | |
+ ], [ | |
+ AC_MSG_RESULT([no]) | |
+ | |
+]) | |
+ | |
+AC_CHECK_DECLS([GLOB_NOMATCH], , , [#include <glob.h>]) | |
+ | |
+AC_MSG_CHECKING([whether struct dirent allocates space for d_name]) | |
+AC_RUN_IFELSE( | |
+ [AC_LANG_PROGRAM([[ | |
+#include <sys/types.h> | |
+#include <dirent.h>]], | |
+ [[ | |
+ struct dirent d; | |
+ exit(sizeof(d.d_name)<=sizeof(char)); | |
+ ]])], | |
+ [AC_MSG_RESULT([yes])], | |
+ [ | |
+ AC_MSG_RESULT([no]) | |
+ AC_DEFINE([BROKEN_ONE_BYTE_DIRENT_D_NAME], [1], | |
+ [Define if your struct dirent expects you to | |
+ allocate extra space for d_name]) | |
+ ], | |
+ [ | |
+ AC_MSG_WARN([cross compiling: assuming BROKEN_ONE_BYTE_DIRENT_D_NAME]) | |
+ AC_DEFINE([BROKEN_ONE_BYTE_DIRENT_D_NAME]) | |
+ ] | |
+) | |
+ | |
+AC_MSG_CHECKING([for /proc/pid/fd directory]) | |
+if test -d "/proc/$$/fd" ; then | |
+ AC_DEFINE([HAVE_PROC_PID], [1], [Define if you have /proc/$pid/fd]) | |
+ AC_MSG_RESULT([yes]) | |
+else | |
+ AC_MSG_RESULT([no]) | |
+fi | |
+ | |
+# Check whether user wants S/Key support | |
+SKEY_MSG="no" | |
+AC_ARG_WITH([skey], | |
+ [ --with-skey[[=PATH]] Enable S/Key support (optionally in PATH)], | |
+ [ | |
+ if test "x$withval" != "xno" ; then | |
+ | |
+ if test "x$withval" != "xyes" ; then | |
+ CPPFLAGS="$CPPFLAGS -I${withval}/include" | |
+ LDFLAGS="$LDFLAGS -L${withval}/lib" | |
+ fi | |
+ | |
+ AC_DEFINE([SKEY], [1], [Define if you want S/Key support]) | |
+ LIBS="-lskey $LIBS" | |
+ SKEY_MSG="yes" | |
+ | |
+ AC_MSG_CHECKING([for s/key support]) | |
+ AC_LINK_IFELSE( | |
+ [AC_LANG_PROGRAM([[ | |
+#include <stdio.h> | |
+#include <skey.h> | |
+ ]], [[ | |
+ char *ff = skey_keyinfo(""); ff=""; | |
+ exit(0); | |
+ ]])], | |
+ [AC_MSG_RESULT([yes])], | |
+ [ | |
+ AC_MSG_RESULT([no]) | |
+ AC_MSG_ERROR([** Incomplete or missing s/key libraries.]) | |
+ ]) | |
+ AC_MSG_CHECKING([if skeychallenge takes 4 arguments]) | |
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ | |
+#include <stdio.h> | |
+#include <skey.h> | |
+ ]], [[ | |
+ (void)skeychallenge(NULL,"name","",0); | |
+ ]])], | |
+ [ | |
+ AC_MSG_RESULT([yes]) | |
+ AC_DEFINE([SKEYCHALLENGE_4ARG], [1], | |
+ [Define if your skeychallenge() | |
+ function takes 4 arguments (NetBSD)])], | |
+ [ | |
+ AC_MSG_RESULT([no]) | |
+ ]) | |
+ fi | |
+ ] | |
+) | |
+ | |
+# Check whether user wants to use ldns | |
+LDNS_MSG="no" | |
+AC_ARG_WITH(ldns, | |
+ [ --with-ldns[[=PATH]] Use ldns for DNSSEC support (optionally in PATH)], | |
+ [ | |
+ if test "x$withval" != "xno" ; then | |
+ | |
+ if test "x$withval" != "xyes" ; then | |
+ CPPFLAGS="$CPPFLAGS -I${withval}/include" | |
+ LDFLAGS="$LDFLAGS -L${withval}/lib" | |
+ fi | |
+ | |
+ AC_DEFINE(HAVE_LDNS, 1, [Define if you want ldns support]) | |
+ LIBS="-lldns $LIBS" | |
+ LDNS_MSG="yes" | |
+ | |
+ AC_MSG_CHECKING([for ldns support]) | |
+ AC_LINK_IFELSE( | |
+ [AC_LANG_SOURCE([[ | |
+#include <stdio.h> | |
+#include <stdlib.h> | |
+#include <stdint.h> | |
+#include <ldns/ldns.h> | |
+int main() { ldns_status status = ldns_verify_trusted(NULL, NULL, NULL, NULL); status=LDNS_STATUS_OK; exit(0); } | |
+ ]]) | |
+ ], | |
+ [AC_MSG_RESULT(yes)], | |
+ [ | |
+ AC_MSG_RESULT(no) | |
+ AC_MSG_ERROR([** Incomplete or missing ldns libraries.]) | |
+ ]) | |
+ fi | |
+ ] | |
+) | |
+ | |
+# Check whether user wants libedit support | |
+LIBEDIT_MSG="no" | |
+AC_ARG_WITH([libedit], | |
+ [ --with-libedit[[=PATH]] Enable libedit support for sftp], | |
+ [ if test "x$withval" != "xno" ; then | |
+ if test "x$withval" = "xyes" ; then | |
+ AC_PATH_TOOL([PKGCONFIG], [pkg-config], [no]) | |
+ if test "x$PKGCONFIG" != "xno"; then | |
+ AC_MSG_CHECKING([if $PKGCONFIG knows about libedit]) | |
+ if "$PKGCONFIG" libedit; then | |
+ AC_MSG_RESULT([yes]) | |
+ use_pkgconfig_for_libedit=yes | |
+ else | |
+ AC_MSG_RESULT([no]) | |
+ fi | |
+ fi | |
+ else | |
+ CPPFLAGS="$CPPFLAGS -I${withval}/include" | |
+ if test -n "${need_dash_r}"; then | |
+ LDFLAGS="-L${withval}/lib -R${withval}/lib ${LDFLAGS}" | |
+ else | |
+ LDFLAGS="-L${withval}/lib ${LDFLAGS}" | |
+ fi | |
+ fi | |
+ if test "x$use_pkgconfig_for_libedit" = "xyes"; then | |
+ LIBEDIT=`$PKGCONFIG --libs libedit` | |
+ CPPFLAGS="$CPPFLAGS `$PKGCONFIG --cflags libedit`" | |
+ else | |
+ LIBEDIT="-ledit -lcurses" | |
+ fi | |
+ OTHERLIBS=`echo $LIBEDIT | sed 's/-ledit//'` | |
+ AC_CHECK_LIB([edit], [el_init], | |
+ [ AC_DEFINE([USE_LIBEDIT], [1], [Use libedit for sftp]) | |
+ LIBEDIT_MSG="yes" | |
+ AC_SUBST([LIBEDIT]) | |
+ ], | |
+ [ AC_MSG_ERROR([libedit not found]) ], | |
+ [ $OTHERLIBS ] | |
+ ) | |
+ AC_MSG_CHECKING([if libedit version is compatible]) | |
+ AC_COMPILE_IFELSE( | |
+ [AC_LANG_PROGRAM([[ #include <histedit.h> ]], | |
+ [[ | |
+ int i = H_SETSIZE; | |
+ el_init("", NULL, NULL, NULL); | |
+ exit(0); | |
+ ]])], | |
+ [ AC_MSG_RESULT([yes]) ], | |
+ [ AC_MSG_RESULT([no]) | |
+ AC_MSG_ERROR([libedit version is not compatible]) ] | |
+ ) | |
+ fi ] | |
+) | |
+ | |
+AUDIT_MODULE=none | |
+AC_ARG_WITH([audit], | |
+ [ --with-audit=module Enable audit support (modules=debug,bsm,linux)], | |
+ [ | |
+ AC_MSG_CHECKING([for supported audit module]) | |
+ case "$withval" in | |
+ bsm) | |
+ AC_MSG_RESULT([bsm]) | |
+ AUDIT_MODULE=bsm | |
+ dnl Checks for headers, libs and functions | |
+ AC_CHECK_HEADERS([bsm/audit.h], [], | |
+ [AC_MSG_ERROR([BSM enabled and bsm/audit.h not found])], | |
+ [ | |
+#ifdef HAVE_TIME_H | |
+# include <time.h> | |
+#endif | |
+ ] | |
+) | |
+ AC_CHECK_LIB([bsm], [getaudit], [], | |
+ [AC_MSG_ERROR([BSM enabled and required library not found])]) | |
+ AC_CHECK_FUNCS([getaudit], [], | |
+ [AC_MSG_ERROR([BSM enabled and required function not found])]) | |
+ # These are optional | |
+ AC_CHECK_FUNCS([getaudit_addr aug_get_machine]) | |
+ AC_DEFINE([USE_BSM_AUDIT], [1], [Use BSM audit module]) | |
+ if test "$sol2ver" -ge 11; then | |
+ SSHDLIBS="$SSHDLIBS -lscf" | |
+ AC_DEFINE([BROKEN_BSM_API], [1], | |
+ [The system has incomplete BSM API]) | |
+ fi | |
+ ;; | |
+ linux) | |
+ AC_MSG_RESULT([linux]) | |
+ AUDIT_MODULE=linux | |
+ dnl Checks for headers, libs and functions | |
+ AC_CHECK_HEADERS([libaudit.h]) | |
+ SSHDLIBS="$SSHDLIBS -laudit" | |
+ AC_DEFINE([USE_LINUX_AUDIT], [1], [Use Linux audit module]) | |
+ ;; | |
+ debug) | |
+ AUDIT_MODULE=debug | |
+ AC_MSG_RESULT([debug]) | |
+ AC_DEFINE([SSH_AUDIT_EVENTS], [1], [Use audit debugging module]) | |
+ ;; | |
+ no) | |
+ AC_MSG_RESULT([no]) | |
+ ;; | |
+ *) | |
+ AC_MSG_ERROR([Unknown audit module $withval]) | |
+ ;; | |
+ esac ] | |
+) | |
+ | |
+AC_ARG_WITH([pie], | |
+ [ --with-pie Build Position Independent Executables if possible], [ | |
+ if test "x$withval" = "xno"; then | |
+ use_pie=no | |
+ fi | |
+ if test "x$withval" = "xyes"; then | |
+ use_pie=yes | |
+ fi | |
+ ] | |
+) | |
+if test "x$use_pie" = "x"; then | |
+ use_pie=no | |
+fi | |
+if test "x$use_toolchain_hardening" != "x1" && test "x$use_pie" = "xauto"; then | |
+ # Turn off automatic PIE when toolchain hardening is off. | |
+ use_pie=no | |
+fi | |
+if test "x$use_pie" = "xauto"; then | |
+ # Automatic PIE requires gcc >= 4.x | |
+ AC_MSG_CHECKING([for gcc >= 4.x]) | |
+ AC_COMPILE_IFELSE([AC_LANG_SOURCE([[ | |
+#if !defined(__GNUC__) || __GNUC__ < 4 | |
+#error gcc is too old | |
+#endif | |
+]])], | |
+ [ AC_MSG_RESULT([yes]) ], | |
+ [ AC_MSG_RESULT([no]) | |
+ use_pie=no ] | |
+) | |
+fi | |
+if test "x$use_pie" != "xno"; then | |
+ SAVED_CFLAGS="$CFLAGS" | |
+ SAVED_LDFLAGS="$LDFLAGS" | |
+ OSSH_CHECK_CFLAG_COMPILE([-fPIE]) | |
+ OSSH_CHECK_LDFLAG_LINK([-pie]) | |
+ # We use both -fPIE and -pie or neither. | |
+ AC_MSG_CHECKING([whether both -fPIE and -pie are supported]) | |
+ if echo "x $CFLAGS" | grep ' -fPIE' >/dev/null 2>&1 && \ | |
+ echo "x $LDFLAGS" | grep ' -pie' >/dev/null 2>&1 ; then | |
+ AC_MSG_RESULT([yes]) | |
+ else | |
+ AC_MSG_RESULT([no]) | |
+ CFLAGS="$SAVED_CFLAGS" | |
+ LDFLAGS="$SAVED_LDFLAGS" | |
+ fi | |
+fi | |
+ | |
+dnl Checks for library functions. Please keep in alphabetical order | |
+AC_CHECK_FUNCS([ \ | |
+ Blowfish_initstate \ | |
+ Blowfish_expandstate \ | |
+ Blowfish_expand0state \ | |
+ Blowfish_stream2word \ | |
+ asprintf \ | |
+ b64_ntop \ | |
+ __b64_ntop \ | |
+ b64_pton \ | |
+ __b64_pton \ | |
+ bcopy \ | |
+ bcrypt_pbkdf \ | |
+ bindresvport_sa \ | |
+ blf_enc \ | |
+ cap_rights_limit \ | |
+ clock \ | |
+ closefrom \ | |
+ dirfd \ | |
+ endgrent \ | |
+ explicit_bzero \ | |
+ fchmod \ | |
+ fchown \ | |
+ freeaddrinfo \ | |
+ fstatfs \ | |
+ fstatvfs \ | |
+ futimes \ | |
+ getaddrinfo \ | |
+ getcwd \ | |
+ getgrouplist \ | |
+ getnameinfo \ | |
+ getopt \ | |
+ getpeereid \ | |
+ getpeerucred \ | |
+ getpgid \ | |
+ getpgrp \ | |
+ _getpty \ | |
+ getrlimit \ | |
+ getttyent \ | |
+ glob \ | |
+ group_from_gid \ | |
+ inet_aton \ | |
+ inet_ntoa \ | |
+ inet_ntop \ | |
+ innetgr \ | |
+ login_getcapbool \ | |
+ mblen \ | |
+ md5_crypt \ | |
+ memmove \ | |
+ memset_s \ | |
+ mkdtemp \ | |
+ mmap \ | |
+ ngetaddrinfo \ | |
+ nsleep \ | |
+ ogetaddrinfo \ | |
+ openlog_r \ | |
+ poll \ | |
+ prctl \ | |
+ pstat \ | |
+ readpassphrase \ | |
+ realpath \ | |
+ recvmsg \ | |
+ rresvport_af \ | |
+ sendmsg \ | |
+ setdtablesize \ | |
+ setegid \ | |
+ setenv \ | |
+ seteuid \ | |
+ setgroupent \ | |
+ setgroups \ | |
+ setlinebuf \ | |
+ setlogin \ | |
+ setpassent\ | |
+ setpcred \ | |
+ setproctitle \ | |
+ setregid \ | |
+ setreuid \ | |
+ setrlimit \ | |
+ setsid \ | |
+ setvbuf \ | |
+ sigaction \ | |
+ sigvec \ | |
+ snprintf \ | |
+ socketpair \ | |
+ statfs \ | |
+ statvfs \ | |
+ strdup \ | |
+ strerror \ | |
+ strlcat \ | |
+ strlcpy \ | |
+ strmode \ | |
+ strnlen \ | |
+ strnvis \ | |
+ strptime \ | |
+ strtonum \ | |
+ strtoll \ | |
+ strtoul \ | |
+ strtoull \ | |
+ swap32 \ | |
+ sysconf \ | |
+ tcgetpgrp \ | |
+ timingsafe_bcmp \ | |
+ truncate \ | |
+ unsetenv \ | |
+ updwtmpx \ | |
+ user_from_uid \ | |
+ usleep \ | |
+ vasprintf \ | |
+ vsnprintf \ | |
+ waitpid \ | |
+]) | |
+ | |
+AC_LINK_IFELSE( | |
+ [AC_LANG_PROGRAM( | |
+ [[ #include <ctype.h> ]], | |
+ [[ return (isblank('a')); ]])], | |
+ [AC_DEFINE([HAVE_ISBLANK], [1], [Define if you have isblank(3C).]) | |
+]) | |
+ | |
+# PKCS#11 support requires dlopen() and co | |
+AC_SEARCH_LIBS([dlopen], [dl], | |
+ [AC_DEFINE([ENABLE_PKCS11], [], [Enable for PKCS#11 support])] | |
+) | |
+ | |
+# IRIX has a const char return value for gai_strerror() | |
+AC_CHECK_FUNCS([gai_strerror], [ | |
+ AC_DEFINE([HAVE_GAI_STRERROR]) | |
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ | |
+#include <sys/types.h> | |
+#include <sys/socket.h> | |
+#include <netdb.h> | |
+ | |
+const char *gai_strerror(int); | |
+ ]], [[ | |
+ char *str; | |
+ str = gai_strerror(0); | |
+ ]])], [ | |
+ AC_DEFINE([HAVE_CONST_GAI_STRERROR_PROTO], [1], | |
+ [Define if gai_strerror() returns const char *])], [])]) | |
+ | |
+AC_SEARCH_LIBS([nanosleep], [rt posix4], [AC_DEFINE([HAVE_NANOSLEEP], [1], | |
+ [Some systems put nanosleep outside of libc])]) | |
+ | |
+AC_SEARCH_LIBS([clock_gettime], [rt], | |
+ [AC_DEFINE([HAVE_CLOCK_GETTIME], [1], [Have clock_gettime])]) | |
+ | |
+dnl Make sure prototypes are defined for these before using them. | |
+AC_CHECK_DECL([getrusage], [AC_CHECK_FUNCS([getrusage])]) | |
+AC_CHECK_DECL([strsep], | |
+ [AC_CHECK_FUNCS([strsep])], | |
+ [], | |
+ [ | |
+#ifdef HAVE_STRING_H | |
+# include <string.h> | |
+#endif | |
+ ]) | |
+ | |
+dnl tcsendbreak might be a macro | |
+AC_CHECK_DECL([tcsendbreak], | |
+ [AC_DEFINE([HAVE_TCSENDBREAK])], | |
+ [AC_CHECK_FUNCS([tcsendbreak])], | |
+ [#include <termios.h>] | |
+) | |
+ | |
+AC_CHECK_DECLS([h_errno], , ,[#include <netdb.h>]) | |
+ | |
+AC_CHECK_DECLS([SHUT_RD], , , | |
+ [ | |
+#include <sys/types.h> | |
+#include <sys/socket.h> | |
+ ]) | |
+ | |
+AC_CHECK_DECLS([O_NONBLOCK], , , | |
+ [ | |
+#include <sys/types.h> | |
+#ifdef HAVE_SYS_STAT_H | |
+# include <sys/stat.h> | |
+#endif | |
+#ifdef HAVE_FCNTL_H | |
+# include <fcntl.h> | |
+#endif | |
+ ]) | |
+ | |
+AC_CHECK_DECLS([writev], , , [ | |
+#include <sys/types.h> | |
+#include <sys/uio.h> | |
+#include <unistd.h> | |
+ ]) | |
+ | |
+AC_CHECK_DECLS([MAXSYMLINKS], , , [ | |
+#include <sys/param.h> | |
+ ]) | |
+ | |
+AC_CHECK_DECLS([offsetof], , , [ | |
+#include <stddef.h> | |
+ ]) | |
+ | |
+# extra bits for select(2) | |
+AC_CHECK_DECLS([howmany, NFDBITS], [], [], [[ | |
+#include <sys/param.h> | |
+#include <sys/types.h> | |
+#ifdef HAVE_SYS_SYSMACROS_H | |
+#include <sys/sysmacros.h> | |
+#endif | |
+#ifdef HAVE_SYS_SELECT_H | |
+#include <sys/select.h> | |
+#endif | |
+#ifdef HAVE_SYS_TIME_H | |
+#include <sys/time.h> | |
+#endif | |
+#ifdef HAVE_UNISTD_H | |
+#include <unistd.h> | |
+#endif | |
+ ]]) | |
+AC_CHECK_TYPES([fd_mask], [], [], [[ | |
+#include <sys/param.h> | |
+#include <sys/types.h> | |
+#ifdef HAVE_SYS_SELECT_H | |
+#include <sys/select.h> | |
+#endif | |
+#ifdef HAVE_SYS_TIME_H | |
+#include <sys/time.h> | |
+#endif | |
+#ifdef HAVE_UNISTD_H | |
+#include <unistd.h> | |
+#endif | |
+ ]]) | |
+ | |
+AC_CHECK_FUNCS([setresuid], [ | |
+ dnl Some platorms have setresuid that isn't implemented, test for this | |
+ AC_MSG_CHECKING([if setresuid seems to work]) | |
+ AC_RUN_IFELSE( | |
+ [AC_LANG_PROGRAM([[ | |
+#include <stdlib.h> | |
+#include <errno.h> | |
+ ]], [[ | |
+ errno=0; | |
+ setresuid(0,0,0); | |
+ if (errno==ENOSYS) | |
+ exit(1); | |
+ else | |
+ exit(0); | |
+ ]])], | |
+ [AC_MSG_RESULT([yes])], | |
+ [AC_DEFINE([BROKEN_SETRESUID], [1], | |
+ [Define if your setresuid() is broken]) | |
+ AC_MSG_RESULT([not implemented])], | |
+ [AC_MSG_WARN([cross compiling: not checking setresuid])] | |
+ ) | |
+]) | |
+ | |
+AC_CHECK_FUNCS([setresgid], [ | |
+ dnl Some platorms have setresgid that isn't implemented, test for this | |
+ AC_MSG_CHECKING([if setresgid seems to work]) | |
+ AC_RUN_IFELSE( | |
+ [AC_LANG_PROGRAM([[ | |
+#include <stdlib.h> | |
+#include <errno.h> | |
+ ]], [[ | |
+ errno=0; | |
+ setresgid(0,0,0); | |
+ if (errno==ENOSYS) | |
+ exit(1); | |
+ else | |
+ exit(0); | |
+ ]])], | |
+ [AC_MSG_RESULT([yes])], | |
+ [AC_DEFINE([BROKEN_SETRESGID], [1], | |
+ [Define if your setresgid() is broken]) | |
+ AC_MSG_RESULT([not implemented])], | |
+ [AC_MSG_WARN([cross compiling: not checking setresuid])] | |
+ ) | |
+]) | |
+ | |
+dnl Checks for time functions | |
+AC_CHECK_FUNCS([gettimeofday time]) | |
+dnl Checks for utmp functions | |
+AC_CHECK_FUNCS([endutent getutent getutid getutline pututline setutent]) | |
+AC_CHECK_FUNCS([utmpname]) | |
+dnl Checks for utmpx functions | |
+AC_CHECK_FUNCS([endutxent getutxent getutxid getutxline getutxuser pututxline]) | |
+AC_CHECK_FUNCS([setutxdb setutxent utmpxname]) | |
+dnl Checks for lastlog functions | |
+AC_CHECK_FUNCS([getlastlogxbyname]) | |
+ | |
+AC_CHECK_FUNC([daemon], | |
+ [AC_DEFINE([HAVE_DAEMON], [1], [Define if your libraries define daemon()])], | |
+ [AC_CHECK_LIB([bsd], [daemon], | |
+ [LIBS="$LIBS -lbsd"; AC_DEFINE([HAVE_DAEMON])])] | |
+) | |
+ | |
+AC_CHECK_FUNC([getpagesize], | |
+ [AC_DEFINE([HAVE_GETPAGESIZE], [1], | |
+ [Define if your libraries define getpagesize()])], | |
+ [AC_CHECK_LIB([ucb], [getpagesize], | |
+ [LIBS="$LIBS -lucb"; AC_DEFINE([HAVE_GETPAGESIZE])])] | |
+) | |
+ | |
+# Check for broken snprintf | |
+if test "x$ac_cv_func_snprintf" = "xyes" ; then | |
+ AC_MSG_CHECKING([whether snprintf correctly terminates long strings]) | |
+ AC_RUN_IFELSE( | |
+ [AC_LANG_PROGRAM([[ #include <stdio.h> ]], | |
+ [[ | |
+ char b[5]; | |
+ snprintf(b,5,"123456789"); | |
+ exit(b[4]!='\0'); | |
+ ]])], | |
+ [AC_MSG_RESULT([yes])], | |
+ [ | |
+ AC_MSG_RESULT([no]) | |
+ AC_DEFINE([BROKEN_SNPRINTF], [1], | |
+ [Define if your snprintf is busted]) | |
+ AC_MSG_WARN([****** Your snprintf() function is broken, complain to your vendor]) | |
+ ], | |
+ [ AC_MSG_WARN([cross compiling: Assuming working snprintf()]) ] | |
+ ) | |
+fi | |
+ | |
+# We depend on vsnprintf returning the right thing on overflow: the | |
+# number of characters it tried to create (as per SUSv3) | |
+if test "x$ac_cv_func_vsnprintf" = "xyes" ; then | |
+ AC_MSG_CHECKING([whether vsnprintf returns correct values on overflow]) | |
+ AC_RUN_IFELSE( | |
+ [AC_LANG_PROGRAM([[ | |
+#include <sys/types.h> | |
+#include <stdio.h> | |
+#include <stdarg.h> | |
+ | |
+int x_snprintf(char *str, size_t count, const char *fmt, ...) | |
+{ | |
+ size_t ret; | |
+ va_list ap; | |
+ | |
+ va_start(ap, fmt); | |
+ ret = vsnprintf(str, count, fmt, ap); | |
+ va_end(ap); | |
+ return ret; | |
+} | |
+ ]], [[ | |
+char x[1]; | |
+if (x_snprintf(x, 1, "%s %d", "hello", 12345) != 11) | |
+ return 1; | |
+if (x_snprintf(NULL, 0, "%s %d", "hello", 12345) != 11) | |
+ return 1; | |
+return 0; | |
+ ]])], | |
+ [AC_MSG_RESULT([yes])], | |
+ [ | |
+ AC_MSG_RESULT([no]) | |
+ AC_DEFINE([BROKEN_SNPRINTF], [1], | |
+ [Define if your snprintf is busted]) | |
+ AC_MSG_WARN([****** Your vsnprintf() function is broken, complain to your vendor]) | |
+ ], | |
+ [ AC_MSG_WARN([cross compiling: Assuming working vsnprintf()]) ] | |
+ ) | |
+fi | |
+ | |
+# On systems where [v]snprintf is broken, but is declared in stdio, | |
+# check that the fmt argument is const char * or just char *. | |
+# This is only useful for when BROKEN_SNPRINTF | |
+AC_MSG_CHECKING([whether snprintf can declare const char *fmt]) | |
+AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ | |
+#include <stdio.h> | |
+int snprintf(char *a, size_t b, const char *c, ...) { return 0; } | |
+ ]], [[ | |
+ snprintf(0, 0, 0); | |
+ ]])], | |
+ [AC_MSG_RESULT([yes]) | |
+ AC_DEFINE([SNPRINTF_CONST], [const], | |
+ [Define as const if snprintf() can declare const char *fmt])], | |
+ [AC_MSG_RESULT([no]) | |
+ AC_DEFINE([SNPRINTF_CONST], [/* not const */])]) | |
+ | |
+# Check for missing getpeereid (or equiv) support | |
+NO_PEERCHECK="" | |
+if test "x$ac_cv_func_getpeereid" != "xyes" -a "x$ac_cv_func_getpeerucred" != "xyes"; then | |
+ AC_MSG_CHECKING([whether system supports SO_PEERCRED getsockopt]) | |
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ | |
+#include <sys/types.h> | |
+#include <sys/socket.h>]], [[int i = SO_PEERCRED;]])], | |
+ [ AC_MSG_RESULT([yes]) | |
+ AC_DEFINE([HAVE_SO_PEERCRED], [1], [Have PEERCRED socket option]) | |
+ ], [AC_MSG_RESULT([no]) | |
+ NO_PEERCHECK=1 | |
+ ]) | |
+fi | |
+ | |
+dnl see whether mkstemp() requires XXXXXX | |
+if test "x$ac_cv_func_mkdtemp" = "xyes" ; then | |
+AC_MSG_CHECKING([for (overly) strict mkstemp]) | |
+AC_RUN_IFELSE( | |
+ [AC_LANG_PROGRAM([[ | |
+#include <stdlib.h> | |
+ ]], [[ | |
+ char template[]="conftest.mkstemp-test"; | |
+ if (mkstemp(template) == -1) | |
+ exit(1); | |
+ unlink(template); | |
+ exit(0); | |
+ ]])], | |
+ [ | |
+ AC_MSG_RESULT([no]) | |
+ ], | |
+ [ | |
+ AC_MSG_RESULT([yes]) | |
+ AC_DEFINE([HAVE_STRICT_MKSTEMP], [1], [Silly mkstemp()]) | |
+ ], | |
+ [ | |
+ AC_MSG_RESULT([yes]) | |
+ AC_DEFINE([HAVE_STRICT_MKSTEMP]) | |
+ ] | |
+) | |
+fi | |
+ | |
+dnl make sure that openpty does not reacquire controlling terminal | |
+if test ! -z "$check_for_openpty_ctty_bug"; then | |
+ AC_MSG_CHECKING([if openpty correctly handles controlling tty]) | |
+ AC_RUN_IFELSE( | |
+ [AC_LANG_PROGRAM([[ | |
+#include <stdio.h> | |
+#include <sys/fcntl.h> | |
+#include <sys/types.h> | |
+#include <sys/wait.h> | |
+ ]], [[ | |
+ pid_t pid; | |
+ int fd, ptyfd, ttyfd, status; | |
+ | |
+ pid = fork(); | |
+ if (pid < 0) { /* failed */ | |
+ exit(1); | |
+ } else if (pid > 0) { /* parent */ | |
+ waitpid(pid, &status, 0); | |
+ if (WIFEXITED(status)) | |
+ exit(WEXITSTATUS(status)); | |
+ else | |
+ exit(2); | |
+ } else { /* child */ | |
+ close(0); close(1); close(2); | |
+ setsid(); | |
+ openpty(&ptyfd, &ttyfd, NULL, NULL, NULL); | |
+ fd = open("/dev/tty", O_RDWR | O_NOCTTY); | |
+ if (fd >= 0) | |
+ exit(3); /* Acquired ctty: broken */ | |
+ else | |
+ exit(0); /* Did not acquire ctty: OK */ | |
+ } | |
+ ]])], | |
+ [ | |
+ AC_MSG_RESULT([yes]) | |
+ ], | |
+ [ | |
+ AC_MSG_RESULT([no]) | |
+ AC_DEFINE([SSHD_ACQUIRES_CTTY]) | |
+ ], | |
+ [ | |
+ AC_MSG_RESULT([cross-compiling, assuming yes]) | |
+ ] | |
+ ) | |
+fi | |
+ | |
+if test "x$ac_cv_func_getaddrinfo" = "xyes" && \ | |
+ test "x$check_for_hpux_broken_getaddrinfo" = "x1"; then | |
+ AC_MSG_CHECKING([if getaddrinfo seems to work]) | |
+ AC_RUN_IFELSE( | |
+ [AC_LANG_PROGRAM([[ | |
+#include <stdio.h> | |
+#include <sys/socket.h> | |
+#include <netdb.h> | |
+#include <errno.h> | |
+#include <netinet/in.h> | |
+ | |
+#define TEST_PORT "2222" | |
+ ]], [[ | |
+ int err, sock; | |
+ struct addrinfo *gai_ai, *ai, hints; | |
+ char ntop[NI_MAXHOST], strport[NI_MAXSERV], *name = NULL; | |
+ | |
+ memset(&hints, 0, sizeof(hints)); | |
+ hints.ai_family = PF_UNSPEC; | |
+ hints.ai_socktype = SOCK_STREAM; | |
+ hints.ai_flags = AI_PASSIVE; | |
+ | |
+ err = getaddrinfo(name, TEST_PORT, &hints, &gai_ai); | |
+ if (err != 0) { | |
+ fprintf(stderr, "getaddrinfo failed (%s)", gai_strerror(err)); | |
+ exit(1); | |
+ } | |
+ | |
+ for (ai = gai_ai; ai != NULL; ai = ai->ai_next) { | |
+ if (ai->ai_family != AF_INET6) | |
+ continue; | |
+ | |
+ err = getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop, | |
+ sizeof(ntop), strport, sizeof(strport), | |
+ NI_NUMERICHOST|NI_NUMERICSERV); | |
+ | |
+ if (err != 0) { | |
+ if (err == EAI_SYSTEM) | |
+ perror("getnameinfo EAI_SYSTEM"); | |
+ else | |
+ fprintf(stderr, "getnameinfo failed: %s\n", | |
+ gai_strerror(err)); | |
+ exit(2); | |
+ } | |
+ | |
+ sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); | |
+ if (sock < 0) | |
+ perror("socket"); | |
+ if (bind(sock, ai->ai_addr, ai->ai_addrlen) < 0) { | |
+ if (errno == EBADF) | |
+ exit(3); | |
+ } | |
+ } | |
+ exit(0); | |
+ ]])], | |
+ [ | |
+ AC_MSG_RESULT([yes]) | |
+ ], | |
+ [ | |
+ AC_MSG_RESULT([no]) | |
+ AC_DEFINE([BROKEN_GETADDRINFO]) | |
+ ], | |
+ [ | |
+ AC_MSG_RESULT([cross-compiling, assuming yes]) | |
+ ] | |
+ ) | |
+fi | |
+ | |
+if test "x$ac_cv_func_getaddrinfo" = "xyes" && \ | |
+ test "x$check_for_aix_broken_getaddrinfo" = "x1"; then | |
+ AC_MSG_CHECKING([if getaddrinfo seems to work]) | |
+ AC_RUN_IFELSE( | |
+ [AC_LANG_PROGRAM([[ | |
+#include <stdio.h> | |
+#include <sys/socket.h> | |
+#include <netdb.h> | |
+#include <errno.h> | |
+#include <netinet/in.h> | |
+ | |
+#define TEST_PORT "2222" | |
+ ]], [[ | |
+ int err, sock; | |
+ struct addrinfo *gai_ai, *ai, hints; | |
+ char ntop[NI_MAXHOST], strport[NI_MAXSERV], *name = NULL; | |
+ | |
+ memset(&hints, 0, sizeof(hints)); | |
+ hints.ai_family = PF_UNSPEC; | |
+ hints.ai_socktype = SOCK_STREAM; | |
+ hints.ai_flags = AI_PASSIVE; | |
+ | |
+ err = getaddrinfo(name, TEST_PORT, &hints, &gai_ai); | |
+ if (err != 0) { | |
+ fprintf(stderr, "getaddrinfo failed (%s)", gai_strerror(err)); | |
+ exit(1); | |
+ } | |
+ | |
+ for (ai = gai_ai; ai != NULL; ai = ai->ai_next) { | |
+ if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) | |
+ continue; | |
+ | |
+ err = getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop, | |
+ sizeof(ntop), strport, sizeof(strport), | |
+ NI_NUMERICHOST|NI_NUMERICSERV); | |
+ | |
+ if (ai->ai_family == AF_INET && err != 0) { | |
+ perror("getnameinfo"); | |
+ exit(2); | |
+ } | |
+ } | |
+ exit(0); | |
+ ]])], | |
+ [ | |
+ AC_MSG_RESULT([yes]) | |
+ AC_DEFINE([AIX_GETNAMEINFO_HACK], [1], | |
+ [Define if you have a getaddrinfo that fails | |
+ for the all-zeros IPv6 address]) | |
+ ], | |
+ [ | |
+ AC_MSG_RESULT([no]) | |
+ AC_DEFINE([BROKEN_GETADDRINFO]) | |
+ ], | |
+ [ | |
+ AC_MSG_RESULT([cross-compiling, assuming no]) | |
+ ] | |
+ ) | |
+fi | |
+ | |
+if test "x$check_for_conflicting_getspnam" = "x1"; then | |
+ AC_MSG_CHECKING([for conflicting getspnam in shadow.h]) | |
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include <shadow.h> ]], | |
+ [[ exit(0); ]])], | |
+ [ | |
+ AC_MSG_RESULT([no]) | |
+ ], | |
+ [ | |
+ AC_MSG_RESULT([yes]) | |
+ AC_DEFINE([GETSPNAM_CONFLICTING_DEFS], [1], | |
+ [Conflicting defs for getspnam]) | |
+ ] | |
+ ) | |
+fi | |
+ | |
+AC_FUNC_GETPGRP | |
+ | |
+# Search for OpenSSL | |
+saved_CPPFLAGS="$CPPFLAGS" | |
+saved_LDFLAGS="$LDFLAGS" | |
+AC_ARG_WITH([ssl-dir], | |
+ [ --with-ssl-dir=PATH Specify path to OpenSSL installation ], | |
+ [ | |
+ if test "x$withval" != "xno" ; then | |
+ case "$withval" in | |
+ # Relative paths | |
+ ./*|../*) withval="`pwd`/$withval" | |
+ esac | |
+ if test -d "$withval/lib"; then | |
+ if test -n "${need_dash_r}"; then | |
+ LDFLAGS="-L${withval}/lib -R${withval}/lib ${LDFLAGS}" | |
+ else | |
+ LDFLAGS="-L${withval}/lib ${LDFLAGS}" | |
+ fi | |
+ elif test -d "$withval/lib64"; then | |
+ if test -n "${need_dash_r}"; then | |
+ LDFLAGS="-L${withval}/lib64 -R${withval}/lib64 ${LDFLAGS}" | |
+ else | |
+ LDFLAGS="-L${withval}/lib64 ${LDFLAGS}" | |
+ fi | |
+ else | |
+ if test -n "${need_dash_r}"; then | |
+ LDFLAGS="-L${withval} -R${withval} ${LDFLAGS}" | |
+ else | |
+ LDFLAGS="-L${withval} ${LDFLAGS}" | |
+ fi | |
+ fi | |
+ if test -d "$withval/include"; then | |
+ CPPFLAGS="-I${withval}/include ${CPPFLAGS}" | |
+ else | |
+ CPPFLAGS="-I${withval} ${CPPFLAGS}" | |
+ fi | |
+ fi | |
+ ] | |
+) | |
+LIBS="-lcrypto $LIBS" | |
+AC_TRY_LINK_FUNC([RAND_add], [AC_DEFINE([HAVE_OPENSSL], [1], | |
+ [Define if your ssl headers are included | |
+ with #include <openssl/header.h>])], | |
+ [ | |
+ dnl Check default openssl install dir | |
+ if test -n "${need_dash_r}"; then | |
+ LDFLAGS="-L/usr/local/ssl/lib -R/usr/local/ssl/lib ${saved_LDFLAGS}" | |
+ else | |
+ LDFLAGS="-L/usr/local/ssl/lib ${saved_LDFLAGS}" | |
+ fi | |
+ CPPFLAGS="-I/usr/local/ssl/include ${saved_CPPFLAGS}" | |
+ AC_CHECK_HEADER([openssl/opensslv.h], , | |
+ [AC_MSG_ERROR([*** OpenSSL headers missing - please install first or check config.log ***])]) | |
+ AC_TRY_LINK_FUNC([RAND_add], [AC_DEFINE([HAVE_OPENSSL])], | |
+ [ | |
+ AC_MSG_ERROR([*** Can't find recent OpenSSL libcrypto (see config.log for details) ***]) | |
+ ] | |
+ ) | |
+ ] | |
+) | |
+ | |
+# Determine OpenSSL header version | |
+AC_MSG_CHECKING([OpenSSL header version]) | |
+AC_RUN_IFELSE( | |
+ [AC_LANG_PROGRAM([[ | |
+#include <stdio.h> | |
+#include <string.h> | |
+#include <openssl/opensslv.h> | |
+#define DATA "conftest.sslincver" | |
+ ]], [[ | |
+ FILE *fd; | |
+ int rc; | |
+ | |
+ fd = fopen(DATA,"w"); | |
+ if(fd == NULL) | |
+ exit(1); | |
+ | |
+ if ((rc = fprintf(fd ,"%08x (%s)\n", OPENSSL_VERSION_NUMBER, OPENSSL_VERSION_TEXT)) <0) | |
+ exit(1); | |
+ | |
+ exit(0); | |
+ ]])], | |
+ [ | |
+ ssl_header_ver=`cat conftest.sslincver` | |
+ AC_MSG_RESULT([$ssl_header_ver]) | |
+ ], | |
+ [ | |
+ AC_MSG_RESULT([not found]) | |
+ AC_MSG_ERROR([OpenSSL version header not found.]) | |
+ ], | |
+ [ | |
+ AC_MSG_WARN([cross compiling: not checking]) | |
+ ] | |
+) | |
+ | |
+# Determine OpenSSL library version | |
+AC_MSG_CHECKING([OpenSSL library version]) | |
+AC_RUN_IFELSE( | |
+ [AC_LANG_PROGRAM([[ | |
+#include <stdio.h> | |
+#include <string.h> | |
+#include <openssl/opensslv.h> | |
+#include <openssl/crypto.h> | |
+#define DATA "conftest.ssllibver" | |
+ ]], [[ | |
+ FILE *fd; | |
+ int rc; | |
+ | |
+ fd = fopen(DATA,"w"); | |
+ if(fd == NULL) | |
+ exit(1); | |
+ | |
+ if ((rc = fprintf(fd ,"%08x (%s)\n", SSLeay(), | |
+ SSLeay_version(SSLEAY_VERSION))) <0) | |
+ exit(1); | |
+ | |
+ exit(0); | |
+ ]])], | |
+ [ | |
+ ssl_library_ver=`cat conftest.ssllibver` | |
+ # Check version is supported. | |
+ case "$ssl_library_ver" in | |
+ 0090[[0-7]]*|009080[[0-5]]*) | |
+ AC_MSG_ERROR([OpenSSL >= 0.9.8f required]) | |
+ ;; | |
+ *) ;; | |
+ esac | |
+ AC_MSG_RESULT([$ssl_library_ver]) | |
+ ], | |
+ [ | |
+ AC_MSG_RESULT([not found]) | |
+ AC_MSG_ERROR([OpenSSL library not found.]) | |
+ ], | |
+ [ | |
+ AC_MSG_WARN([cross compiling: not checking]) | |
+ ] | |
+) | |
+ | |
+# XXX make --without-openssl work | |
+AC_DEFINE_UNQUOTED([WITH_OPENSSL], [1], [use libcrypto for cryptography]) | |
+AC_DEFINE_UNQUOTED([WITH_SSH1], [1], [include SSH protocol version 1 support]) | |
+ | |
+AC_ARG_WITH([openssl-header-check], | |
+ [ --without-openssl-header-check Disable OpenSSL version consistency check], | |
+ [ if test "x$withval" = "xno" ; then | |
+ openssl_check_nonfatal=1 | |
+ fi | |
+ ] | |
+) | |
+ | |
+# Sanity check OpenSSL headers | |
+AC_MSG_CHECKING([whether OpenSSL's headers match the library]) | |
+AC_RUN_IFELSE( | |
+ [AC_LANG_PROGRAM([[ | |
+#include <string.h> | |
+#include <openssl/opensslv.h> | |
+ ]], [[ | |
+ exit(SSLeay() == OPENSSL_VERSION_NUMBER ? 0 : 1); | |
+ ]])], | |
+ [ | |
+ AC_MSG_RESULT([yes]) | |
+ ], | |
+ [ | |
+ AC_MSG_RESULT([no]) | |
+ if test "x$openssl_check_nonfatal" = "x"; then | |
+ AC_MSG_ERROR([Your OpenSSL headers do not match your | |
+library. Check config.log for details. | |
+If you are sure your installation is consistent, you can disable the check | |
+by running "./configure --without-openssl-header-check". | |
+Also see contrib/findssl.sh for help identifying header/library mismatches. | |
+]) | |
+ else | |
+ AC_MSG_WARN([Your OpenSSL headers do not match your | |
+library. Check config.log for details. | |
+Also see contrib/findssl.sh for help identifying header/library mismatches.]) | |
+ fi | |
+ ], | |
+ [ | |
+ AC_MSG_WARN([cross compiling: not checking]) | |
+ ] | |
+) | |
+ | |
+AC_MSG_CHECKING([if programs using OpenSSL functions will link]) | |
+AC_LINK_IFELSE( | |
+ [AC_LANG_PROGRAM([[ #include <openssl/evp.h> ]], | |
+ [[ SSLeay_add_all_algorithms(); ]])], | |
+ [ | |
+ AC_MSG_RESULT([yes]) | |
+ ], | |
+ [ | |
+ AC_MSG_RESULT([no]) | |
+ saved_LIBS="$LIBS" | |
+ LIBS="$LIBS -ldl" | |
+ AC_MSG_CHECKING([if programs using OpenSSL need -ldl]) | |
+ AC_LINK_IFELSE( | |
+ [AC_LANG_PROGRAM([[ #include <openssl/evp.h> ]], | |
+ [[ SSLeay_add_all_algorithms(); ]])], | |
+ [ | |
+ AC_MSG_RESULT([yes]) | |
+ ], | |
+ [ | |
+ AC_MSG_RESULT([no]) | |
+ LIBS="$saved_LIBS" | |
+ ] | |
+ ) | |
+ ] | |
+) | |
+ | |
+AC_CHECK_FUNCS([ \ | |
+ BN_is_prime_ex \ | |
+ DSA_generate_parameters_ex \ | |
+ EVP_DigestInit_ex \ | |
+ EVP_DigestFinal_ex \ | |
+ EVP_MD_CTX_init \ | |
+ EVP_MD_CTX_cleanup \ | |
+ EVP_MD_CTX_copy_ex \ | |
+ HMAC_CTX_init \ | |
+ RSA_generate_key_ex \ | |
+ RSA_get_default_method \ | |
+]) | |
+ | |
+AC_ARG_WITH([ssl-engine], | |
+ [ --with-ssl-engine Enable OpenSSL (hardware) ENGINE support ], | |
+ [ if test "x$withval" != "xno" ; then | |
+ AC_MSG_CHECKING([for OpenSSL ENGINE support]) | |
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ | |
+#include <openssl/engine.h> | |
+ ]], [[ | |
+ ENGINE_load_builtin_engines(); | |
+ ENGINE_register_all_complete(); | |
+ ]])], | |
+ [ AC_MSG_RESULT([yes]) | |
+ AC_DEFINE([USE_OPENSSL_ENGINE], [1], | |
+ [Enable OpenSSL engine support]) | |
+ ], [ AC_MSG_ERROR([OpenSSL ENGINE support not found]) | |
+ ]) | |
+ fi ] | |
+) | |
+ | |
+# Check for OpenSSL without EVP_aes_{192,256}_cbc | |
+AC_MSG_CHECKING([whether OpenSSL has crippled AES support]) | |
+AC_LINK_IFELSE( | |
+ [AC_LANG_PROGRAM([[ | |
+#include <string.h> | |
+#include <openssl/evp.h> | |
+ ]], [[ | |
+ exit(EVP_aes_192_cbc() == NULL || EVP_aes_256_cbc() == NULL); | |
+ ]])], | |
+ [ | |
+ AC_MSG_RESULT([no]) | |
+ ], | |
+ [ | |
+ AC_MSG_RESULT([yes]) | |
+ AC_DEFINE([OPENSSL_LOBOTOMISED_AES], [1], | |
+ [libcrypto is missing AES 192 and 256 bit functions]) | |
+ ] | |
+) | |
+ | |
+# Check for OpenSSL with EVP_aes_*ctr | |
+AC_MSG_CHECKING([whether OpenSSL has AES CTR via EVP]) | |
+AC_LINK_IFELSE( | |
+ [AC_LANG_PROGRAM([[ | |
+#include <string.h> | |
+#include <openssl/evp.h> | |
+ ]], [[ | |
+ exit(EVP_aes_128_ctr() == NULL || | |
+ EVP_aes_192_cbc() == NULL || | |
+ EVP_aes_256_cbc() == NULL); | |
+ ]])], | |
+ [ | |
+ AC_MSG_RESULT([yes]) | |
+ AC_DEFINE([OPENSSL_HAVE_EVPCTR], [1], | |
+ [libcrypto has EVP AES CTR]) | |
+ ], | |
+ [ | |
+ AC_MSG_RESULT([no]) | |
+ ] | |
+) | |
+ | |
+# Check for OpenSSL with EVP_aes_*gcm | |
+AC_MSG_CHECKING([whether OpenSSL has AES GCM via EVP]) | |
+AC_LINK_IFELSE( | |
+ [AC_LANG_PROGRAM([[ | |
+#include <string.h> | |
+#include <openssl/evp.h> | |
+ ]], [[ | |
+ exit(EVP_aes_128_gcm() == NULL || | |
+ EVP_aes_256_gcm() == NULL || | |
+ EVP_CTRL_GCM_SET_IV_FIXED == 0 || | |
+ EVP_CTRL_GCM_IV_GEN == 0 || | |
+ EVP_CTRL_GCM_SET_TAG == 0 || | |
+ EVP_CTRL_GCM_GET_TAG == 0 || | |
+ EVP_CIPHER_CTX_ctrl(NULL, 0, 0, NULL) == 0); | |
+ ]])], | |
+ [ | |
+ AC_MSG_RESULT([yes]) | |
+ AC_DEFINE([OPENSSL_HAVE_EVPGCM], [1], | |
+ [libcrypto has EVP AES GCM]) | |
+ ], | |
+ [ | |
+ AC_MSG_RESULT([no]) | |
+ unsupported_algorithms="$unsupported_cipers \ | |
+ aes128-gcm@openssh.com aes256-gcm@openssh.com" | |
+ ] | |
+) | |
+ | |
+AC_SEARCH_LIBS([EVP_CIPHER_CTX_ctrl], [crypto], | |
+ [AC_DEFINE([HAVE_EVP_CIPHER_CTX_CTRL], [1], | |
+ [Define if libcrypto has EVP_CIPHER_CTX_ctrl])]) | |
+ | |
+AC_MSG_CHECKING([if EVP_DigestUpdate returns an int]) | |
+AC_LINK_IFELSE( | |
+ [AC_LANG_PROGRAM([[ | |
+#include <string.h> | |
+#include <openssl/evp.h> | |
+ ]], [[ | |
+ if(EVP_DigestUpdate(NULL, NULL,0)) | |
+ exit(0); | |
+ ]])], | |
+ [ | |
+ AC_MSG_RESULT([yes]) | |
+ ], | |
+ [ | |
+ AC_MSG_RESULT([no]) | |
+ AC_DEFINE([OPENSSL_EVP_DIGESTUPDATE_VOID], [1], | |
+ [Define if EVP_DigestUpdate returns void]) | |
+ ] | |
+) | |
+ | |
+# Some systems want crypt() from libcrypt, *not* the version in OpenSSL, | |
+# because the system crypt() is more featureful. | |
+if test "x$check_for_libcrypt_before" = "x1"; then | |
+ AC_CHECK_LIB([crypt], [crypt]) | |
+fi | |
+ | |
+# Some Linux systems (Slackware) need crypt() from libcrypt, *not* the | |
+# version in OpenSSL. | |
+if test "x$check_for_libcrypt_later" = "x1"; then | |
+ AC_CHECK_LIB([crypt], [crypt], [LIBS="$LIBS -lcrypt"]) | |
+fi | |
+AC_CHECK_FUNCS([crypt DES_crypt]) | |
+ | |
+# Search for SHA256 support in libc and/or OpenSSL | |
+AC_CHECK_FUNCS([SHA256_Update EVP_sha256], , | |
+ [unsupported_algorithms="$unsupported_algorithms \ | |
+ hmac-sha2-256 hmac-sha2-512 \ | |
+ diffie-hellman-group-exchange-sha256 \ | |
+ hmac-sha2-256-etm@openssh.com hmac-sha2-512-etm@openssh.com" | |
+ ] | |
+) | |
+# Search for RIPE-MD support in OpenSSL | |
+AC_CHECK_FUNCS([EVP_ripemd160], , | |
+ [unsupported_algorithms="$unsupported_algorithms \ | |
+ hmac-ripemd160 | |
+ hmac-ripemd160@openssh.com | |
+ hmac-ripemd160-etm@openssh.com" | |
+ ] | |
+) | |
+ | |
+# Check complete ECC support in OpenSSL | |
+AC_MSG_CHECKING([whether OpenSSL has NID_X9_62_prime256v1]) | |
+AC_LINK_IFELSE( | |
+ [AC_LANG_PROGRAM([[ | |
+#include <openssl/ec.h> | |
+#include <openssl/ecdh.h> | |
+#include <openssl/ecdsa.h> | |
+#include <openssl/evp.h> | |
+#include <openssl/objects.h> | |
+#include <openssl/opensslv.h> | |
+#if OPENSSL_VERSION_NUMBER < 0x0090807f /* 0.9.8g */ | |
+# error "OpenSSL < 0.9.8g has unreliable ECC code" | |
+#endif | |
+ ]], [[ | |
+ EC_KEY *e = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); | |
+ const EVP_MD *m = EVP_sha256(); /* We need this too */ | |
+ ]])], | |
+ [ AC_MSG_RESULT([yes]) | |
+ enable_nistp256=1 ], | |
+ [ AC_MSG_RESULT([no]) ] | |
+) | |
+ | |
+AC_MSG_CHECKING([whether OpenSSL has NID_secp384r1]) | |
+AC_LINK_IFELSE( | |
+ [AC_LANG_PROGRAM([[ | |
+#include <openssl/ec.h> | |
+#include <openssl/ecdh.h> | |
+#include <openssl/ecdsa.h> | |
+#include <openssl/evp.h> | |
+#include <openssl/objects.h> | |
+#include <openssl/opensslv.h> | |
+#if OPENSSL_VERSION_NUMBER < 0x0090807f /* 0.9.8g */ | |
+# error "OpenSSL < 0.9.8g has unreliable ECC code" | |
+#endif | |
+ ]], [[ | |
+ EC_KEY *e = EC_KEY_new_by_curve_name(NID_secp384r1); | |
+ const EVP_MD *m = EVP_sha384(); /* We need this too */ | |
+ ]])], | |
+ [ AC_MSG_RESULT([yes]) | |
+ enable_nistp384=1 ], | |
+ [ AC_MSG_RESULT([no]) ] | |
+) | |
+ | |
+AC_MSG_CHECKING([whether OpenSSL has NID_secp521r1]) | |
+AC_LINK_IFELSE( | |
+ [AC_LANG_PROGRAM([[ | |
+#include <openssl/ec.h> | |
+#include <openssl/ecdh.h> | |
+#include <openssl/ecdsa.h> | |
+#include <openssl/evp.h> | |
+#include <openssl/objects.h> | |
+#include <openssl/opensslv.h> | |
+#if OPENSSL_VERSION_NUMBER < 0x0090807f /* 0.9.8g */ | |
+# error "OpenSSL < 0.9.8g has unreliable ECC code" | |
+#endif | |
+ ]], [[ | |
+ EC_KEY *e = EC_KEY_new_by_curve_name(NID_secp521r1); | |
+ const EVP_MD *m = EVP_sha512(); /* We need this too */ | |
+ ]])], | |
+ [ AC_MSG_RESULT([yes]) | |
+ AC_MSG_CHECKING([if OpenSSL's NID_secp521r1 is functional]) | |
+ AC_RUN_IFELSE( | |
+ [AC_LANG_PROGRAM([[ | |
+#include <openssl/ec.h> | |
+#include <openssl/ecdh.h> | |
+#include <openssl/ecdsa.h> | |
+#include <openssl/evp.h> | |
+#include <openssl/objects.h> | |
+#include <openssl/opensslv.h> | |
+ ]],[[ | |
+ EC_KEY *e = EC_KEY_new_by_curve_name(NID_secp521r1); | |
+ const EVP_MD *m = EVP_sha512(); /* We need this too */ | |
+ exit(e == NULL || m == NULL); | |
+ ]])], | |
+ [ AC_MSG_RESULT([yes]) | |
+ enable_nistp521=1 ], | |
+ [ AC_MSG_RESULT([no]) ], | |
+ [ AC_MSG_WARN([cross-compiling: assuming yes]) | |
+ enable_nistp521=1 ] | |
+ )], | |
+ AC_MSG_RESULT([no]) | |
+) | |
+ | |
+COMMENT_OUT_ECC="#no ecc#" | |
+TEST_SSH_ECC=no | |
+ | |
+if test x$enable_nistp256 = x1 || test x$enable_nistp384 = x1 || \ | |
+ test x$enable_nistp521 = x1; then | |
+ AC_DEFINE(OPENSSL_HAS_ECC, [1], [OpenSSL has ECC]) | |
+fi | |
+if test x$enable_nistp256 = x1; then | |
+ AC_DEFINE([OPENSSL_HAS_NISTP256], [1], | |
+ [libcrypto has NID_X9_62_prime256v1]) | |
+ TEST_SSH_ECC=yes | |
+ COMMENT_OUT_ECC="" | |
+else | |
+ unsupported_algorithms="$unsupported_algorithms ecdsa-sha2-nistp256 \ | |
+ ecdh-sha2-nistp256 ecdsa-sha2-nistp256-cert-v01@openssh.com" | |
+fi | |
+if test x$enable_nistp384 = x1; then | |
+ AC_DEFINE([OPENSSL_HAS_NISTP384], [1], [libcrypto has NID_secp384r1]) | |
+ TEST_SSH_ECC=yes | |
+ COMMENT_OUT_ECC="" | |
+else | |
+ unsupported_algorithms="$unsupported_algorithms ecdsa-sha2-nistp384 \ | |
+ ecdh-sha2-nistp384 ecdsa-sha2-nistp384-cert-v01@openssh.com" | |
+fi | |
+if test x$enable_nistp521 = x1; then | |
+ AC_DEFINE([OPENSSL_HAS_NISTP521], [1], [libcrypto has NID_secp521r1]) | |
+ TEST_SSH_ECC=yes | |
+ COMMENT_OUT_ECC="" | |
+else | |
+ unsupported_algorithms="$unsupported_algorithms ecdh-sha2-nistp521 \ | |
+ ecdsa-sha2-nistp521 ecdsa-sha2-nistp521-cert-v01@openssh.com" | |
+fi | |
+ | |
+AC_SUBST([TEST_SSH_ECC]) | |
+AC_SUBST([COMMENT_OUT_ECC]) | |
+ | |
+AC_CHECK_FUNCS([ \ | |
+ arc4random \ | |
+ arc4random_buf \ | |
+ arc4random_stir \ | |
+ arc4random_uniform \ | |
+]) | |
+ | |
+saved_LIBS="$LIBS" | |
+AC_CHECK_LIB([iaf], [ia_openinfo], [ | |
+ LIBS="$LIBS -liaf" | |
+ AC_CHECK_FUNCS([set_id], [SSHDLIBS="$SSHDLIBS -liaf" | |
+ AC_DEFINE([HAVE_LIBIAF], [1], | |
+ [Define if system has libiaf that supports set_id]) | |
+ ]) | |
+]) | |
+LIBS="$saved_LIBS" | |
+ | |
+### Configure cryptographic random number support | |
+ | |
+# Check wheter OpenSSL seeds itself | |
+AC_MSG_CHECKING([whether OpenSSL's PRNG is internally seeded]) | |
+AC_RUN_IFELSE( | |
+ [AC_LANG_PROGRAM([[ | |
+#include <string.h> | |
+#include <openssl/rand.h> | |
+ ]], [[ | |
+ exit(RAND_status() == 1 ? 0 : 1); | |
+ ]])], | |
+ [ | |
+ OPENSSL_SEEDS_ITSELF=yes | |
+ AC_MSG_RESULT([yes]) | |
+ ], | |
+ [ | |
+ AC_MSG_RESULT([no]) | |
+ ], | |
+ [ | |
+ AC_MSG_WARN([cross compiling: assuming yes]) | |
+ # This is safe, since we will fatal() at runtime if | |
+ # OpenSSL is not seeded correctly. | |
+ OPENSSL_SEEDS_ITSELF=yes | |
+ ] | |
+) | |
+ | |
+# PRNGD TCP socket | |
+AC_ARG_WITH([prngd-port], | |
+ [ --with-prngd-port=PORT read entropy from PRNGD/EGD TCP localhost:PORT], | |
+ [ | |
+ case "$withval" in | |
+ no) | |
+ withval="" | |
+ ;; | |
+ [[0-9]]*) | |
+ ;; | |
+ *) | |
+ AC_MSG_ERROR([You must specify a numeric port number for --with-prngd-port]) | |
+ ;; | |
+ esac | |
+ if test ! -z "$withval" ; then | |
+ PRNGD_PORT="$withval" | |
+ AC_DEFINE_UNQUOTED([PRNGD_PORT], [$PRNGD_PORT], | |
+ [Port number of PRNGD/EGD random number socket]) | |
+ fi | |
+ ] | |
+) | |
+ | |
+# PRNGD Unix domain socket | |
+AC_ARG_WITH([prngd-socket], | |
+ [ --with-prngd-socket=FILE read entropy from PRNGD/EGD socket FILE (default=/var/run/egd-pool)], | |
+ [ | |
+ case "$withval" in | |
+ yes) | |
+ withval="/var/run/egd-pool" | |
+ ;; | |
+ no) | |
+ withval="" | |
+ ;; | |
+ /*) | |
+ ;; | |
+ *) | |
+ AC_MSG_ERROR([You must specify an absolute path to the entropy socket]) | |
+ ;; | |
+ esac | |
+ | |
+ if test ! -z "$withval" ; then | |
+ if test ! -z "$PRNGD_PORT" ; then | |
+ AC_MSG_ERROR([You may not specify both a PRNGD/EGD port and socket]) | |
+ fi | |
+ if test ! -r "$withval" ; then | |
+ AC_MSG_WARN([Entropy socket is not readable]) | |
+ fi | |
+ PRNGD_SOCKET="$withval" | |
+ AC_DEFINE_UNQUOTED([PRNGD_SOCKET], ["$PRNGD_SOCKET"], | |
+ [Location of PRNGD/EGD random number socket]) | |
+ fi | |
+ ], | |
+ [ | |
+ # Check for existing socket only if we don't have a random device already | |
+ if test "x$OPENSSL_SEEDS_ITSELF" != "xyes" ; then | |
+ AC_MSG_CHECKING([for PRNGD/EGD socket]) | |
+ # Insert other locations here | |
+ for sock in /var/run/egd-pool /dev/egd-pool /etc/entropy; do | |
+ if test -r $sock && $TEST_MINUS_S_SH -c "test -S $sock -o -p $sock" ; then | |
+ PRNGD_SOCKET="$sock" | |
+ AC_DEFINE_UNQUOTED([PRNGD_SOCKET], ["$PRNGD_SOCKET"]) | |
+ break; | |
+ fi | |
+ done | |
+ if test ! -z "$PRNGD_SOCKET" ; then | |
+ AC_MSG_RESULT([$PRNGD_SOCKET]) | |
+ else | |
+ AC_MSG_RESULT([not found]) | |
+ fi | |
+ fi | |
+ ] | |
+) | |
+ | |
+# Which randomness source do we use? | |
+if test ! -z "$PRNGD_PORT" ; then | |
+ RAND_MSG="PRNGd port $PRNGD_PORT" | |
+elif test ! -z "$PRNGD_SOCKET" ; then | |
+ RAND_MSG="PRNGd socket $PRNGD_SOCKET" | |
+elif test ! -z "$OPENSSL_SEEDS_ITSELF" ; then | |
+ AC_DEFINE([OPENSSL_PRNG_ONLY], [1], | |
+ [Define if you want OpenSSL's internally seeded PRNG only]) | |
+ RAND_MSG="OpenSSL internal ONLY" | |
+else | |
+ AC_MSG_ERROR([OpenSSH has no source of random numbers. Please configure OpenSSL with an entropy source or re-run configure using one of the --with-prngd-port or --with-prngd-socket options]) | |
+fi | |
+ | |
+# Check for PAM libs | |
+PAM_MSG="no" | |
+AC_ARG_WITH([pam], | |
+ [ --with-pam Enable PAM support ], | |
+ [ | |
+ if test "x$withval" != "xno" ; then | |
+ if test "x$ac_cv_header_security_pam_appl_h" != "xyes" && \ | |
+ test "x$ac_cv_header_pam_pam_appl_h" != "xyes" ; then | |
+ AC_MSG_ERROR([PAM headers not found]) | |
+ fi | |
+ | |
+ saved_LIBS="$LIBS" | |
+ AC_CHECK_LIB([dl], [dlopen], , ) | |
+ AC_CHECK_LIB([pam], [pam_set_item], , [AC_MSG_ERROR([*** libpam missing])]) | |
+ AC_CHECK_FUNCS([pam_getenvlist]) | |
+ AC_CHECK_FUNCS([pam_putenv]) | |
+ LIBS="$saved_LIBS" | |
+ | |
+ PAM_MSG="yes" | |
+ | |
+ SSHDLIBS="$SSHDLIBS -lpam" | |
+ AC_DEFINE([USE_PAM], [1], | |
+ [Define if you want to enable PAM support]) | |
+ | |
+ if test $ac_cv_lib_dl_dlopen = yes; then | |
+ case "$LIBS" in | |
+ *-ldl*) | |
+ # libdl already in LIBS | |
+ ;; | |
+ *) | |
+ SSHDLIBS="$SSHDLIBS -ldl" | |
+ ;; | |
+ esac | |
+ fi | |
+ fi | |
+ ] | |
+) | |
+ | |
+# Check for older PAM | |
+if test "x$PAM_MSG" = "xyes" ; then | |
+ # Check PAM strerror arguments (old PAM) | |
+ AC_MSG_CHECKING([whether pam_strerror takes only one argument]) | |
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ | |
+#include <stdlib.h> | |
+#if defined(HAVE_SECURITY_PAM_APPL_H) | |
+#include <security/pam_appl.h> | |
+#elif defined (HAVE_PAM_PAM_APPL_H) | |
+#include <pam/pam_appl.h> | |
+#endif | |
+ ]], [[ | |
+(void)pam_strerror((pam_handle_t *)NULL, -1); | |
+ ]])], [AC_MSG_RESULT([no])], [ | |
+ AC_DEFINE([HAVE_OLD_PAM], [1], | |
+ [Define if you have an old version of PAM | |
+ which takes only one argument to pam_strerror]) | |
+ AC_MSG_RESULT([yes]) | |
+ PAM_MSG="yes (old library)" | |
+ | |
+ ]) | |
+fi | |
+ | |
+case "$host" in | |
+*-*-cygwin*) | |
+ SSH_PRIVSEP_USER=CYGWIN_SSH_PRIVSEP_USER | |
+ ;; | |
+*) | |
+ SSH_PRIVSEP_USER=sshd | |
+ ;; | |
+esac | |
+AC_ARG_WITH([privsep-user], | |
+ [ --with-privsep-user=user Specify non-privileged user for privilege separation], | |
+ [ | |
+ if test -n "$withval" && test "x$withval" != "xno" && \ | |
+ test "x${withval}" != "xyes"; then | |
+ SSH_PRIVSEP_USER=$withval | |
+ fi | |
+ ] | |
+) | |
+if test "x$SSH_PRIVSEP_USER" = "xCYGWIN_SSH_PRIVSEP_USER" ; then | |
+ AC_DEFINE_UNQUOTED([SSH_PRIVSEP_USER], [CYGWIN_SSH_PRIVSEP_USER], | |
+ [Cygwin function to fetch non-privileged user for privilege separation]) | |
+else | |
+ AC_DEFINE_UNQUOTED([SSH_PRIVSEP_USER], ["$SSH_PRIVSEP_USER"], | |
+ [non-privileged user for privilege separation]) | |
+fi | |
+AC_SUBST([SSH_PRIVSEP_USER]) | |
+ | |
+if test "x$have_linux_no_new_privs" = "x1" ; then | |
+AC_CHECK_DECL([SECCOMP_MODE_FILTER], [have_seccomp_filter=1], , [ | |
+ #include <sys/types.h> | |
+ #include <linux/seccomp.h> | |
+]) | |
+fi | |
+if test "x$have_seccomp_filter" = "x1" ; then | |
+AC_MSG_CHECKING([kernel for seccomp_filter support]) | |
+AC_LINK_IFELSE([AC_LANG_PROGRAM([[ | |
+ #include <errno.h> | |
+ #include <elf.h> | |
+ #include <linux/audit.h> | |
+ #include <linux/seccomp.h> | |
+ #include <stdlib.h> | |
+ #include <sys/prctl.h> | |
+ ]], | |
+ [[ int i = $seccomp_audit_arch; | |
+ errno = 0; | |
+ prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, NULL, 0, 0); | |
+ exit(errno == EFAULT ? 0 : 1); ]])], | |
+ [ AC_MSG_RESULT([yes]) ], [ | |
+ AC_MSG_RESULT([no]) | |
+ # Disable seccomp filter as a target | |
+ have_seccomp_filter=0 | |
+ ] | |
+) | |
+fi | |
+ | |
+# Decide which sandbox style to use | |
+sandbox_arg="" | |
+AC_ARG_WITH([sandbox], | |
+ [ --with-sandbox=style Specify privilege separation sandbox (no, darwin, rlimit, systrace, seccomp_filter, capsicum)], | |
+ [ | |
+ if test "x$withval" = "xyes" ; then | |
+ sandbox_arg="" | |
+ else | |
+ sandbox_arg="$withval" | |
+ fi | |
+ ] | |
+) | |
+ | |
+# Some platforms (seems to be the ones that have a kernel poll(2)-type | |
+# function with which they implement select(2)) use an extra file descriptor | |
+# when calling select(2), which means we can't use the rlimit sandbox. | |
+AC_MSG_CHECKING([if select works with descriptor rlimit]) | |
+AC_RUN_IFELSE( | |
+ [AC_LANG_PROGRAM([[ | |
+#include <sys/types.h> | |
+#ifdef HAVE_SYS_TIME_H | |
+# include <sys/time.h> | |
+#endif | |
+#include <sys/resource.h> | |
+#ifdef HAVE_SYS_SELECT_H | |
+# include <sys/select.h> | |
+#endif | |
+#include <errno.h> | |
+#include <fcntl.h> | |
+#include <stdlib.h> | |
+ ]],[[ | |
+ struct rlimit rl_zero; | |
+ int fd, r; | |
+ fd_set fds; | |
+ struct timeval tv; | |
+ | |
+ fd = open("/dev/null", O_RDONLY); | |
+ FD_ZERO(&fds); | |
+ FD_SET(fd, &fds); | |
+ rl_zero.rlim_cur = rl_zero.rlim_max = 0; | |
+ setrlimit(RLIMIT_FSIZE, &rl_zero); | |
+ setrlimit(RLIMIT_NOFILE, &rl_zero); | |
+ tv.tv_sec = 1; | |
+ tv.tv_usec = 0; | |
+ r = select(fd+1, &fds, NULL, NULL, &tv); | |
+ exit (r == -1 ? 1 : 0); | |
+ ]])], | |
+ [AC_MSG_RESULT([yes]) | |
+ select_works_with_rlimit=yes], | |
+ [AC_MSG_RESULT([no]) | |
+ select_works_with_rlimit=no], | |
+ [AC_MSG_WARN([cross compiling: assuming yes])] | |
+) | |
+ | |
+AC_MSG_CHECKING([if setrlimit(RLIMIT_NOFILE,{0,0}) works]) | |
+AC_RUN_IFELSE( | |
+ [AC_LANG_PROGRAM([[ | |
+#include <sys/types.h> | |
+#ifdef HAVE_SYS_TIME_H | |
+# include <sys/time.h> | |
+#endif | |
+#include <sys/resource.h> | |
+#include <errno.h> | |
+#include <stdlib.h> | |
+ ]],[[ | |
+ struct rlimit rl_zero; | |
+ int fd, r; | |
+ fd_set fds; | |
+ | |
+ rl_zero.rlim_cur = rl_zero.rlim_max = 0; | |
+ r = setrlimit(RLIMIT_NOFILE, &rl_zero); | |
+ exit (r == -1 ? 1 : 0); | |
+ ]])], | |
+ [AC_MSG_RESULT([yes]) | |
+ rlimit_nofile_zero_works=yes], | |
+ [AC_MSG_RESULT([no]) | |
+ rlimit_nofile_zero_works=no], | |
+ [AC_MSG_WARN([cross compiling: assuming yes])] | |
+) | |
+ | |
+AC_MSG_CHECKING([if setrlimit RLIMIT_FSIZE works]) | |
+AC_RUN_IFELSE( | |
+ [AC_LANG_PROGRAM([[ | |
+#include <sys/types.h> | |
+#include <sys/resource.h> | |
+#include <stdlib.h> | |
+ ]],[[ | |
+ struct rlimit rl_zero; | |
+ | |
+ rl_zero.rlim_cur = rl_zero.rlim_max = 0; | |
+ exit(setrlimit(RLIMIT_FSIZE, &rl_zero) != 0); | |
+ ]])], | |
+ [AC_MSG_RESULT([yes])], | |
+ [AC_MSG_RESULT([no]) | |
+ AC_DEFINE(SANDBOX_SKIP_RLIMIT_FSIZE, 1, | |
+ [setrlimit RLIMIT_FSIZE works])], | |
+ [AC_MSG_WARN([cross compiling: assuming yes])] | |
+) | |
+ | |
+if test "x$sandbox_arg" = "xsystrace" || \ | |
+ ( test -z "$sandbox_arg" && test "x$have_systr_policy_kill" = "x1" ) ; then | |
+ test "x$have_systr_policy_kill" != "x1" && \ | |
+ AC_MSG_ERROR([systrace sandbox requires systrace headers and SYSTR_POLICY_KILL support]) | |
+ SANDBOX_STYLE="systrace" | |
+ AC_DEFINE([SANDBOX_SYSTRACE], [1], [Sandbox using systrace(4)]) | |
+elif test "x$sandbox_arg" = "xdarwin" || \ | |
+ ( test -z "$sandbox_arg" && test "x$ac_cv_func_sandbox_init" = "xyes" && \ | |
+ test "x$ac_cv_header_sandbox_h" = "xyes") ; then | |
+ test "x$ac_cv_func_sandbox_init" != "xyes" -o \ | |
+ "x$ac_cv_header_sandbox_h" != "xyes" && \ | |
+ AC_MSG_ERROR([Darwin seatbelt sandbox requires sandbox.h and sandbox_init function]) | |
+ SANDBOX_STYLE="darwin" | |
+ AC_DEFINE([SANDBOX_DARWIN], [1], [Sandbox using Darwin sandbox_init(3)]) | |
+elif test "x$sandbox_arg" = "xseccomp_filter" || \ | |
+ ( test -z "$sandbox_arg" && \ | |
+ test "x$have_seccomp_filter" = "x1" && \ | |
+ test "x$ac_cv_header_elf_h" = "xyes" && \ | |
+ test "x$ac_cv_header_linux_audit_h" = "xyes" && \ | |
+ test "x$ac_cv_header_linux_filter_h" = "xyes" && \ | |
+ test "x$seccomp_audit_arch" != "x" && \ | |
+ test "x$have_linux_no_new_privs" = "x1" && \ | |
+ test "x$ac_cv_func_prctl" = "xyes" ) ; then | |
+ test "x$seccomp_audit_arch" = "x" && \ | |
+ AC_MSG_ERROR([seccomp_filter sandbox not supported on $host]) | |
+ test "x$have_linux_no_new_privs" != "x1" && \ | |
+ AC_MSG_ERROR([seccomp_filter sandbox requires PR_SET_NO_NEW_PRIVS]) | |
+ test "x$have_seccomp_filter" != "x1" && \ | |
+ AC_MSG_ERROR([seccomp_filter sandbox requires seccomp headers]) | |
+ test "x$ac_cv_func_prctl" != "xyes" && \ | |
+ AC_MSG_ERROR([seccomp_filter sandbox requires prctl function]) | |
+ SANDBOX_STYLE="seccomp_filter" | |
+ AC_DEFINE([SANDBOX_SECCOMP_FILTER], [1], [Sandbox using seccomp filter]) | |
+elif test "x$sandbox_arg" = "xcapsicum" || \ | |
+ ( test -z "$sandbox_arg" && \ | |
+ test "x$ac_cv_header_sys_capability_h" = "xyes" && \ | |
+ test "x$ac_cv_func_cap_rights_limit" = "xyes") ; then | |
+ test "x$ac_cv_header_sys_capability_h" != "xyes" && \ | |
+ AC_MSG_ERROR([capsicum sandbox requires sys/capability.h header]) | |
+ test "x$ac_cv_func_cap_rights_limit" != "xyes" && \ | |
+ AC_MSG_ERROR([capsicum sandbox requires cap_rights_limit function]) | |
+ SANDBOX_STYLE="capsicum" | |
+ AC_DEFINE([SANDBOX_CAPSICUM], [1], [Sandbox using capsicum]) | |
+elif test "x$sandbox_arg" = "xrlimit" || \ | |
+ ( test -z "$sandbox_arg" && test "x$ac_cv_func_setrlimit" = "xyes" && \ | |
+ test "x$select_works_with_rlimit" = "xyes" && \ | |
+ test "x$rlimit_nofile_zero_works" = "xyes" ) ; then | |
+ test "x$ac_cv_func_setrlimit" != "xyes" && \ | |
+ AC_MSG_ERROR([rlimit sandbox requires setrlimit function]) | |
+ test "x$select_works_with_rlimit" != "xyes" && \ | |
+ AC_MSG_ERROR([rlimit sandbox requires select to work with rlimit]) | |
+ SANDBOX_STYLE="rlimit" | |
+ AC_DEFINE([SANDBOX_RLIMIT], [1], [Sandbox using setrlimit(2)]) | |
+elif test -z "$sandbox_arg" || test "x$sandbox_arg" = "xno" || \ | |
+ test "x$sandbox_arg" = "xnone" || test "x$sandbox_arg" = "xnull" ; then | |
+ SANDBOX_STYLE="none" | |
+ AC_DEFINE([SANDBOX_NULL], [1], [no privsep sandboxing]) | |
+else | |
+ AC_MSG_ERROR([unsupported --with-sandbox]) | |
+fi | |
+ | |
+# Cheap hack to ensure NEWS-OS libraries are arranged right. | |
+if test ! -z "$SONY" ; then | |
+ LIBS="$LIBS -liberty"; | |
+fi | |
+ | |
+# Check for long long datatypes | |
+AC_CHECK_TYPES([long long, unsigned long long, long double]) | |
+ | |
+# Check datatype sizes | |
+AC_CHECK_SIZEOF([short int], [2]) | |
+AC_CHECK_SIZEOF([int], [4]) | |
+AC_CHECK_SIZEOF([long int], [4]) | |
+AC_CHECK_SIZEOF([long long int], [8]) | |
+ | |
+# Sanity check long long for some platforms (AIX) | |
+if test "x$ac_cv_sizeof_long_long_int" = "x4" ; then | |
+ ac_cv_sizeof_long_long_int=0 | |
+fi | |
+ | |
+# compute LLONG_MIN and LLONG_MAX if we don't know them. | |
+if test -z "$have_llong_max"; then | |
+ AC_MSG_CHECKING([for max value of long long]) | |
+ AC_RUN_IFELSE( | |
+ [AC_LANG_PROGRAM([[ | |
+#include <stdio.h> | |
+/* Why is this so damn hard? */ | |
+#ifdef __GNUC__ | |
+# undef __GNUC__ | |
+#endif | |
+#define __USE_ISOC99 | |
+#include <limits.h> | |
+#define DATA "conftest.llminmax" | |
+#define my_abs(a) ((a) < 0 ? ((a) * -1) : (a)) | |
+ | |
+/* | |
+ * printf in libc on some platforms (eg old Tru64) does not understand %lld so | |
+ * we do this the hard way. | |
+ */ | |
+static int | |
+fprint_ll(FILE *f, long long n) | |
+{ | |
+ unsigned int i; | |
+ int l[sizeof(long long) * 8]; | |
+ | |
+ if (n < 0) | |
+ if (fprintf(f, "-") < 0) | |
+ return -1; | |
+ for (i = 0; n != 0; i++) { | |
+ l[i] = my_abs(n % 10); | |
+ n /= 10; | |
+ } | |
+ do { | |
+ if (fprintf(f, "%d", l[--i]) < 0) | |
+ return -1; | |
+ } while (i != 0); | |
+ if (fprintf(f, " ") < 0) | |
+ return -1; | |
+ return 0; | |
+} | |
+ ]], [[ | |
+ FILE *f; | |
+ long long i, llmin, llmax = 0; | |
+ | |
+ if((f = fopen(DATA,"w")) == NULL) | |
+ exit(1); | |
+ | |
+#if defined(LLONG_MIN) && defined(LLONG_MAX) | |
+ fprintf(stderr, "Using system header for LLONG_MIN and LLONG_MAX\n"); | |
+ llmin = LLONG_MIN; | |
+ llmax = LLONG_MAX; | |
+#else | |
+ fprintf(stderr, "Calculating LLONG_MIN and LLONG_MAX\n"); | |
+ /* This will work on one's complement and two's complement */ | |
+ for (i = 1; i > llmax; i <<= 1, i++) | |
+ llmax = i; | |
+ llmin = llmax + 1LL; /* wrap */ | |
+#endif | |
+ | |
+ /* Sanity check */ | |
+ if (llmin + 1 < llmin || llmin - 1 < llmin || llmax + 1 > llmax | |
+ || llmax - 1 > llmax || llmin == llmax || llmin == 0 | |
+ || llmax == 0 || llmax < LONG_MAX || llmin > LONG_MIN) { | |
+ fprintf(f, "unknown unknown\n"); | |
+ exit(2); | |
+ } | |
+ | |
+ if (fprint_ll(f, llmin) < 0) | |
+ exit(3); | |
+ if (fprint_ll(f, llmax) < 0) | |
+ exit(4); | |
+ if (fclose(f) < 0) | |
+ exit(5); | |
+ exit(0); | |
+ ]])], | |
+ [ | |
+ llong_min=`$AWK '{print $1}' conftest.llminmax` | |
+ llong_max=`$AWK '{print $2}' conftest.llminmax` | |
+ | |
+ AC_MSG_RESULT([$llong_max]) | |
+ AC_DEFINE_UNQUOTED([LLONG_MAX], [${llong_max}LL], | |
+ [max value of long long calculated by configure]) | |
+ AC_MSG_CHECKING([for min value of long long]) | |
+ AC_MSG_RESULT([$llong_min]) | |
+ AC_DEFINE_UNQUOTED([LLONG_MIN], [${llong_min}LL], | |
+ [min value of long long calculated by configure]) | |
+ ], | |
+ [ | |
+ AC_MSG_RESULT([not found]) | |
+ ], | |
+ [ | |
+ AC_MSG_WARN([cross compiling: not checking]) | |
+ ] | |
+ ) | |
+fi | |
+ | |
+ | |
+# More checks for data types | |
+AC_CACHE_CHECK([for u_int type], ac_cv_have_u_int, [ | |
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include <sys/types.h> ]], | |
+ [[ u_int a; a = 1;]])], | |
+ [ ac_cv_have_u_int="yes" ], [ ac_cv_have_u_int="no" | |
+ ]) | |
+]) | |
+if test "x$ac_cv_have_u_int" = "xyes" ; then | |
+ AC_DEFINE([HAVE_U_INT], [1], [define if you have u_int data type]) | |
+ have_u_int=1 | |
+fi | |
+ | |
+AC_CACHE_CHECK([for intXX_t types], ac_cv_have_intxx_t, [ | |
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include <sys/types.h> ]], | |
+ [[ int8_t a; int16_t b; int32_t c; a = b = c = 1;]])], | |
+ [ ac_cv_have_intxx_t="yes" ], [ ac_cv_have_intxx_t="no" | |
+ ]) | |
+]) | |
+if test "x$ac_cv_have_intxx_t" = "xyes" ; then | |
+ AC_DEFINE([HAVE_INTXX_T], [1], [define if you have intxx_t data type]) | |
+ have_intxx_t=1 | |
+fi | |
+ | |
+if (test -z "$have_intxx_t" && \ | |
+ test "x$ac_cv_header_stdint_h" = "xyes") | |
+then | |
+ AC_MSG_CHECKING([for intXX_t types in stdint.h]) | |
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include <stdint.h> ]], | |
+ [[ int8_t a; int16_t b; int32_t c; a = b = c = 1;]])], | |
+ [ | |
+ AC_DEFINE([HAVE_INTXX_T]) | |
+ AC_MSG_RESULT([yes]) | |
+ ], [ AC_MSG_RESULT([no]) | |
+ ]) | |
+fi | |
+ | |
+AC_CACHE_CHECK([for int64_t type], ac_cv_have_int64_t, [ | |
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ | |
+#include <sys/types.h> | |
+#ifdef HAVE_STDINT_H | |
+# include <stdint.h> | |
+#endif | |
+#include <sys/socket.h> | |
+#ifdef HAVE_SYS_BITYPES_H | |
+# include <sys/bitypes.h> | |
+#endif | |
+ ]], [[ | |
+int64_t a; a = 1; | |
+ ]])], | |
+ [ ac_cv_have_int64_t="yes" ], [ ac_cv_have_int64_t="no" | |
+ ]) | |
+]) | |
+if test "x$ac_cv_have_int64_t" = "xyes" ; then | |
+ AC_DEFINE([HAVE_INT64_T], [1], [define if you have int64_t data type]) | |
+fi | |
+ | |
+AC_CACHE_CHECK([for u_intXX_t types], ac_cv_have_u_intxx_t, [ | |
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include <sys/types.h> ]], | |
+ [[ u_int8_t a; u_int16_t b; u_int32_t c; a = b = c = 1;]])], | |
+ [ ac_cv_have_u_intxx_t="yes" ], [ ac_cv_have_u_intxx_t="no" | |
+ ]) | |
+]) | |
+if test "x$ac_cv_have_u_intxx_t" = "xyes" ; then | |
+ AC_DEFINE([HAVE_U_INTXX_T], [1], [define if you have u_intxx_t data type]) | |
+ have_u_intxx_t=1 | |
+fi | |
+ | |
+if test -z "$have_u_intxx_t" ; then | |
+ AC_MSG_CHECKING([for u_intXX_t types in sys/socket.h]) | |
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include <sys/socket.h> ]], | |
+ [[ u_int8_t a; u_int16_t b; u_int32_t c; a = b = c = 1;]])], | |
+ [ | |
+ AC_DEFINE([HAVE_U_INTXX_T]) | |
+ AC_MSG_RESULT([yes]) | |
+ ], [ AC_MSG_RESULT([no]) | |
+ ]) | |
+fi | |
+ | |
+AC_CACHE_CHECK([for u_int64_t types], ac_cv_have_u_int64_t, [ | |
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include <sys/types.h> ]], | |
+ [[ u_int64_t a; a = 1;]])], | |
+ [ ac_cv_have_u_int64_t="yes" ], [ ac_cv_have_u_int64_t="no" | |
+ ]) | |
+]) | |
+if test "x$ac_cv_have_u_int64_t" = "xyes" ; then | |
+ AC_DEFINE([HAVE_U_INT64_T], [1], [define if you have u_int64_t data type]) | |
+ have_u_int64_t=1 | |
+fi | |
+ | |
+if (test -z "$have_u_int64_t" && \ | |
+ test "x$ac_cv_header_sys_bitypes_h" = "xyes") | |
+then | |
+ AC_MSG_CHECKING([for u_int64_t type in sys/bitypes.h]) | |
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include <sys/bitypes.h> ]], | |
+ [[ u_int64_t a; a = 1]])], | |
+ [ | |
+ AC_DEFINE([HAVE_U_INT64_T]) | |
+ AC_MSG_RESULT([yes]) | |
+ ], [ AC_MSG_RESULT([no]) | |
+ ]) | |
+fi | |
+ | |
+if test -z "$have_u_intxx_t" ; then | |
+ AC_CACHE_CHECK([for uintXX_t types], ac_cv_have_uintxx_t, [ | |
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ | |
+#include <sys/types.h> | |
+ ]], [[ | |
+ uint8_t a; | |
+ uint16_t b; | |
+ uint32_t c; | |
+ a = b = c = 1; | |
+ ]])], | |
+ [ ac_cv_have_uintxx_t="yes" ], [ ac_cv_have_uintxx_t="no" | |
+ ]) | |
+ ]) | |
+ if test "x$ac_cv_have_uintxx_t" = "xyes" ; then | |
+ AC_DEFINE([HAVE_UINTXX_T], [1], | |
+ [define if you have uintxx_t data type]) | |
+ fi | |
+fi | |
+ | |
+if (test -z "$have_uintxx_t" && \ | |
+ test "x$ac_cv_header_stdint_h" = "xyes") | |
+then | |
+ AC_MSG_CHECKING([for uintXX_t types in stdint.h]) | |
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include <stdint.h> ]], | |
+ [[ uint8_t a; uint16_t b; uint32_t c; a = b = c = 1;]])], | |
+ [ | |
+ AC_DEFINE([HAVE_UINTXX_T]) | |
+ AC_MSG_RESULT([yes]) | |
+ ], [ AC_MSG_RESULT([no]) | |
+ ]) | |
+fi | |
+ | |
+if (test -z "$have_uintxx_t" && \ | |
+ test "x$ac_cv_header_inttypes_h" = "xyes") | |
+then | |
+ AC_MSG_CHECKING([for uintXX_t types in inttypes.h]) | |
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include <inttypes.h> ]], | |
+ [[ uint8_t a; uint16_t b; uint32_t c; a = b = c = 1;]])], | |
+ [ | |
+ AC_DEFINE([HAVE_UINTXX_T]) | |
+ AC_MSG_RESULT([yes]) | |
+ ], [ AC_MSG_RESULT([no]) | |
+ ]) | |
+fi | |
+ | |
+if (test -z "$have_u_intxx_t" || test -z "$have_intxx_t" && \ | |
+ test "x$ac_cv_header_sys_bitypes_h" = "xyes") | |
+then | |
+ AC_MSG_CHECKING([for intXX_t and u_intXX_t types in sys/bitypes.h]) | |
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ | |
+#include <sys/bitypes.h> | |
+ ]], [[ | |
+ int8_t a; int16_t b; int32_t c; | |
+ u_int8_t e; u_int16_t f; u_int32_t g; | |
+ a = b = c = e = f = g = 1; | |
+ ]])], | |
+ [ | |
+ AC_DEFINE([HAVE_U_INTXX_T]) | |
+ AC_DEFINE([HAVE_INTXX_T]) | |
+ AC_MSG_RESULT([yes]) | |
+ ], [AC_MSG_RESULT([no]) | |
+ ]) | |
+fi | |
+ | |
+ | |
+AC_CACHE_CHECK([for u_char], ac_cv_have_u_char, [ | |
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include <sys/types.h> ]], | |
+ [[ u_char foo; foo = 125; ]])], | |
+ [ ac_cv_have_u_char="yes" ], [ ac_cv_have_u_char="no" | |
+ ]) | |
+]) | |
+if test "x$ac_cv_have_u_char" = "xyes" ; then | |
+ AC_DEFINE([HAVE_U_CHAR], [1], [define if you have u_char data type]) | |
+fi | |
+ | |
+AC_CHECK_TYPES([intmax_t, uintmax_t], , , [ | |
+#include <sys/types.h> | |
+#include <stdint.h> | |
+]) | |
+ | |
+TYPE_SOCKLEN_T | |
+ | |
+AC_CHECK_TYPES([sig_atomic_t], , , [#include <signal.h>]) | |
+AC_CHECK_TYPES([fsblkcnt_t, fsfilcnt_t], , , [ | |
+#include <sys/types.h> | |
+#ifdef HAVE_SYS_BITYPES_H | |
+#include <sys/bitypes.h> | |
+#endif | |
+#ifdef HAVE_SYS_STATFS_H | |
+#include <sys/statfs.h> | |
+#endif | |
+#ifdef HAVE_SYS_STATVFS_H | |
+#include <sys/statvfs.h> | |
+#endif | |
+]) | |
+ | |
+AC_CHECK_TYPES([in_addr_t, in_port_t], , , | |
+[#include <sys/types.h> | |
+#include <netinet/in.h>]) | |
+ | |
+AC_CACHE_CHECK([for size_t], ac_cv_have_size_t, [ | |
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include <sys/types.h> ]], | |
+ [[ size_t foo; foo = 1235; ]])], | |
+ [ ac_cv_have_size_t="yes" ], [ ac_cv_have_size_t="no" | |
+ ]) | |
+]) | |
+if test "x$ac_cv_have_size_t" = "xyes" ; then | |
+ AC_DEFINE([HAVE_SIZE_T], [1], [define if you have size_t data type]) | |
+fi | |
+ | |
+AC_CACHE_CHECK([for ssize_t], ac_cv_have_ssize_t, [ | |
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include <sys/types.h> ]], | |
+ [[ ssize_t foo; foo = 1235; ]])], | |
+ [ ac_cv_have_ssize_t="yes" ], [ ac_cv_have_ssize_t="no" | |
+ ]) | |
+]) | |
+if test "x$ac_cv_have_ssize_t" = "xyes" ; then | |
+ AC_DEFINE([HAVE_SSIZE_T], [1], [define if you have ssize_t data type]) | |
+fi | |
+ | |
+AC_CACHE_CHECK([for clock_t], ac_cv_have_clock_t, [ | |
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include <time.h> ]], | |
+ [[ clock_t foo; foo = 1235; ]])], | |
+ [ ac_cv_have_clock_t="yes" ], [ ac_cv_have_clock_t="no" | |
+ ]) | |
+]) | |
+if test "x$ac_cv_have_clock_t" = "xyes" ; then | |
+ AC_DEFINE([HAVE_CLOCK_T], [1], [define if you have clock_t data type]) | |
+fi | |
+ | |
+AC_CACHE_CHECK([for sa_family_t], ac_cv_have_sa_family_t, [ | |
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ | |
+#include <sys/types.h> | |
+#include <sys/socket.h> | |
+ ]], [[ sa_family_t foo; foo = 1235; ]])], | |
+ [ ac_cv_have_sa_family_t="yes" ], | |
+ [ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ | |
+#include <sys/types.h> | |
+#include <sys/socket.h> | |
+#include <netinet/in.h> | |
+ ]], [[ sa_family_t foo; foo = 1235; ]])], | |
+ [ ac_cv_have_sa_family_t="yes" ], | |
+ [ ac_cv_have_sa_family_t="no" ] | |
+ ) | |
+ ]) | |
+]) | |
+if test "x$ac_cv_have_sa_family_t" = "xyes" ; then | |
+ AC_DEFINE([HAVE_SA_FAMILY_T], [1], | |
+ [define if you have sa_family_t data type]) | |
+fi | |
+ | |
+AC_CACHE_CHECK([for pid_t], ac_cv_have_pid_t, [ | |
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include <sys/types.h> ]], | |
+ [[ pid_t foo; foo = 1235; ]])], | |
+ [ ac_cv_have_pid_t="yes" ], [ ac_cv_have_pid_t="no" | |
+ ]) | |
+]) | |
+if test "x$ac_cv_have_pid_t" = "xyes" ; then | |
+ AC_DEFINE([HAVE_PID_T], [1], [define if you have pid_t data type]) | |
+fi | |
+ | |
+AC_CACHE_CHECK([for mode_t], ac_cv_have_mode_t, [ | |
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include <sys/types.h> ]], | |
+ [[ mode_t foo; foo = 1235; ]])], | |
+ [ ac_cv_have_mode_t="yes" ], [ ac_cv_have_mode_t="no" | |
+ ]) | |
+]) | |
+if test "x$ac_cv_have_mode_t" = "xyes" ; then | |
+ AC_DEFINE([HAVE_MODE_T], [1], [define if you have mode_t data type]) | |
+fi | |
+ | |
+ | |
+AC_CACHE_CHECK([for struct sockaddr_storage], ac_cv_have_struct_sockaddr_storage, [ | |
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ | |
+#include <sys/types.h> | |
+#include <sys/socket.h> | |
+ ]], [[ struct sockaddr_storage s; ]])], | |
+ [ ac_cv_have_struct_sockaddr_storage="yes" ], | |
+ [ ac_cv_have_struct_sockaddr_storage="no" | |
+ ]) | |
+]) | |
+if test "x$ac_cv_have_struct_sockaddr_storage" = "xyes" ; then | |
+ AC_DEFINE([HAVE_STRUCT_SOCKADDR_STORAGE], [1], | |
+ [define if you have struct sockaddr_storage data type]) | |
+fi | |
+ | |
+AC_CACHE_CHECK([for struct sockaddr_in6], ac_cv_have_struct_sockaddr_in6, [ | |
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ | |
+#include <sys/types.h> | |
+#include <netinet/in.h> | |
+ ]], [[ struct sockaddr_in6 s; s.sin6_family = 0; ]])], | |
+ [ ac_cv_have_struct_sockaddr_in6="yes" ], | |
+ [ ac_cv_have_struct_sockaddr_in6="no" | |
+ ]) | |
+]) | |
+if test "x$ac_cv_have_struct_sockaddr_in6" = "xyes" ; then | |
+ AC_DEFINE([HAVE_STRUCT_SOCKADDR_IN6], [1], | |
+ [define if you have struct sockaddr_in6 data type]) | |
+fi | |
+ | |
+AC_CACHE_CHECK([for struct in6_addr], ac_cv_have_struct_in6_addr, [ | |
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ | |
+#include <sys/types.h> | |
+#include <netinet/in.h> | |
+ ]], [[ struct in6_addr s; s.s6_addr[0] = 0; ]])], | |
+ [ ac_cv_have_struct_in6_addr="yes" ], | |
+ [ ac_cv_have_struct_in6_addr="no" | |
+ ]) | |
+]) | |
+if test "x$ac_cv_have_struct_in6_addr" = "xyes" ; then | |
+ AC_DEFINE([HAVE_STRUCT_IN6_ADDR], [1], | |
+ [define if you have struct in6_addr data type]) | |
+ | |
+dnl Now check for sin6_scope_id | |
+ AC_CHECK_MEMBERS([struct sockaddr_in6.sin6_scope_id], , , | |
+ [ | |
+#ifdef HAVE_SYS_TYPES_H | |
+#include <sys/types.h> | |
+#endif | |
+#include <netinet/in.h> | |
+ ]) | |
+fi | |
+ | |
+AC_CACHE_CHECK([for struct addrinfo], ac_cv_have_struct_addrinfo, [ | |
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ | |
+#include <sys/types.h> | |
+#include <sys/socket.h> | |
+#include <netdb.h> | |
+ ]], [[ struct addrinfo s; s.ai_flags = AI_PASSIVE; ]])], | |
+ [ ac_cv_have_struct_addrinfo="yes" ], | |
+ [ ac_cv_have_struct_addrinfo="no" | |
+ ]) | |
+]) | |
+if test "x$ac_cv_have_struct_addrinfo" = "xyes" ; then | |
+ AC_DEFINE([HAVE_STRUCT_ADDRINFO], [1], | |
+ [define if you have struct addrinfo data type]) | |
+fi | |
+ | |
+AC_CACHE_CHECK([for struct timeval], ac_cv_have_struct_timeval, [ | |
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include <sys/time.h> ]], | |
+ [[ struct timeval tv; tv.tv_sec = 1;]])], | |
+ [ ac_cv_have_struct_timeval="yes" ], | |
+ [ ac_cv_have_struct_timeval="no" | |
+ ]) | |
+]) | |
+if test "x$ac_cv_have_struct_timeval" = "xyes" ; then | |
+ AC_DEFINE([HAVE_STRUCT_TIMEVAL], [1], [define if you have struct timeval]) | |
+ have_struct_timeval=1 | |
+fi | |
+ | |
+AC_CHECK_TYPES([struct timespec]) | |
+ | |
+# We need int64_t or else certian parts of the compile will fail. | |
+if test "x$ac_cv_have_int64_t" = "xno" && \ | |
+ test "x$ac_cv_sizeof_long_int" != "x8" && \ | |
+ test "x$ac_cv_sizeof_long_long_int" = "x0" ; then | |
+ echo "OpenSSH requires int64_t support. Contact your vendor or install" | |
+ echo "an alternative compiler (I.E., GCC) before continuing." | |
+ echo "" | |
+ exit 1; | |
+else | |
+dnl test snprintf (broken on SCO w/gcc) | |
+ AC_RUN_IFELSE( | |
+ [AC_LANG_SOURCE([[ | |
+#include <stdio.h> | |
+#include <string.h> | |
+#ifdef HAVE_SNPRINTF | |
+main() | |
+{ | |
+ char buf[50]; | |
+ char expected_out[50]; | |
+ int mazsize = 50 ; | |
+#if (SIZEOF_LONG_INT == 8) | |
+ long int num = 0x7fffffffffffffff; | |
+#else | |
+ long long num = 0x7fffffffffffffffll; | |
+#endif | |
+ strcpy(expected_out, "9223372036854775807"); | |
+ snprintf(buf, mazsize, "%lld", num); | |
+ if(strcmp(buf, expected_out) != 0) | |
+ exit(1); | |
+ exit(0); | |
+} | |
+#else | |
+main() { exit(0); } | |
+#endif | |
+ ]])], [ true ], [ AC_DEFINE([BROKEN_SNPRINTF]) ], | |
+ AC_MSG_WARN([cross compiling: Assuming working snprintf()]) | |
+ ) | |
+fi | |
+ | |
+dnl Checks for structure members | |
+OSSH_CHECK_HEADER_FOR_FIELD([ut_host], [utmp.h], [HAVE_HOST_IN_UTMP]) | |
+OSSH_CHECK_HEADER_FOR_FIELD([ut_host], [utmpx.h], [HAVE_HOST_IN_UTMPX]) | |
+OSSH_CHECK_HEADER_FOR_FIELD([syslen], [utmpx.h], [HAVE_SYSLEN_IN_UTMPX]) | |
+OSSH_CHECK_HEADER_FOR_FIELD([ut_pid], [utmp.h], [HAVE_PID_IN_UTMP]) | |
+OSSH_CHECK_HEADER_FOR_FIELD([ut_type], [utmp.h], [HAVE_TYPE_IN_UTMP]) | |
+OSSH_CHECK_HEADER_FOR_FIELD([ut_type], [utmpx.h], [HAVE_TYPE_IN_UTMPX]) | |
+OSSH_CHECK_HEADER_FOR_FIELD([ut_tv], [utmp.h], [HAVE_TV_IN_UTMP]) | |
+OSSH_CHECK_HEADER_FOR_FIELD([ut_id], [utmp.h], [HAVE_ID_IN_UTMP]) | |
+OSSH_CHECK_HEADER_FOR_FIELD([ut_id], [utmpx.h], [HAVE_ID_IN_UTMPX]) | |
+OSSH_CHECK_HEADER_FOR_FIELD([ut_addr], [utmp.h], [HAVE_ADDR_IN_UTMP]) | |
+OSSH_CHECK_HEADER_FOR_FIELD([ut_addr], [utmpx.h], [HAVE_ADDR_IN_UTMPX]) | |
+OSSH_CHECK_HEADER_FOR_FIELD([ut_addr_v6], [utmp.h], [HAVE_ADDR_V6_IN_UTMP]) | |
+OSSH_CHECK_HEADER_FOR_FIELD([ut_addr_v6], [utmpx.h], [HAVE_ADDR_V6_IN_UTMPX]) | |
+OSSH_CHECK_HEADER_FOR_FIELD([ut_exit], [utmp.h], [HAVE_EXIT_IN_UTMP]) | |
+OSSH_CHECK_HEADER_FOR_FIELD([ut_time], [utmp.h], [HAVE_TIME_IN_UTMP]) | |
+OSSH_CHECK_HEADER_FOR_FIELD([ut_time], [utmpx.h], [HAVE_TIME_IN_UTMPX]) | |
+OSSH_CHECK_HEADER_FOR_FIELD([ut_tv], [utmpx.h], [HAVE_TV_IN_UTMPX]) | |
+ | |
+AC_CHECK_MEMBERS([struct stat.st_blksize]) | |
+AC_CHECK_MEMBERS([struct passwd.pw_gecos, struct passwd.pw_class, | |
+struct passwd.pw_change, struct passwd.pw_expire], | |
+[], [], [[ | |
+#include <sys/types.h> | |
+#include <pwd.h> | |
+]]) | |
+ | |
+AC_CHECK_MEMBER([struct __res_state.retrans], [], [AC_DEFINE([__res_state], [state], | |
+ [Define if we don't have struct __res_state in resolv.h])], | |
+[[ | |
+#include <stdio.h> | |
+#if HAVE_SYS_TYPES_H | |
+# include <sys/types.h> | |
+#endif | |
+#include <netinet/in.h> | |
+#include <arpa/nameser.h> | |
+#include <resolv.h> | |
+]]) | |
+ | |
+AC_CACHE_CHECK([for ss_family field in struct sockaddr_storage], | |
+ ac_cv_have_ss_family_in_struct_ss, [ | |
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ | |
+#include <sys/types.h> | |
+#include <sys/socket.h> | |
+ ]], [[ struct sockaddr_storage s; s.ss_family = 1; ]])], | |
+ [ ac_cv_have_ss_family_in_struct_ss="yes" ], | |
+ [ ac_cv_have_ss_family_in_struct_ss="no" ]) | |
+]) | |
+if test "x$ac_cv_have_ss_family_in_struct_ss" = "xyes" ; then | |
+ AC_DEFINE([HAVE_SS_FAMILY_IN_SS], [1], [Fields in struct sockaddr_storage]) | |
+fi | |
+ | |
+AC_CACHE_CHECK([for __ss_family field in struct sockaddr_storage], | |
+ ac_cv_have___ss_family_in_struct_ss, [ | |
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ | |
+#include <sys/types.h> | |
+#include <sys/socket.h> | |
+ ]], [[ struct sockaddr_storage s; s.__ss_family = 1; ]])], | |
+ [ ac_cv_have___ss_family_in_struct_ss="yes" ], | |
+ [ ac_cv_have___ss_family_in_struct_ss="no" | |
+ ]) | |
+]) | |
+if test "x$ac_cv_have___ss_family_in_struct_ss" = "xyes" ; then | |
+ AC_DEFINE([HAVE___SS_FAMILY_IN_SS], [1], | |
+ [Fields in struct sockaddr_storage]) | |
+fi | |
+ | |
+dnl make sure we're using the real structure members and not defines | |
+AC_CACHE_CHECK([for msg_accrights field in struct msghdr], | |
+ ac_cv_have_accrights_in_msghdr, [ | |
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ | |
+#include <sys/types.h> | |
+#include <sys/socket.h> | |
+#include <sys/uio.h> | |
+ ]], [[ | |
+#ifdef msg_accrights | |
+#error "msg_accrights is a macro" | |
+exit(1); | |
+#endif | |
+struct msghdr m; | |
+m.msg_accrights = 0; | |
+exit(0); | |
+ ]])], | |
+ [ ac_cv_have_accrights_in_msghdr="yes" ], | |
+ [ ac_cv_have_accrights_in_msghdr="no" ] | |
+ ) | |
+]) | |
+if test "x$ac_cv_have_accrights_in_msghdr" = "xyes" ; then | |
+ AC_DEFINE([HAVE_ACCRIGHTS_IN_MSGHDR], [1], | |
+ [Define if your system uses access rights style | |
+ file descriptor passing]) | |
+fi | |
+ | |
+AC_MSG_CHECKING([if struct statvfs.f_fsid is integral type]) | |
+AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ | |
+#include <sys/param.h> | |
+#include <sys/stat.h> | |
+#ifdef HAVE_SYS_TIME_H | |
+# include <sys/time.h> | |
+#endif | |
+#ifdef HAVE_SYS_MOUNT_H | |
+#include <sys/mount.h> | |
+#endif | |
+#ifdef HAVE_SYS_STATVFS_H | |
+#include <sys/statvfs.h> | |
+#endif | |
+ ]], [[ struct statvfs s; s.f_fsid = 0; ]])], | |
+ [ AC_MSG_RESULT([yes]) ], | |
+ [ AC_MSG_RESULT([no]) | |
+ | |
+ AC_MSG_CHECKING([if fsid_t has member val]) | |
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ | |
+#include <sys/types.h> | |
+#include <sys/statvfs.h> | |
+ ]], [[ fsid_t t; t.val[0] = 0; ]])], | |
+ [ AC_MSG_RESULT([yes]) | |
+ AC_DEFINE([FSID_HAS_VAL], [1], [fsid_t has member val]) ], | |
+ [ AC_MSG_RESULT([no]) ]) | |
+ | |
+ AC_MSG_CHECKING([if f_fsid has member __val]) | |
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ | |
+#include <sys/types.h> | |
+#include <sys/statvfs.h> | |
+ ]], [[ fsid_t t; t.__val[0] = 0; ]])], | |
+ [ AC_MSG_RESULT([yes]) | |
+ AC_DEFINE([FSID_HAS___VAL], [1], [fsid_t has member __val]) ], | |
+ [ AC_MSG_RESULT([no]) ]) | |
+]) | |
+ | |
+AC_CACHE_CHECK([for msg_control field in struct msghdr], | |
+ ac_cv_have_control_in_msghdr, [ | |
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ | |
+#include <sys/types.h> | |
+#include <sys/socket.h> | |
+#include <sys/uio.h> | |
+ ]], [[ | |
+#ifdef msg_control | |
+#error "msg_control is a macro" | |
+exit(1); | |
+#endif | |
+struct msghdr m; | |
+m.msg_control = 0; | |
+exit(0); | |
+ ]])], | |
+ [ ac_cv_have_control_in_msghdr="yes" ], | |
+ [ ac_cv_have_control_in_msghdr="no" ] | |
+ ) | |
+]) | |
+if test "x$ac_cv_have_control_in_msghdr" = "xyes" ; then | |
+ AC_DEFINE([HAVE_CONTROL_IN_MSGHDR], [1], | |
+ [Define if your system uses ancillary data style | |
+ file descriptor passing]) | |
+fi | |
+ | |
+AC_CACHE_CHECK([if libc defines __progname], ac_cv_libc_defines___progname, [ | |
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([[]], | |
+ [[ extern char *__progname; printf("%s", __progname); ]])], | |
+ [ ac_cv_libc_defines___progname="yes" ], | |
+ [ ac_cv_libc_defines___progname="no" | |
+ ]) | |
+]) | |
+if test "x$ac_cv_libc_defines___progname" = "xyes" ; then | |
+ AC_DEFINE([HAVE___PROGNAME], [1], [Define if libc defines __progname]) | |
+fi | |
+ | |
+AC_CACHE_CHECK([whether $CC implements __FUNCTION__], ac_cv_cc_implements___FUNCTION__, [ | |
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([[ #include <stdio.h> ]], | |
+ [[ printf("%s", __FUNCTION__); ]])], | |
+ [ ac_cv_cc_implements___FUNCTION__="yes" ], | |
+ [ ac_cv_cc_implements___FUNCTION__="no" | |
+ ]) | |
+]) | |
+if test "x$ac_cv_cc_implements___FUNCTION__" = "xyes" ; then | |
+ AC_DEFINE([HAVE___FUNCTION__], [1], | |
+ [Define if compiler implements __FUNCTION__]) | |
+fi | |
+ | |
+AC_CACHE_CHECK([whether $CC implements __func__], ac_cv_cc_implements___func__, [ | |
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([[ #include <stdio.h> ]], | |
+ [[ printf("%s", __func__); ]])], | |
+ [ ac_cv_cc_implements___func__="yes" ], | |
+ [ ac_cv_cc_implements___func__="no" | |
+ ]) | |
+]) | |
+if test "x$ac_cv_cc_implements___func__" = "xyes" ; then | |
+ AC_DEFINE([HAVE___func__], [1], [Define if compiler implements __func__]) | |
+fi | |
+ | |
+AC_CACHE_CHECK([whether va_copy exists], ac_cv_have_va_copy, [ | |
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([[ | |
+#include <stdarg.h> | |
+va_list x,y; | |
+ ]], [[ va_copy(x,y); ]])], | |
+ [ ac_cv_have_va_copy="yes" ], | |
+ [ ac_cv_have_va_copy="no" | |
+ ]) | |
+]) | |
+if test "x$ac_cv_have_va_copy" = "xyes" ; then | |
+ AC_DEFINE([HAVE_VA_COPY], [1], [Define if va_copy exists]) | |
+fi | |
+ | |
+AC_CACHE_CHECK([whether __va_copy exists], ac_cv_have___va_copy, [ | |
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([[ | |
+#include <stdarg.h> | |
+va_list x,y; | |
+ ]], [[ __va_copy(x,y); ]])], | |
+ [ ac_cv_have___va_copy="yes" ], [ ac_cv_have___va_copy="no" | |
+ ]) | |
+]) | |
+if test "x$ac_cv_have___va_copy" = "xyes" ; then | |
+ AC_DEFINE([HAVE___VA_COPY], [1], [Define if __va_copy exists]) | |
+fi | |
+ | |
+AC_CACHE_CHECK([whether getopt has optreset support], | |
+ ac_cv_have_getopt_optreset, [ | |
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([[ #include <getopt.h> ]], | |
+ [[ extern int optreset; optreset = 0; ]])], | |
+ [ ac_cv_have_getopt_optreset="yes" ], | |
+ [ ac_cv_have_getopt_optreset="no" | |
+ ]) | |
+]) | |
+if test "x$ac_cv_have_getopt_optreset" = "xyes" ; then | |
+ AC_DEFINE([HAVE_GETOPT_OPTRESET], [1], | |
+ [Define if your getopt(3) defines and uses optreset]) | |
+fi | |
+ | |
+AC_CACHE_CHECK([if libc defines sys_errlist], ac_cv_libc_defines_sys_errlist, [ | |
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([[]], | |
+[[ extern const char *const sys_errlist[]; printf("%s", sys_errlist[0]);]])], | |
+ [ ac_cv_libc_defines_sys_errlist="yes" ], | |
+ [ ac_cv_libc_defines_sys_errlist="no" | |
+ ]) | |
+]) | |
+if test "x$ac_cv_libc_defines_sys_errlist" = "xyes" ; then | |
+ AC_DEFINE([HAVE_SYS_ERRLIST], [1], | |
+ [Define if your system defines sys_errlist[]]) | |
+fi | |
+ | |
+ | |
+AC_CACHE_CHECK([if libc defines sys_nerr], ac_cv_libc_defines_sys_nerr, [ | |
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([[]], | |
+[[ extern int sys_nerr; printf("%i", sys_nerr);]])], | |
+ [ ac_cv_libc_defines_sys_nerr="yes" ], | |
+ [ ac_cv_libc_defines_sys_nerr="no" | |
+ ]) | |
+]) | |
+if test "x$ac_cv_libc_defines_sys_nerr" = "xyes" ; then | |
+ AC_DEFINE([HAVE_SYS_NERR], [1], [Define if your system defines sys_nerr]) | |
+fi | |
+ | |
+# Check libraries needed by DNS fingerprint support | |
+AC_SEARCH_LIBS([getrrsetbyname], [resolv], | |
+ [AC_DEFINE([HAVE_GETRRSETBYNAME], [1], | |
+ [Define if getrrsetbyname() exists])], | |
+ [ | |
+ # Needed by our getrrsetbyname() | |
+ AC_SEARCH_LIBS([res_query], [resolv]) | |
+ AC_SEARCH_LIBS([dn_expand], [resolv]) | |
+ AC_MSG_CHECKING([if res_query will link]) | |
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([[ | |
+#include <sys/types.h> | |
+#include <netinet/in.h> | |
+#include <arpa/nameser.h> | |
+#include <netdb.h> | |
+#include <resolv.h> | |
+ ]], [[ | |
+ res_query (0, 0, 0, 0, 0); | |
+ ]])], | |
+ AC_MSG_RESULT([yes]), | |
+ [AC_MSG_RESULT([no]) | |
+ saved_LIBS="$LIBS" | |
+ LIBS="$LIBS -lresolv" | |
+ AC_MSG_CHECKING([for res_query in -lresolv]) | |
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([[ | |
+#include <sys/types.h> | |
+#include <netinet/in.h> | |
+#include <arpa/nameser.h> | |
+#include <netdb.h> | |
+#include <resolv.h> | |
+ ]], [[ | |
+ res_query (0, 0, 0, 0, 0); | |
+ ]])], | |
+ [AC_MSG_RESULT([yes])], | |
+ [LIBS="$saved_LIBS" | |
+ AC_MSG_RESULT([no])]) | |
+ ]) | |
+ AC_CHECK_FUNCS([_getshort _getlong]) | |
+ AC_CHECK_DECLS([_getshort, _getlong], , , | |
+ [#include <sys/types.h> | |
+ #include <arpa/nameser.h>]) | |
+ AC_CHECK_MEMBER([HEADER.ad], | |
+ [AC_DEFINE([HAVE_HEADER_AD], [1], | |
+ [Define if HEADER.ad exists in arpa/nameser.h])], , | |
+ [#include <arpa/nameser.h>]) | |
+ ]) | |
+ | |
+AC_MSG_CHECKING([if struct __res_state _res is an extern]) | |
+AC_LINK_IFELSE([AC_LANG_PROGRAM([[ | |
+#include <stdio.h> | |
+#if HAVE_SYS_TYPES_H | |
+# include <sys/types.h> | |
+#endif | |
+#include <netinet/in.h> | |
+#include <arpa/nameser.h> | |
+#include <resolv.h> | |
+extern struct __res_state _res; | |
+ ]], [[ ]])], | |
+ [AC_MSG_RESULT([yes]) | |
+ AC_DEFINE([HAVE__RES_EXTERN], [1], | |
+ [Define if you have struct __res_state _res as an extern]) | |
+ ], | |
+ [ AC_MSG_RESULT([no]) ] | |
+) | |
+ | |
+# Check whether user wants SELinux support | |
+SELINUX_MSG="no" | |
+LIBSELINUX="" | |
+AC_ARG_WITH([selinux], | |
+ [ --with-selinux Enable SELinux support], | |
+ [ if test "x$withval" != "xno" ; then | |
+ save_LIBS="$LIBS" | |
+ 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" | |
+ LIBS="$LIBS -lselinux" | |
+ ], | |
+ AC_MSG_ERROR([SELinux support requires libselinux library])) | |
+ SSHLIBS="$SSHLIBS $LIBSELINUX" | |
+ SSHDLIBS="$SSHDLIBS $LIBSELINUX" | |
+ AC_CHECK_FUNCS([getseuserbyname get_default_context_with_level]) | |
+ LIBS="$save_LIBS" | |
+ fi ] | |
+) | |
+AC_SUBST([SSHLIBS]) | |
+AC_SUBST([SSHDLIBS]) | |
+ | |
+# Check whether user wants Kerberos 5 support | |
+KRB5_MSG="no" | |
+AC_ARG_WITH([kerberos5], | |
+ [ --with-kerberos5=PATH Enable Kerberos 5 support], | |
+ [ if test "x$withval" != "xno" ; then | |
+ if test "x$withval" = "xyes" ; then | |
+ KRB5ROOT="/usr/local" | |
+ else | |
+ KRB5ROOT=${withval} | |
+ fi | |
+ | |
+ AC_DEFINE([KRB5], [1], [Define if you want Kerberos 5 support]) | |
+ KRB5_MSG="yes" | |
+ | |
+ AC_PATH_PROG([KRB5CONF], [krb5-config], | |
+ [$KRB5ROOT/bin/krb5-config], | |
+ [$KRB5ROOT/bin:$PATH]) | |
+ if test -x $KRB5CONF ; then | |
+ K5CFLAGS="`$KRB5CONF --cflags`" | |
+ K5LIBS="`$KRB5CONF --libs`" | |
+ CPPFLAGS="$CPPFLAGS $K5CFLAGS" | |
+ | |
+ AC_MSG_CHECKING([for gssapi support]) | |
+ if $KRB5CONF | grep gssapi >/dev/null ; then | |
+ AC_MSG_RESULT([yes]) | |
+ AC_DEFINE([GSSAPI], [1], | |
+ [Define this if you want GSSAPI | |
+ support in the version 2 protocol]) | |
+ GSSCFLAGS="`$KRB5CONF --cflags gssapi`" | |
+ GSSLIBS="`$KRB5CONF --libs gssapi`" | |
+ CPPFLAGS="$CPPFLAGS $GSSCFLAGS" | |
+ else | |
+ AC_MSG_RESULT([no]) | |
+ fi | |
+ AC_MSG_CHECKING([whether we are using Heimdal]) | |
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include <krb5.h> | |
+ ]], [[ char *tmp = heimdal_version; ]])], | |
+ [ AC_MSG_RESULT([yes]) | |
+ AC_DEFINE([HEIMDAL], [1], | |
+ [Define this if you are using the Heimdal | |
+ version of Kerberos V5]) ], | |
+ [AC_MSG_RESULT([no]) | |
+ ]) | |
+ else | |
+ CPPFLAGS="$CPPFLAGS -I${KRB5ROOT}/include" | |
+ LDFLAGS="$LDFLAGS -L${KRB5ROOT}/lib" | |
+ AC_MSG_CHECKING([whether we are using Heimdal]) | |
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include <krb5.h> | |
+ ]], [[ char *tmp = heimdal_version; ]])], | |
+ [ AC_MSG_RESULT([yes]) | |
+ AC_DEFINE([HEIMDAL]) | |
+ K5LIBS="-lkrb5" | |
+ K5LIBS="$K5LIBS -lcom_err -lasn1" | |
+ AC_CHECK_LIB([roken], [net_write], | |
+ [K5LIBS="$K5LIBS -lroken"]) | |
+ AC_CHECK_LIB([des], [des_cbc_encrypt], | |
+ [K5LIBS="$K5LIBS -ldes"]) | |
+ ], [ AC_MSG_RESULT([no]) | |
+ K5LIBS="-lkrb5 -lk5crypto -lcom_err" | |
+ | |
+ ]) | |
+ AC_SEARCH_LIBS([dn_expand], [resolv]) | |
+ | |
+ AC_CHECK_LIB([gssapi_krb5], [gss_init_sec_context], | |
+ [ AC_DEFINE([GSSAPI]) | |
+ GSSLIBS="-lgssapi_krb5" ], | |
+ [ AC_CHECK_LIB([gssapi], [gss_init_sec_context], | |
+ [ AC_DEFINE([GSSAPI]) | |
+ GSSLIBS="-lgssapi" ], | |
+ [ AC_CHECK_LIB([gss], [gss_init_sec_context], | |
+ [ AC_DEFINE([GSSAPI]) | |
+ GSSLIBS="-lgss" ], | |
+ AC_MSG_WARN([Cannot find any suitable gss-api library - build may fail])) | |
+ ]) | |
+ ]) | |
+ | |
+ AC_CHECK_HEADER([gssapi.h], , | |
+ [ unset ac_cv_header_gssapi_h | |
+ CPPFLAGS="$CPPFLAGS -I${KRB5ROOT}/include/gssapi" | |
+ AC_CHECK_HEADERS([gssapi.h], , | |
+ AC_MSG_WARN([Cannot find any suitable gss-api header - build may fail]) | |
+ ) | |
+ ] | |
+ ) | |
+ | |
+ oldCPP="$CPPFLAGS" | |
+ CPPFLAGS="$CPPFLAGS -I${KRB5ROOT}/include/gssapi" | |
+ AC_CHECK_HEADER([gssapi_krb5.h], , | |
+ [ CPPFLAGS="$oldCPP" ]) | |
+ | |
+ fi | |
+ if test ! -z "$need_dash_r" ; then | |
+ LDFLAGS="$LDFLAGS -R${KRB5ROOT}/lib" | |
+ fi | |
+ if test ! -z "$blibpath" ; then | |
+ blibpath="$blibpath:${KRB5ROOT}/lib" | |
+ fi | |
+ | |
+ AC_CHECK_HEADERS([gssapi.h gssapi/gssapi.h]) | |
+ AC_CHECK_HEADERS([gssapi_krb5.h gssapi/gssapi_krb5.h]) | |
+ AC_CHECK_HEADERS([gssapi_generic.h gssapi/gssapi_generic.h]) | |
+ | |
+ AC_SEARCH_LIBS([k_hasafs], [kafs], [AC_DEFINE([USE_AFS], [1], | |
+ [Define this if you want to use libkafs' AFS support])]) | |
+ | |
+ AC_CHECK_DECLS([GSS_C_NT_HOSTBASED_SERVICE], [], [], [[ | |
+#ifdef HAVE_GSSAPI_H | |
+# include <gssapi.h> | |
+#elif defined(HAVE_GSSAPI_GSSAPI_H) | |
+# include <gssapi/gssapi.h> | |
+#endif | |
+ | |
+#ifdef HAVE_GSSAPI_GENERIC_H | |
+# include <gssapi_generic.h> | |
+#elif defined(HAVE_GSSAPI_GSSAPI_GENERIC_H) | |
+# include <gssapi/gssapi_generic.h> | |
+#endif | |
+ ]]) | |
+ saved_LIBS="$LIBS" | |
+ LIBS="$LIBS $K5LIBS" | |
+ AC_CHECK_FUNCS([krb5_cc_new_unique krb5_get_error_message krb5_free_error_message]) | |
+ LIBS="$saved_LIBS" | |
+ | |
+ fi | |
+ ] | |
+) | |
+AC_SUBST([GSSLIBS]) | |
+AC_SUBST([K5LIBS]) | |
+ | |
+# Looking for programs, paths and files | |
+ | |
+PRIVSEP_PATH=/var/empty | |
+AC_ARG_WITH([privsep-path], | |
+ [ --with-privsep-path=xxx Path for privilege separation chroot (default=/var/empty)], | |
+ [ | |
+ if test -n "$withval" && test "x$withval" != "xno" && \ | |
+ test "x${withval}" != "xyes"; then | |
+ PRIVSEP_PATH=$withval | |
+ fi | |
+ ] | |
+) | |
+AC_SUBST([PRIVSEP_PATH]) | |
+ | |
+AC_ARG_WITH([xauth], | |
+ [ --with-xauth=PATH Specify path to xauth program ], | |
+ [ | |
+ if test -n "$withval" && test "x$withval" != "xno" && \ | |
+ test "x${withval}" != "xyes"; then | |
+ xauth_path=$withval | |
+ fi | |
+ ], | |
+ [ | |
+ TestPath="$PATH" | |
+ TestPath="${TestPath}${PATH_SEPARATOR}/usr/X/bin" | |
+ TestPath="${TestPath}${PATH_SEPARATOR}/usr/bin/X11" | |
+ TestPath="${TestPath}${PATH_SEPARATOR}/usr/X11R6/bin" | |
+ TestPath="${TestPath}${PATH_SEPARATOR}/usr/openwin/bin" | |
+ AC_PATH_PROG([xauth_path], [xauth], , [$TestPath]) | |
+ if (test ! -z "$xauth_path" && test -x "/usr/openwin/bin/xauth") ; then | |
+ xauth_path="/usr/openwin/bin/xauth" | |
+ fi | |
+ ] | |
+) | |
+ | |
+STRIP_OPT=-s | |
+AC_ARG_ENABLE([strip], | |
+ [ --disable-strip Disable calling strip(1) on install], | |
+ [ | |
+ if test "x$enableval" = "xno" ; then | |
+ STRIP_OPT= | |
+ fi | |
+ ] | |
+) | |
+AC_SUBST([STRIP_OPT]) | |
+ | |
+if test -z "$xauth_path" ; then | |
+ XAUTH_PATH="undefined" | |
+ AC_SUBST([XAUTH_PATH]) | |
+else | |
+ AC_DEFINE_UNQUOTED([XAUTH_PATH], ["$xauth_path"], | |
+ [Define if xauth is found in your path]) | |
+ XAUTH_PATH=$xauth_path | |
+ AC_SUBST([XAUTH_PATH]) | |
+fi | |
+ | |
+dnl # --with-maildir=/path/to/mail gets top priority. | |
+dnl # if maildir is set in the platform case statement above we use that. | |
+dnl # Otherwise we run a program to get the dir from system headers. | |
+dnl # We first look for _PATH_MAILDIR then MAILDIR then _PATH_MAIL | |
+dnl # If we find _PATH_MAILDIR we do nothing because that is what | |
+dnl # session.c expects anyway. Otherwise we set to the value found | |
+dnl # stripping any trailing slash. If for some strage reason our program | |
+dnl # does not find what it needs, we default to /var/spool/mail. | |
+# Check for mail directory | |
+AC_ARG_WITH([maildir], | |
+ [ --with-maildir=/path/to/mail Specify your system mail directory], | |
+ [ | |
+ if test "X$withval" != X && test "x$withval" != xno && \ | |
+ test "x${withval}" != xyes; then | |
+ AC_DEFINE_UNQUOTED([MAIL_DIRECTORY], ["$withval"], | |
+ [Set this to your mail directory if you do not have _PATH_MAILDIR]) | |
+ fi | |
+ ],[ | |
+ if test "X$maildir" != "X"; then | |
+ AC_DEFINE_UNQUOTED([MAIL_DIRECTORY], ["$maildir"]) | |
+ else | |
+ AC_MSG_CHECKING([Discovering system mail directory]) | |
+ AC_RUN_IFELSE( | |
+ [AC_LANG_PROGRAM([[ | |
+#include <stdio.h> | |
+#include <string.h> | |
+#ifdef HAVE_PATHS_H | |
+#include <paths.h> | |
+#endif | |
+#ifdef HAVE_MAILLOCK_H | |
+#include <maillock.h> | |
+#endif | |
+#define DATA "conftest.maildir" | |
+ ]], [[ | |
+ FILE *fd; | |
+ int rc; | |
+ | |
+ fd = fopen(DATA,"w"); | |
+ if(fd == NULL) | |
+ exit(1); | |
+ | |
+#if defined (_PATH_MAILDIR) | |
+ if ((rc = fprintf(fd ,"_PATH_MAILDIR:%s\n", _PATH_MAILDIR)) <0) | |
+ exit(1); | |
+#elif defined (MAILDIR) | |
+ if ((rc = fprintf(fd ,"MAILDIR:%s\n", MAILDIR)) <0) | |
+ exit(1); | |
+#elif defined (_PATH_MAIL) | |
+ if ((rc = fprintf(fd ,"_PATH_MAIL:%s\n", _PATH_MAIL)) <0) | |
+ exit(1); | |
+#else | |
+ exit (2); | |
+#endif | |
+ | |
+ exit(0); | |
+ ]])], | |
+ [ | |
+ maildir_what=`awk -F: '{print $1}' conftest.maildir` | |
+ maildir=`awk -F: '{print $2}' conftest.maildir \ | |
+ | sed 's|/$||'` | |
+ AC_MSG_RESULT([Using: $maildir from $maildir_what]) | |
+ if test "x$maildir_what" != "x_PATH_MAILDIR"; then | |
+ AC_DEFINE_UNQUOTED([MAIL_DIRECTORY], ["$maildir"]) | |
+ fi | |
+ ], | |
+ [ | |
+ if test "X$ac_status" = "X2";then | |
+# our test program didn't find it. Default to /var/spool/mail | |
+ AC_MSG_RESULT([Using: default value of /var/spool/mail]) | |
+ AC_DEFINE_UNQUOTED([MAIL_DIRECTORY], ["/var/spool/mail"]) | |
+ else | |
+ AC_MSG_RESULT([*** not found ***]) | |
+ fi | |
+ ], | |
+ [ | |
+ AC_MSG_WARN([cross compiling: use --with-maildir=/path/to/mail]) | |
+ ] | |
+ ) | |
+ fi | |
+ ] | |
+) # maildir | |
+ | |
+if test ! -z "$cross_compiling" && test "x$cross_compiling" = "xyes"; then | |
+ AC_MSG_WARN([cross compiling: Disabling /dev/ptmx test]) | |
+ disable_ptmx_check=yes | |
+fi | |
+if test -z "$no_dev_ptmx" ; then | |
+ if test "x$disable_ptmx_check" != "xyes" ; then | |
+ AC_CHECK_FILE(["/dev/ptmx"], | |
+ [ | |
+ AC_DEFINE_UNQUOTED([HAVE_DEV_PTMX], [1], | |
+ [Define if you have /dev/ptmx]) | |
+ have_dev_ptmx=1 | |
+ ] | |
+ ) | |
+ fi | |
+fi | |
+ | |
+if test ! -z "$cross_compiling" && test "x$cross_compiling" != "xyes"; then | |
+ AC_CHECK_FILE(["/dev/ptc"], | |
+ [ | |
+ AC_DEFINE_UNQUOTED([HAVE_DEV_PTS_AND_PTC], [1], | |
+ [Define if you have /dev/ptc]) | |
+ have_dev_ptc=1 | |
+ ] | |
+ ) | |
+else | |
+ AC_MSG_WARN([cross compiling: Disabling /dev/ptc test]) | |
+fi | |
+ | |
+# Options from here on. Some of these are preset by platform above | |
+AC_ARG_WITH([mantype], | |
+ [ --with-mantype=man|cat|doc Set man page type], | |
+ [ | |
+ case "$withval" in | |
+ man|cat|doc) | |
+ MANTYPE=$withval | |
+ ;; | |
+ *) | |
+ AC_MSG_ERROR([invalid man type: $withval]) | |
+ ;; | |
+ esac | |
+ ] | |
+) | |
+if test -z "$MANTYPE"; then | |
+ TestPath="/usr/bin${PATH_SEPARATOR}/usr/ucb" | |
+ AC_PATH_PROGS([NROFF], [nroff awf], [/bin/false], [$TestPath]) | |
+ if ${NROFF} -mdoc ${srcdir}/ssh.1 >/dev/null 2>&1; then | |
+ MANTYPE=doc | |
+ elif ${NROFF} -man ${srcdir}/ssh.1 >/dev/null 2>&1; then | |
+ MANTYPE=man | |
+ else | |
+ MANTYPE=cat | |
+ fi | |
+fi | |
+AC_SUBST([MANTYPE]) | |
+if test "$MANTYPE" = "doc"; then | |
+ mansubdir=man; | |
+else | |
+ mansubdir=$MANTYPE; | |
+fi | |
+AC_SUBST([mansubdir]) | |
+ | |
+# Check whether to enable MD5 passwords | |
+MD5_MSG="no" | |
+AC_ARG_WITH([md5-passwords], | |
+ [ --with-md5-passwords Enable use of MD5 passwords], | |
+ [ | |
+ if test "x$withval" != "xno" ; then | |
+ AC_DEFINE([HAVE_MD5_PASSWORDS], [1], | |
+ [Define if you want to allow MD5 passwords]) | |
+ MD5_MSG="yes" | |
+ fi | |
+ ] | |
+) | |
+ | |
+# Whether to disable shadow password support | |
+AC_ARG_WITH([shadow], | |
+ [ --without-shadow Disable shadow password support], | |
+ [ | |
+ if test "x$withval" = "xno" ; then | |
+ AC_DEFINE([DISABLE_SHADOW]) | |
+ disable_shadow=yes | |
+ fi | |
+ ] | |
+) | |
+ | |
+if test -z "$disable_shadow" ; then | |
+ AC_MSG_CHECKING([if the systems has expire shadow information]) | |
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ | |
+#include <sys/types.h> | |
+#include <shadow.h> | |
+struct spwd sp; | |
+ ]], [[ sp.sp_expire = sp.sp_lstchg = sp.sp_inact = 0; ]])], | |
+ [ sp_expire_available=yes ], [ | |
+ ]) | |
+ | |
+ if test "x$sp_expire_available" = "xyes" ; then | |
+ AC_MSG_RESULT([yes]) | |
+ AC_DEFINE([HAS_SHADOW_EXPIRE], [1], | |
+ [Define if you want to use shadow password expire field]) | |
+ else | |
+ AC_MSG_RESULT([no]) | |
+ fi | |
+fi | |
+ | |
+# Use ip address instead of hostname in $DISPLAY | |
+if test ! -z "$IPADDR_IN_DISPLAY" ; then | |
+ DISPLAY_HACK_MSG="yes" | |
+ AC_DEFINE([IPADDR_IN_DISPLAY], [1], | |
+ [Define if you need to use IP address | |
+ instead of hostname in $DISPLAY]) | |
+else | |
+ DISPLAY_HACK_MSG="no" | |
+ AC_ARG_WITH([ipaddr-display], | |
+ [ --with-ipaddr-display Use ip address instead of hostname in \$DISPLAY], | |
+ [ | |
+ if test "x$withval" != "xno" ; then | |
+ AC_DEFINE([IPADDR_IN_DISPLAY]) | |
+ DISPLAY_HACK_MSG="yes" | |
+ fi | |
+ ] | |
+ ) | |
+fi | |
+ | |
+# check for /etc/default/login and use it if present. | |
+AC_ARG_ENABLE([etc-default-login], | |
+ [ --disable-etc-default-login Disable using PATH from /etc/default/login [no]], | |
+ [ if test "x$enableval" = "xno"; then | |
+ AC_MSG_NOTICE([/etc/default/login handling disabled]) | |
+ etc_default_login=no | |
+ else | |
+ etc_default_login=yes | |
+ fi ], | |
+ [ if test ! -z "$cross_compiling" && test "x$cross_compiling" = "xyes"; | |
+ then | |
+ AC_MSG_WARN([cross compiling: not checking /etc/default/login]) | |
+ etc_default_login=no | |
+ else | |
+ etc_default_login=yes | |
+ fi ] | |
+) | |
+ | |
+if test "x$etc_default_login" != "xno"; then | |
+ AC_CHECK_FILE(["/etc/default/login"], | |
+ [ external_path_file=/etc/default/login ]) | |
+ if test "x$external_path_file" = "x/etc/default/login"; then | |
+ AC_DEFINE([HAVE_ETC_DEFAULT_LOGIN], [1], | |
+ [Define if your system has /etc/default/login]) | |
+ fi | |
+fi | |
+ | |
+dnl BSD systems use /etc/login.conf so --with-default-path= has no effect | |
+if test $ac_cv_func_login_getcapbool = "yes" && \ | |
+ test $ac_cv_header_login_cap_h = "yes" ; then | |
+ external_path_file=/etc/login.conf | |
+fi | |
+ | |
+# Whether to mess with the default path | |
+SERVER_PATH_MSG="(default)" | |
+AC_ARG_WITH([default-path], | |
+ [ --with-default-path= Specify default \$PATH environment for server], | |
+ [ | |
+ if test "x$external_path_file" = "x/etc/login.conf" ; then | |
+ AC_MSG_WARN([ | |
+--with-default-path=PATH has no effect on this system. | |
+Edit /etc/login.conf instead.]) | |
+ elif test "x$withval" != "xno" ; then | |
+ if test ! -z "$external_path_file" ; then | |
+ AC_MSG_WARN([ | |
+--with-default-path=PATH will only be used if PATH is not defined in | |
+$external_path_file .]) | |
+ fi | |
+ user_path="$withval" | |
+ SERVER_PATH_MSG="$withval" | |
+ fi | |
+ ], | |
+ [ if test "x$external_path_file" = "x/etc/login.conf" ; then | |
+ AC_MSG_WARN([Make sure the path to scp is in /etc/login.conf]) | |
+ else | |
+ if test ! -z "$external_path_file" ; then | |
+ AC_MSG_WARN([ | |
+If PATH is defined in $external_path_file, ensure the path to scp is included, | |
+otherwise scp will not work.]) | |
+ fi | |
+ AC_RUN_IFELSE( | |
+ [AC_LANG_PROGRAM([[ | |
+/* find out what STDPATH is */ | |
+#include <stdio.h> | |
+#ifdef HAVE_PATHS_H | |
+# include <paths.h> | |
+#endif | |
+#ifndef _PATH_STDPATH | |
+# ifdef _PATH_USERPATH /* Irix */ | |
+# define _PATH_STDPATH _PATH_USERPATH | |
+# else | |
+# define _PATH_STDPATH "/usr/bin:/bin:/usr/sbin:/sbin" | |
+# endif | |
+#endif | |
+#include <sys/types.h> | |
+#include <sys/stat.h> | |
+#include <fcntl.h> | |
+#define DATA "conftest.stdpath" | |
+ ]], [[ | |
+ FILE *fd; | |
+ int rc; | |
+ | |
+ fd = fopen(DATA,"w"); | |
+ if(fd == NULL) | |
+ exit(1); | |
+ | |
+ if ((rc = fprintf(fd,"%s", _PATH_STDPATH)) < 0) | |
+ exit(1); | |
+ | |
+ exit(0); | |
+ ]])], | |
+ [ user_path=`cat conftest.stdpath` ], | |
+ [ user_path="/usr/bin:/bin:/usr/sbin:/sbin" ], | |
+ [ user_path="/usr/bin:/bin:/usr/sbin:/sbin" ] | |
+ ) | |
+# make sure $bindir is in USER_PATH so scp will work | |
+ t_bindir="${bindir}" | |
+ while echo "${t_bindir}" | egrep '\$\{|NONE/' >/dev/null 2>&1; do | |
+ t_bindir=`eval echo ${t_bindir}` | |
+ case $t_bindir in | |
+ NONE/*) t_bindir=`echo $t_bindir | sed "s~NONE~$prefix~"` ;; | |
+ esac | |
+ case $t_bindir in | |
+ NONE/*) t_bindir=`echo $t_bindir | sed "s~NONE~$ac_default_prefix~"` ;; | |
+ esac | |
+ done | |
+ echo $user_path | grep ":$t_bindir" > /dev/null 2>&1 | |
+ if test $? -ne 0 ; then | |
+ echo $user_path | grep "^$t_bindir" > /dev/null 2>&1 | |
+ if test $? -ne 0 ; then | |
+ user_path=$user_path:$t_bindir | |
+ AC_MSG_RESULT([Adding $t_bindir to USER_PATH so scp will work]) | |
+ fi | |
+ fi | |
+ fi ] | |
+) | |
+if test "x$external_path_file" != "x/etc/login.conf" ; then | |
+ AC_DEFINE_UNQUOTED([USER_PATH], ["$user_path"], [Specify default $PATH]) | |
+ AC_SUBST([user_path]) | |
+fi | |
+ | |
+# Set superuser path separately to user path | |
+AC_ARG_WITH([superuser-path], | |
+ [ --with-superuser-path= Specify different path for super-user], | |
+ [ | |
+ if test -n "$withval" && test "x$withval" != "xno" && \ | |
+ test "x${withval}" != "xyes"; then | |
+ AC_DEFINE_UNQUOTED([SUPERUSER_PATH], ["$withval"], | |
+ [Define if you want a different $PATH | |
+ for the superuser]) | |
+ superuser_path=$withval | |
+ fi | |
+ ] | |
+) | |
+ | |
+ | |
+AC_MSG_CHECKING([if we need to convert IPv4 in IPv6-mapped addresses]) | |
+IPV4_IN6_HACK_MSG="no" | |
+AC_ARG_WITH(4in6, | |
+ [ --with-4in6 Check for and convert IPv4 in IPv6 mapped addresses], | |
+ [ | |
+ if test "x$withval" != "xno" ; then | |
+ AC_MSG_RESULT([yes]) | |
+ AC_DEFINE([IPV4_IN_IPV6], [1], | |
+ [Detect IPv4 in IPv6 mapped addresses | |
+ and treat as IPv4]) | |
+ IPV4_IN6_HACK_MSG="yes" | |
+ else | |
+ AC_MSG_RESULT([no]) | |
+ fi | |
+ ], [ | |
+ if test "x$inet6_default_4in6" = "xyes"; then | |
+ AC_MSG_RESULT([yes (default)]) | |
+ AC_DEFINE([IPV4_IN_IPV6]) | |
+ IPV4_IN6_HACK_MSG="yes" | |
+ else | |
+ AC_MSG_RESULT([no (default)]) | |
+ fi | |
+ ] | |
+) | |
+ | |
+# Whether to enable BSD auth support | |
+BSD_AUTH_MSG=no | |
+AC_ARG_WITH([bsd-auth], | |
+ [ --with-bsd-auth Enable BSD auth support], | |
+ [ | |
+ if test "x$withval" != "xno" ; then | |
+ AC_DEFINE([BSD_AUTH], [1], | |
+ [Define if you have BSD auth support]) | |
+ BSD_AUTH_MSG=yes | |
+ fi | |
+ ] | |
+) | |
+ | |
+# Where to place sshd.pid | |
+piddir=/var/run | |
+# make sure the directory exists | |
+if test ! -d $piddir ; then | |
+ piddir=`eval echo ${sysconfdir}` | |
+ case $piddir in | |
+ NONE/*) piddir=`echo $piddir | sed "s~NONE~$ac_default_prefix~"` ;; | |
+ esac | |
+fi | |
+ | |
+AC_ARG_WITH([pid-dir], | |
+ [ --with-pid-dir=PATH Specify location of ssh.pid file], | |
+ [ | |
+ if test -n "$withval" && test "x$withval" != "xno" && \ | |
+ test "x${withval}" != "xyes"; then | |
+ piddir=$withval | |
+ if test ! -d $piddir ; then | |
+ AC_MSG_WARN([** no $piddir directory on this system **]) | |
+ fi | |
+ fi | |
+ ] | |
+) | |
+ | |
+AC_DEFINE_UNQUOTED([_PATH_SSH_PIDDIR], ["$piddir"], | |
+ [Specify location of ssh.pid]) | |
+AC_SUBST([piddir]) | |
+ | |
+dnl allow user to disable some login recording features | |
+AC_ARG_ENABLE([lastlog], | |
+ [ --disable-lastlog disable use of lastlog even if detected [no]], | |
+ [ | |
+ if test "x$enableval" = "xno" ; then | |
+ AC_DEFINE([DISABLE_LASTLOG]) | |
+ fi | |
+ ] | |
+) | |
+AC_ARG_ENABLE([utmp], | |
+ [ --disable-utmp disable use of utmp even if detected [no]], | |
+ [ | |
+ if test "x$enableval" = "xno" ; then | |
+ AC_DEFINE([DISABLE_UTMP]) | |
+ fi | |
+ ] | |
+) | |
+AC_ARG_ENABLE([utmpx], | |
+ [ --disable-utmpx disable use of utmpx even if detected [no]], | |
+ [ | |
+ if test "x$enableval" = "xno" ; then | |
+ AC_DEFINE([DISABLE_UTMPX], [1], | |
+ [Define if you don't want to use utmpx]) | |
+ fi | |
+ ] | |
+) | |
+AC_ARG_ENABLE([wtmp], | |
+ [ --disable-wtmp disable use of wtmp even if detected [no]], | |
+ [ | |
+ if test "x$enableval" = "xno" ; then | |
+ AC_DEFINE([DISABLE_WTMP]) | |
+ fi | |
+ ] | |
+) | |
+AC_ARG_ENABLE([wtmpx], | |
+ [ --disable-wtmpx disable use of wtmpx even if detected [no]], | |
+ [ | |
+ if test "x$enableval" = "xno" ; then | |
+ AC_DEFINE([DISABLE_WTMPX], [1], | |
+ [Define if you don't want to use wtmpx]) | |
+ fi | |
+ ] | |
+) | |
+AC_ARG_ENABLE([libutil], | |
+ [ --disable-libutil disable use of libutil (login() etc.) [no]], | |
+ [ | |
+ if test "x$enableval" = "xno" ; then | |
+ AC_DEFINE([DISABLE_LOGIN]) | |
+ fi | |
+ ] | |
+) | |
+AC_ARG_ENABLE([pututline], | |
+ [ --disable-pututline disable use of pututline() etc. ([uw]tmp) [no]], | |
+ [ | |
+ if test "x$enableval" = "xno" ; then | |
+ AC_DEFINE([DISABLE_PUTUTLINE], [1], | |
+ [Define if you don't want to use pututline() | |
+ etc. to write [uw]tmp]) | |
+ fi | |
+ ] | |
+) | |
+AC_ARG_ENABLE([pututxline], | |
+ [ --disable-pututxline disable use of pututxline() etc. ([uw]tmpx) [no]], | |
+ [ | |
+ if test "x$enableval" = "xno" ; then | |
+ AC_DEFINE([DISABLE_PUTUTXLINE], [1], | |
+ [Define if you don't want to use pututxline() | |
+ etc. to write [uw]tmpx]) | |
+ fi | |
+ ] | |
+) | |
+AC_ARG_WITH([lastlog], | |
+ [ --with-lastlog=FILE|DIR specify lastlog location [common locations]], | |
+ [ | |
+ if test "x$withval" = "xno" ; then | |
+ AC_DEFINE([DISABLE_LASTLOG]) | |
+ elif test -n "$withval" && test "x${withval}" != "xyes"; then | |
+ conf_lastlog_location=$withval | |
+ fi | |
+ ] | |
+) | |
+ | |
+dnl lastlog, [uw]tmpx? detection | |
+dnl NOTE: set the paths in the platform section to avoid the | |
+dnl need for command-line parameters | |
+dnl lastlog and [uw]tmp are subject to a file search if all else fails | |
+ | |
+dnl lastlog detection | |
+dnl NOTE: the code itself will detect if lastlog is a directory | |
+AC_MSG_CHECKING([if your system defines LASTLOG_FILE]) | |
+AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ | |
+#include <sys/types.h> | |
+#include <utmp.h> | |
+#ifdef HAVE_LASTLOG_H | |
+# include <lastlog.h> | |
+#endif | |
+#ifdef HAVE_PATHS_H | |
+# include <paths.h> | |
+#endif | |
+#ifdef HAVE_LOGIN_H | |
+# include <login.h> | |
+#endif | |
+ ]], [[ char *lastlog = LASTLOG_FILE; ]])], | |
+ [ AC_MSG_RESULT([yes]) ], | |
+ [ | |
+ AC_MSG_RESULT([no]) | |
+ AC_MSG_CHECKING([if your system defines _PATH_LASTLOG]) | |
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ | |
+#include <sys/types.h> | |
+#include <utmp.h> | |
+#ifdef HAVE_LASTLOG_H | |
+# include <lastlog.h> | |
+#endif | |
+#ifdef HAVE_PATHS_H | |
+# include <paths.h> | |
+#endif | |
+ ]], [[ char *lastlog = _PATH_LASTLOG; ]])], | |
+ [ AC_MSG_RESULT([yes]) ], | |
+ [ | |
+ AC_MSG_RESULT([no]) | |
+ system_lastlog_path=no | |
+ ]) | |
+]) | |
+ | |
+if test -z "$conf_lastlog_location"; then | |
+ if test x"$system_lastlog_path" = x"no" ; then | |
+ for f in /var/log/lastlog /usr/adm/lastlog /var/adm/lastlog /etc/security/lastlog ; do | |
+ if (test -d "$f" || test -f "$f") ; then | |
+ conf_lastlog_location=$f | |
+ fi | |
+ done | |
+ if test -z "$conf_lastlog_location"; then | |
+ AC_MSG_WARN([** Cannot find lastlog **]) | |
+ dnl Don't define DISABLE_LASTLOG - that means we don't try wtmp/wtmpx | |
+ fi | |
+ fi | |
+fi | |
+ | |
+if test -n "$conf_lastlog_location"; then | |
+ AC_DEFINE_UNQUOTED([CONF_LASTLOG_FILE], ["$conf_lastlog_location"], | |
+ [Define if you want to specify the path to your lastlog file]) | |
+fi | |
+ | |
+dnl utmp detection | |
+AC_MSG_CHECKING([if your system defines UTMP_FILE]) | |
+AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ | |
+#include <sys/types.h> | |
+#include <utmp.h> | |
+#ifdef HAVE_PATHS_H | |
+# include <paths.h> | |
+#endif | |
+ ]], [[ char *utmp = UTMP_FILE; ]])], | |
+ [ AC_MSG_RESULT([yes]) ], | |
+ [ AC_MSG_RESULT([no]) | |
+ system_utmp_path=no | |
+]) | |
+if test -z "$conf_utmp_location"; then | |
+ if test x"$system_utmp_path" = x"no" ; then | |
+ for f in /etc/utmp /usr/adm/utmp /var/run/utmp; do | |
+ if test -f $f ; then | |
+ conf_utmp_location=$f | |
+ fi | |
+ done | |
+ if test -z "$conf_utmp_location"; then | |
+ AC_DEFINE([DISABLE_UTMP]) | |
+ fi | |
+ fi | |
+fi | |
+if test -n "$conf_utmp_location"; then | |
+ AC_DEFINE_UNQUOTED([CONF_UTMP_FILE], ["$conf_utmp_location"], | |
+ [Define if you want to specify the path to your utmp file]) | |
+fi | |
+ | |
+dnl wtmp detection | |
+AC_MSG_CHECKING([if your system defines WTMP_FILE]) | |
+AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ | |
+#include <sys/types.h> | |
+#include <utmp.h> | |
+#ifdef HAVE_PATHS_H | |
+# include <paths.h> | |
+#endif | |
+ ]], [[ char *wtmp = WTMP_FILE; ]])], | |
+ [ AC_MSG_RESULT([yes]) ], | |
+ [ AC_MSG_RESULT([no]) | |
+ system_wtmp_path=no | |
+]) | |
+if test -z "$conf_wtmp_location"; then | |
+ if test x"$system_wtmp_path" = x"no" ; then | |
+ for f in /usr/adm/wtmp /var/log/wtmp; do | |
+ if test -f $f ; then | |
+ conf_wtmp_location=$f | |
+ fi | |
+ done | |
+ if test -z "$conf_wtmp_location"; then | |
+ AC_DEFINE([DISABLE_WTMP]) | |
+ fi | |
+ fi | |
+fi | |
+if test -n "$conf_wtmp_location"; then | |
+ AC_DEFINE_UNQUOTED([CONF_WTMP_FILE], ["$conf_wtmp_location"], | |
+ [Define if you want to specify the path to your wtmp file]) | |
+fi | |
+ | |
+dnl wtmpx detection | |
+AC_MSG_CHECKING([if your system defines WTMPX_FILE]) | |
+AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ | |
+#include <sys/types.h> | |
+#include <utmp.h> | |
+#ifdef HAVE_UTMPX_H | |
+#include <utmpx.h> | |
+#endif | |
+#ifdef HAVE_PATHS_H | |
+# include <paths.h> | |
+#endif | |
+ ]], [[ char *wtmpx = WTMPX_FILE; ]])], | |
+ [ AC_MSG_RESULT([yes]) ], | |
+ [ AC_MSG_RESULT([no]) | |
+ system_wtmpx_path=no | |
+]) | |
+if test -z "$conf_wtmpx_location"; then | |
+ if test x"$system_wtmpx_path" = x"no" ; then | |
+ AC_DEFINE([DISABLE_WTMPX]) | |
+ fi | |
+else | |
+ AC_DEFINE_UNQUOTED([CONF_WTMPX_FILE], ["$conf_wtmpx_location"], | |
+ [Define if you want to specify the path to your wtmpx file]) | |
+fi | |
+ | |
+ | |
+if test ! -z "$blibpath" ; then | |
+ LDFLAGS="$LDFLAGS $blibflags$blibpath" | |
+ AC_MSG_WARN([Please check and edit blibpath in LDFLAGS in Makefile]) | |
+fi | |
+ | |
+AC_CHECK_MEMBER([struct lastlog.ll_line], [], [ | |
+ if test x$SKIP_DISABLE_LASTLOG_DEFINE != "xyes" ; then | |
+ AC_DEFINE([DISABLE_LASTLOG]) | |
+ fi | |
+ ], [ | |
+#ifdef HAVE_SYS_TYPES_H | |
+#include <sys/types.h> | |
+#endif | |
+#ifdef HAVE_UTMP_H | |
+#include <utmp.h> | |
+#endif | |
+#ifdef HAVE_UTMPX_H | |
+#include <utmpx.h> | |
+#endif | |
+#ifdef HAVE_LASTLOG_H | |
+#include <lastlog.h> | |
+#endif | |
+ ]) | |
+ | |
+AC_CHECK_MEMBER([struct utmp.ut_line], [], [ | |
+ AC_DEFINE([DISABLE_UTMP]) | |
+ AC_DEFINE([DISABLE_WTMP]) | |
+ ], [ | |
+#ifdef HAVE_SYS_TYPES_H | |
+#include <sys/types.h> | |
+#endif | |
+#ifdef HAVE_UTMP_H | |
+#include <utmp.h> | |
+#endif | |
+#ifdef HAVE_UTMPX_H | |
+#include <utmpx.h> | |
+#endif | |
+#ifdef HAVE_LASTLOG_H | |
+#include <lastlog.h> | |
+#endif | |
+ ]) | |
+ | |
+dnl Adding -Werror to CFLAGS early prevents configure tests from running. | |
+dnl Add now. | |
+CFLAGS="$CFLAGS $werror_flags" | |
+ | |
+if test "x$ac_cv_func_getaddrinfo" != "xyes" ; then | |
+ TEST_SSH_IPV6=no | |
+else | |
+ TEST_SSH_IPV6=yes | |
+fi | |
+AC_CHECK_DECL([BROKEN_GETADDRINFO], [TEST_SSH_IPV6=no]) | |
+AC_SUBST([TEST_SSH_IPV6], [$TEST_SSH_IPV6]) | |
+AC_SUBST([TEST_MALLOC_OPTIONS], [$TEST_MALLOC_OPTIONS]) | |
+AC_SUBST([UNSUPPORTED_ALGORITHMS], [$unsupported_algorithms]) | |
+ | |
+AC_EXEEXT | |
+AC_CONFIG_FILES([Makefile buildpkg.sh opensshd.init openssh.xml \ | |
+ openbsd-compat/Makefile openbsd-compat/regress/Makefile \ | |
+ survey.sh]) | |
+AC_OUTPUT | |
+ | |
+# Print summary of options | |
+ | |
+# Someone please show me a better way :) | |
+A=`eval echo ${prefix}` ; A=`eval echo ${A}` | |
+B=`eval echo ${bindir}` ; B=`eval echo ${B}` | |
+C=`eval echo ${sbindir}` ; C=`eval echo ${C}` | |
+D=`eval echo ${sysconfdir}` ; D=`eval echo ${D}` | |
+E=`eval echo ${libexecdir}/ssh-askpass` ; E=`eval echo ${E}` | |
+F=`eval echo ${mandir}/${mansubdir}X` ; F=`eval echo ${F}` | |
+G=`eval echo ${piddir}` ; G=`eval echo ${G}` | |
+H=`eval echo ${PRIVSEP_PATH}` ; H=`eval echo ${H}` | |
+I=`eval echo ${user_path}` ; I=`eval echo ${I}` | |
+J=`eval echo ${superuser_path}` ; J=`eval echo ${J}` | |
+ | |
+echo "" | |
+echo "OpenSSH has been configured with the following options:" | |
+echo " User binaries: $B" | |
+echo " System binaries: $C" | |
+echo " Configuration files: $D" | |
+echo " Askpass program: $E" | |
+echo " Manual pages: $F" | |
+echo " PID file: $G" | |
+echo " Privilege separation chroot path: $H" | |
+if test "x$external_path_file" = "x/etc/login.conf" ; then | |
+echo " At runtime, sshd will use the path defined in $external_path_file" | |
+echo " Make sure the path to scp is present, otherwise scp will not work" | |
+else | |
+echo " sshd default user PATH: $I" | |
+ if test ! -z "$external_path_file"; then | |
+echo " (If PATH is set in $external_path_file it will be used instead. If" | |
+echo " used, ensure the path to scp is present, otherwise scp will not work.)" | |
+ fi | |
+fi | |
+if test ! -z "$superuser_path" ; then | |
+echo " sshd superuser user PATH: $J" | |
+fi | |
+echo " Manpage format: $MANTYPE" | |
+echo " PAM support: $PAM_MSG" | |
+echo " OSF SIA support: $SIA_MSG" | |
+echo " KerberosV support: $KRB5_MSG" | |
+echo " SELinux support: $SELINUX_MSG" | |
+echo " Smartcard support: $SCARD_MSG" | |
+echo " S/KEY support: $SKEY_MSG" | |
+echo " MD5 password support: $MD5_MSG" | |
+echo " libedit support: $LIBEDIT_MSG" | |
+echo " Solaris process contract support: $SPC_MSG" | |
+echo " Solaris project support: $SP_MSG" | |
+echo " IP address in \$DISPLAY hack: $DISPLAY_HACK_MSG" | |
+echo " Translate v4 in v6 hack: $IPV4_IN6_HACK_MSG" | |
+echo " BSD Auth support: $BSD_AUTH_MSG" | |
+echo " Random number source: $RAND_MSG" | |
+echo " Privsep sandbox style: $SANDBOX_STYLE" | |
+ | |
+echo "" | |
+ | |
+echo " Host: ${host}" | |
+echo " Compiler: ${CC}" | |
+echo " Compiler flags: ${CFLAGS}" | |
+echo "Preprocessor flags: ${CPPFLAGS}" | |
+echo " Linker flags: ${LDFLAGS}" | |
+echo " Libraries: ${LIBS}" | |
+if test ! -z "${SSHDLIBS}"; then | |
+echo " +for sshd: ${SSHDLIBS}" | |
+fi | |
+if test ! -z "${SSHLIBS}"; then | |
+echo " +for ssh: ${SSHLIBS}" | |
+fi | |
+ | |
+echo "" | |
+ | |
+if test "x$MAKE_PACKAGE_SUPPORTED" = "xyes" ; then | |
+ echo "SVR4 style packages are supported with \"make package\"" | |
+ echo "" | |
+fi | |
+ | |
+if test "x$PAM_MSG" = "xyes" ; then | |
+ echo "PAM is enabled. You may need to install a PAM control file " | |
+ echo "for sshd, otherwise password authentication may fail. " | |
+ echo "Example PAM control files can be found in the contrib/ " | |
+ echo "subdirectory" | |
+ echo "" | |
+fi | |
+ | |
+if test ! -z "$NO_PEERCHECK" ; then | |
+ echo "WARNING: the operating system that you are using does not" | |
+ echo "appear to support getpeereid(), getpeerucred() or the" | |
+ echo "SO_PEERCRED getsockopt() option. These facilities are used to" | |
+ echo "enforce security checks to prevent unauthorised connections to" | |
+ echo "ssh-agent. Their absence increases the risk that a malicious" | |
+ echo "user can connect to your agent." | |
+ echo "" | |
+fi | |
+ | |
+if test "$AUDIT_MODULE" = "bsm" ; then | |
+ echo "WARNING: BSM audit support is currently considered EXPERIMENTAL." | |
+ echo "See the Solaris section in README.platform for details." | |
+fi | |
diff -urp --new-file openssh-6.7p1/groupaccess.c openssh-6.7p1.patched/groupaccess.c | |
--- openssh-6.7p1/groupaccess.c 2013-06-01 15:07:32.000000000 -0700 | |
+++ openssh-6.7p1.patched/groupaccess.c 2014-10-11 21:18:36.000000000 -0700 | |
@@ -34,38 +34,67 @@ | |
#include <stdlib.h> | |
#include <string.h> | |
+#ifdef __APPLE_MEMBERSHIP__ | |
+#include <membership.h> | |
+#endif | |
+ | |
#include "xmalloc.h" | |
#include "groupaccess.h" | |
#include "match.h" | |
#include "log.h" | |
+#ifdef __APPLE_MEMBERSHIP__ | |
+// SPI for 5235093 | |
+int32_t getgrouplist_2(const char *, gid_t, gid_t **); | |
+int32_t getgroupcount(const char *, gid_t); | |
+#endif | |
+ | |
static int ngroups; | |
static char **groups_byname; | |
+#ifdef __APPLE_MEMBERSHIP__ | |
+uuid_t u_uuid; | |
+#endif | |
/* | |
* Initialize group access list for user with primary (base) and | |
* supplementary groups. Return the number of groups in the list. | |
*/ | |
int | |
-ga_init(const char *user, gid_t base) | |
+ga_init(struct passwd *pw) | |
{ | |
- gid_t *groups_bygid; | |
+ gid_t *groups_bygid = NULL; | |
int i, j; | |
struct group *gr; | |
+#ifdef __APPLE_MEMBERSHIP__ | |
+ if (0 != mbr_uid_to_uuid(pw->pw_uid, u_uuid)) | |
+ return 0; | |
+#endif | |
+ | |
if (ngroups > 0) | |
ga_free(); | |
+#ifndef __APPLE_MEMBERSHIP__ | |
ngroups = NGROUPS_MAX; | |
#if defined(HAVE_SYSCONF) && defined(_SC_NGROUPS_MAX) | |
ngroups = MAX(NGROUPS_MAX, sysconf(_SC_NGROUPS_MAX)); | |
-#endif | |
- | |
+#endif | |
groups_bygid = xcalloc(ngroups, sizeof(*groups_bygid)); | |
+#else | |
+ if (-1 == (ngroups = getgrouplist_2(pw->pw_name, pw->pw_gid, | |
+ &groups_bygid))) { | |
+ logit("getgrouplist_2 failed"); | |
+ return 0; | |
+ } | |
+#endif | |
groups_byname = xcalloc(ngroups, sizeof(*groups_byname)); | |
- | |
- if (getgrouplist(user, base, groups_bygid, &ngroups) == -1) | |
- logit("getgrouplist: groups list too small"); | |
+#ifndef __APPLE_MEMBERSHIP__ | |
+ if (getgrouplist(pw->pw_name, pw->pw_gid, groups_bygid, &ngroups) == -1) { | |
+ logit("getgrouplist: groups list too small"); | |
+ free(groups_bygid); | |
+ return 0; | |
+ } | |
+#endif | |
for (i = 0, j = 0; i < ngroups; i++) | |
if ((gr = getgrgid(groups_bygid[i])) != NULL) | |
groups_byname[j++] = xstrdup(gr->gr_name); | |
@@ -76,16 +105,32 @@ ga_init(const char *user, gid_t base) | |
/* | |
* Return 1 if one of user's groups is contained in groups. | |
* Return 0 otherwise. Use match_pattern() for string comparison. | |
+ * Use mbr_check_membership() for membership checking on Mac OS X. | |
*/ | |
int | |
ga_match(char * const *groups, int n) | |
{ | |
+#ifdef __APPLE_MEMBERSHIP__ | |
+ int i, ismember = 0; | |
+ uuid_t g_uuid; | |
+ struct group *grp; | |
+ | |
+ for (i = 0; i < n; i++) { | |
+ if ((grp = getgrnam(groups[i])) == NULL || | |
+ (mbr_gid_to_uuid(grp->gr_gid, g_uuid) != 0) || | |
+ (mbr_check_membership(u_uuid, g_uuid, &ismember) != 0)) | |
+ return 0; | |
+ if (ismember) | |
+ return 1; | |
+ } | |
+#else | |
int i, j; | |
for (i = 0; i < ngroups; i++) | |
for (j = 0; j < n; j++) | |
if (match_pattern(groups_byname[i], groups[j])) | |
return 1; | |
+#endif | |
return 0; | |
} | |
diff -urp --new-file openssh-6.7p1/groupaccess.h openssh-6.7p1.patched/groupaccess.h | |
--- openssh-6.7p1/groupaccess.h 2008-07-03 20:51:12.000000000 -0700 | |
+++ openssh-6.7p1.patched/groupaccess.h 2014-10-11 21:18:36.000000000 -0700 | |
@@ -27,7 +27,7 @@ | |
#ifndef GROUPACCESS_H | |
#define GROUPACCESS_H | |
-int ga_init(const char *, gid_t); | |
+int ga_init(struct passwd *); | |
int ga_match(char * const *, int); | |
int ga_match_pattern_list(const char *); | |
void ga_free(void); | |
diff -urp --new-file openssh-6.7p1/keychain.c openssh-6.7p1.patched/keychain.c | |
--- openssh-6.7p1/keychain.c 1969-12-31 16:00:00.000000000 -0800 | |
+++ openssh-6.7p1.patched/keychain.c 2014-10-11 21:18:36.000000000 -0700 | |
@@ -0,0 +1,694 @@ | |
+/* | |
+ * Copyright (c) 2007 Apple Inc. All rights reserved. | |
+ * | |
+ * @APPLE_BSD_LICENSE_HEADER_START@ | |
+ * | |
+ * 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. | |
+ * 3. Neither the name of Apple Inc. ("Apple") nor the names of its | |
+ * contributors may be used to endorse or promote products derived from | |
+ * this software without specific prior written permission. | |
+ * | |
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS CONTRIBUTORS 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. | |
+ * | |
+ * @APPLE_BSD_LICENSE_HEADER_END@ | |
+ */ | |
+ | |
+#include "includes.h" | |
+ | |
+#include <stdio.h> | |
+#include <string.h> | |
+ | |
+#include "xmalloc.h" | |
+#include "key.h" | |
+#include "authfd.h" | |
+#include "authfile.h" | |
+ | |
+#if defined(__APPLE_KEYCHAIN__) | |
+ | |
+#include <CoreFoundation/CoreFoundation.h> | |
+#include <Security/Security.h> | |
+ | |
+/* Our Security/SecPassword.h is not yet API, so I will define the constants that I am using here. */ | |
+int kSecPasswordGet = 1<<0; // Get password from keychain or user | |
+int kSecPasswordSet = 1<<1; // Set password (passed in if kSecPasswordGet not set, otherwise from user) | |
+int kSecPasswordFail = 1<<2; // Wrong password (ignore item in keychain and flag error) | |
+OSStatus SecGenericPasswordCreate(SecKeychainAttributeList *searchAttrList, SecKeychainAttributeList *itemAttrList, SecPasswordRef *itemRef); | |
+OSStatus SecPasswordAction(SecPasswordRef itemRef, CFTypeRef message, UInt32 flags, UInt32 *length, const void **data); | |
+OSStatus SecPasswordSetInitialAccess(SecPasswordRef itemRef, SecAccessRef accessRef); | |
+ | |
+#endif | |
+ | |
+/* | |
+ * Platform-specific helper functions. | |
+ */ | |
+ | |
+#if defined(__APPLE_KEYCHAIN__) | |
+ | |
+static int get_boolean_preference(const char *key, int default_value, | |
+ int foreground) | |
+{ | |
+ int value = default_value; | |
+ CFStringRef keyRef = NULL; | |
+ CFPropertyListRef valueRef = NULL; | |
+ | |
+ keyRef = CFStringCreateWithCString(NULL, key, kCFStringEncodingUTF8); | |
+ if (keyRef != NULL) | |
+ valueRef = CFPreferencesCopyAppValue(keyRef, | |
+ CFSTR("org.openbsd.openssh")); | |
+ if (valueRef != NULL) | |
+ if (CFGetTypeID(valueRef) == CFBooleanGetTypeID()) | |
+ value = CFBooleanGetValue(valueRef); | |
+ else if (foreground) | |
+ fprintf(stderr, "Ignoring nonboolean %s preference.\n", key); | |
+ | |
+ if (keyRef) | |
+ CFRelease(keyRef); | |
+ if (valueRef) | |
+ CFRelease(valueRef); | |
+ | |
+ return value; | |
+} | |
+ | |
+#endif | |
+ | |
+/* | |
+ * Store the passphrase for a given identity in the keychain. | |
+ */ | |
+void | |
+store_in_keychain(const char *filename, const char *passphrase) | |
+{ | |
+ | |
+#if defined(__APPLE_KEYCHAIN__) | |
+ | |
+ /* | |
+ * store_in_keychain | |
+ * Mac OS X implementation | |
+ */ | |
+ | |
+ CFStringRef cfstr_relative_filename = NULL; | |
+ CFURLRef cfurl_relative_filename = NULL, cfurl_filename = NULL; | |
+ CFStringRef cfstr_filename = NULL; | |
+ CFDataRef cfdata_filename = NULL; | |
+ CFIndex filename_len; | |
+ UInt8 *label = NULL; | |
+ UInt8 *utf8_filename; | |
+ OSStatus rv; | |
+ SecKeychainItemRef itemRef = NULL; | |
+ SecTrustedApplicationRef apps[] = {NULL, NULL, NULL}; | |
+ CFArrayRef trustedlist = NULL; | |
+ SecAccessRef initialAccess = NULL; | |
+ | |
+ /* Bail out if KeychainIntegration preference is -bool NO */ | |
+ if (get_boolean_preference("KeychainIntegration", 1, 1) == 0) { | |
+ fprintf(stderr, "Keychain integration is disabled.\n"); | |
+ goto err; | |
+ } | |
+ | |
+ /* Interpret filename with the correct encoding. */ | |
+ if ((cfstr_relative_filename = | |
+ CFStringCreateWithFileSystemRepresentation(NULL, filename)) == NULL) | |
+ { | |
+ fprintf(stderr, "CFStringCreateWithFileSystemRepresentation failed\n"); | |
+ goto err; | |
+ } | |
+ if ((cfurl_relative_filename = CFURLCreateWithFileSystemPath(NULL, | |
+ cfstr_relative_filename, kCFURLPOSIXPathStyle, false)) == NULL) { | |
+ fprintf(stderr, "CFURLCreateWithFileSystemPath failed\n"); | |
+ goto err; | |
+ } | |
+ if ((cfurl_filename = CFURLCopyAbsoluteURL(cfurl_relative_filename)) == | |
+ NULL) { | |
+ fprintf(stderr, "CFURLCopyAbsoluteURL failed\n"); | |
+ goto err; | |
+ } | |
+ if ((cfstr_filename = CFURLCopyFileSystemPath(cfurl_filename, | |
+ kCFURLPOSIXPathStyle)) == NULL) { | |
+ fprintf(stderr, "CFURLCopyFileSystemPath failed\n"); | |
+ goto err; | |
+ } | |
+ if ((cfdata_filename = CFStringCreateExternalRepresentation(NULL, | |
+ cfstr_filename, kCFStringEncodingUTF8, 0)) == NULL) { | |
+ fprintf(stderr, "CFStringCreateExternalRepresentation failed\n"); | |
+ goto err; | |
+ } | |
+ filename_len = CFDataGetLength(cfdata_filename); | |
+ if ((label = xmalloc(filename_len + 5)) == NULL) { | |
+ fprintf(stderr, "xmalloc failed\n"); | |
+ goto err; | |
+ } | |
+ memcpy(label, "SSH: ", 5); | |
+ utf8_filename = label + 5; | |
+ CFDataGetBytes(cfdata_filename, CFRangeMake(0, filename_len), | |
+ utf8_filename); | |
+ | |
+ /* Check if we already have this passphrase. */ | |
+ rv = SecKeychainFindGenericPassword(NULL, 3, "SSH", filename_len, | |
+ (char *)utf8_filename, NULL, NULL, &itemRef); | |
+ if (rv == errSecItemNotFound) { | |
+ /* Add a new keychain item. */ | |
+ SecKeychainAttribute attrs[] = { | |
+ {kSecLabelItemAttr, filename_len + 5, label}, | |
+ {kSecServiceItemAttr, 3, "SSH"}, | |
+ {kSecAccountItemAttr, filename_len, utf8_filename} | |
+ }; | |
+ SecKeychainAttributeList attrList = | |
+ {sizeof(attrs) / sizeof(attrs[0]), attrs}; | |
+ if (SecTrustedApplicationCreateFromPath("/usr/bin/ssh-agent", | |
+ &apps[0]) != noErr || | |
+ SecTrustedApplicationCreateFromPath("/usr/bin/ssh-add", | |
+ &apps[1]) != noErr || | |
+ SecTrustedApplicationCreateFromPath("/usr/bin/ssh", | |
+ &apps[2]) != noErr) { | |
+ fprintf(stderr, "SecTrustedApplicationCreateFromPath failed\n"); | |
+ goto err; | |
+ } | |
+ if ((trustedlist = CFArrayCreate(NULL, (const void **)apps, | |
+ sizeof(apps) / sizeof(apps[0]), &kCFTypeArrayCallBacks)) == | |
+ NULL) { | |
+ fprintf(stderr, "CFArrayCreate failed\n"); | |
+ goto err; | |
+ } | |
+ if (SecAccessCreate(cfstr_filename, trustedlist, | |
+ &initialAccess) != noErr) { | |
+ fprintf(stderr, "SecAccessCreate failed\n"); | |
+ goto err; | |
+ } | |
+ if (SecKeychainItemCreateFromContent( | |
+ kSecGenericPasswordItemClass, &attrList, strlen(passphrase), | |
+ passphrase, NULL, initialAccess, NULL) == noErr) | |
+ fprintf(stderr, "Passphrase stored in keychain: %s\n", filename); | |
+ else | |
+ fprintf(stderr, "Could not create keychain item\n"); | |
+ } else if (rv == noErr) { | |
+ /* Update an existing keychain item. */ | |
+ if (SecKeychainItemModifyAttributesAndData(itemRef, NULL, | |
+ strlen(passphrase), passphrase) == noErr) | |
+ fprintf(stderr, "Passphrase updated in keychain: %s\n", filename); | |
+ else | |
+ fprintf(stderr, "Could not modify keychain item\n"); | |
+ } else | |
+ fprintf(stderr, "Could not access keychain\n"); | |
+ | |
+err: /* Clean up. */ | |
+ if (cfstr_relative_filename) | |
+ CFRelease(cfstr_relative_filename); | |
+ if (cfurl_relative_filename) | |
+ CFRelease(cfurl_relative_filename); | |
+ if (cfurl_filename) | |
+ CFRelease(cfurl_filename); | |
+ if (cfstr_filename) | |
+ CFRelease(cfstr_filename); | |
+ if (cfdata_filename) | |
+ CFRelease(cfdata_filename); | |
+ if (label) | |
+ free(label); | |
+ if (itemRef) | |
+ CFRelease(itemRef); | |
+ if (apps[0]) | |
+ CFRelease(apps[0]); | |
+ if (apps[1]) | |
+ CFRelease(apps[1]); | |
+ if (apps[2]) | |
+ CFRelease(apps[2]); | |
+ if (trustedlist) | |
+ CFRelease(trustedlist); | |
+ if (initialAccess) | |
+ CFRelease(initialAccess); | |
+ | |
+#else | |
+ | |
+ /* | |
+ * store_in_keychain | |
+ * no keychain implementation | |
+ */ | |
+ | |
+ fprintf(stderr, "Keychain is not available on this system\n"); | |
+ | |
+#endif | |
+ | |
+} | |
+ | |
+/* | |
+ * Remove the passphrase for a given identity from the keychain. | |
+ */ | |
+void | |
+remove_from_keychain(const char *filename) | |
+{ | |
+ | |
+#if defined(__APPLE_KEYCHAIN__) | |
+ | |
+ /* | |
+ * remove_from_keychain | |
+ * Mac OS X implementation | |
+ */ | |
+ | |
+ CFStringRef cfstr_relative_filename = NULL; | |
+ CFURLRef cfurl_relative_filename = NULL, cfurl_filename = NULL; | |
+ CFStringRef cfstr_filename = NULL; | |
+ CFDataRef cfdata_filename = NULL; | |
+ CFIndex filename_len; | |
+ const UInt8 *utf8_filename; | |
+ OSStatus rv; | |
+ SecKeychainItemRef itemRef = NULL; | |
+ | |
+ /* Bail out if KeychainIntegration preference is -bool NO */ | |
+ if (get_boolean_preference("KeychainIntegration", 1, 1) == 0) { | |
+ fprintf(stderr, "Keychain integration is disabled.\n"); | |
+ goto err; | |
+ } | |
+ | |
+ /* Interpret filename with the correct encoding. */ | |
+ if ((cfstr_relative_filename = | |
+ CFStringCreateWithFileSystemRepresentation(NULL, filename)) == NULL) | |
+ { | |
+ fprintf(stderr, "CFStringCreateWithFileSystemRepresentation failed\n"); | |
+ goto err; | |
+ } | |
+ if ((cfurl_relative_filename = CFURLCreateWithFileSystemPath(NULL, | |
+ cfstr_relative_filename, kCFURLPOSIXPathStyle, false)) == NULL) { | |
+ fprintf(stderr, "CFURLCreateWithFileSystemPath failed\n"); | |
+ goto err; | |
+ } | |
+ if ((cfurl_filename = CFURLCopyAbsoluteURL(cfurl_relative_filename)) == | |
+ NULL) { | |
+ fprintf(stderr, "CFURLCopyAbsoluteURL failed\n"); | |
+ goto err; | |
+ } | |
+ if ((cfstr_filename = CFURLCopyFileSystemPath(cfurl_filename, | |
+ kCFURLPOSIXPathStyle)) == NULL) { | |
+ fprintf(stderr, "CFURLCopyFileSystemPath failed\n"); | |
+ goto err; | |
+ } | |
+ if ((cfdata_filename = CFStringCreateExternalRepresentation(NULL, | |
+ cfstr_filename, kCFStringEncodingUTF8, 0)) == NULL) { | |
+ fprintf(stderr, "CFStringCreateExternalRepresentation failed\n"); | |
+ goto err; | |
+ } | |
+ filename_len = CFDataGetLength(cfdata_filename); | |
+ utf8_filename = CFDataGetBytePtr(cfdata_filename); | |
+ | |
+ /* Check if we already have this passphrase. */ | |
+ rv = SecKeychainFindGenericPassword(NULL, 3, "SSH", filename_len, | |
+ (const char *)utf8_filename, NULL, NULL, &itemRef); | |
+ if (rv == noErr) { | |
+ /* Remove the passphrase from the keychain. */ | |
+ if (SecKeychainItemDelete(itemRef) == noErr) | |
+ fprintf(stderr, "Passphrase removed from keychain: %s\n", filename); | |
+ else | |
+ fprintf(stderr, "Could not remove keychain item\n"); | |
+ } else if (rv != errSecItemNotFound) | |
+ fprintf(stderr, "Could not access keychain\n"); | |
+ | |
+err: /* Clean up. */ | |
+ if (cfstr_relative_filename) | |
+ CFRelease(cfstr_relative_filename); | |
+ if (cfurl_relative_filename) | |
+ CFRelease(cfurl_relative_filename); | |
+ if (cfurl_filename) | |
+ CFRelease(cfurl_filename); | |
+ if (cfstr_filename) | |
+ CFRelease(cfstr_filename); | |
+ if (cfdata_filename) | |
+ CFRelease(cfdata_filename); | |
+ if (itemRef) | |
+ CFRelease(itemRef); | |
+ | |
+#else | |
+ | |
+ /* | |
+ * remove_from_keychain | |
+ * no keychain implementation | |
+ */ | |
+ | |
+ fprintf(stderr, "Keychain is not available on this system\n"); | |
+ | |
+#endif | |
+ | |
+} | |
+ | |
+/* | |
+ * Add identities to ssh-agent using passphrases stored in the keychain. | |
+ * Returns zero on success and nonzero on failure. | |
+ * add_identity is a callback into ssh-agent. It takes a filename and a | |
+ * passphrase, and attempts to add the identity to the agent. It returns | |
+ * zero on success and nonzero on failure. | |
+ */ | |
+int | |
+add_identities_using_keychain(int (*add_identity)(const char *, const char *)) | |
+{ | |
+ | |
+#if defined(__APPLE_KEYCHAIN__) | |
+ | |
+ /* | |
+ * add_identities_using_keychain | |
+ * Mac OS X implementation | |
+ */ | |
+ | |
+ OSStatus rv; | |
+ SecKeychainSearchRef searchRef; | |
+ SecKeychainItemRef itemRef; | |
+ UInt32 length; | |
+ void *data; | |
+ CFIndex maxsize; | |
+ | |
+ /* Bail out if KeychainIntegration preference is -bool NO */ | |
+ if (get_boolean_preference("KeychainIntegration", 1, 0) == 0) | |
+ return 0; | |
+ | |
+ /* Search for SSH passphrases in the keychain */ | |
+ SecKeychainAttribute attrs[] = { | |
+ {kSecServiceItemAttr, 3, "SSH"} | |
+ }; | |
+ SecKeychainAttributeList attrList = | |
+ {sizeof(attrs) / sizeof(attrs[0]), attrs}; | |
+ if ((rv = SecKeychainSearchCreateFromAttributes(NULL, | |
+ kSecGenericPasswordItemClass, &attrList, &searchRef)) != noErr) | |
+ return 0; | |
+ | |
+ /* Iterate through the search results. */ | |
+ while ((rv = SecKeychainSearchCopyNext(searchRef, &itemRef)) == noErr) { | |
+ UInt32 tag = kSecAccountItemAttr; | |
+ UInt32 format = kSecFormatUnknown; | |
+ SecKeychainAttributeInfo info = {1, &tag, &format}; | |
+ SecKeychainAttributeList *itemAttrList = NULL; | |
+ CFStringRef cfstr_filename = NULL; | |
+ char *filename = NULL; | |
+ char *passphrase = NULL; | |
+ | |
+ /* Retrieve filename and passphrase. */ | |
+ if ((rv = SecKeychainItemCopyAttributesAndData(itemRef, &info, | |
+ NULL, &itemAttrList, &length, &data)) != noErr) | |
+ goto err; | |
+ if (itemAttrList->count != 1) | |
+ goto err; | |
+ cfstr_filename = CFStringCreateWithBytes(NULL, | |
+ itemAttrList->attr->data, itemAttrList->attr->length, | |
+ kCFStringEncodingUTF8, true); | |
+ maxsize = CFStringGetMaximumSizeOfFileSystemRepresentation( | |
+ cfstr_filename); | |
+ if ((filename = xmalloc(maxsize)) == NULL) | |
+ goto err; | |
+ if (CFStringGetFileSystemRepresentation(cfstr_filename, | |
+ filename, maxsize) == false) | |
+ goto err; | |
+ if ((passphrase = xmalloc(length + 1)) == NULL) | |
+ goto err; | |
+ memcpy(passphrase, data, length); | |
+ passphrase[length] = '\0'; | |
+ | |
+ /* Add the identity. */ | |
+ add_identity(filename, passphrase); | |
+ | |
+err: /* Clean up. */ | |
+ if (itemRef) | |
+ CFRelease(itemRef); | |
+ if (cfstr_filename) | |
+ CFRelease(cfstr_filename); | |
+ if (filename) | |
+ free(filename); | |
+ if (passphrase) | |
+ free(passphrase); | |
+ if (itemAttrList) | |
+ SecKeychainItemFreeAttributesAndData(itemAttrList, | |
+ data); | |
+ } | |
+ | |
+ CFRelease(searchRef); | |
+ | |
+ return 0; | |
+ | |
+#else | |
+ | |
+ /* | |
+ * add_identities_using_keychain | |
+ * no implementation | |
+ */ | |
+ | |
+ return 1; | |
+ | |
+#endif | |
+ | |
+} | |
+ | |
+/* | |
+ * Prompt the user for a key's passphrase. The user will be offered the option | |
+ * of storing the passphrase in their keychain. Returns the passphrase | |
+ * (which the caller is responsible for freeing), or NULL if this function | |
+ * fails or is not implemented. If this function is not implemented, ssh will | |
+ * fall back on the standard read_passphrase function, and the user will need | |
+ * to use ssh-add -K to add their keys to the keychain. | |
+ */ | |
+char * | |
+keychain_read_passphrase(const char *filename, int oAskPassGUI) | |
+{ | |
+ | |
+#if defined(__APPLE_KEYCHAIN__) | |
+ | |
+ /* | |
+ * keychain_read_passphrase | |
+ * Mac OS X implementation | |
+ */ | |
+ | |
+ CFStringRef cfstr_relative_filename = NULL; | |
+ CFURLRef cfurl_relative_filename = NULL, cfurl_filename = NULL; | |
+ CFStringRef cfstr_filename = NULL; | |
+ CFDataRef cfdata_filename = NULL; | |
+ CFIndex filename_len; | |
+ UInt8 *label = NULL; | |
+ UInt8 *utf8_filename; | |
+ SecPasswordRef passRef = NULL; | |
+ SecTrustedApplicationRef apps[] = {NULL, NULL, NULL}; | |
+ CFArrayRef trustedlist = NULL; | |
+ SecAccessRef initialAccess = NULL; | |
+ CFURLRef path = NULL; | |
+ CFStringRef pathFinal = NULL; | |
+ CFURLRef bundle_url = NULL; | |
+ CFBundleRef bundle = NULL; | |
+ CFStringRef promptTemplate = NULL, prompt = NULL; | |
+ UInt32 length; | |
+ const void *data; | |
+ AuthenticationConnection *ac = NULL; | |
+ char *result = NULL; | |
+ | |
+ /* Bail out if KeychainIntegration preference is -bool NO */ | |
+ if (get_boolean_preference("KeychainIntegration", 1, 1) == 0) | |
+ goto err; | |
+ | |
+ /* Bail out if the user set AskPassGUI preference to -bool NO */ | |
+ if (get_boolean_preference("AskPassGUI", 1, 1) == 0 || oAskPassGUI == 0) | |
+ goto err; | |
+ | |
+ /* Bail out if we can't communicate with ssh-agent */ | |
+ if ((ac = ssh_get_authentication_connection()) == NULL) | |
+ goto err; | |
+ | |
+ /* Interpret filename with the correct encoding. */ | |
+ if ((cfstr_relative_filename = | |
+ CFStringCreateWithFileSystemRepresentation(NULL, filename)) == NULL) | |
+ { | |
+ fprintf(stderr, "CFStringCreateWithFileSystemRepresentation failed\n"); | |
+ goto err; | |
+ } | |
+ if ((cfurl_relative_filename = CFURLCreateWithFileSystemPath(NULL, | |
+ cfstr_relative_filename, kCFURLPOSIXPathStyle, false)) == NULL) { | |
+ fprintf(stderr, "CFURLCreateWithFileSystemPath failed\n"); | |
+ goto err; | |
+ } | |
+ if ((cfurl_filename = CFURLCopyAbsoluteURL(cfurl_relative_filename)) == | |
+ NULL) { | |
+ fprintf(stderr, "CFURLCopyAbsoluteURL failed\n"); | |
+ goto err; | |
+ } | |
+ if ((cfstr_filename = CFURLCopyFileSystemPath(cfurl_filename, | |
+ kCFURLPOSIXPathStyle)) == NULL) { | |
+ fprintf(stderr, "CFURLCopyFileSystemPath failed\n"); | |
+ goto err; | |
+ } | |
+ if ((cfdata_filename = CFStringCreateExternalRepresentation(NULL, | |
+ cfstr_filename, kCFStringEncodingUTF8, 0)) == NULL) { | |
+ fprintf(stderr, "CFStringCreateExternalRepresentation failed\n"); | |
+ goto err; | |
+ } | |
+ filename_len = CFDataGetLength(cfdata_filename); | |
+ if ((label = xmalloc(filename_len + 5)) == NULL) { | |
+ fprintf(stderr, "xmalloc failed\n"); | |
+ goto err; | |
+ } | |
+ memcpy(label, "SSH: ", 5); | |
+ utf8_filename = label + 5; | |
+ CFDataGetBytes(cfdata_filename, CFRangeMake(0, filename_len), | |
+ utf8_filename); | |
+ | |
+ /* Build a SecPasswordRef. */ | |
+ SecKeychainAttribute searchAttrs[] = { | |
+ {kSecServiceItemAttr, 3, "SSH"}, | |
+ {kSecAccountItemAttr, filename_len, utf8_filename} | |
+ }; | |
+ SecKeychainAttributeList searchAttrList = | |
+ {sizeof(searchAttrs) / sizeof(searchAttrs[0]), searchAttrs}; | |
+ SecKeychainAttribute attrs[] = { | |
+ {kSecLabelItemAttr, filename_len + 5, label}, | |
+ {kSecServiceItemAttr, 3, "SSH"}, | |
+ {kSecAccountItemAttr, filename_len, utf8_filename} | |
+ }; | |
+ SecKeychainAttributeList attrList = | |
+ {sizeof(attrs) / sizeof(attrs[0]), attrs}; | |
+ if (SecGenericPasswordCreate(&searchAttrList, &attrList, &passRef) != | |
+ noErr) { | |
+ fprintf(stderr, "SecGenericPasswordCreate failed\n"); | |
+ goto err; | |
+ } | |
+ if (SecTrustedApplicationCreateFromPath("/usr/bin/ssh-agent", &apps[0]) | |
+ != noErr || | |
+ SecTrustedApplicationCreateFromPath("/usr/bin/ssh-add", &apps[1]) | |
+ != noErr || | |
+ SecTrustedApplicationCreateFromPath("/usr/bin/ssh", &apps[2]) | |
+ != noErr) { | |
+ fprintf(stderr, "SecTrustedApplicationCreateFromPath failed\n"); | |
+ goto err; | |
+ } | |
+ if ((trustedlist = CFArrayCreate(NULL, (const void **)apps, | |
+ sizeof(apps) / sizeof(apps[0]), &kCFTypeArrayCallBacks)) == NULL) { | |
+ fprintf(stderr, "CFArrayCreate failed\n"); | |
+ goto err; | |
+ } | |
+ if (SecAccessCreate(cfstr_filename, trustedlist, &initialAccess) | |
+ != noErr) { | |
+ fprintf(stderr, "SecAccessCreate failed\n"); | |
+ goto err; | |
+ } | |
+ if (SecPasswordSetInitialAccess(passRef, initialAccess) != noErr) { | |
+ fprintf(stderr, "SecPasswordSetInitialAccess failed\n"); | |
+ goto err; | |
+ } | |
+ | |
+ /* Request the passphrase from the user. */ | |
+ if ((path = CFURLCreateFromFileSystemRepresentation(NULL, | |
+ (UInt8 *)filename, strlen(filename), false)) == NULL) { | |
+ fprintf(stderr, "CFURLCreateFromFileSystemRepresentation failed\n"); | |
+ goto err; | |
+ } | |
+ if ((pathFinal = CFURLCopyLastPathComponent(path)) == NULL) { | |
+ fprintf(stderr, "CFURLCopyLastPathComponent failed\n"); | |
+ goto err; | |
+ } | |
+ if (!((bundle_url = CFURLCreateWithFileSystemPath(NULL, | |
+ CFSTR("/System/Library/CoreServices/"), kCFURLPOSIXPathStyle, true)) | |
+ != NULL && (bundle = CFBundleCreate(NULL, bundle_url)) != NULL && | |
+ (promptTemplate = CFCopyLocalizedStringFromTableInBundle( | |
+ CFSTR("Enter your password for the SSH key \"%@\"."), | |
+ CFSTR("OpenSSH"), bundle, "Text of the dialog asking the user for" | |
+ "their passphrase. The %@ will be replaced with the filename of a" | |
+ "specific key.")) != NULL) && | |
+ (promptTemplate = CFStringCreateCopy(NULL, | |
+ CFSTR("Enter your password for the SSH key \"%@\"."))) == NULL) { | |
+ fprintf(stderr, "CFStringCreateCopy failed\n"); | |
+ goto err; | |
+ } | |
+ if ((prompt = CFStringCreateWithFormat(NULL, NULL, promptTemplate, | |
+ pathFinal)) == NULL) { | |
+ fprintf(stderr, "CFStringCreateWithFormat failed\n"); | |
+ goto err; | |
+ } | |
+ switch (SecPasswordAction(passRef, prompt, | |
+ kSecPasswordGet|kSecPasswordFail, &length, &data)) { | |
+ case noErr: | |
+ result = xmalloc(length + 1); | |
+ memcpy(result, data, length); | |
+ result[length] = '\0'; | |
+ | |
+ /* Save password in keychain if requested. */ | |
+ if (noErr != SecPasswordAction(passRef, CFSTR(""), kSecPasswordSet, &length, &data)) | |
+ fprintf(stderr, "Saving password to keychain failed\n"); | |
+ | |
+ /* Add password to agent. */ | |
+ char *comment = NULL; | |
+ Key *private = key_load_private(filename, result, &comment); | |
+ if (NULL == private) | |
+ break; | |
+ if (ssh_add_identity_constrained(ac, private, comment, 0, 0)) | |
+ fprintf(stderr, "Identity added: %s (%s)\n", filename, comment); | |
+ else | |
+ fprintf(stderr, "Could not add identity: %s\n", filename); | |
+ free(comment); | |
+ key_free(private); | |
+ break; | |
+ case errAuthorizationCanceled: | |
+ result = xmalloc(1); | |
+ *result = '\0'; | |
+ break; | |
+ default: | |
+ goto err; | |
+ } | |
+ | |
+err: /* Clean up. */ | |
+ if (cfstr_relative_filename) | |
+ CFRelease(cfstr_relative_filename); | |
+ if (cfurl_relative_filename) | |
+ CFRelease(cfurl_relative_filename); | |
+ if (cfurl_filename) | |
+ CFRelease(cfurl_filename); | |
+ if (cfstr_filename) | |
+ CFRelease(cfstr_filename); | |
+ if (cfdata_filename) | |
+ CFRelease(cfdata_filename); | |
+ if (label) | |
+ free(label); | |
+ if (passRef) | |
+ CFRelease(passRef); | |
+ if (apps[0]) | |
+ CFRelease(apps[0]); | |
+ if (apps[1]) | |
+ CFRelease(apps[1]); | |
+ if (apps[2]) | |
+ CFRelease(apps[2]); | |
+ if (trustedlist) | |
+ CFRelease(trustedlist); | |
+ if (initialAccess) | |
+ CFRelease(initialAccess); | |
+ if (path) | |
+ CFRelease(path); | |
+ if (pathFinal) | |
+ CFRelease(pathFinal); | |
+ if (bundle_url) | |
+ CFRelease(bundle_url); | |
+ if (bundle) | |
+ CFRelease(bundle); | |
+ if (promptTemplate) | |
+ CFRelease(promptTemplate); | |
+ if (prompt) | |
+ CFRelease(prompt); | |
+ if (ac) | |
+ ssh_close_authentication_connection(ac); | |
+ | |
+ return result; | |
+ | |
+#else | |
+ | |
+ /* | |
+ * keychain_read_passphrase | |
+ * no implementation | |
+ */ | |
+ | |
+ return NULL; | |
+ | |
+#endif | |
+ | |
+} | |
diff -urp --new-file openssh-6.7p1/keychain.h openssh-6.7p1.patched/keychain.h | |
--- openssh-6.7p1/keychain.h 1969-12-31 16:00:00.000000000 -0800 | |
+++ openssh-6.7p1.patched/keychain.h 2014-10-11 21:18:36.000000000 -0700 | |
@@ -0,0 +1,45 @@ | |
+/* | |
+ * Copyright (c) 2007 Apple Inc. All rights reserved. | |
+ * | |
+ * @APPLE_BSD_LICENSE_HEADER_START@ | |
+ * | |
+ * 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. | |
+ * 3. Neither the name of Apple Inc. ("Apple") nor the names of its | |
+ * contributors may be used to endorse or promote products derived from | |
+ * this software without specific prior written permission. | |
+ * | |
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS CONTRIBUTORS 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. | |
+ * | |
+ * @APPLE_BSD_LICENSE_HEADER_END@ | |
+ */ | |
+ | |
+/* | |
+ * KEYCHAIN indicates that keychain functionality is present. | |
+ * KEYCHAIN_* indicates the implementation to use, and implies KEYCHAIN. | |
+ */ | |
+#if defined(__APPLE_KEYCHAIN__) | |
+#define KEYCHAIN | |
+#endif | |
+ | |
+void store_in_keychain(const char *filename, const char *passphrase); | |
+void remove_from_keychain(const char *filename); | |
+int add_identities_using_keychain( | |
+ int (*add_identity)(const char *, const char *)); | |
+char *keychain_read_passphrase(const char *filename, int oAskPassGUI); | |
diff -urp --new-file openssh-6.7p1/readconf.c openssh-6.7p1.patched/readconf.c | |
--- openssh-6.7p1/readconf.c 2014-07-17 21:11:26.000000000 -0700 | |
+++ openssh-6.7p1.patched/readconf.c 2014-10-11 21:18:36.000000000 -0700 | |
@@ -149,6 +149,9 @@ typedef enum { | |
oVisualHostKey, oUseRoaming, | |
oKexAlgorithms, oIPQoS, oRequestTTY, oIgnoreUnknown, oProxyUseFdpass, | |
oCanonicalDomains, oCanonicalizeHostname, oCanonicalizeMaxDots, | |
+#ifdef __APPLE_KEYCHAIN__ | |
+ oAskPassGUI, | |
+#endif | |
oCanonicalizeFallbackLocal, oCanonicalizePermittedCNAMEs, | |
oStreamLocalBindMask, oStreamLocalBindUnlink, | |
oIgnoredUnknownOption, oDeprecated, oUnsupported | |
@@ -266,6 +269,9 @@ static struct { | |
{ "streamlocalbindmask", oStreamLocalBindMask }, | |
{ "streamlocalbindunlink", oStreamLocalBindUnlink }, | |
{ "ignoreunknown", oIgnoreUnknown }, | |
+#ifdef __APPLE_KEYCHAIN__ | |
+ { "askpassgui", oAskPassGUI }, | |
+#endif | |
{ NULL, oBadOption } | |
}; | |
@@ -1358,6 +1364,12 @@ parse_int: | |
charptr = &options->ignored_unknown; | |
goto parse_string; | |
+#ifdef __APPLE_KEYCHAIN__ | |
+ case oAskPassGUI: | |
+ intptr = &options->ask_pass_gui; | |
+ goto parse_flag; | |
+#endif | |
+ | |
case oProxyUseFdpass: | |
intptr = &options->proxy_use_fdpass; | |
goto parse_flag; | |
@@ -1604,6 +1616,9 @@ initialize_options(Options * options) | |
options->request_tty = -1; | |
options->proxy_use_fdpass = -1; | |
options->ignored_unknown = NULL; | |
+#ifdef __APPLE_KEYCHAIN__ | |
+ options->ask_pass_gui = -1; | |
+#endif | |
options->num_canonical_domains = 0; | |
options->num_permitted_cnames = 0; | |
options->canonicalize_max_dots = -1; | |
@@ -1778,6 +1793,10 @@ fill_default_options(Options * options) | |
options->ip_qos_bulk = IPTOS_THROUGHPUT; | |
if (options->request_tty == -1) | |
options->request_tty = REQUEST_TTY_AUTO; | |
+#ifdef __APPLE_KEYCHAIN__ | |
+ if (options->ask_pass_gui == -1) | |
+ options->ask_pass_gui = 1; | |
+#endif | |
if (options->proxy_use_fdpass == -1) | |
options->proxy_use_fdpass = 0; | |
if (options->canonicalize_max_dots == -1) | |
diff -urp --new-file openssh-6.7p1/readconf.c.orig openssh-6.7p1.patched/readconf.c.orig | |
--- openssh-6.7p1/readconf.c.orig 1969-12-31 16:00:00.000000000 -0800 | |
+++ openssh-6.7p1.patched/readconf.c.orig 2014-07-17 21:11:26.000000000 -0700 | |
@@ -0,0 +1,2011 @@ | |
+/* $OpenBSD: readconf.c,v 1.220 2014/07/15 15:54:14 millert Exp $ */ | |
+/* | |
+ * Author: Tatu Ylonen <ylo@cs.hut.fi> | |
+ * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | |
+ * All rights reserved | |
+ * Functions for reading the configuration files. | |
+ * | |
+ * As far as I am concerned, the code I have written for this software | |
+ * can be used freely for any purpose. Any derived versions of this | |
+ * software must be clearly marked as such, and if the derived work is | |
+ * incompatible with the protocol description in the RFC file, it must be | |
+ * called by a name other than "ssh" or "Secure Shell". | |
+ */ | |
+ | |
+#include "includes.h" | |
+ | |
+#include <sys/types.h> | |
+#include <sys/stat.h> | |
+#include <sys/socket.h> | |
+#include <sys/wait.h> | |
+#include <sys/un.h> | |
+ | |
+#include <netinet/in.h> | |
+#include <netinet/in_systm.h> | |
+#include <netinet/ip.h> | |
+#include <arpa/inet.h> | |
+ | |
+#include <ctype.h> | |
+#include <errno.h> | |
+#include <fcntl.h> | |
+#include <netdb.h> | |
+#ifdef HAVE_PATHS_H | |
+# include <paths.h> | |
+#endif | |
+#include <pwd.h> | |
+#include <signal.h> | |
+#include <stdarg.h> | |
+#include <stdio.h> | |
+#include <string.h> | |
+#include <unistd.h> | |
+#ifdef HAVE_UTIL_H | |
+#include <util.h> | |
+#endif | |
+ | |
+#include "xmalloc.h" | |
+#include "ssh.h" | |
+#include "compat.h" | |
+#include "cipher.h" | |
+#include "pathnames.h" | |
+#include "log.h" | |
+#include "key.h" | |
+#include "misc.h" | |
+#include "readconf.h" | |
+#include "match.h" | |
+#include "buffer.h" | |
+#include "kex.h" | |
+#include "mac.h" | |
+#include "uidswap.h" | |
+ | |
+/* Format of the configuration file: | |
+ | |
+ # Configuration data is parsed as follows: | |
+ # 1. command line options | |
+ # 2. user-specific file | |
+ # 3. system-wide file | |
+ # Any configuration value is only changed the first time it is set. | |
+ # Thus, host-specific definitions should be at the beginning of the | |
+ # configuration file, and defaults at the end. | |
+ | |
+ # Host-specific declarations. These may override anything above. A single | |
+ # host may match multiple declarations; these are processed in the order | |
+ # that they are given in. | |
+ | |
+ Host *.ngs.fi ngs.fi | |
+ User foo | |
+ | |
+ Host fake.com | |
+ HostName another.host.name.real.org | |
+ User blaah | |
+ Port 34289 | |
+ ForwardX11 no | |
+ ForwardAgent no | |
+ | |
+ Host books.com | |
+ RemoteForward 9999 shadows.cs.hut.fi:9999 | |
+ Cipher 3des | |
+ | |
+ Host fascist.blob.com | |
+ Port 23123 | |
+ User tylonen | |
+ PasswordAuthentication no | |
+ | |
+ Host puukko.hut.fi | |
+ User t35124p | |
+ ProxyCommand ssh-proxy %h %p | |
+ | |
+ Host *.fr | |
+ PublicKeyAuthentication no | |
+ | |
+ Host *.su | |
+ Cipher none | |
+ PasswordAuthentication no | |
+ | |
+ Host vpn.fake.com | |
+ Tunnel yes | |
+ TunnelDevice 3 | |
+ | |
+ # Defaults for various options | |
+ Host * | |
+ ForwardAgent no | |
+ ForwardX11 no | |
+ PasswordAuthentication yes | |
+ RSAAuthentication yes | |
+ RhostsRSAAuthentication yes | |
+ StrictHostKeyChecking yes | |
+ TcpKeepAlive no | |
+ IdentityFile ~/.ssh/identity | |
+ Port 22 | |
+ EscapeChar ~ | |
+ | |
+*/ | |
+ | |
+/* Keyword tokens. */ | |
+ | |
+typedef enum { | |
+ oBadOption, | |
+ oHost, oMatch, | |
+ oForwardAgent, oForwardX11, oForwardX11Trusted, oForwardX11Timeout, | |
+ oGatewayPorts, oExitOnForwardFailure, | |
+ oPasswordAuthentication, oRSAAuthentication, | |
+ oChallengeResponseAuthentication, oXAuthLocation, | |
+ oIdentityFile, oHostName, oPort, oCipher, oRemoteForward, oLocalForward, | |
+ oUser, oEscapeChar, oRhostsRSAAuthentication, oProxyCommand, | |
+ oGlobalKnownHostsFile, oUserKnownHostsFile, oConnectionAttempts, | |
+ oBatchMode, oCheckHostIP, oStrictHostKeyChecking, oCompression, | |
+ oCompressionLevel, oTCPKeepAlive, oNumberOfPasswordPrompts, | |
+ oUsePrivilegedPort, oLogLevel, oCiphers, oProtocol, oMacs, | |
+ oGlobalKnownHostsFile2, oUserKnownHostsFile2, oPubkeyAuthentication, | |
+ oKbdInteractiveAuthentication, oKbdInteractiveDevices, oHostKeyAlias, | |
+ oDynamicForward, oPreferredAuthentications, oHostbasedAuthentication, | |
+ oHostKeyAlgorithms, oBindAddress, oPKCS11Provider, | |
+ oClearAllForwardings, oNoHostAuthenticationForLocalhost, | |
+ oEnableSSHKeysign, oRekeyLimit, oVerifyHostKeyDNS, oConnectTimeout, | |
+ oAddressFamily, oGssAuthentication, oGssDelegateCreds, | |
+ oServerAliveInterval, oServerAliveCountMax, oIdentitiesOnly, | |
+ oSendEnv, oControlPath, oControlMaster, oControlPersist, | |
+ oHashKnownHosts, | |
+ oTunnel, oTunnelDevice, oLocalCommand, oPermitLocalCommand, | |
+ oVisualHostKey, oUseRoaming, | |
+ oKexAlgorithms, oIPQoS, oRequestTTY, oIgnoreUnknown, oProxyUseFdpass, | |
+ oCanonicalDomains, oCanonicalizeHostname, oCanonicalizeMaxDots, | |
+ oCanonicalizeFallbackLocal, oCanonicalizePermittedCNAMEs, | |
+ oStreamLocalBindMask, oStreamLocalBindUnlink, | |
+ oIgnoredUnknownOption, oDeprecated, oUnsupported | |
+} OpCodes; | |
+ | |
+/* Textual representations of the tokens. */ | |
+ | |
+static struct { | |
+ const char *name; | |
+ OpCodes opcode; | |
+} keywords[] = { | |
+ { "forwardagent", oForwardAgent }, | |
+ { "forwardx11", oForwardX11 }, | |
+ { "forwardx11trusted", oForwardX11Trusted }, | |
+ { "forwardx11timeout", oForwardX11Timeout }, | |
+ { "exitonforwardfailure", oExitOnForwardFailure }, | |
+ { "xauthlocation", oXAuthLocation }, | |
+ { "gatewayports", oGatewayPorts }, | |
+ { "useprivilegedport", oUsePrivilegedPort }, | |
+ { "rhostsauthentication", oDeprecated }, | |
+ { "passwordauthentication", oPasswordAuthentication }, | |
+ { "kbdinteractiveauthentication", oKbdInteractiveAuthentication }, | |
+ { "kbdinteractivedevices", oKbdInteractiveDevices }, | |
+ { "rsaauthentication", oRSAAuthentication }, | |
+ { "pubkeyauthentication", oPubkeyAuthentication }, | |
+ { "dsaauthentication", oPubkeyAuthentication }, /* alias */ | |
+ { "rhostsrsaauthentication", oRhostsRSAAuthentication }, | |
+ { "hostbasedauthentication", oHostbasedAuthentication }, | |
+ { "challengeresponseauthentication", oChallengeResponseAuthentication }, | |
+ { "skeyauthentication", oChallengeResponseAuthentication }, /* alias */ | |
+ { "tisauthentication", oChallengeResponseAuthentication }, /* alias */ | |
+ { "kerberosauthentication", oUnsupported }, | |
+ { "kerberostgtpassing", oUnsupported }, | |
+ { "afstokenpassing", oUnsupported }, | |
+#if defined(GSSAPI) | |
+ { "gssapiauthentication", oGssAuthentication }, | |
+ { "gssapidelegatecredentials", oGssDelegateCreds }, | |
+#else | |
+ { "gssapiauthentication", oUnsupported }, | |
+ { "gssapidelegatecredentials", oUnsupported }, | |
+#endif | |
+ { "fallbacktorsh", oDeprecated }, | |
+ { "usersh", oDeprecated }, | |
+ { "identityfile", oIdentityFile }, | |
+ { "identityfile2", oIdentityFile }, /* obsolete */ | |
+ { "identitiesonly", oIdentitiesOnly }, | |
+ { "hostname", oHostName }, | |
+ { "hostkeyalias", oHostKeyAlias }, | |
+ { "proxycommand", oProxyCommand }, | |
+ { "port", oPort }, | |
+ { "cipher", oCipher }, | |
+ { "ciphers", oCiphers }, | |
+ { "macs", oMacs }, | |
+ { "protocol", oProtocol }, | |
+ { "remoteforward", oRemoteForward }, | |
+ { "localforward", oLocalForward }, | |
+ { "user", oUser }, | |
+ { "host", oHost }, | |
+ { "match", oMatch }, | |
+ { "escapechar", oEscapeChar }, | |
+ { "globalknownhostsfile", oGlobalKnownHostsFile }, | |
+ { "globalknownhostsfile2", oDeprecated }, | |
+ { "userknownhostsfile", oUserKnownHostsFile }, | |
+ { "userknownhostsfile2", oDeprecated }, | |
+ { "connectionattempts", oConnectionAttempts }, | |
+ { "batchmode", oBatchMode }, | |
+ { "checkhostip", oCheckHostIP }, | |
+ { "stricthostkeychecking", oStrictHostKeyChecking }, | |
+ { "compression", oCompression }, | |
+ { "compressionlevel", oCompressionLevel }, | |
+ { "tcpkeepalive", oTCPKeepAlive }, | |
+ { "keepalive", oTCPKeepAlive }, /* obsolete */ | |
+ { "numberofpasswordprompts", oNumberOfPasswordPrompts }, | |
+ { "loglevel", oLogLevel }, | |
+ { "dynamicforward", oDynamicForward }, | |
+ { "preferredauthentications", oPreferredAuthentications }, | |
+ { "hostkeyalgorithms", oHostKeyAlgorithms }, | |
+ { "bindaddress", oBindAddress }, | |
+#ifdef ENABLE_PKCS11 | |
+ { "smartcarddevice", oPKCS11Provider }, | |
+ { "pkcs11provider", oPKCS11Provider }, | |
+#else | |
+ { "smartcarddevice", oUnsupported }, | |
+ { "pkcs11provider", oUnsupported }, | |
+#endif | |
+ { "clearallforwardings", oClearAllForwardings }, | |
+ { "enablesshkeysign", oEnableSSHKeysign }, | |
+ { "verifyhostkeydns", oVerifyHostKeyDNS }, | |
+ { "nohostauthenticationforlocalhost", oNoHostAuthenticationForLocalhost }, | |
+ { "rekeylimit", oRekeyLimit }, | |
+ { "connecttimeout", oConnectTimeout }, | |
+ { "addressfamily", oAddressFamily }, | |
+ { "serveraliveinterval", oServerAliveInterval }, | |
+ { "serveralivecountmax", oServerAliveCountMax }, | |
+ { "sendenv", oSendEnv }, | |
+ { "controlpath", oControlPath }, | |
+ { "controlmaster", oControlMaster }, | |
+ { "controlpersist", oControlPersist }, | |
+ { "hashknownhosts", oHashKnownHosts }, | |
+ { "tunnel", oTunnel }, | |
+ { "tunneldevice", oTunnelDevice }, | |
+ { "localcommand", oLocalCommand }, | |
+ { "permitlocalcommand", oPermitLocalCommand }, | |
+ { "visualhostkey", oVisualHostKey }, | |
+ { "useroaming", oUseRoaming }, | |
+ { "kexalgorithms", oKexAlgorithms }, | |
+ { "ipqos", oIPQoS }, | |
+ { "requesttty", oRequestTTY }, | |
+ { "proxyusefdpass", oProxyUseFdpass }, | |
+ { "canonicaldomains", oCanonicalDomains }, | |
+ { "canonicalizefallbacklocal", oCanonicalizeFallbackLocal }, | |
+ { "canonicalizehostname", oCanonicalizeHostname }, | |
+ { "canonicalizemaxdots", oCanonicalizeMaxDots }, | |
+ { "canonicalizepermittedcnames", oCanonicalizePermittedCNAMEs }, | |
+ { "streamlocalbindmask", oStreamLocalBindMask }, | |
+ { "streamlocalbindunlink", oStreamLocalBindUnlink }, | |
+ { "ignoreunknown", oIgnoreUnknown }, | |
+ | |
+ { NULL, oBadOption } | |
+}; | |
+ | |
+/* | |
+ * Adds a local TCP/IP port forward to options. Never returns if there is an | |
+ * error. | |
+ */ | |
+ | |
+void | |
+add_local_forward(Options *options, const struct Forward *newfwd) | |
+{ | |
+ struct Forward *fwd; | |
+#ifndef NO_IPPORT_RESERVED_CONCEPT | |
+ extern uid_t original_real_uid; | |
+ if (newfwd->listen_port < IPPORT_RESERVED && original_real_uid != 0 && | |
+ newfwd->listen_path == NULL) | |
+ fatal("Privileged ports can only be forwarded by root."); | |
+#endif | |
+ options->local_forwards = xrealloc(options->local_forwards, | |
+ options->num_local_forwards + 1, | |
+ sizeof(*options->local_forwards)); | |
+ fwd = &options->local_forwards[options->num_local_forwards++]; | |
+ | |
+ fwd->listen_host = newfwd->listen_host; | |
+ fwd->listen_port = newfwd->listen_port; | |
+ fwd->listen_path = newfwd->listen_path; | |
+ fwd->connect_host = newfwd->connect_host; | |
+ fwd->connect_port = newfwd->connect_port; | |
+ fwd->connect_path = newfwd->connect_path; | |
+} | |
+ | |
+/* | |
+ * Adds a remote TCP/IP port forward to options. Never returns if there is | |
+ * an error. | |
+ */ | |
+ | |
+void | |
+add_remote_forward(Options *options, const struct Forward *newfwd) | |
+{ | |
+ struct Forward *fwd; | |
+ | |
+ options->remote_forwards = xrealloc(options->remote_forwards, | |
+ options->num_remote_forwards + 1, | |
+ sizeof(*options->remote_forwards)); | |
+ fwd = &options->remote_forwards[options->num_remote_forwards++]; | |
+ | |
+ fwd->listen_host = newfwd->listen_host; | |
+ fwd->listen_port = newfwd->listen_port; | |
+ fwd->listen_path = newfwd->listen_path; | |
+ fwd->connect_host = newfwd->connect_host; | |
+ fwd->connect_port = newfwd->connect_port; | |
+ fwd->connect_path = newfwd->connect_path; | |
+ fwd->handle = newfwd->handle; | |
+ fwd->allocated_port = 0; | |
+} | |
+ | |
+static void | |
+clear_forwardings(Options *options) | |
+{ | |
+ int i; | |
+ | |
+ for (i = 0; i < options->num_local_forwards; i++) { | |
+ free(options->local_forwards[i].listen_host); | |
+ free(options->local_forwards[i].listen_path); | |
+ free(options->local_forwards[i].connect_host); | |
+ free(options->local_forwards[i].connect_path); | |
+ } | |
+ if (options->num_local_forwards > 0) { | |
+ free(options->local_forwards); | |
+ options->local_forwards = NULL; | |
+ } | |
+ options->num_local_forwards = 0; | |
+ for (i = 0; i < options->num_remote_forwards; i++) { | |
+ free(options->remote_forwards[i].listen_host); | |
+ free(options->remote_forwards[i].listen_path); | |
+ free(options->remote_forwards[i].connect_host); | |
+ free(options->remote_forwards[i].connect_path); | |
+ } | |
+ if (options->num_remote_forwards > 0) { | |
+ free(options->remote_forwards); | |
+ options->remote_forwards = NULL; | |
+ } | |
+ options->num_remote_forwards = 0; | |
+ options->tun_open = SSH_TUNMODE_NO; | |
+} | |
+ | |
+void | |
+add_identity_file(Options *options, const char *dir, const char *filename, | |
+ int userprovided) | |
+{ | |
+ char *path; | |
+ int i; | |
+ | |
+ if (options->num_identity_files >= SSH_MAX_IDENTITY_FILES) | |
+ fatal("Too many identity files specified (max %d)", | |
+ SSH_MAX_IDENTITY_FILES); | |
+ | |
+ if (dir == NULL) /* no dir, filename is absolute */ | |
+ path = xstrdup(filename); | |
+ else | |
+ (void)xasprintf(&path, "%.100s%.100s", dir, filename); | |
+ | |
+ /* Avoid registering duplicates */ | |
+ for (i = 0; i < options->num_identity_files; i++) { | |
+ if (options->identity_file_userprovided[i] == userprovided && | |
+ strcmp(options->identity_files[i], path) == 0) { | |
+ debug2("%s: ignoring duplicate key %s", __func__, path); | |
+ free(path); | |
+ return; | |
+ } | |
+ } | |
+ | |
+ options->identity_file_userprovided[options->num_identity_files] = | |
+ userprovided; | |
+ options->identity_files[options->num_identity_files++] = path; | |
+} | |
+ | |
+int | |
+default_ssh_port(void) | |
+{ | |
+ static int port; | |
+ struct servent *sp; | |
+ | |
+ if (port == 0) { | |
+ sp = getservbyname(SSH_SERVICE_NAME, "tcp"); | |
+ port = sp ? ntohs(sp->s_port) : SSH_DEFAULT_PORT; | |
+ } | |
+ return port; | |
+} | |
+ | |
+/* | |
+ * Execute a command in a shell. | |
+ * Return its exit status or -1 on abnormal exit. | |
+ */ | |
+static int | |
+execute_in_shell(const char *cmd) | |
+{ | |
+ char *shell, *command_string; | |
+ pid_t pid; | |
+ int devnull, status; | |
+ extern uid_t original_real_uid; | |
+ | |
+ if ((shell = getenv("SHELL")) == NULL) | |
+ shell = _PATH_BSHELL; | |
+ | |
+ /* | |
+ * Use "exec" to avoid "sh -c" processes on some platforms | |
+ * (e.g. Solaris) | |
+ */ | |
+ xasprintf(&command_string, "exec %s", cmd); | |
+ | |
+ /* Need this to redirect subprocess stdin/out */ | |
+ if ((devnull = open(_PATH_DEVNULL, O_RDWR)) == -1) | |
+ fatal("open(/dev/null): %s", strerror(errno)); | |
+ | |
+ debug("Executing command: '%.500s'", cmd); | |
+ | |
+ /* Fork and execute the command. */ | |
+ if ((pid = fork()) == 0) { | |
+ char *argv[4]; | |
+ | |
+ /* Child. Permanently give up superuser privileges. */ | |
+ permanently_drop_suid(original_real_uid); | |
+ | |
+ /* Redirect child stdin and stdout. Leave stderr */ | |
+ if (dup2(devnull, STDIN_FILENO) == -1) | |
+ fatal("dup2: %s", strerror(errno)); | |
+ if (dup2(devnull, STDOUT_FILENO) == -1) | |
+ fatal("dup2: %s", strerror(errno)); | |
+ if (devnull > STDERR_FILENO) | |
+ close(devnull); | |
+ closefrom(STDERR_FILENO + 1); | |
+ | |
+ argv[0] = shell; | |
+ argv[1] = "-c"; | |
+ argv[2] = command_string; | |
+ argv[3] = NULL; | |
+ | |
+ execv(argv[0], argv); | |
+ error("Unable to execute '%.100s': %s", cmd, strerror(errno)); | |
+ /* Die with signal to make this error apparent to parent. */ | |
+ signal(SIGTERM, SIG_DFL); | |
+ kill(getpid(), SIGTERM); | |
+ _exit(1); | |
+ } | |
+ /* Parent. */ | |
+ if (pid < 0) | |
+ fatal("%s: fork: %.100s", __func__, strerror(errno)); | |
+ | |
+ close(devnull); | |
+ free(command_string); | |
+ | |
+ while (waitpid(pid, &status, 0) == -1) { | |
+ if (errno != EINTR && errno != EAGAIN) | |
+ fatal("%s: waitpid: %s", __func__, strerror(errno)); | |
+ } | |
+ if (!WIFEXITED(status)) { | |
+ error("command '%.100s' exited abnormally", cmd); | |
+ return -1; | |
+ } | |
+ debug3("command returned status %d", WEXITSTATUS(status)); | |
+ return WEXITSTATUS(status); | |
+} | |
+ | |
+/* | |
+ * Parse and execute a Match directive. | |
+ */ | |
+static int | |
+match_cfg_line(Options *options, char **condition, struct passwd *pw, | |
+ const char *host_arg, const char *filename, int linenum) | |
+{ | |
+ char *arg, *attrib, *cmd, *cp = *condition, *host; | |
+ const char *ruser; | |
+ int r, port, result = 1, attributes = 0; | |
+ size_t len; | |
+ char thishost[NI_MAXHOST], shorthost[NI_MAXHOST], portstr[NI_MAXSERV]; | |
+ | |
+ /* | |
+ * Configuration is likely to be incomplete at this point so we | |
+ * must be prepared to use default values. | |
+ */ | |
+ port = options->port <= 0 ? default_ssh_port() : options->port; | |
+ ruser = options->user == NULL ? pw->pw_name : options->user; | |
+ if (options->hostname != NULL) { | |
+ /* NB. Please keep in sync with ssh.c:main() */ | |
+ host = percent_expand(options->hostname, | |
+ "h", host_arg, (char *)NULL); | |
+ } else | |
+ host = xstrdup(host_arg); | |
+ | |
+ debug3("checking match for '%s' host %s", cp, host); | |
+ while ((attrib = strdelim(&cp)) && *attrib != '\0') { | |
+ attributes++; | |
+ if (strcasecmp(attrib, "all") == 0) { | |
+ if (attributes != 1 || | |
+ ((arg = strdelim(&cp)) != NULL && *arg != '\0')) { | |
+ error("'all' cannot be combined with other " | |
+ "Match attributes"); | |
+ result = -1; | |
+ goto out; | |
+ } | |
+ *condition = cp; | |
+ result = 1; | |
+ goto out; | |
+ } | |
+ if ((arg = strdelim(&cp)) == NULL || *arg == '\0') { | |
+ error("Missing Match criteria for %s", attrib); | |
+ result = -1; | |
+ goto out; | |
+ } | |
+ len = strlen(arg); | |
+ if (strcasecmp(attrib, "host") == 0) { | |
+ if (match_hostname(host, arg, len) != 1) | |
+ result = 0; | |
+ else | |
+ debug("%.200s line %d: matched 'Host %.100s' ", | |
+ filename, linenum, host); | |
+ } else if (strcasecmp(attrib, "originalhost") == 0) { | |
+ if (match_hostname(host_arg, arg, len) != 1) | |
+ result = 0; | |
+ else | |
+ debug("%.200s line %d: matched " | |
+ "'OriginalHost %.100s' ", | |
+ filename, linenum, host_arg); | |
+ } else if (strcasecmp(attrib, "user") == 0) { | |
+ if (match_pattern_list(ruser, arg, len, 0) != 1) | |
+ result = 0; | |
+ else | |
+ debug("%.200s line %d: matched 'User %.100s' ", | |
+ filename, linenum, ruser); | |
+ } else if (strcasecmp(attrib, "localuser") == 0) { | |
+ if (match_pattern_list(pw->pw_name, arg, len, 0) != 1) | |
+ result = 0; | |
+ else | |
+ debug("%.200s line %d: matched " | |
+ "'LocalUser %.100s' ", | |
+ filename, linenum, pw->pw_name); | |
+ } else if (strcasecmp(attrib, "exec") == 0) { | |
+ if (gethostname(thishost, sizeof(thishost)) == -1) | |
+ fatal("gethostname: %s", strerror(errno)); | |
+ strlcpy(shorthost, thishost, sizeof(shorthost)); | |
+ shorthost[strcspn(thishost, ".")] = '\0'; | |
+ snprintf(portstr, sizeof(portstr), "%d", port); | |
+ | |
+ cmd = percent_expand(arg, | |
+ "L", shorthost, | |
+ "d", pw->pw_dir, | |
+ "h", host, | |
+ "l", thishost, | |
+ "n", host_arg, | |
+ "p", portstr, | |
+ "r", ruser, | |
+ "u", pw->pw_name, | |
+ (char *)NULL); | |
+ if (result != 1) { | |
+ /* skip execution if prior predicate failed */ | |
+ debug("%.200s line %d: skipped exec \"%.100s\"", | |
+ filename, linenum, cmd); | |
+ } else { | |
+ r = execute_in_shell(cmd); | |
+ if (r == -1) { | |
+ fatal("%.200s line %d: match exec " | |
+ "'%.100s' error", filename, | |
+ linenum, cmd); | |
+ } else if (r == 0) { | |
+ debug("%.200s line %d: matched " | |
+ "'exec \"%.100s\"'", filename, | |
+ linenum, cmd); | |
+ } else { | |
+ debug("%.200s line %d: no match " | |
+ "'exec \"%.100s\"'", filename, | |
+ linenum, cmd); | |
+ result = 0; | |
+ } | |
+ } | |
+ free(cmd); | |
+ } else { | |
+ error("Unsupported Match attribute %s", attrib); | |
+ result = -1; | |
+ goto out; | |
+ } | |
+ } | |
+ if (attributes == 0) { | |
+ error("One or more attributes required for Match"); | |
+ result = -1; | |
+ goto out; | |
+ } | |
+ debug3("match %sfound", result ? "" : "not "); | |
+ *condition = cp; | |
+ out: | |
+ free(host); | |
+ return result; | |
+} | |
+ | |
+/* Check and prepare a domain name: removes trailing '.' and lowercases */ | |
+static void | |
+valid_domain(char *name, const char *filename, int linenum) | |
+{ | |
+ size_t i, l = strlen(name); | |
+ u_char c, last = '\0'; | |
+ | |
+ if (l == 0) | |
+ fatal("%s line %d: empty hostname suffix", filename, linenum); | |
+ if (!isalpha((u_char)name[0]) && !isdigit((u_char)name[0])) | |
+ fatal("%s line %d: hostname suffix \"%.100s\" " | |
+ "starts with invalid character", filename, linenum, name); | |
+ for (i = 0; i < l; i++) { | |
+ c = tolower((u_char)name[i]); | |
+ name[i] = (char)c; | |
+ if (last == '.' && c == '.') | |
+ fatal("%s line %d: hostname suffix \"%.100s\" contains " | |
+ "consecutive separators", filename, linenum, name); | |
+ if (c != '.' && c != '-' && !isalnum(c) && | |
+ c != '_') /* technically invalid, but common */ | |
+ fatal("%s line %d: hostname suffix \"%.100s\" contains " | |
+ "invalid characters", filename, linenum, name); | |
+ last = c; | |
+ } | |
+ if (name[l - 1] == '.') | |
+ name[l - 1] = '\0'; | |
+} | |
+ | |
+/* | |
+ * Returns the number of the token pointed to by cp or oBadOption. | |
+ */ | |
+static OpCodes | |
+parse_token(const char *cp, const char *filename, int linenum, | |
+ const char *ignored_unknown) | |
+{ | |
+ int i; | |
+ | |
+ for (i = 0; keywords[i].name; i++) | |
+ if (strcmp(cp, keywords[i].name) == 0) | |
+ return keywords[i].opcode; | |
+ if (ignored_unknown != NULL && match_pattern_list(cp, ignored_unknown, | |
+ strlen(ignored_unknown), 1) == 1) | |
+ return oIgnoredUnknownOption; | |
+ error("%s: line %d: Bad configuration option: %s", | |
+ filename, linenum, cp); | |
+ return oBadOption; | |
+} | |
+ | |
+/* Multistate option parsing */ | |
+struct multistate { | |
+ char *key; | |
+ int value; | |
+}; | |
+static const struct multistate multistate_flag[] = { | |
+ { "true", 1 }, | |
+ { "false", 0 }, | |
+ { "yes", 1 }, | |
+ { "no", 0 }, | |
+ { NULL, -1 } | |
+}; | |
+static const struct multistate multistate_yesnoask[] = { | |
+ { "true", 1 }, | |
+ { "false", 0 }, | |
+ { "yes", 1 }, | |
+ { "no", 0 }, | |
+ { "ask", 2 }, | |
+ { NULL, -1 } | |
+}; | |
+static const struct multistate multistate_addressfamily[] = { | |
+ { "inet", AF_INET }, | |
+ { "inet6", AF_INET6 }, | |
+ { "any", AF_UNSPEC }, | |
+ { NULL, -1 } | |
+}; | |
+static const struct multistate multistate_controlmaster[] = { | |
+ { "true", SSHCTL_MASTER_YES }, | |
+ { "yes", SSHCTL_MASTER_YES }, | |
+ { "false", SSHCTL_MASTER_NO }, | |
+ { "no", SSHCTL_MASTER_NO }, | |
+ { "auto", SSHCTL_MASTER_AUTO }, | |
+ { "ask", SSHCTL_MASTER_ASK }, | |
+ { "autoask", SSHCTL_MASTER_AUTO_ASK }, | |
+ { NULL, -1 } | |
+}; | |
+static const struct multistate multistate_tunnel[] = { | |
+ { "ethernet", SSH_TUNMODE_ETHERNET }, | |
+ { "point-to-point", SSH_TUNMODE_POINTOPOINT }, | |
+ { "true", SSH_TUNMODE_DEFAULT }, | |
+ { "yes", SSH_TUNMODE_DEFAULT }, | |
+ { "false", SSH_TUNMODE_NO }, | |
+ { "no", SSH_TUNMODE_NO }, | |
+ { NULL, -1 } | |
+}; | |
+static const struct multistate multistate_requesttty[] = { | |
+ { "true", REQUEST_TTY_YES }, | |
+ { "yes", REQUEST_TTY_YES }, | |
+ { "false", REQUEST_TTY_NO }, | |
+ { "no", REQUEST_TTY_NO }, | |
+ { "force", REQUEST_TTY_FORCE }, | |
+ { "auto", REQUEST_TTY_AUTO }, | |
+ { NULL, -1 } | |
+}; | |
+static const struct multistate multistate_canonicalizehostname[] = { | |
+ { "true", SSH_CANONICALISE_YES }, | |
+ { "false", SSH_CANONICALISE_NO }, | |
+ { "yes", SSH_CANONICALISE_YES }, | |
+ { "no", SSH_CANONICALISE_NO }, | |
+ { "always", SSH_CANONICALISE_ALWAYS }, | |
+ { NULL, -1 } | |
+}; | |
+ | |
+/* | |
+ * Processes a single option line as used in the configuration files. This | |
+ * only sets those values that have not already been set. | |
+ */ | |
+#define WHITESPACE " \t\r\n" | |
+int | |
+process_config_line(Options *options, struct passwd *pw, const char *host, | |
+ char *line, const char *filename, int linenum, int *activep, int userconfig) | |
+{ | |
+ char *s, **charptr, *endofnumber, *keyword, *arg, *arg2; | |
+ char **cpptr, fwdarg[256]; | |
+ u_int i, *uintptr, max_entries = 0; | |
+ int negated, opcode, *intptr, value, value2, cmdline = 0; | |
+ LogLevel *log_level_ptr; | |
+ long long val64; | |
+ size_t len; | |
+ struct Forward fwd; | |
+ const struct multistate *multistate_ptr; | |
+ struct allowed_cname *cname; | |
+ | |
+ if (activep == NULL) { /* We are processing a command line directive */ | |
+ cmdline = 1; | |
+ activep = &cmdline; | |
+ } | |
+ | |
+ /* Strip trailing whitespace */ | |
+ for (len = strlen(line) - 1; len > 0; len--) { | |
+ if (strchr(WHITESPACE, line[len]) == NULL) | |
+ break; | |
+ line[len] = '\0'; | |
+ } | |
+ | |
+ s = line; | |
+ /* Get the keyword. (Each line is supposed to begin with a keyword). */ | |
+ if ((keyword = strdelim(&s)) == NULL) | |
+ return 0; | |
+ /* Ignore leading whitespace. */ | |
+ if (*keyword == '\0') | |
+ keyword = strdelim(&s); | |
+ if (keyword == NULL || !*keyword || *keyword == '\n' || *keyword == '#') | |
+ return 0; | |
+ /* Match lowercase keyword */ | |
+ lowercase(keyword); | |
+ | |
+ opcode = parse_token(keyword, filename, linenum, | |
+ options->ignored_unknown); | |
+ | |
+ switch (opcode) { | |
+ case oBadOption: | |
+ /* don't panic, but count bad options */ | |
+ return -1; | |
+ /* NOTREACHED */ | |
+ case oIgnoredUnknownOption: | |
+ debug("%s line %d: Ignored unknown option \"%s\"", | |
+ filename, linenum, keyword); | |
+ return 0; | |
+ case oConnectTimeout: | |
+ intptr = &options->connection_timeout; | |
+parse_time: | |
+ arg = strdelim(&s); | |
+ if (!arg || *arg == '\0') | |
+ fatal("%s line %d: missing time value.", | |
+ filename, linenum); | |
+ if ((value = convtime(arg)) == -1) | |
+ fatal("%s line %d: invalid time value.", | |
+ filename, linenum); | |
+ if (*activep && *intptr == -1) | |
+ *intptr = value; | |
+ break; | |
+ | |
+ case oForwardAgent: | |
+ intptr = &options->forward_agent; | |
+ parse_flag: | |
+ multistate_ptr = multistate_flag; | |
+ parse_multistate: | |
+ arg = strdelim(&s); | |
+ if (!arg || *arg == '\0') | |
+ fatal("%s line %d: missing argument.", | |
+ filename, linenum); | |
+ value = -1; | |
+ for (i = 0; multistate_ptr[i].key != NULL; i++) { | |
+ if (strcasecmp(arg, multistate_ptr[i].key) == 0) { | |
+ value = multistate_ptr[i].value; | |
+ break; | |
+ } | |
+ } | |
+ if (value == -1) | |
+ fatal("%s line %d: unsupported option \"%s\".", | |
+ filename, linenum, arg); | |
+ if (*activep && *intptr == -1) | |
+ *intptr = value; | |
+ break; | |
+ | |
+ case oForwardX11: | |
+ intptr = &options->forward_x11; | |
+ goto parse_flag; | |
+ | |
+ case oForwardX11Trusted: | |
+ intptr = &options->forward_x11_trusted; | |
+ goto parse_flag; | |
+ | |
+ case oForwardX11Timeout: | |
+ intptr = &options->forward_x11_timeout; | |
+ goto parse_time; | |
+ | |
+ case oGatewayPorts: | |
+ intptr = &options->fwd_opts.gateway_ports; | |
+ goto parse_flag; | |
+ | |
+ case oExitOnForwardFailure: | |
+ intptr = &options->exit_on_forward_failure; | |
+ goto parse_flag; | |
+ | |
+ case oUsePrivilegedPort: | |
+ intptr = &options->use_privileged_port; | |
+ goto parse_flag; | |
+ | |
+ case oPasswordAuthentication: | |
+ intptr = &options->password_authentication; | |
+ goto parse_flag; | |
+ | |
+ case oKbdInteractiveAuthentication: | |
+ intptr = &options->kbd_interactive_authentication; | |
+ goto parse_flag; | |
+ | |
+ case oKbdInteractiveDevices: | |
+ charptr = &options->kbd_interactive_devices; | |
+ goto parse_string; | |
+ | |
+ case oPubkeyAuthentication: | |
+ intptr = &options->pubkey_authentication; | |
+ goto parse_flag; | |
+ | |
+ case oRSAAuthentication: | |
+ intptr = &options->rsa_authentication; | |
+ goto parse_flag; | |
+ | |
+ case oRhostsRSAAuthentication: | |
+ intptr = &options->rhosts_rsa_authentication; | |
+ goto parse_flag; | |
+ | |
+ case oHostbasedAuthentication: | |
+ intptr = &options->hostbased_authentication; | |
+ goto parse_flag; | |
+ | |
+ case oChallengeResponseAuthentication: | |
+ intptr = &options->challenge_response_authentication; | |
+ goto parse_flag; | |
+ | |
+ case oGssAuthentication: | |
+ intptr = &options->gss_authentication; | |
+ goto parse_flag; | |
+ | |
+ case oGssDelegateCreds: | |
+ intptr = &options->gss_deleg_creds; | |
+ goto parse_flag; | |
+ | |
+ case oBatchMode: | |
+ intptr = &options->batch_mode; | |
+ goto parse_flag; | |
+ | |
+ case oCheckHostIP: | |
+ intptr = &options->check_host_ip; | |
+ goto parse_flag; | |
+ | |
+ case oVerifyHostKeyDNS: | |
+ intptr = &options->verify_host_key_dns; | |
+ multistate_ptr = multistate_yesnoask; | |
+ goto parse_multistate; | |
+ | |
+ case oStrictHostKeyChecking: | |
+ intptr = &options->strict_host_key_checking; | |
+ multistate_ptr = multistate_yesnoask; | |
+ goto parse_multistate; | |
+ | |
+ case oCompression: | |
+ intptr = &options->compression; | |
+ goto parse_flag; | |
+ | |
+ case oTCPKeepAlive: | |
+ intptr = &options->tcp_keep_alive; | |
+ goto parse_flag; | |
+ | |
+ case oNoHostAuthenticationForLocalhost: | |
+ intptr = &options->no_host_authentication_for_localhost; | |
+ goto parse_flag; | |
+ | |
+ case oNumberOfPasswordPrompts: | |
+ intptr = &options->number_of_password_prompts; | |
+ goto parse_int; | |
+ | |
+ case oCompressionLevel: | |
+ intptr = &options->compression_level; | |
+ goto parse_int; | |
+ | |
+ case oRekeyLimit: | |
+ arg = strdelim(&s); | |
+ if (!arg || *arg == '\0') | |
+ fatal("%.200s line %d: Missing argument.", filename, | |
+ linenum); | |
+ if (strcmp(arg, "default") == 0) { | |
+ val64 = 0; | |
+ } else { | |
+ if (scan_scaled(arg, &val64) == -1) | |
+ fatal("%.200s line %d: Bad number '%s': %s", | |
+ filename, linenum, arg, strerror(errno)); | |
+ /* check for too-large or too-small limits */ | |
+ if (val64 > UINT_MAX) | |
+ fatal("%.200s line %d: RekeyLimit too large", | |
+ filename, linenum); | |
+ if (val64 != 0 && val64 < 16) | |
+ fatal("%.200s line %d: RekeyLimit too small", | |
+ filename, linenum); | |
+ } | |
+ if (*activep && options->rekey_limit == -1) | |
+ options->rekey_limit = (u_int32_t)val64; | |
+ if (s != NULL) { /* optional rekey interval present */ | |
+ if (strcmp(s, "none") == 0) { | |
+ (void)strdelim(&s); /* discard */ | |
+ break; | |
+ } | |
+ intptr = &options->rekey_interval; | |
+ goto parse_time; | |
+ } | |
+ break; | |
+ | |
+ case oIdentityFile: | |
+ arg = strdelim(&s); | |
+ if (!arg || *arg == '\0') | |
+ fatal("%.200s line %d: Missing argument.", filename, linenum); | |
+ if (*activep) { | |
+ intptr = &options->num_identity_files; | |
+ if (*intptr >= SSH_MAX_IDENTITY_FILES) | |
+ fatal("%.200s line %d: Too many identity files specified (max %d).", | |
+ filename, linenum, SSH_MAX_IDENTITY_FILES); | |
+ add_identity_file(options, NULL, arg, userconfig); | |
+ } | |
+ break; | |
+ | |
+ case oXAuthLocation: | |
+ charptr=&options->xauth_location; | |
+ goto parse_string; | |
+ | |
+ case oUser: | |
+ charptr = &options->user; | |
+parse_string: | |
+ arg = strdelim(&s); | |
+ if (!arg || *arg == '\0') | |
+ fatal("%.200s line %d: Missing argument.", | |
+ filename, linenum); | |
+ if (*activep && *charptr == NULL) | |
+ *charptr = xstrdup(arg); | |
+ break; | |
+ | |
+ case oGlobalKnownHostsFile: | |
+ cpptr = (char **)&options->system_hostfiles; | |
+ uintptr = &options->num_system_hostfiles; | |
+ max_entries = SSH_MAX_HOSTS_FILES; | |
+parse_char_array: | |
+ if (*activep && *uintptr == 0) { | |
+ while ((arg = strdelim(&s)) != NULL && *arg != '\0') { | |
+ if ((*uintptr) >= max_entries) | |
+ fatal("%s line %d: " | |
+ "too many authorized keys files.", | |
+ filename, linenum); | |
+ cpptr[(*uintptr)++] = xstrdup(arg); | |
+ } | |
+ } | |
+ return 0; | |
+ | |
+ case oUserKnownHostsFile: | |
+ cpptr = (char **)&options->user_hostfiles; | |
+ uintptr = &options->num_user_hostfiles; | |
+ max_entries = SSH_MAX_HOSTS_FILES; | |
+ goto parse_char_array; | |
+ | |
+ case oHostName: | |
+ charptr = &options->hostname; | |
+ goto parse_string; | |
+ | |
+ case oHostKeyAlias: | |
+ charptr = &options->host_key_alias; | |
+ goto parse_string; | |
+ | |
+ case oPreferredAuthentications: | |
+ charptr = &options->preferred_authentications; | |
+ goto parse_string; | |
+ | |
+ case oBindAddress: | |
+ charptr = &options->bind_address; | |
+ goto parse_string; | |
+ | |
+ case oPKCS11Provider: | |
+ charptr = &options->pkcs11_provider; | |
+ goto parse_string; | |
+ | |
+ case oProxyCommand: | |
+ charptr = &options->proxy_command; | |
+parse_command: | |
+ if (s == NULL) | |
+ fatal("%.200s line %d: Missing argument.", filename, linenum); | |
+ len = strspn(s, WHITESPACE "="); | |
+ if (*activep && *charptr == NULL) | |
+ *charptr = xstrdup(s + len); | |
+ return 0; | |
+ | |
+ case oPort: | |
+ intptr = &options->port; | |
+parse_int: | |
+ arg = strdelim(&s); | |
+ if (!arg || *arg == '\0') | |
+ fatal("%.200s line %d: Missing argument.", filename, linenum); | |
+ if (arg[0] < '0' || arg[0] > '9') | |
+ fatal("%.200s line %d: Bad number.", filename, linenum); | |
+ | |
+ /* Octal, decimal, or hex format? */ | |
+ value = strtol(arg, &endofnumber, 0); | |
+ if (arg == endofnumber) | |
+ fatal("%.200s line %d: Bad number.", filename, linenum); | |
+ if (*activep && *intptr == -1) | |
+ *intptr = value; | |
+ break; | |
+ | |
+ case oConnectionAttempts: | |
+ intptr = &options->connection_attempts; | |
+ goto parse_int; | |
+ | |
+ case oCipher: | |
+ intptr = &options->cipher; | |
+ arg = strdelim(&s); | |
+ if (!arg || *arg == '\0') | |
+ fatal("%.200s line %d: Missing argument.", filename, linenum); | |
+ value = cipher_number(arg); | |
+ if (value == -1) | |
+ fatal("%.200s line %d: Bad cipher '%s'.", | |
+ filename, linenum, arg ? arg : "<NONE>"); | |
+ if (*activep && *intptr == -1) | |
+ *intptr = value; | |
+ break; | |
+ | |
+ case oCiphers: | |
+ arg = strdelim(&s); | |
+ if (!arg || *arg == '\0') | |
+ fatal("%.200s line %d: Missing argument.", filename, linenum); | |
+ if (!ciphers_valid(arg)) | |
+ fatal("%.200s line %d: Bad SSH2 cipher spec '%s'.", | |
+ filename, linenum, arg ? arg : "<NONE>"); | |
+ if (*activep && options->ciphers == NULL) | |
+ options->ciphers = xstrdup(arg); | |
+ break; | |
+ | |
+ case oMacs: | |
+ arg = strdelim(&s); | |
+ if (!arg || *arg == '\0') | |
+ fatal("%.200s line %d: Missing argument.", filename, linenum); | |
+ if (!mac_valid(arg)) | |
+ fatal("%.200s line %d: Bad SSH2 Mac spec '%s'.", | |
+ filename, linenum, arg ? arg : "<NONE>"); | |
+ if (*activep && options->macs == NULL) | |
+ options->macs = xstrdup(arg); | |
+ break; | |
+ | |
+ case oKexAlgorithms: | |
+ arg = strdelim(&s); | |
+ if (!arg || *arg == '\0') | |
+ fatal("%.200s line %d: Missing argument.", | |
+ filename, linenum); | |
+ if (!kex_names_valid(arg)) | |
+ fatal("%.200s line %d: Bad SSH2 KexAlgorithms '%s'.", | |
+ filename, linenum, arg ? arg : "<NONE>"); | |
+ if (*activep && options->kex_algorithms == NULL) | |
+ options->kex_algorithms = xstrdup(arg); | |
+ break; | |
+ | |
+ case oHostKeyAlgorithms: | |
+ arg = strdelim(&s); | |
+ if (!arg || *arg == '\0') | |
+ fatal("%.200s line %d: Missing argument.", filename, linenum); | |
+ if (!key_names_valid2(arg)) | |
+ fatal("%.200s line %d: Bad protocol 2 host key algorithms '%s'.", | |
+ filename, linenum, arg ? arg : "<NONE>"); | |
+ if (*activep && options->hostkeyalgorithms == NULL) | |
+ options->hostkeyalgorithms = xstrdup(arg); | |
+ break; | |
+ | |
+ case oProtocol: | |
+ intptr = &options->protocol; | |
+ arg = strdelim(&s); | |
+ if (!arg || *arg == '\0') | |
+ fatal("%.200s line %d: Missing argument.", filename, linenum); | |
+ value = proto_spec(arg); | |
+ if (value == SSH_PROTO_UNKNOWN) | |
+ fatal("%.200s line %d: Bad protocol spec '%s'.", | |
+ filename, linenum, arg ? arg : "<NONE>"); | |
+ if (*activep && *intptr == SSH_PROTO_UNKNOWN) | |
+ *intptr = value; | |
+ break; | |
+ | |
+ case oLogLevel: | |
+ log_level_ptr = &options->log_level; | |
+ arg = strdelim(&s); | |
+ value = log_level_number(arg); | |
+ if (value == SYSLOG_LEVEL_NOT_SET) | |
+ fatal("%.200s line %d: unsupported log level '%s'", | |
+ filename, linenum, arg ? arg : "<NONE>"); | |
+ if (*activep && *log_level_ptr == SYSLOG_LEVEL_NOT_SET) | |
+ *log_level_ptr = (LogLevel) value; | |
+ break; | |
+ | |
+ case oLocalForward: | |
+ case oRemoteForward: | |
+ case oDynamicForward: | |
+ arg = strdelim(&s); | |
+ if (arg == NULL || *arg == '\0') | |
+ fatal("%.200s line %d: Missing port argument.", | |
+ filename, linenum); | |
+ | |
+ if (opcode == oLocalForward || | |
+ opcode == oRemoteForward) { | |
+ arg2 = strdelim(&s); | |
+ if (arg2 == NULL || *arg2 == '\0') | |
+ fatal("%.200s line %d: Missing target argument.", | |
+ filename, linenum); | |
+ | |
+ /* construct a string for parse_forward */ | |
+ snprintf(fwdarg, sizeof(fwdarg), "%s:%s", arg, arg2); | |
+ } else if (opcode == oDynamicForward) { | |
+ strlcpy(fwdarg, arg, sizeof(fwdarg)); | |
+ } | |
+ | |
+ if (parse_forward(&fwd, fwdarg, | |
+ opcode == oDynamicForward ? 1 : 0, | |
+ opcode == oRemoteForward ? 1 : 0) == 0) | |
+ fatal("%.200s line %d: Bad forwarding specification.", | |
+ filename, linenum); | |
+ | |
+ if (*activep) { | |
+ if (opcode == oLocalForward || | |
+ opcode == oDynamicForward) | |
+ add_local_forward(options, &fwd); | |
+ else if (opcode == oRemoteForward) | |
+ add_remote_forward(options, &fwd); | |
+ } | |
+ break; | |
+ | |
+ case oClearAllForwardings: | |
+ intptr = &options->clear_forwardings; | |
+ goto parse_flag; | |
+ | |
+ case oHost: | |
+ if (cmdline) | |
+ fatal("Host directive not supported as a command-line " | |
+ "option"); | |
+ *activep = 0; | |
+ arg2 = NULL; | |
+ while ((arg = strdelim(&s)) != NULL && *arg != '\0') { | |
+ negated = *arg == '!'; | |
+ if (negated) | |
+ arg++; | |
+ if (match_pattern(host, arg)) { | |
+ if (negated) { | |
+ debug("%.200s line %d: Skipping Host " | |
+ "block because of negated match " | |
+ "for %.100s", filename, linenum, | |
+ arg); | |
+ *activep = 0; | |
+ break; | |
+ } | |
+ if (!*activep) | |
+ arg2 = arg; /* logged below */ | |
+ *activep = 1; | |
+ } | |
+ } | |
+ if (*activep) | |
+ debug("%.200s line %d: Applying options for %.100s", | |
+ filename, linenum, arg2); | |
+ /* Avoid garbage check below, as strdelim is done. */ | |
+ return 0; | |
+ | |
+ case oMatch: | |
+ if (cmdline) | |
+ fatal("Host directive not supported as a command-line " | |
+ "option"); | |
+ value = match_cfg_line(options, &s, pw, host, | |
+ filename, linenum); | |
+ if (value < 0) | |
+ fatal("%.200s line %d: Bad Match condition", filename, | |
+ linenum); | |
+ *activep = value; | |
+ break; | |
+ | |
+ case oEscapeChar: | |
+ intptr = &options->escape_char; | |
+ arg = strdelim(&s); | |
+ if (!arg || *arg == '\0') | |
+ fatal("%.200s line %d: Missing argument.", filename, linenum); | |
+ if (arg[0] == '^' && arg[2] == 0 && | |
+ (u_char) arg[1] >= 64 && (u_char) arg[1] < 128) | |
+ value = (u_char) arg[1] & 31; | |
+ else if (strlen(arg) == 1) | |
+ value = (u_char) arg[0]; | |
+ else if (strcmp(arg, "none") == 0) | |
+ value = SSH_ESCAPECHAR_NONE; | |
+ else { | |
+ fatal("%.200s line %d: Bad escape character.", | |
+ filename, linenum); | |
+ /* NOTREACHED */ | |
+ value = 0; /* Avoid compiler warning. */ | |
+ } | |
+ if (*activep && *intptr == -1) | |
+ *intptr = value; | |
+ break; | |
+ | |
+ case oAddressFamily: | |
+ intptr = &options->address_family; | |
+ multistate_ptr = multistate_addressfamily; | |
+ goto parse_multistate; | |
+ | |
+ case oEnableSSHKeysign: | |
+ intptr = &options->enable_ssh_keysign; | |
+ goto parse_flag; | |
+ | |
+ case oIdentitiesOnly: | |
+ intptr = &options->identities_only; | |
+ goto parse_flag; | |
+ | |
+ case oServerAliveInterval: | |
+ intptr = &options->server_alive_interval; | |
+ goto parse_time; | |
+ | |
+ case oServerAliveCountMax: | |
+ intptr = &options->server_alive_count_max; | |
+ goto parse_int; | |
+ | |
+ case oSendEnv: | |
+ while ((arg = strdelim(&s)) != NULL && *arg != '\0') { | |
+ if (strchr(arg, '=') != NULL) | |
+ fatal("%s line %d: Invalid environment name.", | |
+ filename, linenum); | |
+ if (!*activep) | |
+ continue; | |
+ if (options->num_send_env >= MAX_SEND_ENV) | |
+ fatal("%s line %d: too many send env.", | |
+ filename, linenum); | |
+ options->send_env[options->num_send_env++] = | |
+ xstrdup(arg); | |
+ } | |
+ break; | |
+ | |
+ case oControlPath: | |
+ charptr = &options->control_path; | |
+ goto parse_string; | |
+ | |
+ case oControlMaster: | |
+ intptr = &options->control_master; | |
+ multistate_ptr = multistate_controlmaster; | |
+ goto parse_multistate; | |
+ | |
+ case oControlPersist: | |
+ /* no/false/yes/true, or a time spec */ | |
+ intptr = &options->control_persist; | |
+ arg = strdelim(&s); | |
+ if (!arg || *arg == '\0') | |
+ fatal("%.200s line %d: Missing ControlPersist" | |
+ " argument.", filename, linenum); | |
+ value = 0; | |
+ value2 = 0; /* timeout */ | |
+ if (strcmp(arg, "no") == 0 || strcmp(arg, "false") == 0) | |
+ value = 0; | |
+ else if (strcmp(arg, "yes") == 0 || strcmp(arg, "true") == 0) | |
+ value = 1; | |
+ else if ((value2 = convtime(arg)) >= 0) | |
+ value = 1; | |
+ else | |
+ fatal("%.200s line %d: Bad ControlPersist argument.", | |
+ filename, linenum); | |
+ if (*activep && *intptr == -1) { | |
+ *intptr = value; | |
+ options->control_persist_timeout = value2; | |
+ } | |
+ break; | |
+ | |
+ case oHashKnownHosts: | |
+ intptr = &options->hash_known_hosts; | |
+ goto parse_flag; | |
+ | |
+ case oTunnel: | |
+ intptr = &options->tun_open; | |
+ multistate_ptr = multistate_tunnel; | |
+ goto parse_multistate; | |
+ | |
+ case oTunnelDevice: | |
+ arg = strdelim(&s); | |
+ if (!arg || *arg == '\0') | |
+ fatal("%.200s line %d: Missing argument.", filename, linenum); | |
+ value = a2tun(arg, &value2); | |
+ if (value == SSH_TUNID_ERR) | |
+ fatal("%.200s line %d: Bad tun device.", filename, linenum); | |
+ if (*activep) { | |
+ options->tun_local = value; | |
+ options->tun_remote = value2; | |
+ } | |
+ break; | |
+ | |
+ case oLocalCommand: | |
+ charptr = &options->local_command; | |
+ goto parse_command; | |
+ | |
+ case oPermitLocalCommand: | |
+ intptr = &options->permit_local_command; | |
+ goto parse_flag; | |
+ | |
+ case oVisualHostKey: | |
+ intptr = &options->visual_host_key; | |
+ goto parse_flag; | |
+ | |
+ case oIPQoS: | |
+ arg = strdelim(&s); | |
+ if ((value = parse_ipqos(arg)) == -1) | |
+ fatal("%s line %d: Bad IPQoS value: %s", | |
+ filename, linenum, arg); | |
+ arg = strdelim(&s); | |
+ if (arg == NULL) | |
+ value2 = value; | |
+ else if ((value2 = parse_ipqos(arg)) == -1) | |
+ fatal("%s line %d: Bad IPQoS value: %s", | |
+ filename, linenum, arg); | |
+ if (*activep) { | |
+ options->ip_qos_interactive = value; | |
+ options->ip_qos_bulk = value2; | |
+ } | |
+ break; | |
+ | |
+ case oUseRoaming: | |
+ intptr = &options->use_roaming; | |
+ goto parse_flag; | |
+ | |
+ case oRequestTTY: | |
+ intptr = &options->request_tty; | |
+ multistate_ptr = multistate_requesttty; | |
+ goto parse_multistate; | |
+ | |
+ case oIgnoreUnknown: | |
+ charptr = &options->ignored_unknown; | |
+ goto parse_string; | |
+ | |
+ case oProxyUseFdpass: | |
+ intptr = &options->proxy_use_fdpass; | |
+ goto parse_flag; | |
+ | |
+ case oCanonicalDomains: | |
+ value = options->num_canonical_domains != 0; | |
+ while ((arg = strdelim(&s)) != NULL && *arg != '\0') { | |
+ valid_domain(arg, filename, linenum); | |
+ if (!*activep || value) | |
+ continue; | |
+ if (options->num_canonical_domains >= MAX_CANON_DOMAINS) | |
+ fatal("%s line %d: too many hostname suffixes.", | |
+ filename, linenum); | |
+ options->canonical_domains[ | |
+ options->num_canonical_domains++] = xstrdup(arg); | |
+ } | |
+ break; | |
+ | |
+ case oCanonicalizePermittedCNAMEs: | |
+ value = options->num_permitted_cnames != 0; | |
+ while ((arg = strdelim(&s)) != NULL && *arg != '\0') { | |
+ /* Either '*' for everything or 'list:list' */ | |
+ if (strcmp(arg, "*") == 0) | |
+ arg2 = arg; | |
+ else { | |
+ lowercase(arg); | |
+ if ((arg2 = strchr(arg, ':')) == NULL || | |
+ arg2[1] == '\0') { | |
+ fatal("%s line %d: " | |
+ "Invalid permitted CNAME \"%s\"", | |
+ filename, linenum, arg); | |
+ } | |
+ *arg2 = '\0'; | |
+ arg2++; | |
+ } | |
+ if (!*activep || value) | |
+ continue; | |
+ if (options->num_permitted_cnames >= MAX_CANON_DOMAINS) | |
+ fatal("%s line %d: too many permitted CNAMEs.", | |
+ filename, linenum); | |
+ cname = options->permitted_cnames + | |
+ options->num_permitted_cnames++; | |
+ cname->source_list = xstrdup(arg); | |
+ cname->target_list = xstrdup(arg2); | |
+ } | |
+ break; | |
+ | |
+ case oCanonicalizeHostname: | |
+ intptr = &options->canonicalize_hostname; | |
+ multistate_ptr = multistate_canonicalizehostname; | |
+ goto parse_multistate; | |
+ | |
+ case oCanonicalizeMaxDots: | |
+ intptr = &options->canonicalize_max_dots; | |
+ goto parse_int; | |
+ | |
+ case oCanonicalizeFallbackLocal: | |
+ intptr = &options->canonicalize_fallback_local; | |
+ goto parse_flag; | |
+ | |
+ case oStreamLocalBindMask: | |
+ arg = strdelim(&s); | |
+ if (!arg || *arg == '\0') | |
+ fatal("%.200s line %d: Missing StreamLocalBindMask argument.", filename, linenum); | |
+ /* Parse mode in octal format */ | |
+ value = strtol(arg, &endofnumber, 8); | |
+ if (arg == endofnumber || value < 0 || value > 0777) | |
+ fatal("%.200s line %d: Bad mask.", filename, linenum); | |
+ options->fwd_opts.streamlocal_bind_mask = (mode_t)value; | |
+ break; | |
+ | |
+ case oStreamLocalBindUnlink: | |
+ intptr = &options->fwd_opts.streamlocal_bind_unlink; | |
+ goto parse_flag; | |
+ | |
+ case oDeprecated: | |
+ debug("%s line %d: Deprecated option \"%s\"", | |
+ filename, linenum, keyword); | |
+ return 0; | |
+ | |
+ case oUnsupported: | |
+ error("%s line %d: Unsupported option \"%s\"", | |
+ filename, linenum, keyword); | |
+ return 0; | |
+ | |
+ default: | |
+ fatal("process_config_line: Unimplemented opcode %d", opcode); | |
+ } | |
+ | |
+ /* Check that there is no garbage at end of line. */ | |
+ if ((arg = strdelim(&s)) != NULL && *arg != '\0') { | |
+ fatal("%.200s line %d: garbage at end of line; \"%.200s\".", | |
+ filename, linenum, arg); | |
+ } | |
+ return 0; | |
+} | |
+ | |
+ | |
+/* | |
+ * Reads the config file and modifies the options accordingly. Options | |
+ * should already be initialized before this call. This never returns if | |
+ * there is an error. If the file does not exist, this returns 0. | |
+ */ | |
+ | |
+int | |
+read_config_file(const char *filename, struct passwd *pw, const char *host, | |
+ Options *options, int flags) | |
+{ | |
+ FILE *f; | |
+ char line[1024]; | |
+ int active, linenum; | |
+ int bad_options = 0; | |
+ | |
+ if ((f = fopen(filename, "r")) == NULL) | |
+ return 0; | |
+ | |
+ if (flags & SSHCONF_CHECKPERM) { | |
+ struct stat sb; | |
+ | |
+ 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)) | |
+ fatal("Bad owner or permissions on %s", filename); | |
+ } | |
+ | |
+ debug("Reading configuration data %.200s", filename); | |
+ | |
+ /* | |
+ * Mark that we are now processing the options. This flag is turned | |
+ * on/off by Host specifications. | |
+ */ | |
+ active = 1; | |
+ linenum = 0; | |
+ while (fgets(line, sizeof(line), f)) { | |
+ /* Update line number counter. */ | |
+ linenum++; | |
+ if (process_config_line(options, pw, host, line, filename, | |
+ linenum, &active, flags & SSHCONF_USERCONF) != 0) | |
+ bad_options++; | |
+ } | |
+ fclose(f); | |
+ if (bad_options > 0) | |
+ fatal("%s: terminating, %d bad configuration options", | |
+ filename, bad_options); | |
+ return 1; | |
+} | |
+ | |
+/* Returns 1 if a string option is unset or set to "none" or 0 otherwise. */ | |
+int | |
+option_clear_or_none(const char *o) | |
+{ | |
+ return o == NULL || strcasecmp(o, "none") == 0; | |
+} | |
+ | |
+/* | |
+ * Initializes options to special values that indicate that they have not yet | |
+ * been set. Read_config_file will only set options with this value. Options | |
+ * are processed in the following order: command line, user config file, | |
+ * system config file. Last, fill_default_options is called. | |
+ */ | |
+ | |
+void | |
+initialize_options(Options * options) | |
+{ | |
+ memset(options, 'X', sizeof(*options)); | |
+ options->forward_agent = -1; | |
+ options->forward_x11 = -1; | |
+ options->forward_x11_trusted = -1; | |
+ options->forward_x11_timeout = -1; | |
+ options->exit_on_forward_failure = -1; | |
+ options->xauth_location = NULL; | |
+ options->fwd_opts.gateway_ports = -1; | |
+ options->fwd_opts.streamlocal_bind_mask = (mode_t)-1; | |
+ options->fwd_opts.streamlocal_bind_unlink = -1; | |
+ options->use_privileged_port = -1; | |
+ options->rsa_authentication = -1; | |
+ options->pubkey_authentication = -1; | |
+ options->challenge_response_authentication = -1; | |
+ options->gss_authentication = -1; | |
+ options->gss_deleg_creds = -1; | |
+ options->password_authentication = -1; | |
+ options->kbd_interactive_authentication = -1; | |
+ options->kbd_interactive_devices = NULL; | |
+ options->rhosts_rsa_authentication = -1; | |
+ options->hostbased_authentication = -1; | |
+ options->batch_mode = -1; | |
+ options->check_host_ip = -1; | |
+ options->strict_host_key_checking = -1; | |
+ options->compression = -1; | |
+ options->tcp_keep_alive = -1; | |
+ options->compression_level = -1; | |
+ options->port = -1; | |
+ options->address_family = -1; | |
+ options->connection_attempts = -1; | |
+ options->connection_timeout = -1; | |
+ options->number_of_password_prompts = -1; | |
+ options->cipher = -1; | |
+ options->ciphers = NULL; | |
+ options->macs = NULL; | |
+ options->kex_algorithms = NULL; | |
+ options->hostkeyalgorithms = NULL; | |
+ options->protocol = SSH_PROTO_UNKNOWN; | |
+ options->num_identity_files = 0; | |
+ options->hostname = NULL; | |
+ options->host_key_alias = NULL; | |
+ options->proxy_command = NULL; | |
+ options->user = NULL; | |
+ options->escape_char = -1; | |
+ options->num_system_hostfiles = 0; | |
+ options->num_user_hostfiles = 0; | |
+ options->local_forwards = NULL; | |
+ options->num_local_forwards = 0; | |
+ options->remote_forwards = NULL; | |
+ options->num_remote_forwards = 0; | |
+ options->clear_forwardings = -1; | |
+ options->log_level = SYSLOG_LEVEL_NOT_SET; | |
+ options->preferred_authentications = NULL; | |
+ options->bind_address = NULL; | |
+ options->pkcs11_provider = NULL; | |
+ options->enable_ssh_keysign = - 1; | |
+ options->no_host_authentication_for_localhost = - 1; | |
+ options->identities_only = - 1; | |
+ options->rekey_limit = - 1; | |
+ options->rekey_interval = -1; | |
+ options->verify_host_key_dns = -1; | |
+ options->server_alive_interval = -1; | |
+ options->server_alive_count_max = -1; | |
+ options->num_send_env = 0; | |
+ options->control_path = NULL; | |
+ options->control_master = -1; | |
+ options->control_persist = -1; | |
+ options->control_persist_timeout = 0; | |
+ options->hash_known_hosts = -1; | |
+ options->tun_open = -1; | |
+ options->tun_local = -1; | |
+ options->tun_remote = -1; | |
+ options->local_command = NULL; | |
+ options->permit_local_command = -1; | |
+ options->use_roaming = -1; | |
+ options->visual_host_key = -1; | |
+ options->ip_qos_interactive = -1; | |
+ options->ip_qos_bulk = -1; | |
+ options->request_tty = -1; | |
+ options->proxy_use_fdpass = -1; | |
+ options->ignored_unknown = NULL; | |
+ options->num_canonical_domains = 0; | |
+ options->num_permitted_cnames = 0; | |
+ options->canonicalize_max_dots = -1; | |
+ options->canonicalize_fallback_local = -1; | |
+ options->canonicalize_hostname = -1; | |
+} | |
+ | |
+/* | |
+ * A petite version of fill_default_options() that just fills the options | |
+ * needed for hostname canonicalization to proceed. | |
+ */ | |
+void | |
+fill_default_options_for_canonicalization(Options *options) | |
+{ | |
+ if (options->canonicalize_max_dots == -1) | |
+ options->canonicalize_max_dots = 1; | |
+ if (options->canonicalize_fallback_local == -1) | |
+ options->canonicalize_fallback_local = 1; | |
+ if (options->canonicalize_hostname == -1) | |
+ options->canonicalize_hostname = SSH_CANONICALISE_NO; | |
+} | |
+ | |
+/* | |
+ * Called after processing other sources of option data, this fills those | |
+ * options for which no value has been specified with their default values. | |
+ */ | |
+void | |
+fill_default_options(Options * options) | |
+{ | |
+ if (options->forward_agent == -1) | |
+ options->forward_agent = 0; | |
+ if (options->forward_x11 == -1) | |
+ options->forward_x11 = 0; | |
+ if (options->forward_x11_trusted == -1) | |
+ options->forward_x11_trusted = 0; | |
+ if (options->forward_x11_timeout == -1) | |
+ options->forward_x11_timeout = 1200; | |
+ if (options->exit_on_forward_failure == -1) | |
+ options->exit_on_forward_failure = 0; | |
+ if (options->xauth_location == NULL) | |
+ options->xauth_location = _PATH_XAUTH; | |
+ if (options->fwd_opts.gateway_ports == -1) | |
+ options->fwd_opts.gateway_ports = 0; | |
+ if (options->fwd_opts.streamlocal_bind_mask == (mode_t)-1) | |
+ options->fwd_opts.streamlocal_bind_mask = 0177; | |
+ if (options->fwd_opts.streamlocal_bind_unlink == -1) | |
+ options->fwd_opts.streamlocal_bind_unlink = 0; | |
+ if (options->use_privileged_port == -1) | |
+ options->use_privileged_port = 0; | |
+ if (options->rsa_authentication == -1) | |
+ options->rsa_authentication = 1; | |
+ if (options->pubkey_authentication == -1) | |
+ options->pubkey_authentication = 1; | |
+ if (options->challenge_response_authentication == -1) | |
+ options->challenge_response_authentication = 1; | |
+ if (options->gss_authentication == -1) | |
+ options->gss_authentication = 0; | |
+ if (options->gss_deleg_creds == -1) | |
+ options->gss_deleg_creds = 0; | |
+ if (options->password_authentication == -1) | |
+ options->password_authentication = 1; | |
+ if (options->kbd_interactive_authentication == -1) | |
+ options->kbd_interactive_authentication = 1; | |
+ if (options->rhosts_rsa_authentication == -1) | |
+ options->rhosts_rsa_authentication = 0; | |
+ if (options->hostbased_authentication == -1) | |
+ options->hostbased_authentication = 0; | |
+ if (options->batch_mode == -1) | |
+ options->batch_mode = 0; | |
+ if (options->check_host_ip == -1) | |
+ options->check_host_ip = 1; | |
+ if (options->strict_host_key_checking == -1) | |
+ options->strict_host_key_checking = 2; /* 2 is default */ | |
+ if (options->compression == -1) | |
+ options->compression = 0; | |
+ if (options->tcp_keep_alive == -1) | |
+ options->tcp_keep_alive = 1; | |
+ if (options->compression_level == -1) | |
+ options->compression_level = 6; | |
+ if (options->port == -1) | |
+ options->port = 0; /* Filled in ssh_connect. */ | |
+ if (options->address_family == -1) | |
+ options->address_family = AF_UNSPEC; | |
+ if (options->connection_attempts == -1) | |
+ options->connection_attempts = 1; | |
+ if (options->number_of_password_prompts == -1) | |
+ options->number_of_password_prompts = 3; | |
+ /* Selected in ssh_login(). */ | |
+ if (options->cipher == -1) | |
+ options->cipher = SSH_CIPHER_NOT_SET; | |
+ /* options->ciphers, default set in myproposals.h */ | |
+ /* options->macs, default set in myproposals.h */ | |
+ /* options->kex_algorithms, default set in myproposals.h */ | |
+ /* options->hostkeyalgorithms, default set in myproposals.h */ | |
+ if (options->protocol == SSH_PROTO_UNKNOWN) | |
+ options->protocol = SSH_PROTO_2; | |
+ if (options->num_identity_files == 0) { | |
+ if (options->protocol & SSH_PROTO_1) { | |
+ add_identity_file(options, "~/", | |
+ _PATH_SSH_CLIENT_IDENTITY, 0); | |
+ } | |
+ if (options->protocol & SSH_PROTO_2) { | |
+ add_identity_file(options, "~/", | |
+ _PATH_SSH_CLIENT_ID_RSA, 0); | |
+ add_identity_file(options, "~/", | |
+ _PATH_SSH_CLIENT_ID_DSA, 0); | |
+#ifdef OPENSSL_HAS_ECC | |
+ add_identity_file(options, "~/", | |
+ _PATH_SSH_CLIENT_ID_ECDSA, 0); | |
+#endif | |
+ add_identity_file(options, "~/", | |
+ _PATH_SSH_CLIENT_ID_ED25519, 0); | |
+ } | |
+ } | |
+ if (options->escape_char == -1) | |
+ options->escape_char = '~'; | |
+ if (options->num_system_hostfiles == 0) { | |
+ options->system_hostfiles[options->num_system_hostfiles++] = | |
+ xstrdup(_PATH_SSH_SYSTEM_HOSTFILE); | |
+ options->system_hostfiles[options->num_system_hostfiles++] = | |
+ xstrdup(_PATH_SSH_SYSTEM_HOSTFILE2); | |
+ } | |
+ if (options->num_user_hostfiles == 0) { | |
+ options->user_hostfiles[options->num_user_hostfiles++] = | |
+ xstrdup(_PATH_SSH_USER_HOSTFILE); | |
+ options->user_hostfiles[options->num_user_hostfiles++] = | |
+ xstrdup(_PATH_SSH_USER_HOSTFILE2); | |
+ } | |
+ if (options->log_level == SYSLOG_LEVEL_NOT_SET) | |
+ options->log_level = SYSLOG_LEVEL_INFO; | |
+ if (options->clear_forwardings == 1) | |
+ clear_forwardings(options); | |
+ if (options->no_host_authentication_for_localhost == - 1) | |
+ options->no_host_authentication_for_localhost = 0; | |
+ if (options->identities_only == -1) | |
+ options->identities_only = 0; | |
+ if (options->enable_ssh_keysign == -1) | |
+ options->enable_ssh_keysign = 0; | |
+ if (options->rekey_limit == -1) | |
+ options->rekey_limit = 0; | |
+ if (options->rekey_interval == -1) | |
+ options->rekey_interval = 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_count_max == -1) | |
+ options->server_alive_count_max = 3; | |
+ if (options->control_master == -1) | |
+ options->control_master = 0; | |
+ if (options->control_persist == -1) { | |
+ options->control_persist = 0; | |
+ options->control_persist_timeout = 0; | |
+ } | |
+ if (options->hash_known_hosts == -1) | |
+ options->hash_known_hosts = 0; | |
+ if (options->tun_open == -1) | |
+ options->tun_open = SSH_TUNMODE_NO; | |
+ if (options->tun_local == -1) | |
+ options->tun_local = SSH_TUNID_ANY; | |
+ if (options->tun_remote == -1) | |
+ options->tun_remote = SSH_TUNID_ANY; | |
+ if (options->permit_local_command == -1) | |
+ options->permit_local_command = 0; | |
+ if (options->use_roaming == -1) | |
+ options->use_roaming = 1; | |
+ if (options->visual_host_key == -1) | |
+ options->visual_host_key = 0; | |
+ if (options->ip_qos_interactive == -1) | |
+ options->ip_qos_interactive = IPTOS_LOWDELAY; | |
+ if (options->ip_qos_bulk == -1) | |
+ options->ip_qos_bulk = IPTOS_THROUGHPUT; | |
+ if (options->request_tty == -1) | |
+ options->request_tty = REQUEST_TTY_AUTO; | |
+ if (options->proxy_use_fdpass == -1) | |
+ options->proxy_use_fdpass = 0; | |
+ if (options->canonicalize_max_dots == -1) | |
+ options->canonicalize_max_dots = 1; | |
+ if (options->canonicalize_fallback_local == -1) | |
+ options->canonicalize_fallback_local = 1; | |
+ if (options->canonicalize_hostname == -1) | |
+ options->canonicalize_hostname = SSH_CANONICALISE_NO; | |
+#define CLEAR_ON_NONE(v) \ | |
+ do { \ | |
+ if (option_clear_or_none(v)) { \ | |
+ free(v); \ | |
+ v = NULL; \ | |
+ } \ | |
+ } while(0) | |
+ CLEAR_ON_NONE(options->local_command); | |
+ CLEAR_ON_NONE(options->proxy_command); | |
+ CLEAR_ON_NONE(options->control_path); | |
+ /* options->user will be set in the main program if appropriate */ | |
+ /* options->hostname will be set in the main program if appropriate */ | |
+ /* options->host_key_alias should not be set by default */ | |
+ /* options->preferred_authentications will be set in ssh */ | |
+} | |
+ | |
+struct fwdarg { | |
+ char *arg; | |
+ int ispath; | |
+}; | |
+ | |
+/* | |
+ * parse_fwd_field | |
+ * parses the next field in a port forwarding specification. | |
+ * sets fwd to the parsed field and advances p past the colon | |
+ * or sets it to NULL at end of string. | |
+ * returns 0 on success, else non-zero. | |
+ */ | |
+static int | |
+parse_fwd_field(char **p, struct fwdarg *fwd) | |
+{ | |
+ char *ep, *cp = *p; | |
+ int ispath = 0; | |
+ | |
+ if (*cp == '\0') { | |
+ *p = NULL; | |
+ return -1; /* end of string */ | |
+ } | |
+ | |
+ /* | |
+ * A field escaped with square brackets is used literally. | |
+ * XXX - allow ']' to be escaped via backslash? | |
+ */ | |
+ if (*cp == '[') { | |
+ /* find matching ']' */ | |
+ for (ep = cp + 1; *ep != ']' && *ep != '\0'; ep++) { | |
+ if (*ep == '/') | |
+ ispath = 1; | |
+ } | |
+ /* no matching ']' or not at end of field. */ | |
+ if (ep[0] != ']' || (ep[1] != ':' && ep[1] != '\0')) | |
+ return -1; | |
+ /* NUL terminate the field and advance p past the colon */ | |
+ *ep++ = '\0'; | |
+ if (*ep != '\0') | |
+ *ep++ = '\0'; | |
+ fwd->arg = cp + 1; | |
+ fwd->ispath = ispath; | |
+ *p = ep; | |
+ return 0; | |
+ } | |
+ | |
+ for (cp = *p; *cp != '\0'; cp++) { | |
+ switch (*cp) { | |
+ case '\\': | |
+ memmove(cp, cp + 1, strlen(cp + 1) + 1); | |
+ cp++; | |
+ break; | |
+ case '/': | |
+ ispath = 1; | |
+ break; | |
+ case ':': | |
+ *cp++ = '\0'; | |
+ goto done; | |
+ } | |
+ } | |
+done: | |
+ fwd->arg = *p; | |
+ fwd->ispath = ispath; | |
+ *p = cp; | |
+ return 0; | |
+} | |
+ | |
+/* | |
+ * parse_forward | |
+ * parses a string containing a port forwarding specification of the form: | |
+ * dynamicfwd == 0 | |
+ * [listenhost:]listenport|listenpath:connecthost:connectport|connectpath | |
+ * listenpath:connectpath | |
+ * dynamicfwd == 1 | |
+ * [listenhost:]listenport | |
+ * returns number of arguments parsed or zero on error | |
+ */ | |
+int | |
+parse_forward(struct Forward *fwd, const char *fwdspec, int dynamicfwd, int remotefwd) | |
+{ | |
+ struct fwdarg fwdargs[4]; | |
+ char *p, *cp; | |
+ int i; | |
+ | |
+ memset(fwd, 0, sizeof(*fwd)); | |
+ memset(fwdargs, 0, sizeof(fwdargs)); | |
+ | |
+ cp = p = xstrdup(fwdspec); | |
+ | |
+ /* skip leading spaces */ | |
+ while (isspace((u_char)*cp)) | |
+ cp++; | |
+ | |
+ for (i = 0; i < 4; ++i) { | |
+ if (parse_fwd_field(&cp, &fwdargs[i]) != 0) | |
+ break; | |
+ } | |
+ | |
+ /* Check for trailing garbage */ | |
+ if (cp != NULL && *cp != '\0') { | |
+ i = 0; /* failure */ | |
+ } | |
+ | |
+ switch (i) { | |
+ case 1: | |
+ if (fwdargs[0].ispath) { | |
+ fwd->listen_path = xstrdup(fwdargs[0].arg); | |
+ fwd->listen_port = PORT_STREAMLOCAL; | |
+ } else { | |
+ fwd->listen_host = NULL; | |
+ fwd->listen_port = a2port(fwdargs[0].arg); | |
+ } | |
+ fwd->connect_host = xstrdup("socks"); | |
+ break; | |
+ | |
+ case 2: | |
+ if (fwdargs[0].ispath && fwdargs[1].ispath) { | |
+ fwd->listen_path = xstrdup(fwdargs[0].arg); | |
+ fwd->listen_port = PORT_STREAMLOCAL; | |
+ fwd->connect_path = xstrdup(fwdargs[1].arg); | |
+ fwd->connect_port = PORT_STREAMLOCAL; | |
+ } else if (fwdargs[1].ispath) { | |
+ fwd->listen_host = NULL; | |
+ fwd->listen_port = a2port(fwdargs[0].arg); | |
+ fwd->connect_path = xstrdup(fwdargs[1].arg); | |
+ fwd->connect_port = PORT_STREAMLOCAL; | |
+ } else { | |
+ fwd->listen_host = xstrdup(fwdargs[0].arg); | |
+ fwd->listen_port = a2port(fwdargs[1].arg); | |
+ fwd->connect_host = xstrdup("socks"); | |
+ } | |
+ break; | |
+ | |
+ case 3: | |
+ if (fwdargs[0].ispath) { | |
+ fwd->listen_path = xstrdup(fwdargs[0].arg); | |
+ fwd->listen_port = PORT_STREAMLOCAL; | |
+ fwd->connect_host = xstrdup(fwdargs[1].arg); | |
+ fwd->connect_port = a2port(fwdargs[2].arg); | |
+ } else if (fwdargs[2].ispath) { | |
+ fwd->listen_host = xstrdup(fwdargs[0].arg); | |
+ fwd->listen_port = a2port(fwdargs[1].arg); | |
+ fwd->connect_path = xstrdup(fwdargs[2].arg); | |
+ fwd->connect_port = PORT_STREAMLOCAL; | |
+ } else { | |
+ fwd->listen_host = NULL; | |
+ fwd->listen_port = a2port(fwdargs[0].arg); | |
+ fwd->connect_host = xstrdup(fwdargs[1].arg); | |
+ fwd->connect_port = a2port(fwdargs[2].arg); | |
+ } | |
+ break; | |
+ | |
+ case 4: | |
+ fwd->listen_host = xstrdup(fwdargs[0].arg); | |
+ fwd->listen_port = a2port(fwdargs[1].arg); | |
+ fwd->connect_host = xstrdup(fwdargs[2].arg); | |
+ fwd->connect_port = a2port(fwdargs[3].arg); | |
+ break; | |
+ default: | |
+ i = 0; /* failure */ | |
+ } | |
+ | |
+ free(p); | |
+ | |
+ if (dynamicfwd) { | |
+ if (!(i == 1 || i == 2)) | |
+ goto fail_free; | |
+ } else { | |
+ if (!(i == 3 || i == 4)) { | |
+ if (fwd->connect_path == NULL && | |
+ fwd->listen_path == NULL) | |
+ goto fail_free; | |
+ } | |
+ if (fwd->connect_port <= 0 && fwd->connect_path == NULL) | |
+ goto fail_free; | |
+ } | |
+ | |
+ if ((fwd->listen_port < 0 && fwd->listen_path == NULL) || | |
+ (!remotefwd && fwd->listen_port == 0)) | |
+ goto fail_free; | |
+ if (fwd->connect_host != NULL && | |
+ strlen(fwd->connect_host) >= NI_MAXHOST) | |
+ goto fail_free; | |
+ /* XXX - if connecting to a remote socket, max sun len may not match this host */ | |
+ if (fwd->connect_path != NULL && | |
+ strlen(fwd->connect_path) >= PATH_MAX_SUN) | |
+ goto fail_free; | |
+ if (fwd->listen_host != NULL && | |
+ strlen(fwd->listen_host) >= NI_MAXHOST) | |
+ goto fail_free; | |
+ if (fwd->listen_path != NULL && | |
+ strlen(fwd->listen_path) >= PATH_MAX_SUN) | |
+ goto fail_free; | |
+ | |
+ return (i); | |
+ | |
+ fail_free: | |
+ free(fwd->connect_host); | |
+ fwd->connect_host = NULL; | |
+ free(fwd->connect_path); | |
+ fwd->connect_path = NULL; | |
+ free(fwd->listen_host); | |
+ fwd->listen_host = NULL; | |
+ free(fwd->listen_path); | |
+ fwd->listen_path = NULL; | |
+ return (0); | |
+} | |
diff -urp --new-file openssh-6.7p1/readconf.h openssh-6.7p1.patched/readconf.h | |
--- openssh-6.7p1/readconf.h 2014-07-17 21:11:26.000000000 -0700 | |
+++ openssh-6.7p1.patched/readconf.h 2014-10-11 21:18:36.000000000 -0700 | |
@@ -145,6 +145,10 @@ typedef struct { | |
struct allowed_cname permitted_cnames[MAX_CANON_DOMAINS]; | |
char *ignored_unknown; /* Pattern list of unknown tokens to ignore */ | |
+ | |
+#ifdef __APPLE_KEYCHAIN__ | |
+ int ask_pass_gui; | |
+#endif | |
} Options; | |
#define SSH_CANONICALISE_NO 0 | |
diff -urp --new-file openssh-6.7p1/readconf.h.orig openssh-6.7p1.patched/readconf.h.orig | |
--- openssh-6.7p1/readconf.h.orig 1969-12-31 16:00:00.000000000 -0800 | |
+++ openssh-6.7p1.patched/readconf.h.orig 2014-07-17 21:11:26.000000000 -0700 | |
@@ -0,0 +1,183 @@ | |
+/* $OpenBSD: readconf.h,v 1.102 2014/07/15 15:54:14 millert Exp $ */ | |
+ | |
+/* | |
+ * Author: Tatu Ylonen <ylo@cs.hut.fi> | |
+ * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | |
+ * All rights reserved | |
+ * Functions for reading the configuration file. | |
+ * | |
+ * As far as I am concerned, the code I have written for this software | |
+ * can be used freely for any purpose. Any derived versions of this | |
+ * software must be clearly marked as such, and if the derived work is | |
+ * incompatible with the protocol description in the RFC file, it must be | |
+ * called by a name other than "ssh" or "Secure Shell". | |
+ */ | |
+ | |
+#ifndef READCONF_H | |
+#define READCONF_H | |
+ | |
+/* Data structure for representing option data. */ | |
+ | |
+#define MAX_SEND_ENV 256 | |
+#define SSH_MAX_HOSTS_FILES 32 | |
+#define MAX_CANON_DOMAINS 32 | |
+#define PATH_MAX_SUN (sizeof((struct sockaddr_un *)0)->sun_path) | |
+ | |
+struct allowed_cname { | |
+ char *source_list; | |
+ char *target_list; | |
+}; | |
+ | |
+typedef struct { | |
+ int forward_agent; /* Forward authentication agent. */ | |
+ int forward_x11; /* Forward X11 display. */ | |
+ int forward_x11_timeout; /* Expiration for Cookies */ | |
+ int forward_x11_trusted; /* Trust Forward X11 display. */ | |
+ int exit_on_forward_failure; /* Exit if bind(2) fails for -L/-R */ | |
+ char *xauth_location; /* Location for xauth program */ | |
+ struct ForwardOptions fwd_opts; /* forwarding options */ | |
+ int use_privileged_port; /* Don't use privileged port if false. */ | |
+ int rhosts_rsa_authentication; /* Try rhosts with RSA | |
+ * authentication. */ | |
+ int rsa_authentication; /* Try RSA authentication. */ | |
+ int pubkey_authentication; /* Try ssh2 pubkey authentication. */ | |
+ int hostbased_authentication; /* ssh2's rhosts_rsa */ | |
+ int challenge_response_authentication; | |
+ /* Try S/Key or TIS, authentication. */ | |
+ int gss_authentication; /* Try GSS authentication */ | |
+ int gss_deleg_creds; /* Delegate GSS credentials */ | |
+ int password_authentication; /* Try password | |
+ * authentication. */ | |
+ int kbd_interactive_authentication; /* Try keyboard-interactive auth. */ | |
+ char *kbd_interactive_devices; /* Keyboard-interactive auth devices. */ | |
+ 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. */ | |
+ int compression; /* Compress packets in both directions. */ | |
+ int compression_level; /* Compression level 1 (fast) to 9 | |
+ * (best). */ | |
+ int tcp_keep_alive; /* Set SO_KEEPALIVE. */ | |
+ int ip_qos_interactive; /* IP ToS/DSCP/class for interactive */ | |
+ int ip_qos_bulk; /* IP ToS/DSCP/class for bulk traffic */ | |
+ LogLevel log_level; /* Level for logging. */ | |
+ | |
+ int port; /* Port to connect. */ | |
+ int address_family; | |
+ int connection_attempts; /* Max attempts (seconds) before | |
+ * giving up */ | |
+ int connection_timeout; /* Max time (seconds) before | |
+ * aborting connection attempt */ | |
+ int number_of_password_prompts; /* Max number of password | |
+ * prompts. */ | |
+ int cipher; /* Cipher to use. */ | |
+ char *ciphers; /* SSH2 ciphers in order of preference. */ | |
+ char *macs; /* SSH2 macs in order of preference. */ | |
+ char *hostkeyalgorithms; /* SSH2 server key types in order of preference. */ | |
+ char *kex_algorithms; /* SSH2 kex methods in order of preference. */ | |
+ int protocol; /* Protocol in order of preference. */ | |
+ char *hostname; /* Real host to connect. */ | |
+ char *host_key_alias; /* hostname alias for .ssh/known_hosts */ | |
+ char *proxy_command; /* Proxy command for connecting the host. */ | |
+ char *user; /* User to log in as. */ | |
+ int escape_char; /* Escape character; -2 = none */ | |
+ | |
+ u_int num_system_hostfiles; /* Paths for /etc/ssh/ssh_known_hosts */ | |
+ char *system_hostfiles[SSH_MAX_HOSTS_FILES]; | |
+ u_int num_user_hostfiles; /* Path for $HOME/.ssh/known_hosts */ | |
+ char *user_hostfiles[SSH_MAX_HOSTS_FILES]; | |
+ char *preferred_authentications; | |
+ char *bind_address; /* local socket address for connection to sshd */ | |
+ char *pkcs11_provider; /* PKCS#11 provider */ | |
+ int verify_host_key_dns; /* Verify host key using DNS */ | |
+ | |
+ int num_identity_files; /* Number of files for RSA/DSA identities. */ | |
+ char *identity_files[SSH_MAX_IDENTITY_FILES]; | |
+ int identity_file_userprovided[SSH_MAX_IDENTITY_FILES]; | |
+ Key *identity_keys[SSH_MAX_IDENTITY_FILES]; | |
+ | |
+ /* Local TCP/IP forward requests. */ | |
+ int num_local_forwards; | |
+ struct Forward *local_forwards; | |
+ | |
+ /* Remote TCP/IP forward requests. */ | |
+ int num_remote_forwards; | |
+ struct Forward *remote_forwards; | |
+ int clear_forwardings; | |
+ | |
+ int enable_ssh_keysign; | |
+ int64_t rekey_limit; | |
+ int rekey_interval; | |
+ int no_host_authentication_for_localhost; | |
+ int identities_only; | |
+ int server_alive_interval; | |
+ int server_alive_count_max; | |
+ | |
+ int num_send_env; | |
+ char *send_env[MAX_SEND_ENV]; | |
+ | |
+ char *control_path; | |
+ int control_master; | |
+ int control_persist; /* ControlPersist flag */ | |
+ int control_persist_timeout; /* ControlPersist timeout (seconds) */ | |
+ | |
+ int hash_known_hosts; | |
+ | |
+ int tun_open; /* tun(4) */ | |
+ int tun_local; /* force tun device (optional) */ | |
+ int tun_remote; /* force tun device (optional) */ | |
+ | |
+ char *local_command; | |
+ int permit_local_command; | |
+ int visual_host_key; | |
+ | |
+ int use_roaming; | |
+ | |
+ int request_tty; | |
+ | |
+ int proxy_use_fdpass; | |
+ | |
+ int num_canonical_domains; | |
+ char *canonical_domains[MAX_CANON_DOMAINS]; | |
+ int canonicalize_hostname; | |
+ int canonicalize_max_dots; | |
+ int canonicalize_fallback_local; | |
+ int num_permitted_cnames; | |
+ struct allowed_cname permitted_cnames[MAX_CANON_DOMAINS]; | |
+ | |
+ char *ignored_unknown; /* Pattern list of unknown tokens to ignore */ | |
+} Options; | |
+ | |
+#define SSH_CANONICALISE_NO 0 | |
+#define SSH_CANONICALISE_YES 1 | |
+#define SSH_CANONICALISE_ALWAYS 2 | |
+ | |
+#define SSHCTL_MASTER_NO 0 | |
+#define SSHCTL_MASTER_YES 1 | |
+#define SSHCTL_MASTER_AUTO 2 | |
+#define SSHCTL_MASTER_ASK 3 | |
+#define SSHCTL_MASTER_AUTO_ASK 4 | |
+ | |
+#define REQUEST_TTY_AUTO 0 | |
+#define REQUEST_TTY_NO 1 | |
+#define REQUEST_TTY_YES 2 | |
+#define REQUEST_TTY_FORCE 3 | |
+ | |
+#define SSHCONF_CHECKPERM 1 /* check permissions on config file */ | |
+#define SSHCONF_USERCONF 2 /* user provided config file not system */ | |
+ | |
+void initialize_options(Options *); | |
+void fill_default_options(Options *); | |
+void fill_default_options_for_canonicalization(Options *); | |
+int process_config_line(Options *, struct passwd *, const char *, char *, | |
+ const char *, int, int *, int); | |
+int read_config_file(const char *, struct passwd *, const char *, | |
+ Options *, int); | |
+int parse_forward(struct Forward *, const char *, int, int); | |
+int default_ssh_port(void); | |
+int option_clear_or_none(const char *); | |
+ | |
+void add_local_forward(Options *, const struct Forward *); | |
+void add_remote_forward(Options *, const struct Forward *); | |
+void add_identity_file(Options *, const char *, const char *, int); | |
+ | |
+#endif /* READCONF_H */ | |
diff -urp --new-file openssh-6.7p1/scp.1 openssh-6.7p1.patched/scp.1 | |
--- openssh-6.7p1/scp.1 2014-04-19 20:02:58.000000000 -0700 | |
+++ openssh-6.7p1.patched/scp.1 2014-10-11 21:18:36.000000000 -0700 | |
@@ -19,7 +19,7 @@ | |
.Sh SYNOPSIS | |
.Nm scp | |
.Bk -words | |
-.Op Fl 12346BCpqrv | |
+.Op Fl 12346BCEpqrv | |
.Op Fl c Ar cipher | |
.Op Fl F Ar ssh_config | |
.Op Fl i Ar identity_file | |
@@ -95,6 +95,8 @@ Passes the | |
flag to | |
.Xr ssh 1 | |
to enable compression. | |
+.It Fl E | |
+Preserves extended attributes, resource forks, and ACLs. Requires both ends to be running Mac OS X 10.4 or later. | |
.It Fl c Ar cipher | |
Selects the cipher to use for encrypting the data transfer. | |
This option is directly passed to | |
diff -urp --new-file openssh-6.7p1/scp.1.orig openssh-6.7p1.patched/scp.1.orig | |
--- openssh-6.7p1/scp.1.orig 1969-12-31 16:00:00.000000000 -0800 | |
+++ openssh-6.7p1.patched/scp.1.orig 2014-04-19 20:02:58.000000000 -0700 | |
@@ -0,0 +1,239 @@ | |
+.\" | |
+.\" scp.1 | |
+.\" | |
+.\" Author: Tatu Ylonen <ylo@cs.hut.fi> | |
+.\" | |
+.\" Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | |
+.\" All rights reserved | |
+.\" | |
+.\" Created: Sun May 7 00:14:37 1995 ylo | |
+.\" | |
+.\" $OpenBSD: scp.1,v 1.62 2014/03/19 14:42:44 tedu Exp $ | |
+.\" | |
+.Dd $Mdocdate: March 19 2014 $ | |
+.Dt SCP 1 | |
+.Os | |
+.Sh NAME | |
+.Nm scp | |
+.Nd secure copy (remote file copy program) | |
+.Sh SYNOPSIS | |
+.Nm scp | |
+.Bk -words | |
+.Op Fl 12346BCpqrv | |
+.Op Fl c Ar cipher | |
+.Op Fl F Ar ssh_config | |
+.Op Fl i Ar identity_file | |
+.Op Fl l Ar limit | |
+.Op Fl o Ar ssh_option | |
+.Op Fl P Ar port | |
+.Op Fl S Ar program | |
+.Sm off | |
+.Oo | |
+.Op Ar user No @ | |
+.Ar host1 No : | |
+.Oc Ar file1 | |
+.Sm on | |
+.Ar ... | |
+.Sm off | |
+.Oo | |
+.Op Ar user No @ | |
+.Ar host2 No : | |
+.Oc Ar file2 | |
+.Sm on | |
+.Ek | |
+.Sh DESCRIPTION | |
+.Nm | |
+copies files between hosts on a network. | |
+It uses | |
+.Xr ssh 1 | |
+for data transfer, and uses the same authentication and provides the | |
+same security as | |
+.Xr ssh 1 . | |
+.Nm | |
+will ask for passwords or passphrases if they are needed for | |
+authentication. | |
+.Pp | |
+File names may contain a user and host specification to indicate | |
+that the file is to be copied to/from that host. | |
+Local file names can be made explicit using absolute or relative pathnames | |
+to avoid | |
+.Nm | |
+treating file names containing | |
+.Sq :\& | |
+as host specifiers. | |
+Copies between two remote hosts are also permitted. | |
+.Pp | |
+The options are as follows: | |
+.Bl -tag -width Ds | |
+.It Fl 1 | |
+Forces | |
+.Nm | |
+to use protocol 1. | |
+.It Fl 2 | |
+Forces | |
+.Nm | |
+to use protocol 2. | |
+.It Fl 3 | |
+Copies between two remote hosts are transferred through the local host. | |
+Without this option the data is copied directly between the two remote | |
+hosts. | |
+Note that this option disables the progress meter. | |
+.It Fl 4 | |
+Forces | |
+.Nm | |
+to use IPv4 addresses only. | |
+.It Fl 6 | |
+Forces | |
+.Nm | |
+to use IPv6 addresses only. | |
+.It Fl B | |
+Selects batch mode (prevents asking for passwords or passphrases). | |
+.It Fl C | |
+Compression enable. | |
+Passes the | |
+.Fl C | |
+flag to | |
+.Xr ssh 1 | |
+to enable compression. | |
+.It Fl c Ar cipher | |
+Selects the cipher to use for encrypting the data transfer. | |
+This option is directly passed to | |
+.Xr ssh 1 . | |
+.It Fl F Ar ssh_config | |
+Specifies an alternative | |
+per-user configuration file for | |
+.Nm ssh . | |
+This option is directly passed to | |
+.Xr ssh 1 . | |
+.It Fl i Ar identity_file | |
+Selects the file from which the identity (private key) for public key | |
+authentication is read. | |
+This option is directly passed to | |
+.Xr ssh 1 . | |
+.It Fl l Ar limit | |
+Limits the used bandwidth, specified in Kbit/s. | |
+.It Fl o Ar ssh_option | |
+Can be used to pass options to | |
+.Nm ssh | |
+in the format used in | |
+.Xr ssh_config 5 . | |
+This is useful for specifying options | |
+for which there is no separate | |
+.Nm scp | |
+command-line flag. | |
+For full details of the options listed below, and their possible values, see | |
+.Xr ssh_config 5 . | |
+.Pp | |
+.Bl -tag -width Ds -offset indent -compact | |
+.It AddressFamily | |
+.It BatchMode | |
+.It BindAddress | |
+.It CanonicalDomains | |
+.It CanonicalizeFallbackLocal | |
+.It CanonicalizeHostname | |
+.It CanonicalizeMaxDots | |
+.It CanonicalizePermittedCNAMEs | |
+.It ChallengeResponseAuthentication | |
+.It CheckHostIP | |
+.It Cipher | |
+.It Ciphers | |
+.It Compression | |
+.It CompressionLevel | |
+.It ConnectionAttempts | |
+.It ConnectTimeout | |
+.It ControlMaster | |
+.It ControlPath | |
+.It ControlPersist | |
+.It GlobalKnownHostsFile | |
+.It GSSAPIAuthentication | |
+.It GSSAPIDelegateCredentials | |
+.It HashKnownHosts | |
+.It Host | |
+.It HostbasedAuthentication | |
+.It HostKeyAlgorithms | |
+.It HostKeyAlias | |
+.It HostName | |
+.It IdentityFile | |
+.It IdentitiesOnly | |
+.It IPQoS | |
+.It KbdInteractiveAuthentication | |
+.It KbdInteractiveDevices | |
+.It KexAlgorithms | |
+.It LogLevel | |
+.It MACs | |
+.It NoHostAuthenticationForLocalhost | |
+.It NumberOfPasswordPrompts | |
+.It PasswordAuthentication | |
+.It PKCS11Provider | |
+.It Port | |
+.It PreferredAuthentications | |
+.It Protocol | |
+.It ProxyCommand | |
+.It PubkeyAuthentication | |
+.It RekeyLimit | |
+.It RhostsRSAAuthentication | |
+.It RSAAuthentication | |
+.It SendEnv | |
+.It ServerAliveInterval | |
+.It ServerAliveCountMax | |
+.It StrictHostKeyChecking | |
+.It TCPKeepAlive | |
+.It UsePrivilegedPort | |
+.It User | |
+.It UserKnownHostsFile | |
+.It VerifyHostKeyDNS | |
+.El | |
+.It Fl P Ar port | |
+Specifies the port to connect to on the remote host. | |
+Note that this option is written with a capital | |
+.Sq P , | |
+because | |
+.Fl p | |
+is already reserved for preserving the times and modes of the file. | |
+.It Fl p | |
+Preserves modification times, access times, and modes from the | |
+original file. | |
+.It Fl q | |
+Quiet mode: disables the progress meter as well as warning and diagnostic | |
+messages from | |
+.Xr ssh 1 . | |
+.It Fl r | |
+Recursively copy entire directories. | |
+Note that | |
+.Nm | |
+follows symbolic links encountered in the tree traversal. | |
+.It Fl S Ar program | |
+Name of | |
+.Ar program | |
+to use for the encrypted connection. | |
+The program must understand | |
+.Xr ssh 1 | |
+options. | |
+.It Fl v | |
+Verbose mode. | |
+Causes | |
+.Nm | |
+and | |
+.Xr ssh 1 | |
+to print debugging messages about their progress. | |
+This is helpful in | |
+debugging connection, authentication, and configuration problems. | |
+.El | |
+.Sh EXIT STATUS | |
+.Ex -std scp | |
+.Sh SEE ALSO | |
+.Xr sftp 1 , | |
+.Xr ssh 1 , | |
+.Xr ssh-add 1 , | |
+.Xr ssh-agent 1 , | |
+.Xr ssh-keygen 1 , | |
+.Xr ssh_config 5 , | |
+.Xr sshd 8 | |
+.Sh HISTORY | |
+.Nm | |
+is based on the rcp program in | |
+.Bx | |
+source code from the Regents of the University of California. | |
+.Sh AUTHORS | |
+.An Timo Rinne Aq Mt tri@iki.fi | |
+.An Tatu Ylonen Aq Mt ylo@cs.hut.fi | |
diff -urp --new-file openssh-6.7p1/scp.c openssh-6.7p1.patched/scp.c | |
--- openssh-6.7p1/scp.c 2014-07-01 22:29:01.000000000 -0700 | |
+++ openssh-6.7p1.patched/scp.c 2014-10-11 21:18:36.000000000 -0700 | |
@@ -78,6 +78,9 @@ | |
#ifdef HAVE_SYS_STAT_H | |
# include <sys/stat.h> | |
#endif | |
+#ifdef __APPLE_XSAN__ | |
+#include <sys/mount.h> | |
+#endif | |
#ifdef HAVE_POLL_H | |
#include <poll.h> | |
#else | |
@@ -114,6 +117,11 @@ | |
#include "misc.h" | |
#include "progressmeter.h" | |
+#ifdef HAVE_COPYFILE_H | |
+#include <libgen.h> | |
+#include <copyfile.h> | |
+#endif | |
+ | |
extern char *__progname; | |
#define COPY_BUFLEN 16384 | |
@@ -150,6 +158,12 @@ char *ssh_program = _PATH_SSH_PROGRAM; | |
/* This is used to store the pid of ssh_program */ | |
pid_t do_cmd_pid = -1; | |
+#ifdef HAVE_COPYFILE | |
+int copy_xattr = 0; | |
+int md_flag = 0; | |
+#endif | |
+ | |
+ | |
static void | |
killchild(int signo) | |
{ | |
@@ -395,7 +409,11 @@ main(int argc, char **argv) | |
addargs(&args, "-oClearAllForwardings=yes"); | |
fflag = tflag = 0; | |
+#if HAVE_COPYFILE | |
+ while ((ch = getopt(argc, argv, "dfl:prtvBCEc:i:P:q12346S:o:F:")) != -1) | |
+#else | |
while ((ch = getopt(argc, argv, "dfl:prtvBCc:i:P:q12346S:o:F:")) != -1) | |
+#endif | |
switch (ch) { | |
/* User-visible flags. */ | |
case '1': | |
@@ -456,6 +474,11 @@ main(int argc, char **argv) | |
showprogress = 0; | |
break; | |
+#ifdef HAVE_COPYFILE | |
+ case 'E': | |
+ copy_xattr = 1; | |
+ break; | |
+#endif | |
/* Server options. */ | |
case 'd': | |
targetshouldbedirectory = 1; | |
@@ -505,7 +528,12 @@ main(int argc, char **argv) | |
remin = remout = -1; | |
do_cmd_pid = -1; | |
/* Command to be executed on remote system using "ssh". */ | |
+#if HAVE_COPYFILE | |
+ (void) snprintf(cmd, sizeof cmd, "scp%s%s%s%s%s", | |
+ copy_xattr ? " -E" : "", | |
+#else | |
(void) snprintf(cmd, sizeof cmd, "scp%s%s%s%s", | |
+#endif | |
verbose_mode ? " -v" : "", | |
iamrecursive ? " -r" : "", pflag ? " -p" : "", | |
targetshouldbedirectory ? " -d" : ""); | |
@@ -751,6 +779,10 @@ source(int argc, char **argv) | |
int fd = -1, haderr, indx; | |
char *last, *name, buf[2048], encname[MAXPATHLEN]; | |
int len; | |
+#if HAVE_COPYFILE | |
+ char md_name[MAXPATHLEN]; | |
+ char *md_tmp; | |
+#endif | |
for (indx = 0; indx < argc; ++indx) { | |
name = argv[indx]; | |
@@ -758,12 +790,26 @@ source(int argc, char **argv) | |
len = strlen(name); | |
while (len > 1 && name[len-1] == '/') | |
name[--len] = '\0'; | |
+#if HAVE_COPYFILE | |
+md_next: | |
+ statbytes = 0; | |
+ if (md_flag) { | |
+ fd = open(md_tmp, O_RDONLY, 0); | |
+ unlink(md_tmp); | |
+ free(md_tmp); | |
+ if (fd < 0) | |
+ goto syserr; | |
+ } else { | |
+#endif | |
if ((fd = open(name, O_RDONLY|O_NONBLOCK, 0)) < 0) | |
goto syserr; | |
if (strchr(name, '\n') != NULL) { | |
strnvis(encname, name, sizeof(encname), VIS_NL); | |
name = encname; | |
} | |
+#if HAVE_COPYFILE | |
+ } | |
+#endif | |
if (fstat(fd, &stb) < 0) { | |
syserr: run_err("%s: %s", name, strerror(errno)); | |
goto next; | |
@@ -850,6 +896,36 @@ next: if (fd != -1) { | |
else | |
run_err("%s: %s", name, strerror(haderr)); | |
(void) response(); | |
+#ifdef HAVE_COPYFILE | |
+ if (copy_xattr && md_flag == 0) | |
+ { | |
+ if (!copyfile(name, NULL, 0, | |
+ COPYFILE_ACL | COPYFILE_XATTR | COPYFILE_CHECK)) | |
+ continue; | |
+ | |
+ /* | |
+ * this file will hold the actual metadata | |
+ * to be transferred | |
+ */ | |
+ md_tmp = strdup("/tmp/scp.md.XXXXXX"); | |
+ md_tmp = mktemp(md_tmp); | |
+ | |
+ if(copyfile(name, md_tmp, 0, | |
+ COPYFILE_ACL | COPYFILE_XATTR | COPYFILE_PACK) == 0) | |
+ { | |
+ /* | |
+ * this is the fake name to display | |
+ */ | |
+ snprintf(md_name, sizeof md_name, "%s/._%s", dirname(name), basename(name)); | |
+ name = md_name; | |
+ md_flag = 1; | |
+ if (verbose_mode) | |
+ fprintf(stderr, "copyfile(%s, %s, PACK)\n", name, md_tmp); | |
+ goto md_next; | |
+ } | |
+ } else | |
+ md_flag = 0; | |
+#endif | |
} | |
} | |
@@ -941,6 +1017,10 @@ sink(int argc, char **argv) | |
if (stat(targ, &stb) == 0 && S_ISDIR(stb.st_mode)) | |
targisdir = 1; | |
for (first = 1;; first = 0) { | |
+#if HAVE_COPYFILE | |
+ char md_src[MAXPATHLEN]; | |
+ char md_dst[MAXPATHLEN]; | |
+#endif | |
cp = buf; | |
if (atomicio(read, remin, cp, 1) != 1) | |
return; | |
@@ -1086,10 +1166,51 @@ sink(int argc, char **argv) | |
} | |
omode = mode; | |
mode |= S_IWUSR; | |
+ | |
+#if HAVE_COPYFILE | |
+ if (copy_xattr && !strncmp(basename(curfile), "._", 2)) | |
+ { | |
+ int mdfd; | |
+ if (targisdir) | |
+ { | |
+ snprintf(md_src, sizeof md_src, "%s.XXXXXX", np); | |
+ snprintf(md_dst, sizeof md_dst, "%s/%s", | |
+ dirname(np), basename(np) + 2); | |
+ if((mdfd = mkstemp(md_src)) < 0) | |
+ continue; | |
+ } | |
+ else | |
+ { | |
+ snprintf(md_src, sizeof md_src, "%s/._%s.XXXXXX", | |
+ dirname(np), basename(np)); | |
+ snprintf(md_dst, sizeof md_dst, "%s", np); | |
+ if((mdfd = mkstemp(md_src)) < 0) | |
+ continue; | |
+ } | |
+ if (mdfd >= 0) | |
+ close(mdfd); | |
+ np = md_src; | |
+ } | |
+#endif | |
if ((ofd = open(np, O_WRONLY|O_CREAT, mode)) < 0) { | |
bad: run_err("%s: %s", np, strerror(errno)); | |
continue; | |
} | |
+#ifdef __APPLE_XSAN__ | |
+ { | |
+ /* | |
+ * Pre-allocate blocks for the destination file. | |
+ */ | |
+ fstore_t fst; | |
+ | |
+ fst.fst_flags = 0; | |
+ fst.fst_posmode = F_PEOFPOSMODE; | |
+ fst.fst_offset = 0; | |
+ fst.fst_length = size; | |
+ | |
+ (void) fcntl(ofd, F_PREALLOCATE, &fst); | |
+ } | |
+#endif /* __APPLE_XSAN__ */ | |
(void) atomicio(vwrite, remout, "", 1); | |
if ((bp = allocbuf(&buffer, ofd, COPY_BUFLEN)) == NULL) { | |
(void) close(ofd); | |
@@ -1174,6 +1295,29 @@ bad: run_err("%s: %s", np, strerror(er | |
wrerrno = errno; | |
} | |
(void) response(); | |
+#ifdef HAVE_COPYFILE | |
+ if (copy_xattr && strncmp(basename(np), "._", 2) == 0) | |
+ { | |
+ if (verbose_mode) | |
+ fprintf(stderr, "copyfile(%s, %s, UNPACK)\n", md_src, md_dst); | |
+ if(!copyfile(md_src, md_dst, 0, | |
+ COPYFILE_ACL | COPYFILE_XATTR | COPYFILE_UNPACK) < 0) | |
+ { | |
+ snprintf(md_dst, sizeof md_dst, "%s/._%s", | |
+ dirname(md_dst), basename(md_dst)); | |
+ rename(md_src, md_dst); | |
+ } else | |
+ unlink(md_src); | |
+ if (setimes && wrerr == NO) { | |
+ setimes = 0; | |
+ if (utimes(md_dst, tv) < 0) { | |
+ run_err("%s: set times: %s", | |
+ np, strerror(errno)); | |
+ wrerr = DISPLAYED; | |
+ } | |
+ } | |
+ } else | |
+#endif | |
if (setimes && wrerr == NO) { | |
setimes = 0; | |
if (utimes(np, tv) < 0) { | |
@@ -1235,7 +1379,11 @@ void | |
usage(void) | |
{ | |
(void) fprintf(stderr, | |
+#if HAVE_COPYFILE | |
+ "usage: scp [-12346BCEpqrv] [-c cipher] [-F ssh_config] [-i identity_file]\n" | |
+#else | |
"usage: scp [-12346BCpqrv] [-c cipher] [-F ssh_config] [-i identity_file]\n" | |
+#endif | |
" [-l limit] [-o ssh_option] [-P port] [-S program]\n" | |
" [[user@]host1:]file1 ... [[user@]host2:]file2\n"); | |
exit(1); | |
diff -urp --new-file openssh-6.7p1/scp.c.orig openssh-6.7p1.patched/scp.c.orig | |
--- openssh-6.7p1/scp.c.orig 1969-12-31 16:00:00.000000000 -0800 | |
+++ openssh-6.7p1.patched/scp.c.orig 2014-07-01 22:29:01.000000000 -0700 | |
@@ -0,0 +1,1350 @@ | |
+/* $OpenBSD: scp.c,v 1.180 2014/06/24 02:21:01 djm Exp $ */ | |
+/* | |
+ * scp - secure remote copy. This is basically patched BSD rcp which | |
+ * uses ssh to do the data transfer (instead of using rcmd). | |
+ * | |
+ * NOTE: This version should NOT be suid root. (This uses ssh to | |
+ * do the transfer and ssh has the necessary privileges.) | |
+ * | |
+ * 1995 Timo Rinne <tri@iki.fi>, Tatu Ylonen <ylo@cs.hut.fi> | |
+ * | |
+ * As far as I am concerned, the code I have written for this software | |
+ * can be used freely for any purpose. Any derived versions of this | |
+ * software must be clearly marked as such, and if the derived work is | |
+ * incompatible with the protocol description in the RFC file, it must be | |
+ * called by a name other than "ssh" or "Secure Shell". | |
+ */ | |
+/* | |
+ * Copyright (c) 1999 Theo de Raadt. All rights reserved. | |
+ * Copyright (c) 1999 Aaron Campbell. 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. | |
+ */ | |
+ | |
+/* | |
+ * Parts from: | |
+ * | |
+ * Copyright (c) 1983, 1990, 1992, 1993, 1995 | |
+ * The Regents of the University of California. 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. | |
+ * 3. Neither the name of the University nor the names of its contributors | |
+ * may be used to endorse or promote products derived from this software | |
+ * without specific prior written permission. | |
+ * | |
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 REGENTS OR CONTRIBUTORS 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 <sys/types.h> | |
+#include <sys/param.h> | |
+#ifdef HAVE_SYS_STAT_H | |
+# include <sys/stat.h> | |
+#endif | |
+#ifdef HAVE_POLL_H | |
+#include <poll.h> | |
+#else | |
+# ifdef HAVE_SYS_POLL_H | |
+# include <sys/poll.h> | |
+# endif | |
+#endif | |
+#ifdef HAVE_SYS_TIME_H | |
+# include <sys/time.h> | |
+#endif | |
+#include <sys/wait.h> | |
+#include <sys/uio.h> | |
+ | |
+#include <ctype.h> | |
+#include <dirent.h> | |
+#include <errno.h> | |
+#include <fcntl.h> | |
+#include <pwd.h> | |
+#include <signal.h> | |
+#include <stdarg.h> | |
+#include <stdio.h> | |
+#include <stdlib.h> | |
+#include <string.h> | |
+#include <time.h> | |
+#include <unistd.h> | |
+#if defined(HAVE_STRNVIS) && defined(HAVE_VIS_H) && !defined(BROKEN_STRNVIS) | |
+#include <vis.h> | |
+#endif | |
+ | |
+#include "xmalloc.h" | |
+#include "atomicio.h" | |
+#include "pathnames.h" | |
+#include "log.h" | |
+#include "misc.h" | |
+#include "progressmeter.h" | |
+ | |
+extern char *__progname; | |
+ | |
+#define COPY_BUFLEN 16384 | |
+ | |
+int do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout); | |
+int do_cmd2(char *host, char *remuser, char *cmd, int fdin, int fdout); | |
+ | |
+/* Struct for addargs */ | |
+arglist args; | |
+arglist remote_remote_args; | |
+ | |
+/* Bandwidth limit */ | |
+long long limit_kbps = 0; | |
+struct bwlimit bwlimit; | |
+ | |
+/* Name of current file being transferred. */ | |
+char *curfile; | |
+ | |
+/* This is set to non-zero to enable verbose mode. */ | |
+int verbose_mode = 0; | |
+ | |
+/* This is set to zero if the progressmeter is not desired. */ | |
+int showprogress = 1; | |
+ | |
+/* | |
+ * This is set to non-zero if remote-remote copy should be piped | |
+ * through this process. | |
+ */ | |
+int throughlocal = 0; | |
+ | |
+/* This is the program to execute for the secured connection. ("ssh" or -S) */ | |
+char *ssh_program = _PATH_SSH_PROGRAM; | |
+ | |
+/* This is used to store the pid of ssh_program */ | |
+pid_t do_cmd_pid = -1; | |
+ | |
+static void | |
+killchild(int signo) | |
+{ | |
+ if (do_cmd_pid > 1) { | |
+ kill(do_cmd_pid, signo ? signo : SIGTERM); | |
+ waitpid(do_cmd_pid, NULL, 0); | |
+ } | |
+ | |
+ if (signo) | |
+ _exit(1); | |
+ exit(1); | |
+} | |
+ | |
+static void | |
+suspchild(int signo) | |
+{ | |
+ int status; | |
+ | |
+ if (do_cmd_pid > 1) { | |
+ kill(do_cmd_pid, signo); | |
+ while (waitpid(do_cmd_pid, &status, WUNTRACED) == -1 && | |
+ errno == EINTR) | |
+ ; | |
+ kill(getpid(), SIGSTOP); | |
+ } | |
+} | |
+ | |
+static int | |
+do_local_cmd(arglist *a) | |
+{ | |
+ u_int i; | |
+ int status; | |
+ pid_t pid; | |
+ | |
+ if (a->num == 0) | |
+ fatal("do_local_cmd: no arguments"); | |
+ | |
+ if (verbose_mode) { | |
+ fprintf(stderr, "Executing:"); | |
+ for (i = 0; i < a->num; i++) | |
+ fprintf(stderr, " %s", a->list[i]); | |
+ fprintf(stderr, "\n"); | |
+ } | |
+ if ((pid = fork()) == -1) | |
+ fatal("do_local_cmd: fork: %s", strerror(errno)); | |
+ | |
+ if (pid == 0) { | |
+ execvp(a->list[0], a->list); | |
+ perror(a->list[0]); | |
+ exit(1); | |
+ } | |
+ | |
+ do_cmd_pid = pid; | |
+ signal(SIGTERM, killchild); | |
+ signal(SIGINT, killchild); | |
+ signal(SIGHUP, killchild); | |
+ | |
+ while (waitpid(pid, &status, 0) == -1) | |
+ if (errno != EINTR) | |
+ fatal("do_local_cmd: waitpid: %s", strerror(errno)); | |
+ | |
+ do_cmd_pid = -1; | |
+ | |
+ if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) | |
+ return (-1); | |
+ | |
+ return (0); | |
+} | |
+ | |
+/* | |
+ * This function executes the given command as the specified user on the | |
+ * given host. This returns < 0 if execution fails, and >= 0 otherwise. This | |
+ * assigns the input and output file descriptors on success. | |
+ */ | |
+ | |
+int | |
+do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout) | |
+{ | |
+ int pin[2], pout[2], reserved[2]; | |
+ | |
+ if (verbose_mode) | |
+ fprintf(stderr, | |
+ "Executing: program %s host %s, user %s, command %s\n", | |
+ ssh_program, host, | |
+ remuser ? remuser : "(unspecified)", cmd); | |
+ | |
+ /* | |
+ * Reserve two descriptors so that the real pipes won't get | |
+ * descriptors 0 and 1 because that will screw up dup2 below. | |
+ */ | |
+ if (pipe(reserved) < 0) | |
+ fatal("pipe: %s", strerror(errno)); | |
+ | |
+ /* Create a socket pair for communicating with ssh. */ | |
+ if (pipe(pin) < 0) | |
+ fatal("pipe: %s", strerror(errno)); | |
+ if (pipe(pout) < 0) | |
+ fatal("pipe: %s", strerror(errno)); | |
+ | |
+ /* Free the reserved descriptors. */ | |
+ close(reserved[0]); | |
+ close(reserved[1]); | |
+ | |
+ signal(SIGTSTP, suspchild); | |
+ signal(SIGTTIN, suspchild); | |
+ signal(SIGTTOU, suspchild); | |
+ | |
+ /* Fork a child to execute the command on the remote host using ssh. */ | |
+ do_cmd_pid = fork(); | |
+ if (do_cmd_pid == 0) { | |
+ /* Child. */ | |
+ close(pin[1]); | |
+ close(pout[0]); | |
+ dup2(pin[0], 0); | |
+ dup2(pout[1], 1); | |
+ close(pin[0]); | |
+ close(pout[1]); | |
+ | |
+ replacearg(&args, 0, "%s", ssh_program); | |
+ if (remuser != NULL) { | |
+ addargs(&args, "-l"); | |
+ addargs(&args, "%s", remuser); | |
+ } | |
+ addargs(&args, "--"); | |
+ addargs(&args, "%s", host); | |
+ addargs(&args, "%s", cmd); | |
+ | |
+ execvp(ssh_program, args.list); | |
+ perror(ssh_program); | |
+ exit(1); | |
+ } else if (do_cmd_pid == -1) { | |
+ fatal("fork: %s", strerror(errno)); | |
+ } | |
+ /* Parent. Close the other side, and return the local side. */ | |
+ close(pin[0]); | |
+ *fdout = pin[1]; | |
+ close(pout[1]); | |
+ *fdin = pout[0]; | |
+ signal(SIGTERM, killchild); | |
+ signal(SIGINT, killchild); | |
+ signal(SIGHUP, killchild); | |
+ return 0; | |
+} | |
+ | |
+/* | |
+ * This functions executes a command simlar to do_cmd(), but expects the | |
+ * input and output descriptors to be setup by a previous call to do_cmd(). | |
+ * This way the input and output of two commands can be connected. | |
+ */ | |
+int | |
+do_cmd2(char *host, char *remuser, char *cmd, int fdin, int fdout) | |
+{ | |
+ pid_t pid; | |
+ int status; | |
+ | |
+ if (verbose_mode) | |
+ fprintf(stderr, | |
+ "Executing: 2nd program %s host %s, user %s, command %s\n", | |
+ ssh_program, host, | |
+ remuser ? remuser : "(unspecified)", cmd); | |
+ | |
+ /* Fork a child to execute the command on the remote host using ssh. */ | |
+ pid = fork(); | |
+ if (pid == 0) { | |
+ dup2(fdin, 0); | |
+ dup2(fdout, 1); | |
+ | |
+ replacearg(&args, 0, "%s", ssh_program); | |
+ if (remuser != NULL) { | |
+ addargs(&args, "-l"); | |
+ addargs(&args, "%s", remuser); | |
+ } | |
+ addargs(&args, "--"); | |
+ addargs(&args, "%s", host); | |
+ addargs(&args, "%s", cmd); | |
+ | |
+ execvp(ssh_program, args.list); | |
+ perror(ssh_program); | |
+ exit(1); | |
+ } else if (pid == -1) { | |
+ fatal("fork: %s", strerror(errno)); | |
+ } | |
+ while (waitpid(pid, &status, 0) == -1) | |
+ if (errno != EINTR) | |
+ fatal("do_cmd2: waitpid: %s", strerror(errno)); | |
+ return 0; | |
+} | |
+ | |
+typedef struct { | |
+ size_t cnt; | |
+ char *buf; | |
+} BUF; | |
+ | |
+BUF *allocbuf(BUF *, int, int); | |
+void lostconn(int); | |
+int okname(char *); | |
+void run_err(const char *,...); | |
+void verifydir(char *); | |
+ | |
+struct passwd *pwd; | |
+uid_t userid; | |
+int errs, remin, remout; | |
+int pflag, iamremote, iamrecursive, targetshouldbedirectory; | |
+ | |
+#define CMDNEEDS 64 | |
+char cmd[CMDNEEDS]; /* must hold "rcp -r -p -d\0" */ | |
+ | |
+int response(void); | |
+void rsource(char *, struct stat *); | |
+void sink(int, char *[]); | |
+void source(int, char *[]); | |
+void tolocal(int, char *[]); | |
+void toremote(char *, int, char *[]); | |
+void usage(void); | |
+ | |
+int | |
+main(int argc, char **argv) | |
+{ | |
+ int ch, fflag, tflag, status, n; | |
+ char *targ, **newargv; | |
+ const char *errstr; | |
+ extern char *optarg; | |
+ extern int optind; | |
+ | |
+ /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ | |
+ sanitise_stdfd(); | |
+ | |
+ /* Copy argv, because we modify it */ | |
+ newargv = xcalloc(MAX(argc + 1, 1), sizeof(*newargv)); | |
+ for (n = 0; n < argc; n++) | |
+ newargv[n] = xstrdup(argv[n]); | |
+ argv = newargv; | |
+ | |
+ __progname = ssh_get_progname(argv[0]); | |
+ | |
+ memset(&args, '\0', sizeof(args)); | |
+ memset(&remote_remote_args, '\0', sizeof(remote_remote_args)); | |
+ args.list = remote_remote_args.list = NULL; | |
+ addargs(&args, "%s", ssh_program); | |
+ addargs(&args, "-x"); | |
+ addargs(&args, "-oForwardAgent=no"); | |
+ addargs(&args, "-oPermitLocalCommand=no"); | |
+ addargs(&args, "-oClearAllForwardings=yes"); | |
+ | |
+ fflag = tflag = 0; | |
+ while ((ch = getopt(argc, argv, "dfl:prtvBCc:i:P:q12346S:o:F:")) != -1) | |
+ switch (ch) { | |
+ /* User-visible flags. */ | |
+ case '1': | |
+ case '2': | |
+ case '4': | |
+ case '6': | |
+ case 'C': | |
+ addargs(&args, "-%c", ch); | |
+ addargs(&remote_remote_args, "-%c", ch); | |
+ break; | |
+ case '3': | |
+ throughlocal = 1; | |
+ break; | |
+ case 'o': | |
+ case 'c': | |
+ case 'i': | |
+ case 'F': | |
+ addargs(&remote_remote_args, "-%c", ch); | |
+ addargs(&remote_remote_args, "%s", optarg); | |
+ addargs(&args, "-%c", ch); | |
+ addargs(&args, "%s", optarg); | |
+ break; | |
+ case 'P': | |
+ addargs(&remote_remote_args, "-p"); | |
+ addargs(&remote_remote_args, "%s", optarg); | |
+ addargs(&args, "-p"); | |
+ addargs(&args, "%s", optarg); | |
+ break; | |
+ case 'B': | |
+ addargs(&remote_remote_args, "-oBatchmode=yes"); | |
+ addargs(&args, "-oBatchmode=yes"); | |
+ break; | |
+ case 'l': | |
+ limit_kbps = strtonum(optarg, 1, 100 * 1024 * 1024, | |
+ &errstr); | |
+ if (errstr != NULL) | |
+ usage(); | |
+ limit_kbps *= 1024; /* kbps */ | |
+ bandwidth_limit_init(&bwlimit, limit_kbps, COPY_BUFLEN); | |
+ break; | |
+ case 'p': | |
+ pflag = 1; | |
+ break; | |
+ case 'r': | |
+ iamrecursive = 1; | |
+ break; | |
+ case 'S': | |
+ ssh_program = xstrdup(optarg); | |
+ break; | |
+ case 'v': | |
+ addargs(&args, "-v"); | |
+ addargs(&remote_remote_args, "-v"); | |
+ verbose_mode = 1; | |
+ break; | |
+ case 'q': | |
+ addargs(&args, "-q"); | |
+ addargs(&remote_remote_args, "-q"); | |
+ showprogress = 0; | |
+ break; | |
+ | |
+ /* Server options. */ | |
+ case 'd': | |
+ targetshouldbedirectory = 1; | |
+ break; | |
+ case 'f': /* "from" */ | |
+ iamremote = 1; | |
+ fflag = 1; | |
+ break; | |
+ case 't': /* "to" */ | |
+ iamremote = 1; | |
+ tflag = 1; | |
+#ifdef HAVE_CYGWIN | |
+ setmode(0, O_BINARY); | |
+#endif | |
+ break; | |
+ default: | |
+ usage(); | |
+ } | |
+ argc -= optind; | |
+ argv += optind; | |
+ | |
+ if ((pwd = getpwuid(userid = getuid())) == NULL) | |
+ fatal("unknown user %u", (u_int) userid); | |
+ | |
+ if (!isatty(STDOUT_FILENO)) | |
+ showprogress = 0; | |
+ | |
+ remin = STDIN_FILENO; | |
+ remout = STDOUT_FILENO; | |
+ | |
+ if (fflag) { | |
+ /* Follow "protocol", send data. */ | |
+ (void) response(); | |
+ source(argc, argv); | |
+ exit(errs != 0); | |
+ } | |
+ if (tflag) { | |
+ /* Receive data. */ | |
+ sink(argc, argv); | |
+ exit(errs != 0); | |
+ } | |
+ if (argc < 2) | |
+ usage(); | |
+ if (argc > 2) | |
+ targetshouldbedirectory = 1; | |
+ | |
+ remin = remout = -1; | |
+ do_cmd_pid = -1; | |
+ /* Command to be executed on remote system using "ssh". */ | |
+ (void) snprintf(cmd, sizeof cmd, "scp%s%s%s%s", | |
+ verbose_mode ? " -v" : "", | |
+ iamrecursive ? " -r" : "", pflag ? " -p" : "", | |
+ targetshouldbedirectory ? " -d" : ""); | |
+ | |
+ (void) signal(SIGPIPE, lostconn); | |
+ | |
+ if ((targ = colon(argv[argc - 1]))) /* Dest is remote host. */ | |
+ toremote(targ, argc, argv); | |
+ else { | |
+ if (targetshouldbedirectory) | |
+ verifydir(argv[argc - 1]); | |
+ tolocal(argc, argv); /* Dest is local host. */ | |
+ } | |
+ /* | |
+ * Finally check the exit status of the ssh process, if one was forked | |
+ * and no error has occurred yet | |
+ */ | |
+ if (do_cmd_pid != -1 && errs == 0) { | |
+ if (remin != -1) | |
+ (void) close(remin); | |
+ if (remout != -1) | |
+ (void) close(remout); | |
+ if (waitpid(do_cmd_pid, &status, 0) == -1) | |
+ errs = 1; | |
+ else { | |
+ if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) | |
+ errs = 1; | |
+ } | |
+ } | |
+ exit(errs != 0); | |
+} | |
+ | |
+/* Callback from atomicio6 to update progress meter and limit bandwidth */ | |
+static int | |
+scpio(void *_cnt, size_t s) | |
+{ | |
+ off_t *cnt = (off_t *)_cnt; | |
+ | |
+ *cnt += s; | |
+ if (limit_kbps > 0) | |
+ bandwidth_limit(&bwlimit, s); | |
+ return 0; | |
+} | |
+ | |
+static int | |
+do_times(int fd, int verb, const struct stat *sb) | |
+{ | |
+ /* strlen(2^64) == 20; strlen(10^6) == 7 */ | |
+ char buf[(20 + 7 + 2) * 2 + 2]; | |
+ | |
+ (void)snprintf(buf, sizeof(buf), "T%llu 0 %llu 0\n", | |
+ (unsigned long long) (sb->st_mtime < 0 ? 0 : sb->st_mtime), | |
+ (unsigned long long) (sb->st_atime < 0 ? 0 : sb->st_atime)); | |
+ if (verb) { | |
+ fprintf(stderr, "File mtime %lld atime %lld\n", | |
+ (long long)sb->st_mtime, (long long)sb->st_atime); | |
+ fprintf(stderr, "Sending file timestamps: %s", buf); | |
+ } | |
+ (void) atomicio(vwrite, fd, buf, strlen(buf)); | |
+ return (response()); | |
+} | |
+ | |
+void | |
+toremote(char *targ, int argc, char **argv) | |
+{ | |
+ char *bp, *host, *src, *suser, *thost, *tuser, *arg; | |
+ arglist alist; | |
+ int i; | |
+ u_int j; | |
+ | |
+ memset(&alist, '\0', sizeof(alist)); | |
+ alist.list = NULL; | |
+ | |
+ *targ++ = 0; | |
+ if (*targ == 0) | |
+ targ = "."; | |
+ | |
+ arg = xstrdup(argv[argc - 1]); | |
+ if ((thost = strrchr(arg, '@'))) { | |
+ /* user@host */ | |
+ *thost++ = 0; | |
+ tuser = arg; | |
+ if (*tuser == '\0') | |
+ tuser = NULL; | |
+ } else { | |
+ thost = arg; | |
+ tuser = NULL; | |
+ } | |
+ | |
+ if (tuser != NULL && !okname(tuser)) { | |
+ free(arg); | |
+ return; | |
+ } | |
+ | |
+ for (i = 0; i < argc - 1; i++) { | |
+ src = colon(argv[i]); | |
+ if (src && throughlocal) { /* extended remote to remote */ | |
+ *src++ = 0; | |
+ if (*src == 0) | |
+ src = "."; | |
+ host = strrchr(argv[i], '@'); | |
+ if (host) { | |
+ *host++ = 0; | |
+ host = cleanhostname(host); | |
+ suser = argv[i]; | |
+ if (*suser == '\0') | |
+ suser = pwd->pw_name; | |
+ else if (!okname(suser)) | |
+ continue; | |
+ } else { | |
+ host = cleanhostname(argv[i]); | |
+ suser = NULL; | |
+ } | |
+ xasprintf(&bp, "%s -f %s%s", cmd, | |
+ *src == '-' ? "-- " : "", src); | |
+ if (do_cmd(host, suser, bp, &remin, &remout) < 0) | |
+ exit(1); | |
+ free(bp); | |
+ host = cleanhostname(thost); | |
+ xasprintf(&bp, "%s -t %s%s", cmd, | |
+ *targ == '-' ? "-- " : "", targ); | |
+ if (do_cmd2(host, tuser, bp, remin, remout) < 0) | |
+ exit(1); | |
+ free(bp); | |
+ (void) close(remin); | |
+ (void) close(remout); | |
+ remin = remout = -1; | |
+ } else if (src) { /* standard remote to remote */ | |
+ freeargs(&alist); | |
+ addargs(&alist, "%s", ssh_program); | |
+ addargs(&alist, "-x"); | |
+ addargs(&alist, "-oClearAllForwardings=yes"); | |
+ addargs(&alist, "-n"); | |
+ for (j = 0; j < remote_remote_args.num; j++) { | |
+ addargs(&alist, "%s", | |
+ remote_remote_args.list[j]); | |
+ } | |
+ *src++ = 0; | |
+ if (*src == 0) | |
+ src = "."; | |
+ host = strrchr(argv[i], '@'); | |
+ | |
+ if (host) { | |
+ *host++ = 0; | |
+ host = cleanhostname(host); | |
+ suser = argv[i]; | |
+ if (*suser == '\0') | |
+ suser = pwd->pw_name; | |
+ else if (!okname(suser)) | |
+ continue; | |
+ addargs(&alist, "-l"); | |
+ addargs(&alist, "%s", suser); | |
+ } else { | |
+ host = cleanhostname(argv[i]); | |
+ } | |
+ addargs(&alist, "--"); | |
+ addargs(&alist, "%s", host); | |
+ addargs(&alist, "%s", cmd); | |
+ addargs(&alist, "%s", src); | |
+ addargs(&alist, "%s%s%s:%s", | |
+ tuser ? tuser : "", tuser ? "@" : "", | |
+ thost, targ); | |
+ if (do_local_cmd(&alist) != 0) | |
+ errs = 1; | |
+ } else { /* local to remote */ | |
+ if (remin == -1) { | |
+ xasprintf(&bp, "%s -t %s%s", cmd, | |
+ *targ == '-' ? "-- " : "", targ); | |
+ host = cleanhostname(thost); | |
+ if (do_cmd(host, tuser, bp, &remin, | |
+ &remout) < 0) | |
+ exit(1); | |
+ if (response() < 0) | |
+ exit(1); | |
+ free(bp); | |
+ } | |
+ source(1, argv + i); | |
+ } | |
+ } | |
+ free(arg); | |
+} | |
+ | |
+void | |
+tolocal(int argc, char **argv) | |
+{ | |
+ char *bp, *host, *src, *suser; | |
+ arglist alist; | |
+ int i; | |
+ | |
+ memset(&alist, '\0', sizeof(alist)); | |
+ alist.list = NULL; | |
+ | |
+ for (i = 0; i < argc - 1; i++) { | |
+ if (!(src = colon(argv[i]))) { /* Local to local. */ | |
+ freeargs(&alist); | |
+ addargs(&alist, "%s", _PATH_CP); | |
+ if (iamrecursive) | |
+ addargs(&alist, "-r"); | |
+ if (pflag) | |
+ addargs(&alist, "-p"); | |
+ addargs(&alist, "--"); | |
+ addargs(&alist, "%s", argv[i]); | |
+ addargs(&alist, "%s", argv[argc-1]); | |
+ if (do_local_cmd(&alist)) | |
+ ++errs; | |
+ continue; | |
+ } | |
+ *src++ = 0; | |
+ if (*src == 0) | |
+ src = "."; | |
+ if ((host = strrchr(argv[i], '@')) == NULL) { | |
+ host = argv[i]; | |
+ suser = NULL; | |
+ } else { | |
+ *host++ = 0; | |
+ suser = argv[i]; | |
+ if (*suser == '\0') | |
+ suser = pwd->pw_name; | |
+ } | |
+ host = cleanhostname(host); | |
+ xasprintf(&bp, "%s -f %s%s", | |
+ cmd, *src == '-' ? "-- " : "", src); | |
+ if (do_cmd(host, suser, bp, &remin, &remout) < 0) { | |
+ free(bp); | |
+ ++errs; | |
+ continue; | |
+ } | |
+ free(bp); | |
+ sink(1, argv + argc - 1); | |
+ (void) close(remin); | |
+ remin = remout = -1; | |
+ } | |
+} | |
+ | |
+void | |
+source(int argc, char **argv) | |
+{ | |
+ struct stat stb; | |
+ static BUF buffer; | |
+ BUF *bp; | |
+ off_t i, statbytes; | |
+ size_t amt, nr; | |
+ int fd = -1, haderr, indx; | |
+ char *last, *name, buf[2048], encname[MAXPATHLEN]; | |
+ int len; | |
+ | |
+ for (indx = 0; indx < argc; ++indx) { | |
+ name = argv[indx]; | |
+ statbytes = 0; | |
+ len = strlen(name); | |
+ while (len > 1 && name[len-1] == '/') | |
+ name[--len] = '\0'; | |
+ if ((fd = open(name, O_RDONLY|O_NONBLOCK, 0)) < 0) | |
+ goto syserr; | |
+ if (strchr(name, '\n') != NULL) { | |
+ strnvis(encname, name, sizeof(encname), VIS_NL); | |
+ name = encname; | |
+ } | |
+ if (fstat(fd, &stb) < 0) { | |
+syserr: run_err("%s: %s", name, strerror(errno)); | |
+ goto next; | |
+ } | |
+ if (stb.st_size < 0) { | |
+ run_err("%s: %s", name, "Negative file size"); | |
+ goto next; | |
+ } | |
+ unset_nonblock(fd); | |
+ switch (stb.st_mode & S_IFMT) { | |
+ case S_IFREG: | |
+ break; | |
+ case S_IFDIR: | |
+ if (iamrecursive) { | |
+ rsource(name, &stb); | |
+ goto next; | |
+ } | |
+ /* FALLTHROUGH */ | |
+ default: | |
+ run_err("%s: not a regular file", name); | |
+ goto next; | |
+ } | |
+ if ((last = strrchr(name, '/')) == NULL) | |
+ last = name; | |
+ else | |
+ ++last; | |
+ curfile = last; | |
+ if (pflag) { | |
+ if (do_times(remout, verbose_mode, &stb) < 0) | |
+ goto next; | |
+ } | |
+#define FILEMODEMASK (S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO) | |
+ snprintf(buf, sizeof buf, "C%04o %lld %s\n", | |
+ (u_int) (stb.st_mode & FILEMODEMASK), | |
+ (long long)stb.st_size, last); | |
+ if (verbose_mode) { | |
+ fprintf(stderr, "Sending file modes: %s", buf); | |
+ } | |
+ (void) atomicio(vwrite, remout, buf, strlen(buf)); | |
+ if (response() < 0) | |
+ goto next; | |
+ if ((bp = allocbuf(&buffer, fd, COPY_BUFLEN)) == NULL) { | |
+next: if (fd != -1) { | |
+ (void) close(fd); | |
+ fd = -1; | |
+ } | |
+ continue; | |
+ } | |
+ if (showprogress) | |
+ start_progress_meter(curfile, stb.st_size, &statbytes); | |
+ set_nonblock(remout); | |
+ for (haderr = i = 0; i < stb.st_size; i += bp->cnt) { | |
+ amt = bp->cnt; | |
+ if (i + (off_t)amt > stb.st_size) | |
+ amt = stb.st_size - i; | |
+ if (!haderr) { | |
+ if ((nr = atomicio(read, fd, | |
+ bp->buf, amt)) != amt) { | |
+ haderr = errno; | |
+ memset(bp->buf + nr, 0, amt - nr); | |
+ } | |
+ } | |
+ /* Keep writing after error to retain sync */ | |
+ if (haderr) { | |
+ (void)atomicio(vwrite, remout, bp->buf, amt); | |
+ memset(bp->buf, 0, amt); | |
+ continue; | |
+ } | |
+ if (atomicio6(vwrite, remout, bp->buf, amt, scpio, | |
+ &statbytes) != amt) | |
+ haderr = errno; | |
+ } | |
+ unset_nonblock(remout); | |
+ if (showprogress) | |
+ stop_progress_meter(); | |
+ | |
+ if (fd != -1) { | |
+ if (close(fd) < 0 && !haderr) | |
+ haderr = errno; | |
+ fd = -1; | |
+ } | |
+ if (!haderr) | |
+ (void) atomicio(vwrite, remout, "", 1); | |
+ else | |
+ run_err("%s: %s", name, strerror(haderr)); | |
+ (void) response(); | |
+ } | |
+} | |
+ | |
+void | |
+rsource(char *name, struct stat *statp) | |
+{ | |
+ DIR *dirp; | |
+ struct dirent *dp; | |
+ char *last, *vect[1], path[MAXPATHLEN]; | |
+ | |
+ if (!(dirp = opendir(name))) { | |
+ run_err("%s: %s", name, strerror(errno)); | |
+ return; | |
+ } | |
+ last = strrchr(name, '/'); | |
+ if (last == 0) | |
+ last = name; | |
+ else | |
+ last++; | |
+ if (pflag) { | |
+ if (do_times(remout, verbose_mode, statp) < 0) { | |
+ closedir(dirp); | |
+ return; | |
+ } | |
+ } | |
+ (void) snprintf(path, sizeof path, "D%04o %d %.1024s\n", | |
+ (u_int) (statp->st_mode & FILEMODEMASK), 0, last); | |
+ if (verbose_mode) | |
+ fprintf(stderr, "Entering directory: %s", path); | |
+ (void) atomicio(vwrite, remout, path, strlen(path)); | |
+ if (response() < 0) { | |
+ closedir(dirp); | |
+ return; | |
+ } | |
+ while ((dp = readdir(dirp)) != NULL) { | |
+ if (dp->d_ino == 0) | |
+ continue; | |
+ if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) | |
+ continue; | |
+ if (strlen(name) + 1 + strlen(dp->d_name) >= sizeof(path) - 1) { | |
+ run_err("%s/%s: name too long", name, dp->d_name); | |
+ continue; | |
+ } | |
+ (void) snprintf(path, sizeof path, "%s/%s", name, dp->d_name); | |
+ vect[0] = path; | |
+ source(1, vect); | |
+ } | |
+ (void) closedir(dirp); | |
+ (void) atomicio(vwrite, remout, "E\n", 2); | |
+ (void) response(); | |
+} | |
+ | |
+void | |
+sink(int argc, char **argv) | |
+{ | |
+ static BUF buffer; | |
+ struct stat stb; | |
+ enum { | |
+ YES, NO, DISPLAYED | |
+ } wrerr; | |
+ BUF *bp; | |
+ off_t i; | |
+ size_t j, count; | |
+ int amt, exists, first, ofd; | |
+ mode_t mode, omode, mask; | |
+ off_t size, statbytes; | |
+ unsigned long long ull; | |
+ int setimes, targisdir, wrerrno = 0; | |
+ char ch, *cp, *np, *targ, *why, *vect[1], buf[2048]; | |
+ struct timeval tv[2]; | |
+ | |
+#define atime tv[0] | |
+#define mtime tv[1] | |
+#define SCREWUP(str) { why = str; goto screwup; } | |
+ | |
+ setimes = targisdir = 0; | |
+ mask = umask(0); | |
+ if (!pflag) | |
+ (void) umask(mask); | |
+ if (argc != 1) { | |
+ run_err("ambiguous target"); | |
+ exit(1); | |
+ } | |
+ targ = *argv; | |
+ if (targetshouldbedirectory) | |
+ verifydir(targ); | |
+ | |
+ (void) atomicio(vwrite, remout, "", 1); | |
+ if (stat(targ, &stb) == 0 && S_ISDIR(stb.st_mode)) | |
+ targisdir = 1; | |
+ for (first = 1;; first = 0) { | |
+ cp = buf; | |
+ if (atomicio(read, remin, cp, 1) != 1) | |
+ return; | |
+ if (*cp++ == '\n') | |
+ SCREWUP("unexpected <newline>"); | |
+ do { | |
+ if (atomicio(read, remin, &ch, sizeof(ch)) != sizeof(ch)) | |
+ SCREWUP("lost connection"); | |
+ *cp++ = ch; | |
+ } while (cp < &buf[sizeof(buf) - 1] && ch != '\n'); | |
+ *cp = 0; | |
+ if (verbose_mode) | |
+ fprintf(stderr, "Sink: %s", buf); | |
+ | |
+ if (buf[0] == '\01' || buf[0] == '\02') { | |
+ if (iamremote == 0) | |
+ (void) atomicio(vwrite, STDERR_FILENO, | |
+ buf + 1, strlen(buf + 1)); | |
+ if (buf[0] == '\02') | |
+ exit(1); | |
+ ++errs; | |
+ continue; | |
+ } | |
+ if (buf[0] == 'E') { | |
+ (void) atomicio(vwrite, remout, "", 1); | |
+ return; | |
+ } | |
+ if (ch == '\n') | |
+ *--cp = 0; | |
+ | |
+ cp = buf; | |
+ if (*cp == 'T') { | |
+ setimes++; | |
+ cp++; | |
+ if (!isdigit((unsigned char)*cp)) | |
+ SCREWUP("mtime.sec not present"); | |
+ ull = strtoull(cp, &cp, 10); | |
+ if (!cp || *cp++ != ' ') | |
+ SCREWUP("mtime.sec not delimited"); | |
+ if ((time_t)ull < 0 || | |
+ (unsigned long long)(time_t)ull != ull) | |
+ setimes = 0; /* out of range */ | |
+ mtime.tv_sec = ull; | |
+ mtime.tv_usec = strtol(cp, &cp, 10); | |
+ if (!cp || *cp++ != ' ' || mtime.tv_usec < 0 || | |
+ mtime.tv_usec > 999999) | |
+ SCREWUP("mtime.usec not delimited"); | |
+ if (!isdigit((unsigned char)*cp)) | |
+ SCREWUP("atime.sec not present"); | |
+ ull = strtoull(cp, &cp, 10); | |
+ if (!cp || *cp++ != ' ') | |
+ SCREWUP("atime.sec not delimited"); | |
+ if ((time_t)ull < 0 || | |
+ (unsigned long long)(time_t)ull != ull) | |
+ setimes = 0; /* out of range */ | |
+ atime.tv_sec = ull; | |
+ atime.tv_usec = strtol(cp, &cp, 10); | |
+ if (!cp || *cp++ != '\0' || atime.tv_usec < 0 || | |
+ atime.tv_usec > 999999) | |
+ SCREWUP("atime.usec not delimited"); | |
+ (void) atomicio(vwrite, remout, "", 1); | |
+ continue; | |
+ } | |
+ if (*cp != 'C' && *cp != 'D') { | |
+ /* | |
+ * Check for the case "rcp remote:foo\* local:bar". | |
+ * In this case, the line "No match." can be returned | |
+ * by the shell before the rcp command on the remote is | |
+ * executed so the ^Aerror_message convention isn't | |
+ * followed. | |
+ */ | |
+ if (first) { | |
+ run_err("%s", cp); | |
+ exit(1); | |
+ } | |
+ SCREWUP("expected control record"); | |
+ } | |
+ mode = 0; | |
+ for (++cp; cp < buf + 5; cp++) { | |
+ if (*cp < '0' || *cp > '7') | |
+ SCREWUP("bad mode"); | |
+ mode = (mode << 3) | (*cp - '0'); | |
+ } | |
+ if (*cp++ != ' ') | |
+ SCREWUP("mode not delimited"); | |
+ | |
+ for (size = 0; isdigit((unsigned char)*cp);) | |
+ size = size * 10 + (*cp++ - '0'); | |
+ if (*cp++ != ' ') | |
+ SCREWUP("size not delimited"); | |
+ if ((strchr(cp, '/') != NULL) || (strcmp(cp, "..") == 0)) { | |
+ run_err("error: unexpected filename: %s", cp); | |
+ exit(1); | |
+ } | |
+ if (targisdir) { | |
+ static char *namebuf; | |
+ static size_t cursize; | |
+ size_t need; | |
+ | |
+ need = strlen(targ) + strlen(cp) + 250; | |
+ if (need > cursize) { | |
+ free(namebuf); | |
+ namebuf = xmalloc(need); | |
+ cursize = need; | |
+ } | |
+ (void) snprintf(namebuf, need, "%s%s%s", targ, | |
+ strcmp(targ, "/") ? "/" : "", cp); | |
+ np = namebuf; | |
+ } else | |
+ np = targ; | |
+ curfile = cp; | |
+ exists = stat(np, &stb) == 0; | |
+ if (buf[0] == 'D') { | |
+ int mod_flag = pflag; | |
+ if (!iamrecursive) | |
+ SCREWUP("received directory without -r"); | |
+ if (exists) { | |
+ if (!S_ISDIR(stb.st_mode)) { | |
+ errno = ENOTDIR; | |
+ goto bad; | |
+ } | |
+ if (pflag) | |
+ (void) chmod(np, mode); | |
+ } else { | |
+ /* Handle copying from a read-only | |
+ directory */ | |
+ mod_flag = 1; | |
+ if (mkdir(np, mode | S_IRWXU) < 0) | |
+ goto bad; | |
+ } | |
+ vect[0] = xstrdup(np); | |
+ sink(1, vect); | |
+ if (setimes) { | |
+ setimes = 0; | |
+ if (utimes(vect[0], tv) < 0) | |
+ run_err("%s: set times: %s", | |
+ vect[0], strerror(errno)); | |
+ } | |
+ if (mod_flag) | |
+ (void) chmod(vect[0], mode); | |
+ free(vect[0]); | |
+ continue; | |
+ } | |
+ omode = mode; | |
+ mode |= S_IWUSR; | |
+ if ((ofd = open(np, O_WRONLY|O_CREAT, mode)) < 0) { | |
+bad: run_err("%s: %s", np, strerror(errno)); | |
+ continue; | |
+ } | |
+ (void) atomicio(vwrite, remout, "", 1); | |
+ if ((bp = allocbuf(&buffer, ofd, COPY_BUFLEN)) == NULL) { | |
+ (void) close(ofd); | |
+ continue; | |
+ } | |
+ cp = bp->buf; | |
+ wrerr = NO; | |
+ | |
+ statbytes = 0; | |
+ if (showprogress) | |
+ start_progress_meter(curfile, size, &statbytes); | |
+ set_nonblock(remin); | |
+ for (count = i = 0; i < size; i += bp->cnt) { | |
+ amt = bp->cnt; | |
+ if (i + amt > size) | |
+ amt = size - i; | |
+ count += amt; | |
+ do { | |
+ j = atomicio6(read, remin, cp, amt, | |
+ scpio, &statbytes); | |
+ if (j == 0) { | |
+ run_err("%s", j != EPIPE ? | |
+ strerror(errno) : | |
+ "dropped connection"); | |
+ exit(1); | |
+ } | |
+ amt -= j; | |
+ cp += j; | |
+ } while (amt > 0); | |
+ | |
+ if (count == bp->cnt) { | |
+ /* Keep reading so we stay sync'd up. */ | |
+ if (wrerr == NO) { | |
+ if (atomicio(vwrite, ofd, bp->buf, | |
+ count) != count) { | |
+ wrerr = YES; | |
+ wrerrno = errno; | |
+ } | |
+ } | |
+ count = 0; | |
+ cp = bp->buf; | |
+ } | |
+ } | |
+ unset_nonblock(remin); | |
+ if (showprogress) | |
+ stop_progress_meter(); | |
+ if (count != 0 && wrerr == NO && | |
+ atomicio(vwrite, ofd, bp->buf, count) != count) { | |
+ wrerr = YES; | |
+ wrerrno = errno; | |
+ } | |
+ if (wrerr == NO && (!exists || S_ISREG(stb.st_mode)) && | |
+ ftruncate(ofd, size) != 0) { | |
+ run_err("%s: truncate: %s", np, strerror(errno)); | |
+ wrerr = DISPLAYED; | |
+ } | |
+ if (pflag) { | |
+ if (exists || omode != mode) | |
+#ifdef HAVE_FCHMOD | |
+ if (fchmod(ofd, omode)) { | |
+#else /* HAVE_FCHMOD */ | |
+ if (chmod(np, omode)) { | |
+#endif /* HAVE_FCHMOD */ | |
+ run_err("%s: set mode: %s", | |
+ np, strerror(errno)); | |
+ wrerr = DISPLAYED; | |
+ } | |
+ } else { | |
+ if (!exists && omode != mode) | |
+#ifdef HAVE_FCHMOD | |
+ if (fchmod(ofd, omode & ~mask)) { | |
+#else /* HAVE_FCHMOD */ | |
+ if (chmod(np, omode & ~mask)) { | |
+#endif /* HAVE_FCHMOD */ | |
+ run_err("%s: set mode: %s", | |
+ np, strerror(errno)); | |
+ wrerr = DISPLAYED; | |
+ } | |
+ } | |
+ if (close(ofd) == -1) { | |
+ wrerr = YES; | |
+ wrerrno = errno; | |
+ } | |
+ (void) response(); | |
+ if (setimes && wrerr == NO) { | |
+ setimes = 0; | |
+ if (utimes(np, tv) < 0) { | |
+ run_err("%s: set times: %s", | |
+ np, strerror(errno)); | |
+ wrerr = DISPLAYED; | |
+ } | |
+ } | |
+ switch (wrerr) { | |
+ case YES: | |
+ run_err("%s: %s", np, strerror(wrerrno)); | |
+ break; | |
+ case NO: | |
+ (void) atomicio(vwrite, remout, "", 1); | |
+ break; | |
+ case DISPLAYED: | |
+ break; | |
+ } | |
+ } | |
+screwup: | |
+ run_err("protocol error: %s", why); | |
+ exit(1); | |
+} | |
+ | |
+int | |
+response(void) | |
+{ | |
+ char ch, *cp, resp, rbuf[2048]; | |
+ | |
+ if (atomicio(read, remin, &resp, sizeof(resp)) != sizeof(resp)) | |
+ lostconn(0); | |
+ | |
+ cp = rbuf; | |
+ switch (resp) { | |
+ case 0: /* ok */ | |
+ return (0); | |
+ default: | |
+ *cp++ = resp; | |
+ /* FALLTHROUGH */ | |
+ case 1: /* error, followed by error msg */ | |
+ case 2: /* fatal error, "" */ | |
+ do { | |
+ if (atomicio(read, remin, &ch, sizeof(ch)) != sizeof(ch)) | |
+ lostconn(0); | |
+ *cp++ = ch; | |
+ } while (cp < &rbuf[sizeof(rbuf) - 1] && ch != '\n'); | |
+ | |
+ if (!iamremote) | |
+ (void) atomicio(vwrite, STDERR_FILENO, rbuf, cp - rbuf); | |
+ ++errs; | |
+ if (resp == 1) | |
+ return (-1); | |
+ exit(1); | |
+ } | |
+ /* NOTREACHED */ | |
+} | |
+ | |
+void | |
+usage(void) | |
+{ | |
+ (void) fprintf(stderr, | |
+ "usage: scp [-12346BCpqrv] [-c cipher] [-F ssh_config] [-i identity_file]\n" | |
+ " [-l limit] [-o ssh_option] [-P port] [-S program]\n" | |
+ " [[user@]host1:]file1 ... [[user@]host2:]file2\n"); | |
+ exit(1); | |
+} | |
+ | |
+void | |
+run_err(const char *fmt,...) | |
+{ | |
+ static FILE *fp; | |
+ va_list ap; | |
+ | |
+ ++errs; | |
+ if (fp != NULL || (remout != -1 && (fp = fdopen(remout, "w")))) { | |
+ (void) fprintf(fp, "%c", 0x01); | |
+ (void) fprintf(fp, "scp: "); | |
+ va_start(ap, fmt); | |
+ (void) vfprintf(fp, fmt, ap); | |
+ va_end(ap); | |
+ (void) fprintf(fp, "\n"); | |
+ (void) fflush(fp); | |
+ } | |
+ | |
+ if (!iamremote) { | |
+ va_start(ap, fmt); | |
+ vfprintf(stderr, fmt, ap); | |
+ va_end(ap); | |
+ fprintf(stderr, "\n"); | |
+ } | |
+} | |
+ | |
+void | |
+verifydir(char *cp) | |
+{ | |
+ struct stat stb; | |
+ | |
+ if (!stat(cp, &stb)) { | |
+ if (S_ISDIR(stb.st_mode)) | |
+ return; | |
+ errno = ENOTDIR; | |
+ } | |
+ run_err("%s: %s", cp, strerror(errno)); | |
+ killchild(0); | |
+} | |
+ | |
+int | |
+okname(char *cp0) | |
+{ | |
+ int c; | |
+ char *cp; | |
+ | |
+ cp = cp0; | |
+ do { | |
+ c = (int)*cp; | |
+ if (c & 0200) | |
+ goto bad; | |
+ if (!isalpha(c) && !isdigit((unsigned char)c)) { | |
+ switch (c) { | |
+ case '\'': | |
+ case '"': | |
+ case '`': | |
+ case ' ': | |
+ case '#': | |
+ goto bad; | |
+ default: | |
+ break; | |
+ } | |
+ } | |
+ } while (*++cp); | |
+ return (1); | |
+ | |
+bad: fprintf(stderr, "%s: invalid user name\n", cp0); | |
+ return (0); | |
+} | |
+ | |
+BUF * | |
+allocbuf(BUF *bp, int fd, int blksize) | |
+{ | |
+ size_t size; | |
+#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE | |
+ struct stat stb; | |
+ | |
+ if (fstat(fd, &stb) < 0) { | |
+ run_err("fstat: %s", strerror(errno)); | |
+ return (0); | |
+ } | |
+ size = roundup(stb.st_blksize, blksize); | |
+ if (size == 0) | |
+ size = blksize; | |
+#else /* HAVE_STRUCT_STAT_ST_BLKSIZE */ | |
+ size = blksize; | |
+#endif /* HAVE_STRUCT_STAT_ST_BLKSIZE */ | |
+ if (bp->cnt >= size) | |
+ return (bp); | |
+ if (bp->buf == NULL) | |
+ bp->buf = xmalloc(size); | |
+ else | |
+ bp->buf = xrealloc(bp->buf, 1, size); | |
+ memset(bp->buf, 0, size); | |
+ bp->cnt = size; | |
+ return (bp); | |
+} | |
+ | |
+void | |
+lostconn(int signo) | |
+{ | |
+ if (!iamremote) | |
+ (void)write(STDERR_FILENO, "lost connection\n", 16); | |
+ if (signo) | |
+ _exit(1); | |
+ else | |
+ exit(1); | |
+} | |
diff -urp --new-file openssh-6.7p1/servconf.c openssh-6.7p1.patched/servconf.c | |
--- openssh-6.7p1/servconf.c 2014-07-17 21:11:26.000000000 -0700 | |
+++ openssh-6.7p1.patched/servconf.c 2014-10-11 21:18:37.000000000 -0700 | |
@@ -253,7 +253,7 @@ fill_default_server_options(ServerOption | |
if (options->gss_cleanup_creds == -1) | |
options->gss_cleanup_creds = 1; | |
if (options->password_authentication == -1) | |
- options->password_authentication = 1; | |
+ options->password_authentication = 0; | |
if (options->kbd_interactive_authentication == -1) | |
options->kbd_interactive_authentication = 0; | |
if (options->challenge_response_authentication == -1) | |
@@ -639,7 +639,7 @@ match_cfg_line_group(const char *grps, i | |
if ((pw = getpwnam(user)) == NULL) { | |
debug("Can't match group at line %d because user %.100s does " | |
"not exist", line, user); | |
- } else if (ga_init(pw->pw_name, pw->pw_gid) == 0) { | |
+ } else if (ga_init(pw) == 0) { | |
debug("Can't Match group because user %.100s not in any group " | |
"at line %d", user, line); | |
} else if (ga_match_pattern_list(grps) != 1) { | |
diff -urp --new-file openssh-6.7p1/servconf.c.orig openssh-6.7p1.patched/servconf.c.orig | |
--- openssh-6.7p1/servconf.c.orig 1969-12-31 16:00:00.000000000 -0800 | |
+++ openssh-6.7p1.patched/servconf.c.orig 2014-07-17 21:11:26.000000000 -0700 | |
@@ -0,0 +1,2131 @@ | |
+ | |
+/* $OpenBSD: servconf.c,v 1.251 2014/07/15 15:54:14 millert Exp $ */ | |
+/* | |
+ * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | |
+ * All rights reserved | |
+ * | |
+ * As far as I am concerned, the code I have written for this software | |
+ * can be used freely for any purpose. Any derived versions of this | |
+ * software must be clearly marked as such, and if the derived work is | |
+ * incompatible with the protocol description in the RFC file, it must be | |
+ * called by a name other than "ssh" or "Secure Shell". | |
+ */ | |
+ | |
+#include "includes.h" | |
+ | |
+#include <sys/types.h> | |
+#include <sys/socket.h> | |
+ | |
+#include <netinet/in.h> | |
+#include <netinet/in_systm.h> | |
+#include <netinet/ip.h> | |
+ | |
+#include <ctype.h> | |
+#include <netdb.h> | |
+#include <pwd.h> | |
+#include <stdio.h> | |
+#include <stdlib.h> | |
+#include <string.h> | |
+#include <signal.h> | |
+#include <unistd.h> | |
+#include <stdarg.h> | |
+#include <errno.h> | |
+#ifdef HAVE_UTIL_H | |
+#include <util.h> | |
+#endif | |
+ | |
+#include "openbsd-compat/sys-queue.h" | |
+#include "xmalloc.h" | |
+#include "ssh.h" | |
+#include "log.h" | |
+#include "buffer.h" | |
+#include "misc.h" | |
+#include "servconf.h" | |
+#include "compat.h" | |
+#include "pathnames.h" | |
+#include "cipher.h" | |
+#include "key.h" | |
+#include "kex.h" | |
+#include "mac.h" | |
+#include "match.h" | |
+#include "channels.h" | |
+#include "groupaccess.h" | |
+#include "canohost.h" | |
+#include "packet.h" | |
+#include "hostfile.h" | |
+#include "auth.h" | |
+ | |
+static void add_listen_addr(ServerOptions *, char *, int); | |
+static void add_one_listen_addr(ServerOptions *, char *, int); | |
+ | |
+/* Use of privilege separation or not */ | |
+extern int use_privsep; | |
+extern Buffer cfg; | |
+ | |
+/* Initializes the server options to their default values. */ | |
+ | |
+void | |
+initialize_server_options(ServerOptions *options) | |
+{ | |
+ memset(options, 0, sizeof(*options)); | |
+ | |
+ /* Portable-specific options */ | |
+ options->use_pam = -1; | |
+ | |
+ /* Standard Options */ | |
+ options->num_ports = 0; | |
+ options->ports_from_cmdline = 0; | |
+ options->listen_addrs = NULL; | |
+ options->address_family = -1; | |
+ options->num_host_key_files = 0; | |
+ options->num_host_cert_files = 0; | |
+ options->host_key_agent = NULL; | |
+ options->pid_file = NULL; | |
+ options->server_key_bits = -1; | |
+ options->login_grace_time = -1; | |
+ options->key_regeneration_time = -1; | |
+ options->permit_root_login = PERMIT_NOT_SET; | |
+ options->ignore_rhosts = -1; | |
+ options->ignore_user_known_hosts = -1; | |
+ options->print_motd = -1; | |
+ options->print_lastlog = -1; | |
+ options->x11_forwarding = -1; | |
+ options->x11_display_offset = -1; | |
+ options->x11_use_localhost = -1; | |
+ options->permit_tty = -1; | |
+ options->permit_user_rc = -1; | |
+ options->xauth_location = NULL; | |
+ options->strict_modes = -1; | |
+ options->tcp_keep_alive = -1; | |
+ options->log_facility = SYSLOG_FACILITY_NOT_SET; | |
+ options->log_level = SYSLOG_LEVEL_NOT_SET; | |
+ options->rhosts_rsa_authentication = -1; | |
+ options->hostbased_authentication = -1; | |
+ options->hostbased_uses_name_from_packet_only = -1; | |
+ options->rsa_authentication = -1; | |
+ options->pubkey_authentication = -1; | |
+ options->kerberos_authentication = -1; | |
+ options->kerberos_or_local_passwd = -1; | |
+ options->kerberos_ticket_cleanup = -1; | |
+ options->kerberos_get_afs_token = -1; | |
+ options->gss_authentication=-1; | |
+ options->gss_cleanup_creds = -1; | |
+ options->password_authentication = -1; | |
+ options->kbd_interactive_authentication = -1; | |
+ options->challenge_response_authentication = -1; | |
+ options->permit_empty_passwd = -1; | |
+ options->permit_user_env = -1; | |
+ options->use_login = -1; | |
+ options->compression = -1; | |
+ options->rekey_limit = -1; | |
+ options->rekey_interval = -1; | |
+ options->allow_tcp_forwarding = -1; | |
+ options->allow_streamlocal_forwarding = -1; | |
+ options->allow_agent_forwarding = -1; | |
+ options->num_allow_users = 0; | |
+ options->num_deny_users = 0; | |
+ options->num_allow_groups = 0; | |
+ options->num_deny_groups = 0; | |
+ options->ciphers = NULL; | |
+ options->macs = NULL; | |
+ options->kex_algorithms = NULL; | |
+ options->protocol = SSH_PROTO_UNKNOWN; | |
+ options->fwd_opts.gateway_ports = -1; | |
+ options->fwd_opts.streamlocal_bind_mask = (mode_t)-1; | |
+ options->fwd_opts.streamlocal_bind_unlink = -1; | |
+ options->num_subsystems = 0; | |
+ options->max_startups_begin = -1; | |
+ options->max_startups_rate = -1; | |
+ options->max_startups = -1; | |
+ options->max_authtries = -1; | |
+ options->max_sessions = -1; | |
+ options->banner = NULL; | |
+ options->use_dns = -1; | |
+ options->client_alive_interval = -1; | |
+ options->client_alive_count_max = -1; | |
+ options->num_authkeys_files = 0; | |
+ options->num_accept_env = 0; | |
+ options->permit_tun = -1; | |
+ options->num_permitted_opens = -1; | |
+ options->adm_forced_command = NULL; | |
+ options->chroot_directory = NULL; | |
+ options->authorized_keys_command = NULL; | |
+ options->authorized_keys_command_user = NULL; | |
+ options->revoked_keys_file = NULL; | |
+ options->trusted_user_ca_keys = NULL; | |
+ options->authorized_principals_file = NULL; | |
+ options->ip_qos_interactive = -1; | |
+ options->ip_qos_bulk = -1; | |
+ options->version_addendum = NULL; | |
+} | |
+ | |
+void | |
+fill_default_server_options(ServerOptions *options) | |
+{ | |
+ /* Portable-specific options */ | |
+ if (options->use_pam == -1) | |
+ options->use_pam = 0; | |
+ | |
+ /* Standard Options */ | |
+ if (options->protocol == SSH_PROTO_UNKNOWN) | |
+ options->protocol = SSH_PROTO_2; | |
+ if (options->num_host_key_files == 0) { | |
+ /* fill default hostkeys for protocols */ | |
+ if (options->protocol & SSH_PROTO_1) | |
+ options->host_key_files[options->num_host_key_files++] = | |
+ _PATH_HOST_KEY_FILE; | |
+ if (options->protocol & SSH_PROTO_2) { | |
+ options->host_key_files[options->num_host_key_files++] = | |
+ _PATH_HOST_RSA_KEY_FILE; | |
+ options->host_key_files[options->num_host_key_files++] = | |
+ _PATH_HOST_DSA_KEY_FILE; | |
+#ifdef OPENSSL_HAS_ECC | |
+ options->host_key_files[options->num_host_key_files++] = | |
+ _PATH_HOST_ECDSA_KEY_FILE; | |
+#endif | |
+ options->host_key_files[options->num_host_key_files++] = | |
+ _PATH_HOST_ED25519_KEY_FILE; | |
+ } | |
+ } | |
+ /* No certificates by default */ | |
+ if (options->num_ports == 0) | |
+ options->ports[options->num_ports++] = SSH_DEFAULT_PORT; | |
+ if (options->listen_addrs == NULL) | |
+ add_listen_addr(options, NULL, 0); | |
+ if (options->pid_file == NULL) | |
+ options->pid_file = _PATH_SSH_DAEMON_PID_FILE; | |
+ if (options->server_key_bits == -1) | |
+ options->server_key_bits = 1024; | |
+ if (options->login_grace_time == -1) | |
+ options->login_grace_time = 120; | |
+ if (options->key_regeneration_time == -1) | |
+ options->key_regeneration_time = 3600; | |
+ if (options->permit_root_login == PERMIT_NOT_SET) | |
+ options->permit_root_login = PERMIT_YES; | |
+ if (options->ignore_rhosts == -1) | |
+ options->ignore_rhosts = 1; | |
+ if (options->ignore_user_known_hosts == -1) | |
+ options->ignore_user_known_hosts = 0; | |
+ if (options->print_motd == -1) | |
+ options->print_motd = 1; | |
+ if (options->print_lastlog == -1) | |
+ options->print_lastlog = 1; | |
+ if (options->x11_forwarding == -1) | |
+ options->x11_forwarding = 0; | |
+ if (options->x11_display_offset == -1) | |
+ options->x11_display_offset = 10; | |
+ if (options->x11_use_localhost == -1) | |
+ options->x11_use_localhost = 1; | |
+ if (options->xauth_location == NULL) | |
+ options->xauth_location = _PATH_XAUTH; | |
+ if (options->permit_tty == -1) | |
+ options->permit_tty = 1; | |
+ if (options->permit_user_rc == -1) | |
+ options->permit_user_rc = 1; | |
+ if (options->strict_modes == -1) | |
+ options->strict_modes = 1; | |
+ if (options->tcp_keep_alive == -1) | |
+ options->tcp_keep_alive = 1; | |
+ if (options->log_facility == SYSLOG_FACILITY_NOT_SET) | |
+ options->log_facility = SYSLOG_FACILITY_AUTH; | |
+ if (options->log_level == SYSLOG_LEVEL_NOT_SET) | |
+ options->log_level = SYSLOG_LEVEL_INFO; | |
+ if (options->rhosts_rsa_authentication == -1) | |
+ options->rhosts_rsa_authentication = 0; | |
+ if (options->hostbased_authentication == -1) | |
+ options->hostbased_authentication = 0; | |
+ if (options->hostbased_uses_name_from_packet_only == -1) | |
+ options->hostbased_uses_name_from_packet_only = 0; | |
+ if (options->rsa_authentication == -1) | |
+ options->rsa_authentication = 1; | |
+ if (options->pubkey_authentication == -1) | |
+ options->pubkey_authentication = 1; | |
+ if (options->kerberos_authentication == -1) | |
+ options->kerberos_authentication = 0; | |
+ if (options->kerberos_or_local_passwd == -1) | |
+ options->kerberos_or_local_passwd = 1; | |
+ if (options->kerberos_ticket_cleanup == -1) | |
+ options->kerberos_ticket_cleanup = 1; | |
+ if (options->kerberos_get_afs_token == -1) | |
+ options->kerberos_get_afs_token = 0; | |
+ if (options->gss_authentication == -1) | |
+ options->gss_authentication = 0; | |
+ if (options->gss_cleanup_creds == -1) | |
+ options->gss_cleanup_creds = 1; | |
+ if (options->password_authentication == -1) | |
+ options->password_authentication = 1; | |
+ if (options->kbd_interactive_authentication == -1) | |
+ options->kbd_interactive_authentication = 0; | |
+ if (options->challenge_response_authentication == -1) | |
+ options->challenge_response_authentication = 1; | |
+ if (options->permit_empty_passwd == -1) | |
+ options->permit_empty_passwd = 0; | |
+ if (options->permit_user_env == -1) | |
+ options->permit_user_env = 0; | |
+ if (options->use_login == -1) | |
+ options->use_login = 0; | |
+ if (options->compression == -1) | |
+ options->compression = COMP_DELAYED; | |
+ if (options->rekey_limit == -1) | |
+ options->rekey_limit = 0; | |
+ if (options->rekey_interval == -1) | |
+ options->rekey_interval = 0; | |
+ if (options->allow_tcp_forwarding == -1) | |
+ options->allow_tcp_forwarding = FORWARD_ALLOW; | |
+ if (options->allow_streamlocal_forwarding == -1) | |
+ options->allow_streamlocal_forwarding = FORWARD_ALLOW; | |
+ if (options->allow_agent_forwarding == -1) | |
+ options->allow_agent_forwarding = 1; | |
+ if (options->fwd_opts.gateway_ports == -1) | |
+ options->fwd_opts.gateway_ports = 0; | |
+ if (options->max_startups == -1) | |
+ options->max_startups = 100; | |
+ if (options->max_startups_rate == -1) | |
+ options->max_startups_rate = 30; /* 30% */ | |
+ if (options->max_startups_begin == -1) | |
+ options->max_startups_begin = 10; | |
+ if (options->max_authtries == -1) | |
+ options->max_authtries = DEFAULT_AUTH_FAIL_MAX; | |
+ if (options->max_sessions == -1) | |
+ options->max_sessions = DEFAULT_SESSIONS_MAX; | |
+ if (options->use_dns == -1) | |
+ options->use_dns = 1; | |
+ if (options->client_alive_interval == -1) | |
+ options->client_alive_interval = 0; | |
+ if (options->client_alive_count_max == -1) | |
+ options->client_alive_count_max = 3; | |
+ if (options->num_authkeys_files == 0) { | |
+ options->authorized_keys_files[options->num_authkeys_files++] = | |
+ xstrdup(_PATH_SSH_USER_PERMITTED_KEYS); | |
+ options->authorized_keys_files[options->num_authkeys_files++] = | |
+ xstrdup(_PATH_SSH_USER_PERMITTED_KEYS2); | |
+ } | |
+ if (options->permit_tun == -1) | |
+ options->permit_tun = SSH_TUNMODE_NO; | |
+ if (options->ip_qos_interactive == -1) | |
+ options->ip_qos_interactive = IPTOS_LOWDELAY; | |
+ if (options->ip_qos_bulk == -1) | |
+ options->ip_qos_bulk = IPTOS_THROUGHPUT; | |
+ if (options->version_addendum == NULL) | |
+ options->version_addendum = xstrdup(""); | |
+ if (options->fwd_opts.streamlocal_bind_mask == (mode_t)-1) | |
+ options->fwd_opts.streamlocal_bind_mask = 0177; | |
+ if (options->fwd_opts.streamlocal_bind_unlink == -1) | |
+ options->fwd_opts.streamlocal_bind_unlink = 0; | |
+ /* Turn privilege separation on by default */ | |
+ if (use_privsep == -1) | |
+ use_privsep = PRIVSEP_NOSANDBOX; | |
+ | |
+#ifndef HAVE_MMAP | |
+ if (use_privsep && options->compression == 1) { | |
+ error("This platform does not support both privilege " | |
+ "separation and compression"); | |
+ error("Compression disabled"); | |
+ options->compression = 0; | |
+ } | |
+#endif | |
+ | |
+} | |
+ | |
+/* Keyword tokens. */ | |
+typedef enum { | |
+ sBadOption, /* == unknown option */ | |
+ /* Portable-specific options */ | |
+ sUsePAM, | |
+ /* Standard Options */ | |
+ sPort, sHostKeyFile, sServerKeyBits, sLoginGraceTime, sKeyRegenerationTime, | |
+ sPermitRootLogin, sLogFacility, sLogLevel, | |
+ sRhostsRSAAuthentication, sRSAAuthentication, | |
+ sKerberosAuthentication, sKerberosOrLocalPasswd, sKerberosTicketCleanup, | |
+ sKerberosGetAFSToken, | |
+ sKerberosTgtPassing, sChallengeResponseAuthentication, | |
+ sPasswordAuthentication, sKbdInteractiveAuthentication, | |
+ sListenAddress, sAddressFamily, | |
+ sPrintMotd, sPrintLastLog, sIgnoreRhosts, | |
+ sX11Forwarding, sX11DisplayOffset, sX11UseLocalhost, | |
+ sPermitTTY, sStrictModes, sEmptyPasswd, sTCPKeepAlive, | |
+ sPermitUserEnvironment, sUseLogin, sAllowTcpForwarding, sCompression, | |
+ sRekeyLimit, sAllowUsers, sDenyUsers, sAllowGroups, sDenyGroups, | |
+ sIgnoreUserKnownHosts, sCiphers, sMacs, sProtocol, sPidFile, | |
+ sGatewayPorts, sPubkeyAuthentication, sXAuthLocation, sSubsystem, | |
+ sMaxStartups, sMaxAuthTries, sMaxSessions, | |
+ sBanner, sUseDNS, sHostbasedAuthentication, | |
+ sHostbasedUsesNameFromPacketOnly, sClientAliveInterval, | |
+ sClientAliveCountMax, sAuthorizedKeysFile, | |
+ sGssAuthentication, sGssCleanupCreds, sAcceptEnv, sPermitTunnel, | |
+ sMatch, sPermitOpen, sForceCommand, sChrootDirectory, | |
+ sUsePrivilegeSeparation, sAllowAgentForwarding, | |
+ sHostCertificate, | |
+ sRevokedKeys, sTrustedUserCAKeys, sAuthorizedPrincipalsFile, | |
+ sKexAlgorithms, sIPQoS, sVersionAddendum, | |
+ sAuthorizedKeysCommand, sAuthorizedKeysCommandUser, | |
+ sAuthenticationMethods, sHostKeyAgent, sPermitUserRC, | |
+ sStreamLocalBindMask, sStreamLocalBindUnlink, | |
+ sAllowStreamLocalForwarding, | |
+ sDeprecated, sUnsupported | |
+} ServerOpCodes; | |
+ | |
+#define SSHCFG_GLOBAL 0x01 /* allowed in main section of sshd_config */ | |
+#define SSHCFG_MATCH 0x02 /* allowed inside a Match section */ | |
+#define SSHCFG_ALL (SSHCFG_GLOBAL|SSHCFG_MATCH) | |
+ | |
+/* Textual representation of the tokens. */ | |
+static struct { | |
+ const char *name; | |
+ ServerOpCodes opcode; | |
+ u_int flags; | |
+} keywords[] = { | |
+ /* Portable-specific options */ | |
+#ifdef USE_PAM | |
+ { "usepam", sUsePAM, SSHCFG_GLOBAL }, | |
+#else | |
+ { "usepam", sUnsupported, SSHCFG_GLOBAL }, | |
+#endif | |
+ { "pamauthenticationviakbdint", sDeprecated, SSHCFG_GLOBAL }, | |
+ /* Standard Options */ | |
+ { "port", sPort, SSHCFG_GLOBAL }, | |
+ { "hostkey", sHostKeyFile, SSHCFG_GLOBAL }, | |
+ { "hostdsakey", sHostKeyFile, SSHCFG_GLOBAL }, /* alias */ | |
+ { "hostkeyagent", sHostKeyAgent, SSHCFG_GLOBAL }, | |
+ { "pidfile", sPidFile, SSHCFG_GLOBAL }, | |
+ { "serverkeybits", sServerKeyBits, SSHCFG_GLOBAL }, | |
+ { "logingracetime", sLoginGraceTime, SSHCFG_GLOBAL }, | |
+ { "keyregenerationinterval", sKeyRegenerationTime, SSHCFG_GLOBAL }, | |
+ { "permitrootlogin", sPermitRootLogin, SSHCFG_ALL }, | |
+ { "syslogfacility", sLogFacility, SSHCFG_GLOBAL }, | |
+ { "loglevel", sLogLevel, SSHCFG_GLOBAL }, | |
+ { "rhostsauthentication", sDeprecated, SSHCFG_GLOBAL }, | |
+ { "rhostsrsaauthentication", sRhostsRSAAuthentication, SSHCFG_ALL }, | |
+ { "hostbasedauthentication", sHostbasedAuthentication, SSHCFG_ALL }, | |
+ { "hostbasedusesnamefrompacketonly", sHostbasedUsesNameFromPacketOnly, SSHCFG_ALL }, | |
+ { "rsaauthentication", sRSAAuthentication, SSHCFG_ALL }, | |
+ { "pubkeyauthentication", sPubkeyAuthentication, SSHCFG_ALL }, | |
+ { "dsaauthentication", sPubkeyAuthentication, SSHCFG_GLOBAL }, /* alias */ | |
+#ifdef KRB5 | |
+ { "kerberosauthentication", sKerberosAuthentication, SSHCFG_ALL }, | |
+ { "kerberosorlocalpasswd", sKerberosOrLocalPasswd, SSHCFG_GLOBAL }, | |
+ { "kerberosticketcleanup", sKerberosTicketCleanup, SSHCFG_GLOBAL }, | |
+#ifdef USE_AFS | |
+ { "kerberosgetafstoken", sKerberosGetAFSToken, SSHCFG_GLOBAL }, | |
+#else | |
+ { "kerberosgetafstoken", sUnsupported, SSHCFG_GLOBAL }, | |
+#endif | |
+#else | |
+ { "kerberosauthentication", sUnsupported, SSHCFG_ALL }, | |
+ { "kerberosorlocalpasswd", sUnsupported, SSHCFG_GLOBAL }, | |
+ { "kerberosticketcleanup", sUnsupported, SSHCFG_GLOBAL }, | |
+ { "kerberosgetafstoken", sUnsupported, SSHCFG_GLOBAL }, | |
+#endif | |
+ { "kerberostgtpassing", sUnsupported, SSHCFG_GLOBAL }, | |
+ { "afstokenpassing", sUnsupported, SSHCFG_GLOBAL }, | |
+#ifdef GSSAPI | |
+ { "gssapiauthentication", sGssAuthentication, SSHCFG_ALL }, | |
+ { "gssapicleanupcredentials", sGssCleanupCreds, SSHCFG_GLOBAL }, | |
+#else | |
+ { "gssapiauthentication", sUnsupported, SSHCFG_ALL }, | |
+ { "gssapicleanupcredentials", sUnsupported, SSHCFG_GLOBAL }, | |
+#endif | |
+ { "passwordauthentication", sPasswordAuthentication, SSHCFG_ALL }, | |
+ { "kbdinteractiveauthentication", sKbdInteractiveAuthentication, SSHCFG_ALL }, | |
+ { "challengeresponseauthentication", sChallengeResponseAuthentication, SSHCFG_GLOBAL }, | |
+ { "skeyauthentication", sChallengeResponseAuthentication, SSHCFG_GLOBAL }, /* alias */ | |
+ { "checkmail", sDeprecated, SSHCFG_GLOBAL }, | |
+ { "listenaddress", sListenAddress, SSHCFG_GLOBAL }, | |
+ { "addressfamily", sAddressFamily, SSHCFG_GLOBAL }, | |
+ { "printmotd", sPrintMotd, SSHCFG_GLOBAL }, | |
+ { "printlastlog", sPrintLastLog, SSHCFG_GLOBAL }, | |
+ { "ignorerhosts", sIgnoreRhosts, SSHCFG_GLOBAL }, | |
+ { "ignoreuserknownhosts", sIgnoreUserKnownHosts, SSHCFG_GLOBAL }, | |
+ { "x11forwarding", sX11Forwarding, SSHCFG_ALL }, | |
+ { "x11displayoffset", sX11DisplayOffset, SSHCFG_ALL }, | |
+ { "x11uselocalhost", sX11UseLocalhost, SSHCFG_ALL }, | |
+ { "xauthlocation", sXAuthLocation, SSHCFG_GLOBAL }, | |
+ { "strictmodes", sStrictModes, SSHCFG_GLOBAL }, | |
+ { "permitemptypasswords", sEmptyPasswd, SSHCFG_ALL }, | |
+ { "permituserenvironment", sPermitUserEnvironment, SSHCFG_GLOBAL }, | |
+ { "uselogin", sUseLogin, SSHCFG_GLOBAL }, | |
+ { "compression", sCompression, SSHCFG_GLOBAL }, | |
+ { "rekeylimit", sRekeyLimit, SSHCFG_ALL }, | |
+ { "tcpkeepalive", sTCPKeepAlive, SSHCFG_GLOBAL }, | |
+ { "keepalive", sTCPKeepAlive, SSHCFG_GLOBAL }, /* obsolete alias */ | |
+ { "allowtcpforwarding", sAllowTcpForwarding, SSHCFG_ALL }, | |
+ { "allowagentforwarding", sAllowAgentForwarding, SSHCFG_ALL }, | |
+ { "allowusers", sAllowUsers, SSHCFG_ALL }, | |
+ { "denyusers", sDenyUsers, SSHCFG_ALL }, | |
+ { "allowgroups", sAllowGroups, SSHCFG_ALL }, | |
+ { "denygroups", sDenyGroups, SSHCFG_ALL }, | |
+ { "ciphers", sCiphers, SSHCFG_GLOBAL }, | |
+ { "macs", sMacs, SSHCFG_GLOBAL }, | |
+ { "protocol", sProtocol, SSHCFG_GLOBAL }, | |
+ { "gatewayports", sGatewayPorts, SSHCFG_ALL }, | |
+ { "subsystem", sSubsystem, SSHCFG_GLOBAL }, | |
+ { "maxstartups", sMaxStartups, SSHCFG_GLOBAL }, | |
+ { "maxauthtries", sMaxAuthTries, SSHCFG_ALL }, | |
+ { "maxsessions", sMaxSessions, SSHCFG_ALL }, | |
+ { "banner", sBanner, SSHCFG_ALL }, | |
+ { "usedns", sUseDNS, SSHCFG_GLOBAL }, | |
+ { "verifyreversemapping", sDeprecated, SSHCFG_GLOBAL }, | |
+ { "reversemappingcheck", sDeprecated, SSHCFG_GLOBAL }, | |
+ { "clientaliveinterval", sClientAliveInterval, SSHCFG_GLOBAL }, | |
+ { "clientalivecountmax", sClientAliveCountMax, SSHCFG_GLOBAL }, | |
+ { "authorizedkeysfile", sAuthorizedKeysFile, SSHCFG_ALL }, | |
+ { "authorizedkeysfile2", sDeprecated, SSHCFG_ALL }, | |
+ { "useprivilegeseparation", sUsePrivilegeSeparation, SSHCFG_GLOBAL}, | |
+ { "acceptenv", sAcceptEnv, SSHCFG_ALL }, | |
+ { "permittunnel", sPermitTunnel, SSHCFG_ALL }, | |
+ { "permittty", sPermitTTY, SSHCFG_ALL }, | |
+ { "permituserrc", sPermitUserRC, SSHCFG_ALL }, | |
+ { "match", sMatch, SSHCFG_ALL }, | |
+ { "permitopen", sPermitOpen, SSHCFG_ALL }, | |
+ { "forcecommand", sForceCommand, SSHCFG_ALL }, | |
+ { "chrootdirectory", sChrootDirectory, SSHCFG_ALL }, | |
+ { "hostcertificate", sHostCertificate, SSHCFG_GLOBAL }, | |
+ { "revokedkeys", sRevokedKeys, SSHCFG_ALL }, | |
+ { "trustedusercakeys", sTrustedUserCAKeys, SSHCFG_ALL }, | |
+ { "authorizedprincipalsfile", sAuthorizedPrincipalsFile, SSHCFG_ALL }, | |
+ { "kexalgorithms", sKexAlgorithms, SSHCFG_GLOBAL }, | |
+ { "ipqos", sIPQoS, SSHCFG_ALL }, | |
+ { "authorizedkeyscommand", sAuthorizedKeysCommand, SSHCFG_ALL }, | |
+ { "authorizedkeyscommanduser", sAuthorizedKeysCommandUser, SSHCFG_ALL }, | |
+ { "versionaddendum", sVersionAddendum, SSHCFG_GLOBAL }, | |
+ { "authenticationmethods", sAuthenticationMethods, SSHCFG_ALL }, | |
+ { "streamlocalbindmask", sStreamLocalBindMask, SSHCFG_ALL }, | |
+ { "streamlocalbindunlink", sStreamLocalBindUnlink, SSHCFG_ALL }, | |
+ { "allowstreamlocalforwarding", sAllowStreamLocalForwarding, SSHCFG_ALL }, | |
+ { NULL, sBadOption, 0 } | |
+}; | |
+ | |
+static struct { | |
+ int val; | |
+ char *text; | |
+} tunmode_desc[] = { | |
+ { SSH_TUNMODE_NO, "no" }, | |
+ { SSH_TUNMODE_POINTOPOINT, "point-to-point" }, | |
+ { SSH_TUNMODE_ETHERNET, "ethernet" }, | |
+ { SSH_TUNMODE_YES, "yes" }, | |
+ { -1, NULL } | |
+}; | |
+ | |
+/* | |
+ * Returns the number of the token pointed to by cp or sBadOption. | |
+ */ | |
+ | |
+static ServerOpCodes | |
+parse_token(const char *cp, const char *filename, | |
+ int linenum, u_int *flags) | |
+{ | |
+ u_int i; | |
+ | |
+ for (i = 0; keywords[i].name; i++) | |
+ if (strcasecmp(cp, keywords[i].name) == 0) { | |
+ *flags = keywords[i].flags; | |
+ return keywords[i].opcode; | |
+ } | |
+ | |
+ error("%s: line %d: Bad configuration option: %s", | |
+ filename, linenum, cp); | |
+ return sBadOption; | |
+} | |
+ | |
+char * | |
+derelativise_path(const char *path) | |
+{ | |
+ char *expanded, *ret, cwd[MAXPATHLEN]; | |
+ | |
+ expanded = tilde_expand_filename(path, getuid()); | |
+ if (*expanded == '/') | |
+ return expanded; | |
+ if (getcwd(cwd, sizeof(cwd)) == NULL) | |
+ fatal("%s: getcwd: %s", __func__, strerror(errno)); | |
+ xasprintf(&ret, "%s/%s", cwd, expanded); | |
+ free(expanded); | |
+ return ret; | |
+} | |
+ | |
+static void | |
+add_listen_addr(ServerOptions *options, char *addr, int port) | |
+{ | |
+ u_int i; | |
+ | |
+ if (options->num_ports == 0) | |
+ options->ports[options->num_ports++] = SSH_DEFAULT_PORT; | |
+ if (options->address_family == -1) | |
+ options->address_family = AF_UNSPEC; | |
+ if (port == 0) | |
+ for (i = 0; i < options->num_ports; i++) | |
+ add_one_listen_addr(options, addr, options->ports[i]); | |
+ else | |
+ add_one_listen_addr(options, addr, port); | |
+} | |
+ | |
+static void | |
+add_one_listen_addr(ServerOptions *options, char *addr, int port) | |
+{ | |
+ struct addrinfo hints, *ai, *aitop; | |
+ char strport[NI_MAXSERV]; | |
+ int gaierr; | |
+ | |
+ memset(&hints, 0, sizeof(hints)); | |
+ hints.ai_family = options->address_family; | |
+ hints.ai_socktype = SOCK_STREAM; | |
+ hints.ai_flags = (addr == NULL) ? AI_PASSIVE : 0; | |
+ snprintf(strport, sizeof strport, "%d", port); | |
+ if ((gaierr = getaddrinfo(addr, strport, &hints, &aitop)) != 0) | |
+ fatal("bad addr or host: %s (%s)", | |
+ addr ? addr : "<NULL>", | |
+ ssh_gai_strerror(gaierr)); | |
+ for (ai = aitop; ai->ai_next; ai = ai->ai_next) | |
+ ; | |
+ ai->ai_next = options->listen_addrs; | |
+ options->listen_addrs = aitop; | |
+} | |
+ | |
+struct connection_info * | |
+get_connection_info(int populate, int use_dns) | |
+{ | |
+ static struct connection_info ci; | |
+ | |
+ if (!populate) | |
+ return &ci; | |
+ ci.host = get_canonical_hostname(use_dns); | |
+ ci.address = get_remote_ipaddr(); | |
+ ci.laddress = get_local_ipaddr(packet_get_connection_in()); | |
+ ci.lport = get_local_port(); | |
+ return &ci; | |
+} | |
+ | |
+/* | |
+ * The strategy for the Match blocks is that the config file is parsed twice. | |
+ * | |
+ * The first time is at startup. activep is initialized to 1 and the | |
+ * directives in the global context are processed and acted on. Hitting a | |
+ * Match directive unsets activep and the directives inside the block are | |
+ * checked for syntax only. | |
+ * | |
+ * The second time is after a connection has been established but before | |
+ * authentication. activep is initialized to 2 and global config directives | |
+ * are ignored since they have already been processed. If the criteria in a | |
+ * Match block is met, activep is set and the subsequent directives | |
+ * processed and actioned until EOF or another Match block unsets it. Any | |
+ * options set are copied into the main server config. | |
+ * | |
+ * Potential additions/improvements: | |
+ * - Add Match support for pre-kex directives, eg Protocol, Ciphers. | |
+ * | |
+ * - Add a Tag directive (idea from David Leonard) ala pf, eg: | |
+ * Match Address 192.168.0.* | |
+ * Tag trusted | |
+ * Match Group wheel | |
+ * Tag trusted | |
+ * Match Tag trusted | |
+ * AllowTcpForwarding yes | |
+ * GatewayPorts clientspecified | |
+ * [...] | |
+ * | |
+ * - Add a PermittedChannelRequests directive | |
+ * Match Group shell | |
+ * PermittedChannelRequests session,forwarded-tcpip | |
+ */ | |
+ | |
+static int | |
+match_cfg_line_group(const char *grps, int line, const char *user) | |
+{ | |
+ int result = 0; | |
+ struct passwd *pw; | |
+ | |
+ if (user == NULL) | |
+ goto out; | |
+ | |
+ if ((pw = getpwnam(user)) == NULL) { | |
+ debug("Can't match group at line %d because user %.100s does " | |
+ "not exist", line, user); | |
+ } else if (ga_init(pw->pw_name, pw->pw_gid) == 0) { | |
+ debug("Can't Match group because user %.100s not in any group " | |
+ "at line %d", user, line); | |
+ } else if (ga_match_pattern_list(grps) != 1) { | |
+ debug("user %.100s does not match group list %.100s at line %d", | |
+ user, grps, line); | |
+ } else { | |
+ debug("user %.100s matched group list %.100s at line %d", user, | |
+ grps, line); | |
+ result = 1; | |
+ } | |
+out: | |
+ ga_free(); | |
+ return result; | |
+} | |
+ | |
+/* | |
+ * All of the attributes on a single Match line are ANDed together, so we need | |
+ * to check every attribute and set the result to zero if any attribute does | |
+ * not match. | |
+ */ | |
+static int | |
+match_cfg_line(char **condition, int line, struct connection_info *ci) | |
+{ | |
+ int result = 1, attributes = 0, port; | |
+ char *arg, *attrib, *cp = *condition; | |
+ size_t len; | |
+ | |
+ if (ci == NULL) | |
+ debug3("checking syntax for 'Match %s'", cp); | |
+ else | |
+ debug3("checking match for '%s' user %s host %s addr %s " | |
+ "laddr %s lport %d", cp, ci->user ? ci->user : "(null)", | |
+ ci->host ? ci->host : "(null)", | |
+ ci->address ? ci->address : "(null)", | |
+ ci->laddress ? ci->laddress : "(null)", ci->lport); | |
+ | |
+ while ((attrib = strdelim(&cp)) && *attrib != '\0') { | |
+ attributes++; | |
+ if (strcasecmp(attrib, "all") == 0) { | |
+ if (attributes != 1 || | |
+ ((arg = strdelim(&cp)) != NULL && *arg != '\0')) { | |
+ error("'all' cannot be combined with other " | |
+ "Match attributes"); | |
+ return -1; | |
+ } | |
+ *condition = cp; | |
+ return 1; | |
+ } | |
+ if ((arg = strdelim(&cp)) == NULL || *arg == '\0') { | |
+ error("Missing Match criteria for %s", attrib); | |
+ return -1; | |
+ } | |
+ len = strlen(arg); | |
+ if (strcasecmp(attrib, "user") == 0) { | |
+ if (ci == NULL || ci->user == NULL) { | |
+ result = 0; | |
+ continue; | |
+ } | |
+ if (match_pattern_list(ci->user, arg, len, 0) != 1) | |
+ result = 0; | |
+ else | |
+ debug("user %.100s matched 'User %.100s' at " | |
+ "line %d", ci->user, arg, line); | |
+ } else if (strcasecmp(attrib, "group") == 0) { | |
+ if (ci == NULL || ci->user == NULL) { | |
+ result = 0; | |
+ continue; | |
+ } | |
+ switch (match_cfg_line_group(arg, line, ci->user)) { | |
+ case -1: | |
+ return -1; | |
+ case 0: | |
+ result = 0; | |
+ } | |
+ } else if (strcasecmp(attrib, "host") == 0) { | |
+ if (ci == NULL || ci->host == NULL) { | |
+ result = 0; | |
+ continue; | |
+ } | |
+ if (match_hostname(ci->host, arg, len) != 1) | |
+ result = 0; | |
+ else | |
+ debug("connection from %.100s matched 'Host " | |
+ "%.100s' at line %d", ci->host, arg, line); | |
+ } else if (strcasecmp(attrib, "address") == 0) { | |
+ if (ci == NULL || ci->address == NULL) { | |
+ result = 0; | |
+ continue; | |
+ } | |
+ switch (addr_match_list(ci->address, arg)) { | |
+ case 1: | |
+ debug("connection from %.100s matched 'Address " | |
+ "%.100s' at line %d", ci->address, arg, line); | |
+ break; | |
+ case 0: | |
+ case -1: | |
+ result = 0; | |
+ break; | |
+ case -2: | |
+ return -1; | |
+ } | |
+ } else if (strcasecmp(attrib, "localaddress") == 0){ | |
+ if (ci == NULL || ci->laddress == NULL) { | |
+ result = 0; | |
+ continue; | |
+ } | |
+ switch (addr_match_list(ci->laddress, arg)) { | |
+ case 1: | |
+ debug("connection from %.100s matched " | |
+ "'LocalAddress %.100s' at line %d", | |
+ ci->laddress, arg, line); | |
+ break; | |
+ case 0: | |
+ case -1: | |
+ result = 0; | |
+ break; | |
+ case -2: | |
+ return -1; | |
+ } | |
+ } else if (strcasecmp(attrib, "localport") == 0) { | |
+ if ((port = a2port(arg)) == -1) { | |
+ error("Invalid LocalPort '%s' on Match line", | |
+ arg); | |
+ return -1; | |
+ } | |
+ if (ci == NULL || ci->lport == 0) { | |
+ result = 0; | |
+ continue; | |
+ } | |
+ /* TODO support port lists */ | |
+ if (port == ci->lport) | |
+ debug("connection from %.100s matched " | |
+ "'LocalPort %d' at line %d", | |
+ ci->laddress, port, line); | |
+ else | |
+ result = 0; | |
+ } else { | |
+ error("Unsupported Match attribute %s", attrib); | |
+ return -1; | |
+ } | |
+ } | |
+ if (attributes == 0) { | |
+ error("One or more attributes required for Match"); | |
+ return -1; | |
+ } | |
+ if (ci != NULL) | |
+ debug3("match %sfound", result ? "" : "not "); | |
+ *condition = cp; | |
+ return result; | |
+} | |
+ | |
+#define WHITESPACE " \t\r\n" | |
+ | |
+/* Multistate option parsing */ | |
+struct multistate { | |
+ char *key; | |
+ int value; | |
+}; | |
+static const struct multistate multistate_addressfamily[] = { | |
+ { "inet", AF_INET }, | |
+ { "inet6", AF_INET6 }, | |
+ { "any", AF_UNSPEC }, | |
+ { NULL, -1 } | |
+}; | |
+static const struct multistate multistate_permitrootlogin[] = { | |
+ { "without-password", PERMIT_NO_PASSWD }, | |
+ { "forced-commands-only", PERMIT_FORCED_ONLY }, | |
+ { "yes", PERMIT_YES }, | |
+ { "no", PERMIT_NO }, | |
+ { NULL, -1 } | |
+}; | |
+static const struct multistate multistate_compression[] = { | |
+ { "delayed", COMP_DELAYED }, | |
+ { "yes", COMP_ZLIB }, | |
+ { "no", COMP_NONE }, | |
+ { NULL, -1 } | |
+}; | |
+static const struct multistate multistate_gatewayports[] = { | |
+ { "clientspecified", 2 }, | |
+ { "yes", 1 }, | |
+ { "no", 0 }, | |
+ { NULL, -1 } | |
+}; | |
+static const struct multistate multistate_privsep[] = { | |
+ { "yes", PRIVSEP_NOSANDBOX }, | |
+ { "sandbox", PRIVSEP_ON }, | |
+ { "nosandbox", PRIVSEP_NOSANDBOX }, | |
+ { "no", PRIVSEP_OFF }, | |
+ { NULL, -1 } | |
+}; | |
+static const struct multistate multistate_tcpfwd[] = { | |
+ { "yes", FORWARD_ALLOW }, | |
+ { "all", FORWARD_ALLOW }, | |
+ { "no", FORWARD_DENY }, | |
+ { "remote", FORWARD_REMOTE }, | |
+ { "local", FORWARD_LOCAL }, | |
+ { NULL, -1 } | |
+}; | |
+ | |
+int | |
+process_server_config_line(ServerOptions *options, char *line, | |
+ const char *filename, int linenum, int *activep, | |
+ struct connection_info *connectinfo) | |
+{ | |
+ char *cp, **charptr, *arg, *p; | |
+ int cmdline = 0, *intptr, value, value2, n, port; | |
+ SyslogFacility *log_facility_ptr; | |
+ LogLevel *log_level_ptr; | |
+ ServerOpCodes opcode; | |
+ u_int i, flags = 0; | |
+ size_t len; | |
+ long long val64; | |
+ const struct multistate *multistate_ptr; | |
+ | |
+ cp = line; | |
+ if ((arg = strdelim(&cp)) == NULL) | |
+ return 0; | |
+ /* Ignore leading whitespace */ | |
+ if (*arg == '\0') | |
+ arg = strdelim(&cp); | |
+ if (!arg || !*arg || *arg == '#') | |
+ return 0; | |
+ intptr = NULL; | |
+ charptr = NULL; | |
+ opcode = parse_token(arg, filename, linenum, &flags); | |
+ | |
+ if (activep == NULL) { /* We are processing a command line directive */ | |
+ cmdline = 1; | |
+ activep = &cmdline; | |
+ } | |
+ if (*activep && opcode != sMatch) | |
+ debug3("%s:%d setting %s %s", filename, linenum, arg, cp); | |
+ if (*activep == 0 && !(flags & SSHCFG_MATCH)) { | |
+ if (connectinfo == NULL) { | |
+ fatal("%s line %d: Directive '%s' is not allowed " | |
+ "within a Match block", filename, linenum, arg); | |
+ } else { /* this is a directive we have already processed */ | |
+ while (arg) | |
+ arg = strdelim(&cp); | |
+ return 0; | |
+ } | |
+ } | |
+ | |
+ switch (opcode) { | |
+ /* Portable-specific options */ | |
+ case sUsePAM: | |
+ intptr = &options->use_pam; | |
+ goto parse_flag; | |
+ | |
+ /* Standard Options */ | |
+ case sBadOption: | |
+ return -1; | |
+ case sPort: | |
+ /* ignore ports from configfile if cmdline specifies ports */ | |
+ if (options->ports_from_cmdline) | |
+ return 0; | |
+ if (options->listen_addrs != NULL) | |
+ fatal("%s line %d: ports must be specified before " | |
+ "ListenAddress.", filename, linenum); | |
+ if (options->num_ports >= MAX_PORTS) | |
+ fatal("%s line %d: too many ports.", | |
+ filename, linenum); | |
+ arg = strdelim(&cp); | |
+ if (!arg || *arg == '\0') | |
+ fatal("%s line %d: missing port number.", | |
+ filename, linenum); | |
+ options->ports[options->num_ports++] = a2port(arg); | |
+ if (options->ports[options->num_ports-1] <= 0) | |
+ fatal("%s line %d: Badly formatted port number.", | |
+ filename, linenum); | |
+ break; | |
+ | |
+ case sServerKeyBits: | |
+ intptr = &options->server_key_bits; | |
+ parse_int: | |
+ arg = strdelim(&cp); | |
+ if (!arg || *arg == '\0') | |
+ fatal("%s line %d: missing integer value.", | |
+ filename, linenum); | |
+ value = atoi(arg); | |
+ if (*activep && *intptr == -1) | |
+ *intptr = value; | |
+ break; | |
+ | |
+ case sLoginGraceTime: | |
+ intptr = &options->login_grace_time; | |
+ parse_time: | |
+ arg = strdelim(&cp); | |
+ if (!arg || *arg == '\0') | |
+ fatal("%s line %d: missing time value.", | |
+ filename, linenum); | |
+ if ((value = convtime(arg)) == -1) | |
+ fatal("%s line %d: invalid time value.", | |
+ filename, linenum); | |
+ if (*intptr == -1) | |
+ *intptr = value; | |
+ break; | |
+ | |
+ case sKeyRegenerationTime: | |
+ intptr = &options->key_regeneration_time; | |
+ goto parse_time; | |
+ | |
+ case sListenAddress: | |
+ arg = strdelim(&cp); | |
+ if (arg == NULL || *arg == '\0') | |
+ fatal("%s line %d: missing address", | |
+ filename, linenum); | |
+ /* check for bare IPv6 address: no "[]" and 2 or more ":" */ | |
+ if (strchr(arg, '[') == NULL && (p = strchr(arg, ':')) != NULL | |
+ && strchr(p+1, ':') != NULL) { | |
+ add_listen_addr(options, arg, 0); | |
+ break; | |
+ } | |
+ p = hpdelim(&arg); | |
+ if (p == NULL) | |
+ fatal("%s line %d: bad address:port usage", | |
+ filename, linenum); | |
+ p = cleanhostname(p); | |
+ if (arg == NULL) | |
+ port = 0; | |
+ else if ((port = a2port(arg)) <= 0) | |
+ fatal("%s line %d: bad port number", filename, linenum); | |
+ | |
+ add_listen_addr(options, p, port); | |
+ | |
+ break; | |
+ | |
+ case sAddressFamily: | |
+ intptr = &options->address_family; | |
+ multistate_ptr = multistate_addressfamily; | |
+ if (options->listen_addrs != NULL) | |
+ fatal("%s line %d: address family must be specified " | |
+ "before ListenAddress.", filename, linenum); | |
+ parse_multistate: | |
+ arg = strdelim(&cp); | |
+ if (!arg || *arg == '\0') | |
+ fatal("%s line %d: missing argument.", | |
+ filename, linenum); | |
+ value = -1; | |
+ for (i = 0; multistate_ptr[i].key != NULL; i++) { | |
+ if (strcasecmp(arg, multistate_ptr[i].key) == 0) { | |
+ value = multistate_ptr[i].value; | |
+ break; | |
+ } | |
+ } | |
+ if (value == -1) | |
+ fatal("%s line %d: unsupported option \"%s\".", | |
+ filename, linenum, arg); | |
+ if (*activep && *intptr == -1) | |
+ *intptr = value; | |
+ break; | |
+ | |
+ case sHostKeyFile: | |
+ intptr = &options->num_host_key_files; | |
+ if (*intptr >= MAX_HOSTKEYS) | |
+ fatal("%s line %d: too many host keys specified (max %d).", | |
+ filename, linenum, MAX_HOSTKEYS); | |
+ charptr = &options->host_key_files[*intptr]; | |
+ parse_filename: | |
+ arg = strdelim(&cp); | |
+ if (!arg || *arg == '\0') | |
+ fatal("%s line %d: missing file name.", | |
+ filename, linenum); | |
+ if (*activep && *charptr == NULL) { | |
+ *charptr = derelativise_path(arg); | |
+ /* increase optional counter */ | |
+ if (intptr != NULL) | |
+ *intptr = *intptr + 1; | |
+ } | |
+ break; | |
+ | |
+ case sHostKeyAgent: | |
+ charptr = &options->host_key_agent; | |
+ arg = strdelim(&cp); | |
+ if (!arg || *arg == '\0') | |
+ fatal("%s line %d: missing socket name.", | |
+ filename, linenum); | |
+ if (*activep && *charptr == NULL) | |
+ *charptr = !strcmp(arg, SSH_AUTHSOCKET_ENV_NAME) ? | |
+ xstrdup(arg) : derelativise_path(arg); | |
+ break; | |
+ | |
+ case sHostCertificate: | |
+ intptr = &options->num_host_cert_files; | |
+ if (*intptr >= MAX_HOSTKEYS) | |
+ fatal("%s line %d: too many host certificates " | |
+ "specified (max %d).", filename, linenum, | |
+ MAX_HOSTCERTS); | |
+ charptr = &options->host_cert_files[*intptr]; | |
+ goto parse_filename; | |
+ break; | |
+ | |
+ case sPidFile: | |
+ charptr = &options->pid_file; | |
+ goto parse_filename; | |
+ | |
+ case sPermitRootLogin: | |
+ intptr = &options->permit_root_login; | |
+ multistate_ptr = multistate_permitrootlogin; | |
+ goto parse_multistate; | |
+ | |
+ case sIgnoreRhosts: | |
+ intptr = &options->ignore_rhosts; | |
+ parse_flag: | |
+ arg = strdelim(&cp); | |
+ if (!arg || *arg == '\0') | |
+ fatal("%s line %d: missing yes/no argument.", | |
+ filename, linenum); | |
+ value = 0; /* silence compiler */ | |
+ if (strcmp(arg, "yes") == 0) | |
+ value = 1; | |
+ else if (strcmp(arg, "no") == 0) | |
+ value = 0; | |
+ else | |
+ fatal("%s line %d: Bad yes/no argument: %s", | |
+ filename, linenum, arg); | |
+ if (*activep && *intptr == -1) | |
+ *intptr = value; | |
+ break; | |
+ | |
+ case sIgnoreUserKnownHosts: | |
+ intptr = &options->ignore_user_known_hosts; | |
+ goto parse_flag; | |
+ | |
+ case sRhostsRSAAuthentication: | |
+ intptr = &options->rhosts_rsa_authentication; | |
+ goto parse_flag; | |
+ | |
+ case sHostbasedAuthentication: | |
+ intptr = &options->hostbased_authentication; | |
+ goto parse_flag; | |
+ | |
+ case sHostbasedUsesNameFromPacketOnly: | |
+ intptr = &options->hostbased_uses_name_from_packet_only; | |
+ goto parse_flag; | |
+ | |
+ case sRSAAuthentication: | |
+ intptr = &options->rsa_authentication; | |
+ goto parse_flag; | |
+ | |
+ case sPubkeyAuthentication: | |
+ intptr = &options->pubkey_authentication; | |
+ goto parse_flag; | |
+ | |
+ case sKerberosAuthentication: | |
+ intptr = &options->kerberos_authentication; | |
+ goto parse_flag; | |
+ | |
+ case sKerberosOrLocalPasswd: | |
+ intptr = &options->kerberos_or_local_passwd; | |
+ goto parse_flag; | |
+ | |
+ case sKerberosTicketCleanup: | |
+ intptr = &options->kerberos_ticket_cleanup; | |
+ goto parse_flag; | |
+ | |
+ case sKerberosGetAFSToken: | |
+ intptr = &options->kerberos_get_afs_token; | |
+ goto parse_flag; | |
+ | |
+ case sGssAuthentication: | |
+ intptr = &options->gss_authentication; | |
+ goto parse_flag; | |
+ | |
+ case sGssCleanupCreds: | |
+ intptr = &options->gss_cleanup_creds; | |
+ goto parse_flag; | |
+ | |
+ case sPasswordAuthentication: | |
+ intptr = &options->password_authentication; | |
+ goto parse_flag; | |
+ | |
+ case sKbdInteractiveAuthentication: | |
+ intptr = &options->kbd_interactive_authentication; | |
+ goto parse_flag; | |
+ | |
+ case sChallengeResponseAuthentication: | |
+ intptr = &options->challenge_response_authentication; | |
+ goto parse_flag; | |
+ | |
+ case sPrintMotd: | |
+ intptr = &options->print_motd; | |
+ goto parse_flag; | |
+ | |
+ case sPrintLastLog: | |
+ intptr = &options->print_lastlog; | |
+ goto parse_flag; | |
+ | |
+ case sX11Forwarding: | |
+ intptr = &options->x11_forwarding; | |
+ goto parse_flag; | |
+ | |
+ case sX11DisplayOffset: | |
+ intptr = &options->x11_display_offset; | |
+ goto parse_int; | |
+ | |
+ case sX11UseLocalhost: | |
+ intptr = &options->x11_use_localhost; | |
+ goto parse_flag; | |
+ | |
+ case sXAuthLocation: | |
+ charptr = &options->xauth_location; | |
+ goto parse_filename; | |
+ | |
+ case sPermitTTY: | |
+ intptr = &options->permit_tty; | |
+ goto parse_flag; | |
+ | |
+ case sPermitUserRC: | |
+ intptr = &options->permit_user_rc; | |
+ goto parse_flag; | |
+ | |
+ case sStrictModes: | |
+ intptr = &options->strict_modes; | |
+ goto parse_flag; | |
+ | |
+ case sTCPKeepAlive: | |
+ intptr = &options->tcp_keep_alive; | |
+ goto parse_flag; | |
+ | |
+ case sEmptyPasswd: | |
+ intptr = &options->permit_empty_passwd; | |
+ goto parse_flag; | |
+ | |
+ case sPermitUserEnvironment: | |
+ intptr = &options->permit_user_env; | |
+ goto parse_flag; | |
+ | |
+ case sUseLogin: | |
+ intptr = &options->use_login; | |
+ goto parse_flag; | |
+ | |
+ case sCompression: | |
+ intptr = &options->compression; | |
+ multistate_ptr = multistate_compression; | |
+ goto parse_multistate; | |
+ | |
+ case sRekeyLimit: | |
+ arg = strdelim(&cp); | |
+ if (!arg || *arg == '\0') | |
+ fatal("%.200s line %d: Missing argument.", filename, | |
+ linenum); | |
+ if (strcmp(arg, "default") == 0) { | |
+ val64 = 0; | |
+ } else { | |
+ if (scan_scaled(arg, &val64) == -1) | |
+ fatal("%.200s line %d: Bad number '%s': %s", | |
+ filename, linenum, arg, strerror(errno)); | |
+ /* check for too-large or too-small limits */ | |
+ if (val64 > UINT_MAX) | |
+ fatal("%.200s line %d: RekeyLimit too large", | |
+ filename, linenum); | |
+ if (val64 != 0 && val64 < 16) | |
+ fatal("%.200s line %d: RekeyLimit too small", | |
+ filename, linenum); | |
+ } | |
+ if (*activep && options->rekey_limit == -1) | |
+ options->rekey_limit = (u_int32_t)val64; | |
+ if (cp != NULL) { /* optional rekey interval present */ | |
+ if (strcmp(cp, "none") == 0) { | |
+ (void)strdelim(&cp); /* discard */ | |
+ break; | |
+ } | |
+ intptr = &options->rekey_interval; | |
+ goto parse_time; | |
+ } | |
+ break; | |
+ | |
+ case sGatewayPorts: | |
+ intptr = &options->fwd_opts.gateway_ports; | |
+ multistate_ptr = multistate_gatewayports; | |
+ goto parse_multistate; | |
+ | |
+ case sUseDNS: | |
+ intptr = &options->use_dns; | |
+ goto parse_flag; | |
+ | |
+ case sLogFacility: | |
+ log_facility_ptr = &options->log_facility; | |
+ arg = strdelim(&cp); | |
+ value = log_facility_number(arg); | |
+ if (value == SYSLOG_FACILITY_NOT_SET) | |
+ fatal("%.200s line %d: unsupported log facility '%s'", | |
+ filename, linenum, arg ? arg : "<NONE>"); | |
+ if (*log_facility_ptr == -1) | |
+ *log_facility_ptr = (SyslogFacility) value; | |
+ break; | |
+ | |
+ case sLogLevel: | |
+ log_level_ptr = &options->log_level; | |
+ arg = strdelim(&cp); | |
+ value = log_level_number(arg); | |
+ if (value == SYSLOG_LEVEL_NOT_SET) | |
+ fatal("%.200s line %d: unsupported log level '%s'", | |
+ filename, linenum, arg ? arg : "<NONE>"); | |
+ if (*log_level_ptr == -1) | |
+ *log_level_ptr = (LogLevel) value; | |
+ break; | |
+ | |
+ case sAllowTcpForwarding: | |
+ intptr = &options->allow_tcp_forwarding; | |
+ multistate_ptr = multistate_tcpfwd; | |
+ goto parse_multistate; | |
+ | |
+ case sAllowStreamLocalForwarding: | |
+ intptr = &options->allow_streamlocal_forwarding; | |
+ multistate_ptr = multistate_tcpfwd; | |
+ goto parse_multistate; | |
+ | |
+ case sAllowAgentForwarding: | |
+ intptr = &options->allow_agent_forwarding; | |
+ goto parse_flag; | |
+ | |
+ case sUsePrivilegeSeparation: | |
+ intptr = &use_privsep; | |
+ multistate_ptr = multistate_privsep; | |
+ goto parse_multistate; | |
+ | |
+ case sAllowUsers: | |
+ while ((arg = strdelim(&cp)) && *arg != '\0') { | |
+ if (options->num_allow_users >= MAX_ALLOW_USERS) | |
+ fatal("%s line %d: too many allow users.", | |
+ filename, linenum); | |
+ if (!*activep) | |
+ continue; | |
+ options->allow_users[options->num_allow_users++] = | |
+ xstrdup(arg); | |
+ } | |
+ break; | |
+ | |
+ case sDenyUsers: | |
+ while ((arg = strdelim(&cp)) && *arg != '\0') { | |
+ if (options->num_deny_users >= MAX_DENY_USERS) | |
+ fatal("%s line %d: too many deny users.", | |
+ filename, linenum); | |
+ if (!*activep) | |
+ continue; | |
+ options->deny_users[options->num_deny_users++] = | |
+ xstrdup(arg); | |
+ } | |
+ break; | |
+ | |
+ case sAllowGroups: | |
+ while ((arg = strdelim(&cp)) && *arg != '\0') { | |
+ if (options->num_allow_groups >= MAX_ALLOW_GROUPS) | |
+ fatal("%s line %d: too many allow groups.", | |
+ filename, linenum); | |
+ if (!*activep) | |
+ continue; | |
+ options->allow_groups[options->num_allow_groups++] = | |
+ xstrdup(arg); | |
+ } | |
+ break; | |
+ | |
+ case sDenyGroups: | |
+ while ((arg = strdelim(&cp)) && *arg != '\0') { | |
+ if (options->num_deny_groups >= MAX_DENY_GROUPS) | |
+ fatal("%s line %d: too many deny groups.", | |
+ filename, linenum); | |
+ if (!*activep) | |
+ continue; | |
+ options->deny_groups[options->num_deny_groups++] = | |
+ xstrdup(arg); | |
+ } | |
+ break; | |
+ | |
+ case sCiphers: | |
+ arg = strdelim(&cp); | |
+ if (!arg || *arg == '\0') | |
+ fatal("%s line %d: Missing argument.", filename, linenum); | |
+ if (!ciphers_valid(arg)) | |
+ fatal("%s line %d: Bad SSH2 cipher spec '%s'.", | |
+ filename, linenum, arg ? arg : "<NONE>"); | |
+ if (options->ciphers == NULL) | |
+ options->ciphers = xstrdup(arg); | |
+ break; | |
+ | |
+ case sMacs: | |
+ arg = strdelim(&cp); | |
+ if (!arg || *arg == '\0') | |
+ fatal("%s line %d: Missing argument.", filename, linenum); | |
+ if (!mac_valid(arg)) | |
+ fatal("%s line %d: Bad SSH2 mac spec '%s'.", | |
+ filename, linenum, arg ? arg : "<NONE>"); | |
+ if (options->macs == NULL) | |
+ options->macs = xstrdup(arg); | |
+ break; | |
+ | |
+ case sKexAlgorithms: | |
+ arg = strdelim(&cp); | |
+ if (!arg || *arg == '\0') | |
+ fatal("%s line %d: Missing argument.", | |
+ filename, linenum); | |
+ if (!kex_names_valid(arg)) | |
+ fatal("%s line %d: Bad SSH2 KexAlgorithms '%s'.", | |
+ filename, linenum, arg ? arg : "<NONE>"); | |
+ if (options->kex_algorithms == NULL) | |
+ options->kex_algorithms = xstrdup(arg); | |
+ break; | |
+ | |
+ case sProtocol: | |
+ intptr = &options->protocol; | |
+ arg = strdelim(&cp); | |
+ if (!arg || *arg == '\0') | |
+ fatal("%s line %d: Missing argument.", filename, linenum); | |
+ value = proto_spec(arg); | |
+ if (value == SSH_PROTO_UNKNOWN) | |
+ fatal("%s line %d: Bad protocol spec '%s'.", | |
+ filename, linenum, arg ? arg : "<NONE>"); | |
+ if (*intptr == SSH_PROTO_UNKNOWN) | |
+ *intptr = value; | |
+ break; | |
+ | |
+ case sSubsystem: | |
+ if (options->num_subsystems >= MAX_SUBSYSTEMS) { | |
+ fatal("%s line %d: too many subsystems defined.", | |
+ filename, linenum); | |
+ } | |
+ arg = strdelim(&cp); | |
+ if (!arg || *arg == '\0') | |
+ fatal("%s line %d: Missing subsystem name.", | |
+ filename, linenum); | |
+ if (!*activep) { | |
+ arg = strdelim(&cp); | |
+ break; | |
+ } | |
+ for (i = 0; i < options->num_subsystems; i++) | |
+ if (strcmp(arg, options->subsystem_name[i]) == 0) | |
+ fatal("%s line %d: Subsystem '%s' already defined.", | |
+ filename, linenum, arg); | |
+ options->subsystem_name[options->num_subsystems] = xstrdup(arg); | |
+ arg = strdelim(&cp); | |
+ if (!arg || *arg == '\0') | |
+ fatal("%s line %d: Missing subsystem command.", | |
+ filename, linenum); | |
+ options->subsystem_command[options->num_subsystems] = xstrdup(arg); | |
+ | |
+ /* Collect arguments (separate to executable) */ | |
+ p = xstrdup(arg); | |
+ len = strlen(p) + 1; | |
+ while ((arg = strdelim(&cp)) != NULL && *arg != '\0') { | |
+ len += 1 + strlen(arg); | |
+ p = xrealloc(p, 1, len); | |
+ strlcat(p, " ", len); | |
+ strlcat(p, arg, len); | |
+ } | |
+ options->subsystem_args[options->num_subsystems] = p; | |
+ options->num_subsystems++; | |
+ break; | |
+ | |
+ case sMaxStartups: | |
+ arg = strdelim(&cp); | |
+ if (!arg || *arg == '\0') | |
+ fatal("%s line %d: Missing MaxStartups spec.", | |
+ filename, linenum); | |
+ if ((n = sscanf(arg, "%d:%d:%d", | |
+ &options->max_startups_begin, | |
+ &options->max_startups_rate, | |
+ &options->max_startups)) == 3) { | |
+ if (options->max_startups_begin > | |
+ options->max_startups || | |
+ options->max_startups_rate > 100 || | |
+ options->max_startups_rate < 1) | |
+ fatal("%s line %d: Illegal MaxStartups spec.", | |
+ filename, linenum); | |
+ } else if (n != 1) | |
+ fatal("%s line %d: Illegal MaxStartups spec.", | |
+ filename, linenum); | |
+ else | |
+ options->max_startups = options->max_startups_begin; | |
+ break; | |
+ | |
+ case sMaxAuthTries: | |
+ intptr = &options->max_authtries; | |
+ goto parse_int; | |
+ | |
+ case sMaxSessions: | |
+ intptr = &options->max_sessions; | |
+ goto parse_int; | |
+ | |
+ case sBanner: | |
+ charptr = &options->banner; | |
+ goto parse_filename; | |
+ | |
+ /* | |
+ * These options can contain %X options expanded at | |
+ * connect time, so that you can specify paths like: | |
+ * | |
+ * AuthorizedKeysFile /etc/ssh_keys/%u | |
+ */ | |
+ case sAuthorizedKeysFile: | |
+ if (*activep && options->num_authkeys_files == 0) { | |
+ while ((arg = strdelim(&cp)) && *arg != '\0') { | |
+ if (options->num_authkeys_files >= | |
+ MAX_AUTHKEYS_FILES) | |
+ fatal("%s line %d: " | |
+ "too many authorized keys files.", | |
+ filename, linenum); | |
+ options->authorized_keys_files[ | |
+ options->num_authkeys_files++] = | |
+ tilde_expand_filename(arg, getuid()); | |
+ } | |
+ } | |
+ return 0; | |
+ | |
+ case sAuthorizedPrincipalsFile: | |
+ charptr = &options->authorized_principals_file; | |
+ arg = strdelim(&cp); | |
+ if (!arg || *arg == '\0') | |
+ fatal("%s line %d: missing file name.", | |
+ filename, linenum); | |
+ if (*activep && *charptr == NULL) { | |
+ *charptr = tilde_expand_filename(arg, getuid()); | |
+ /* increase optional counter */ | |
+ if (intptr != NULL) | |
+ *intptr = *intptr + 1; | |
+ } | |
+ break; | |
+ | |
+ case sClientAliveInterval: | |
+ intptr = &options->client_alive_interval; | |
+ goto parse_time; | |
+ | |
+ case sClientAliveCountMax: | |
+ intptr = &options->client_alive_count_max; | |
+ goto parse_int; | |
+ | |
+ case sAcceptEnv: | |
+ while ((arg = strdelim(&cp)) && *arg != '\0') { | |
+ if (strchr(arg, '=') != NULL) | |
+ fatal("%s line %d: Invalid environment name.", | |
+ filename, linenum); | |
+ if (options->num_accept_env >= MAX_ACCEPT_ENV) | |
+ fatal("%s line %d: too many allow env.", | |
+ filename, linenum); | |
+ if (!*activep) | |
+ continue; | |
+ options->accept_env[options->num_accept_env++] = | |
+ xstrdup(arg); | |
+ } | |
+ break; | |
+ | |
+ case sPermitTunnel: | |
+ intptr = &options->permit_tun; | |
+ arg = strdelim(&cp); | |
+ if (!arg || *arg == '\0') | |
+ fatal("%s line %d: Missing yes/point-to-point/" | |
+ "ethernet/no argument.", filename, linenum); | |
+ value = -1; | |
+ for (i = 0; tunmode_desc[i].val != -1; i++) | |
+ if (strcmp(tunmode_desc[i].text, arg) == 0) { | |
+ value = tunmode_desc[i].val; | |
+ break; | |
+ } | |
+ if (value == -1) | |
+ fatal("%s line %d: Bad yes/point-to-point/ethernet/" | |
+ "no argument: %s", filename, linenum, arg); | |
+ if (*intptr == -1) | |
+ *intptr = value; | |
+ break; | |
+ | |
+ case sMatch: | |
+ if (cmdline) | |
+ fatal("Match directive not supported as a command-line " | |
+ "option"); | |
+ value = match_cfg_line(&cp, linenum, connectinfo); | |
+ if (value < 0) | |
+ fatal("%s line %d: Bad Match condition", filename, | |
+ linenum); | |
+ *activep = value; | |
+ break; | |
+ | |
+ case sPermitOpen: | |
+ arg = strdelim(&cp); | |
+ if (!arg || *arg == '\0') | |
+ fatal("%s line %d: missing PermitOpen specification", | |
+ filename, linenum); | |
+ n = options->num_permitted_opens; /* modified later */ | |
+ if (strcmp(arg, "any") == 0) { | |
+ if (*activep && n == -1) { | |
+ channel_clear_adm_permitted_opens(); | |
+ options->num_permitted_opens = 0; | |
+ } | |
+ break; | |
+ } | |
+ if (strcmp(arg, "none") == 0) { | |
+ if (*activep && n == -1) { | |
+ options->num_permitted_opens = 1; | |
+ channel_disable_adm_local_opens(); | |
+ } | |
+ break; | |
+ } | |
+ if (*activep && n == -1) | |
+ channel_clear_adm_permitted_opens(); | |
+ for (; arg != NULL && *arg != '\0'; arg = strdelim(&cp)) { | |
+ p = hpdelim(&arg); | |
+ if (p == NULL) | |
+ fatal("%s line %d: missing host in PermitOpen", | |
+ filename, linenum); | |
+ p = cleanhostname(p); | |
+ if (arg == NULL || ((port = permitopen_port(arg)) < 0)) | |
+ fatal("%s line %d: bad port number in " | |
+ "PermitOpen", filename, linenum); | |
+ if (*activep && n == -1) | |
+ options->num_permitted_opens = | |
+ channel_add_adm_permitted_opens(p, port); | |
+ } | |
+ break; | |
+ | |
+ case sForceCommand: | |
+ if (cp == NULL) | |
+ fatal("%.200s line %d: Missing argument.", filename, | |
+ linenum); | |
+ len = strspn(cp, WHITESPACE); | |
+ if (*activep && options->adm_forced_command == NULL) | |
+ options->adm_forced_command = xstrdup(cp + len); | |
+ return 0; | |
+ | |
+ case sChrootDirectory: | |
+ charptr = &options->chroot_directory; | |
+ | |
+ arg = strdelim(&cp); | |
+ if (!arg || *arg == '\0') | |
+ fatal("%s line %d: missing file name.", | |
+ filename, linenum); | |
+ if (*activep && *charptr == NULL) | |
+ *charptr = xstrdup(arg); | |
+ break; | |
+ | |
+ case sTrustedUserCAKeys: | |
+ charptr = &options->trusted_user_ca_keys; | |
+ goto parse_filename; | |
+ | |
+ case sRevokedKeys: | |
+ charptr = &options->revoked_keys_file; | |
+ goto parse_filename; | |
+ | |
+ case sIPQoS: | |
+ arg = strdelim(&cp); | |
+ if ((value = parse_ipqos(arg)) == -1) | |
+ fatal("%s line %d: Bad IPQoS value: %s", | |
+ filename, linenum, arg); | |
+ arg = strdelim(&cp); | |
+ if (arg == NULL) | |
+ value2 = value; | |
+ else if ((value2 = parse_ipqos(arg)) == -1) | |
+ fatal("%s line %d: Bad IPQoS value: %s", | |
+ filename, linenum, arg); | |
+ if (*activep) { | |
+ options->ip_qos_interactive = value; | |
+ options->ip_qos_bulk = value2; | |
+ } | |
+ break; | |
+ | |
+ case sVersionAddendum: | |
+ if (cp == NULL) | |
+ fatal("%.200s line %d: Missing argument.", filename, | |
+ linenum); | |
+ len = strspn(cp, WHITESPACE); | |
+ if (*activep && options->version_addendum == NULL) { | |
+ if (strcasecmp(cp + len, "none") == 0) | |
+ options->version_addendum = xstrdup(""); | |
+ else if (strchr(cp + len, '\r') != NULL) | |
+ fatal("%.200s line %d: Invalid argument", | |
+ filename, linenum); | |
+ else | |
+ options->version_addendum = xstrdup(cp + len); | |
+ } | |
+ return 0; | |
+ | |
+ case sAuthorizedKeysCommand: | |
+ len = strspn(cp, WHITESPACE); | |
+ if (*activep && options->authorized_keys_command == NULL) { | |
+ if (cp[len] != '/' && strcasecmp(cp + len, "none") != 0) | |
+ fatal("%.200s line %d: AuthorizedKeysCommand " | |
+ "must be an absolute path", | |
+ filename, linenum); | |
+ options->authorized_keys_command = xstrdup(cp + len); | |
+ } | |
+ return 0; | |
+ | |
+ case sAuthorizedKeysCommandUser: | |
+ charptr = &options->authorized_keys_command_user; | |
+ | |
+ arg = strdelim(&cp); | |
+ if (*activep && *charptr == NULL) | |
+ *charptr = xstrdup(arg); | |
+ break; | |
+ | |
+ case sAuthenticationMethods: | |
+ if (*activep && options->num_auth_methods == 0) { | |
+ while ((arg = strdelim(&cp)) && *arg != '\0') { | |
+ if (options->num_auth_methods >= | |
+ MAX_AUTH_METHODS) | |
+ fatal("%s line %d: " | |
+ "too many authentication methods.", | |
+ filename, linenum); | |
+ if (auth2_methods_valid(arg, 0) != 0) | |
+ fatal("%s line %d: invalid " | |
+ "authentication method list.", | |
+ filename, linenum); | |
+ options->auth_methods[ | |
+ options->num_auth_methods++] = xstrdup(arg); | |
+ } | |
+ } | |
+ return 0; | |
+ | |
+ case sStreamLocalBindMask: | |
+ arg = strdelim(&cp); | |
+ if (!arg || *arg == '\0') | |
+ fatal("%s line %d: missing StreamLocalBindMask argument.", | |
+ filename, linenum); | |
+ /* Parse mode in octal format */ | |
+ value = strtol(arg, &p, 8); | |
+ if (arg == p || value < 0 || value > 0777) | |
+ fatal("%s line %d: Bad mask.", filename, linenum); | |
+ options->fwd_opts.streamlocal_bind_mask = (mode_t)value; | |
+ break; | |
+ | |
+ case sStreamLocalBindUnlink: | |
+ intptr = &options->fwd_opts.streamlocal_bind_unlink; | |
+ goto parse_flag; | |
+ | |
+ case sDeprecated: | |
+ logit("%s line %d: Deprecated option %s", | |
+ filename, linenum, arg); | |
+ while (arg) | |
+ arg = strdelim(&cp); | |
+ break; | |
+ | |
+ case sUnsupported: | |
+ logit("%s line %d: Unsupported option %s", | |
+ filename, linenum, arg); | |
+ while (arg) | |
+ arg = strdelim(&cp); | |
+ break; | |
+ | |
+ default: | |
+ fatal("%s line %d: Missing handler for opcode %s (%d)", | |
+ filename, linenum, arg, opcode); | |
+ } | |
+ if ((arg = strdelim(&cp)) != NULL && *arg != '\0') | |
+ fatal("%s line %d: garbage at end of line; \"%.200s\".", | |
+ filename, linenum, arg); | |
+ return 0; | |
+} | |
+ | |
+/* Reads the server configuration file. */ | |
+ | |
+void | |
+load_server_config(const char *filename, Buffer *conf) | |
+{ | |
+ char line[4096], *cp; | |
+ FILE *f; | |
+ int lineno = 0; | |
+ | |
+ debug2("%s: filename %s", __func__, filename); | |
+ if ((f = fopen(filename, "r")) == NULL) { | |
+ perror(filename); | |
+ exit(1); | |
+ } | |
+ buffer_clear(conf); | |
+ while (fgets(line, sizeof(line), f)) { | |
+ lineno++; | |
+ if (strlen(line) == sizeof(line) - 1) | |
+ fatal("%s line %d too long", filename, lineno); | |
+ /* | |
+ * Trim out comments and strip whitespace | |
+ * NB - preserve newlines, they are needed to reproduce | |
+ * line numbers later for error messages | |
+ */ | |
+ if ((cp = strchr(line, '#')) != NULL) | |
+ memcpy(cp, "\n", 2); | |
+ cp = line + strspn(line, " \t\r"); | |
+ | |
+ buffer_append(conf, cp, strlen(cp)); | |
+ } | |
+ buffer_append(conf, "\0", 1); | |
+ fclose(f); | |
+ debug2("%s: done config len = %d", __func__, buffer_len(conf)); | |
+} | |
+ | |
+void | |
+parse_server_match_config(ServerOptions *options, | |
+ struct connection_info *connectinfo) | |
+{ | |
+ ServerOptions mo; | |
+ | |
+ initialize_server_options(&mo); | |
+ parse_server_config(&mo, "reprocess config", &cfg, connectinfo); | |
+ copy_set_server_options(options, &mo, 0); | |
+} | |
+ | |
+int parse_server_match_testspec(struct connection_info *ci, char *spec) | |
+{ | |
+ char *p; | |
+ | |
+ while ((p = strsep(&spec, ",")) && *p != '\0') { | |
+ if (strncmp(p, "addr=", 5) == 0) { | |
+ ci->address = xstrdup(p + 5); | |
+ } else if (strncmp(p, "host=", 5) == 0) { | |
+ ci->host = xstrdup(p + 5); | |
+ } else if (strncmp(p, "user=", 5) == 0) { | |
+ ci->user = xstrdup(p + 5); | |
+ } else if (strncmp(p, "laddr=", 6) == 0) { | |
+ ci->laddress = xstrdup(p + 6); | |
+ } else if (strncmp(p, "lport=", 6) == 0) { | |
+ ci->lport = a2port(p + 6); | |
+ if (ci->lport == -1) { | |
+ fprintf(stderr, "Invalid port '%s' in test mode" | |
+ " specification %s\n", p+6, p); | |
+ return -1; | |
+ } | |
+ } else { | |
+ fprintf(stderr, "Invalid test mode specification %s\n", | |
+ p); | |
+ return -1; | |
+ } | |
+ } | |
+ return 0; | |
+} | |
+ | |
+/* | |
+ * returns 1 for a complete spec, 0 for partial spec and -1 for an | |
+ * empty spec. | |
+ */ | |
+int server_match_spec_complete(struct connection_info *ci) | |
+{ | |
+ if (ci->user && ci->host && ci->address) | |
+ return 1; /* complete */ | |
+ if (!ci->user && !ci->host && !ci->address) | |
+ return -1; /* empty */ | |
+ return 0; /* partial */ | |
+} | |
+ | |
+/* | |
+ * Copy any supported values that are set. | |
+ * | |
+ * If the preauth flag is set, we do not bother copying the string or | |
+ * array values that are not used pre-authentication, because any that we | |
+ * do use must be explictly sent in mm_getpwnamallow(). | |
+ */ | |
+void | |
+copy_set_server_options(ServerOptions *dst, ServerOptions *src, int preauth) | |
+{ | |
+#define M_CP_INTOPT(n) do {\ | |
+ if (src->n != -1) \ | |
+ dst->n = src->n; \ | |
+} while (0) | |
+ | |
+ M_CP_INTOPT(password_authentication); | |
+ M_CP_INTOPT(gss_authentication); | |
+ M_CP_INTOPT(rsa_authentication); | |
+ M_CP_INTOPT(pubkey_authentication); | |
+ M_CP_INTOPT(kerberos_authentication); | |
+ M_CP_INTOPT(hostbased_authentication); | |
+ M_CP_INTOPT(hostbased_uses_name_from_packet_only); | |
+ M_CP_INTOPT(kbd_interactive_authentication); | |
+ M_CP_INTOPT(permit_root_login); | |
+ M_CP_INTOPT(permit_empty_passwd); | |
+ | |
+ M_CP_INTOPT(allow_tcp_forwarding); | |
+ M_CP_INTOPT(allow_streamlocal_forwarding); | |
+ M_CP_INTOPT(allow_agent_forwarding); | |
+ M_CP_INTOPT(permit_tun); | |
+ M_CP_INTOPT(fwd_opts.gateway_ports); | |
+ M_CP_INTOPT(x11_display_offset); | |
+ M_CP_INTOPT(x11_forwarding); | |
+ M_CP_INTOPT(x11_use_localhost); | |
+ M_CP_INTOPT(permit_tty); | |
+ M_CP_INTOPT(permit_user_rc); | |
+ M_CP_INTOPT(max_sessions); | |
+ M_CP_INTOPT(max_authtries); | |
+ M_CP_INTOPT(ip_qos_interactive); | |
+ M_CP_INTOPT(ip_qos_bulk); | |
+ M_CP_INTOPT(rekey_limit); | |
+ M_CP_INTOPT(rekey_interval); | |
+ | |
+ /* M_CP_STROPT and M_CP_STRARRAYOPT should not appear before here */ | |
+#define M_CP_STROPT(n) do {\ | |
+ if (src->n != NULL && dst->n != src->n) { \ | |
+ free(dst->n); \ | |
+ dst->n = src->n; \ | |
+ } \ | |
+} while(0) | |
+#define M_CP_STRARRAYOPT(n, num_n) do {\ | |
+ if (src->num_n != 0) { \ | |
+ for (dst->num_n = 0; dst->num_n < src->num_n; dst->num_n++) \ | |
+ dst->n[dst->num_n] = xstrdup(src->n[dst->num_n]); \ | |
+ } \ | |
+} while(0) | |
+ | |
+ /* See comment in servconf.h */ | |
+ COPY_MATCH_STRING_OPTS(); | |
+ | |
+ /* | |
+ * The only things that should be below this point are string options | |
+ * which are only used after authentication. | |
+ */ | |
+ if (preauth) | |
+ return; | |
+ | |
+ M_CP_STROPT(adm_forced_command); | |
+ M_CP_STROPT(chroot_directory); | |
+} | |
+ | |
+#undef M_CP_INTOPT | |
+#undef M_CP_STROPT | |
+#undef M_CP_STRARRAYOPT | |
+ | |
+void | |
+parse_server_config(ServerOptions *options, const char *filename, Buffer *conf, | |
+ struct connection_info *connectinfo) | |
+{ | |
+ int active, linenum, bad_options = 0; | |
+ char *cp, *obuf, *cbuf; | |
+ | |
+ debug2("%s: config %s len %d", __func__, filename, buffer_len(conf)); | |
+ | |
+ obuf = cbuf = xstrdup(buffer_ptr(conf)); | |
+ active = connectinfo ? 0 : 1; | |
+ linenum = 1; | |
+ while ((cp = strsep(&cbuf, "\n")) != NULL) { | |
+ if (process_server_config_line(options, cp, filename, | |
+ linenum++, &active, connectinfo) != 0) | |
+ bad_options++; | |
+ } | |
+ free(obuf); | |
+ if (bad_options > 0) | |
+ fatal("%s: terminating, %d bad configuration options", | |
+ filename, bad_options); | |
+} | |
+ | |
+static const char * | |
+fmt_multistate_int(int val, const struct multistate *m) | |
+{ | |
+ u_int i; | |
+ | |
+ for (i = 0; m[i].key != NULL; i++) { | |
+ if (m[i].value == val) | |
+ return m[i].key; | |
+ } | |
+ return "UNKNOWN"; | |
+} | |
+ | |
+static const char * | |
+fmt_intarg(ServerOpCodes code, int val) | |
+{ | |
+ if (val == -1) | |
+ return "unset"; | |
+ switch (code) { | |
+ case sAddressFamily: | |
+ return fmt_multistate_int(val, multistate_addressfamily); | |
+ case sPermitRootLogin: | |
+ return fmt_multistate_int(val, multistate_permitrootlogin); | |
+ case sGatewayPorts: | |
+ return fmt_multistate_int(val, multistate_gatewayports); | |
+ case sCompression: | |
+ return fmt_multistate_int(val, multistate_compression); | |
+ case sUsePrivilegeSeparation: | |
+ return fmt_multistate_int(val, multistate_privsep); | |
+ case sAllowTcpForwarding: | |
+ return fmt_multistate_int(val, multistate_tcpfwd); | |
+ case sAllowStreamLocalForwarding: | |
+ return fmt_multistate_int(val, multistate_tcpfwd); | |
+ case sProtocol: | |
+ switch (val) { | |
+ case SSH_PROTO_1: | |
+ return "1"; | |
+ case SSH_PROTO_2: | |
+ return "2"; | |
+ case (SSH_PROTO_1|SSH_PROTO_2): | |
+ return "2,1"; | |
+ default: | |
+ return "UNKNOWN"; | |
+ } | |
+ default: | |
+ switch (val) { | |
+ case 0: | |
+ return "no"; | |
+ case 1: | |
+ return "yes"; | |
+ default: | |
+ return "UNKNOWN"; | |
+ } | |
+ } | |
+} | |
+ | |
+static const char * | |
+lookup_opcode_name(ServerOpCodes code) | |
+{ | |
+ u_int i; | |
+ | |
+ for (i = 0; keywords[i].name != NULL; i++) | |
+ if (keywords[i].opcode == code) | |
+ return(keywords[i].name); | |
+ return "UNKNOWN"; | |
+} | |
+ | |
+static void | |
+dump_cfg_int(ServerOpCodes code, int val) | |
+{ | |
+ printf("%s %d\n", lookup_opcode_name(code), val); | |
+} | |
+ | |
+static void | |
+dump_cfg_fmtint(ServerOpCodes code, int val) | |
+{ | |
+ printf("%s %s\n", lookup_opcode_name(code), fmt_intarg(code, val)); | |
+} | |
+ | |
+static void | |
+dump_cfg_string(ServerOpCodes code, const char *val) | |
+{ | |
+ if (val == NULL) | |
+ return; | |
+ printf("%s %s\n", lookup_opcode_name(code), val); | |
+} | |
+ | |
+static void | |
+dump_cfg_strarray(ServerOpCodes code, u_int count, char **vals) | |
+{ | |
+ u_int i; | |
+ | |
+ for (i = 0; i < count; i++) | |
+ printf("%s %s\n", lookup_opcode_name(code), vals[i]); | |
+} | |
+ | |
+static void | |
+dump_cfg_strarray_oneline(ServerOpCodes code, u_int count, char **vals) | |
+{ | |
+ u_int i; | |
+ | |
+ printf("%s", lookup_opcode_name(code)); | |
+ for (i = 0; i < count; i++) | |
+ printf(" %s", vals[i]); | |
+ printf("\n"); | |
+} | |
+ | |
+void | |
+dump_config(ServerOptions *o) | |
+{ | |
+ u_int i; | |
+ int ret; | |
+ struct addrinfo *ai; | |
+ char addr[NI_MAXHOST], port[NI_MAXSERV], *s = NULL; | |
+ | |
+ /* these are usually at the top of the config */ | |
+ for (i = 0; i < o->num_ports; i++) | |
+ printf("port %d\n", o->ports[i]); | |
+ dump_cfg_fmtint(sProtocol, o->protocol); | |
+ dump_cfg_fmtint(sAddressFamily, o->address_family); | |
+ | |
+ /* ListenAddress must be after Port */ | |
+ for (ai = o->listen_addrs; ai; ai = ai->ai_next) { | |
+ if ((ret = getnameinfo(ai->ai_addr, ai->ai_addrlen, addr, | |
+ sizeof(addr), port, sizeof(port), | |
+ NI_NUMERICHOST|NI_NUMERICSERV)) != 0) { | |
+ error("getnameinfo failed: %.100s", | |
+ (ret != EAI_SYSTEM) ? gai_strerror(ret) : | |
+ strerror(errno)); | |
+ } else { | |
+ if (ai->ai_family == AF_INET6) | |
+ printf("listenaddress [%s]:%s\n", addr, port); | |
+ else | |
+ printf("listenaddress %s:%s\n", addr, port); | |
+ } | |
+ } | |
+ | |
+ /* integer arguments */ | |
+#ifdef USE_PAM | |
+ dump_cfg_int(sUsePAM, o->use_pam); | |
+#endif | |
+ dump_cfg_int(sServerKeyBits, o->server_key_bits); | |
+ dump_cfg_int(sLoginGraceTime, o->login_grace_time); | |
+ dump_cfg_int(sKeyRegenerationTime, o->key_regeneration_time); | |
+ dump_cfg_int(sX11DisplayOffset, o->x11_display_offset); | |
+ dump_cfg_int(sMaxAuthTries, o->max_authtries); | |
+ dump_cfg_int(sMaxSessions, o->max_sessions); | |
+ dump_cfg_int(sClientAliveInterval, o->client_alive_interval); | |
+ dump_cfg_int(sClientAliveCountMax, o->client_alive_count_max); | |
+ | |
+ /* formatted integer arguments */ | |
+ dump_cfg_fmtint(sPermitRootLogin, o->permit_root_login); | |
+ dump_cfg_fmtint(sIgnoreRhosts, o->ignore_rhosts); | |
+ dump_cfg_fmtint(sIgnoreUserKnownHosts, o->ignore_user_known_hosts); | |
+ dump_cfg_fmtint(sRhostsRSAAuthentication, o->rhosts_rsa_authentication); | |
+ dump_cfg_fmtint(sHostbasedAuthentication, o->hostbased_authentication); | |
+ dump_cfg_fmtint(sHostbasedUsesNameFromPacketOnly, | |
+ o->hostbased_uses_name_from_packet_only); | |
+ dump_cfg_fmtint(sRSAAuthentication, o->rsa_authentication); | |
+ dump_cfg_fmtint(sPubkeyAuthentication, o->pubkey_authentication); | |
+#ifdef KRB5 | |
+ dump_cfg_fmtint(sKerberosAuthentication, o->kerberos_authentication); | |
+ dump_cfg_fmtint(sKerberosOrLocalPasswd, o->kerberos_or_local_passwd); | |
+ dump_cfg_fmtint(sKerberosTicketCleanup, o->kerberos_ticket_cleanup); | |
+# ifdef USE_AFS | |
+ dump_cfg_fmtint(sKerberosGetAFSToken, o->kerberos_get_afs_token); | |
+# endif | |
+#endif | |
+#ifdef GSSAPI | |
+ dump_cfg_fmtint(sGssAuthentication, o->gss_authentication); | |
+ dump_cfg_fmtint(sGssCleanupCreds, o->gss_cleanup_creds); | |
+#endif | |
+ dump_cfg_fmtint(sPasswordAuthentication, o->password_authentication); | |
+ dump_cfg_fmtint(sKbdInteractiveAuthentication, | |
+ o->kbd_interactive_authentication); | |
+ dump_cfg_fmtint(sChallengeResponseAuthentication, | |
+ o->challenge_response_authentication); | |
+ dump_cfg_fmtint(sPrintMotd, o->print_motd); | |
+ dump_cfg_fmtint(sPrintLastLog, o->print_lastlog); | |
+ dump_cfg_fmtint(sX11Forwarding, o->x11_forwarding); | |
+ dump_cfg_fmtint(sX11UseLocalhost, o->x11_use_localhost); | |
+ dump_cfg_fmtint(sPermitTTY, o->permit_tty); | |
+ dump_cfg_fmtint(sPermitUserRC, o->permit_user_rc); | |
+ dump_cfg_fmtint(sStrictModes, o->strict_modes); | |
+ dump_cfg_fmtint(sTCPKeepAlive, o->tcp_keep_alive); | |
+ dump_cfg_fmtint(sEmptyPasswd, o->permit_empty_passwd); | |
+ dump_cfg_fmtint(sPermitUserEnvironment, o->permit_user_env); | |
+ dump_cfg_fmtint(sUseLogin, o->use_login); | |
+ dump_cfg_fmtint(sCompression, o->compression); | |
+ dump_cfg_fmtint(sGatewayPorts, o->fwd_opts.gateway_ports); | |
+ dump_cfg_fmtint(sUseDNS, o->use_dns); | |
+ dump_cfg_fmtint(sAllowTcpForwarding, o->allow_tcp_forwarding); | |
+ dump_cfg_fmtint(sAllowStreamLocalForwarding, o->allow_streamlocal_forwarding); | |
+ dump_cfg_fmtint(sUsePrivilegeSeparation, use_privsep); | |
+ | |
+ /* string arguments */ | |
+ dump_cfg_string(sPidFile, o->pid_file); | |
+ dump_cfg_string(sXAuthLocation, o->xauth_location); | |
+ dump_cfg_string(sCiphers, o->ciphers ? o->ciphers : | |
+ cipher_alg_list(',', 0)); | |
+ dump_cfg_string(sMacs, o->macs ? o->macs : mac_alg_list(',')); | |
+ dump_cfg_string(sBanner, o->banner); | |
+ dump_cfg_string(sForceCommand, o->adm_forced_command); | |
+ dump_cfg_string(sChrootDirectory, o->chroot_directory); | |
+ dump_cfg_string(sTrustedUserCAKeys, o->trusted_user_ca_keys); | |
+ dump_cfg_string(sRevokedKeys, o->revoked_keys_file); | |
+ dump_cfg_string(sAuthorizedPrincipalsFile, | |
+ o->authorized_principals_file); | |
+ dump_cfg_string(sVersionAddendum, o->version_addendum); | |
+ dump_cfg_string(sAuthorizedKeysCommand, o->authorized_keys_command); | |
+ dump_cfg_string(sAuthorizedKeysCommandUser, o->authorized_keys_command_user); | |
+ dump_cfg_string(sHostKeyAgent, o->host_key_agent); | |
+ dump_cfg_string(sKexAlgorithms, o->kex_algorithms ? o->kex_algorithms : | |
+ kex_alg_list(',')); | |
+ | |
+ /* string arguments requiring a lookup */ | |
+ dump_cfg_string(sLogLevel, log_level_name(o->log_level)); | |
+ dump_cfg_string(sLogFacility, log_facility_name(o->log_facility)); | |
+ | |
+ /* string array arguments */ | |
+ dump_cfg_strarray_oneline(sAuthorizedKeysFile, o->num_authkeys_files, | |
+ o->authorized_keys_files); | |
+ dump_cfg_strarray(sHostKeyFile, o->num_host_key_files, | |
+ o->host_key_files); | |
+ dump_cfg_strarray(sHostKeyFile, o->num_host_cert_files, | |
+ o->host_cert_files); | |
+ dump_cfg_strarray(sAllowUsers, o->num_allow_users, o->allow_users); | |
+ dump_cfg_strarray(sDenyUsers, o->num_deny_users, o->deny_users); | |
+ dump_cfg_strarray(sAllowGroups, o->num_allow_groups, o->allow_groups); | |
+ dump_cfg_strarray(sDenyGroups, o->num_deny_groups, o->deny_groups); | |
+ dump_cfg_strarray(sAcceptEnv, o->num_accept_env, o->accept_env); | |
+ dump_cfg_strarray_oneline(sAuthenticationMethods, | |
+ o->num_auth_methods, o->auth_methods); | |
+ | |
+ /* other arguments */ | |
+ for (i = 0; i < o->num_subsystems; i++) | |
+ printf("subsystem %s %s\n", o->subsystem_name[i], | |
+ o->subsystem_args[i]); | |
+ | |
+ printf("maxstartups %d:%d:%d\n", o->max_startups_begin, | |
+ o->max_startups_rate, o->max_startups); | |
+ | |
+ for (i = 0; tunmode_desc[i].val != -1; i++) | |
+ if (tunmode_desc[i].val == o->permit_tun) { | |
+ s = tunmode_desc[i].text; | |
+ break; | |
+ } | |
+ dump_cfg_string(sPermitTunnel, s); | |
+ | |
+ printf("ipqos %s ", iptos2str(o->ip_qos_interactive)); | |
+ printf("%s\n", iptos2str(o->ip_qos_bulk)); | |
+ | |
+ printf("rekeylimit %lld %d\n", (long long)o->rekey_limit, | |
+ o->rekey_interval); | |
+ | |
+ channel_print_adm_permitted_opens(); | |
+} | |
diff -urp --new-file openssh-6.7p1/session.c openssh-6.7p1.patched/session.c | |
--- openssh-6.7p1/session.c 2014-07-17 21:11:26.000000000 -0700 | |
+++ openssh-6.7p1.patched/session.c 2014-10-11 21:18:37.000000000 -0700 | |
@@ -2113,8 +2113,10 @@ session_pty_req(Session *s) | |
n_bytes = packet_remaining(); | |
tty_parse_modes(s->ttyfd, &n_bytes); | |
+#ifndef __APPLE_PRIVPTY__ | |
if (!use_privsep) | |
pty_setowner(s->pw, s->tty); | |
+#endif | |
/* Set window size from the packet. */ | |
pty_change_window_size(s->ptyfd, s->row, s->col, s->xpixel, s->ypixel); | |
@@ -2354,9 +2356,11 @@ session_pty_cleanup2(Session *s) | |
if (s->pid != 0) | |
record_logout(s->pid, s->tty, s->pw->pw_name); | |
+#ifndef __APPLE_PRIVPTY__ | |
/* Release the pseudo-tty. */ | |
if (getuid() == 0) | |
pty_release(s->tty); | |
+#endif | |
/* | |
* Close the server side of the socket pairs. We must do this after | |
diff -urp --new-file openssh-6.7p1/session.c.orig openssh-6.7p1.patched/session.c.orig | |
--- openssh-6.7p1/session.c.orig 1969-12-31 16:00:00.000000000 -0800 | |
+++ openssh-6.7p1.patched/session.c.orig 2014-07-17 21:11:26.000000000 -0700 | |
@@ -0,0 +1,2771 @@ | |
+/* $OpenBSD: session.c,v 1.274 2014/07/15 15:54:14 millert Exp $ */ | |
+/* | |
+ * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | |
+ * All rights reserved | |
+ * | |
+ * As far as I am concerned, the code I have written for this software | |
+ * can be used freely for any purpose. Any derived versions of this | |
+ * software must be clearly marked as such, and if the derived work is | |
+ * incompatible with the protocol description in the RFC file, it must be | |
+ * called by a name other than "ssh" or "Secure Shell". | |
+ * | |
+ * SSH2 support by Markus Friedl. | |
+ * Copyright (c) 2000, 2001 Markus Friedl. 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 <sys/types.h> | |
+#include <sys/param.h> | |
+#ifdef HAVE_SYS_STAT_H | |
+# include <sys/stat.h> | |
+#endif | |
+#include <sys/socket.h> | |
+#include <sys/un.h> | |
+#include <sys/wait.h> | |
+ | |
+#include <arpa/inet.h> | |
+ | |
+#include <errno.h> | |
+#include <fcntl.h> | |
+#include <grp.h> | |
+#include <netdb.h> | |
+#ifdef HAVE_PATHS_H | |
+#include <paths.h> | |
+#endif | |
+#include <pwd.h> | |
+#include <signal.h> | |
+#include <stdarg.h> | |
+#include <stdio.h> | |
+#include <stdlib.h> | |
+#include <string.h> | |
+#include <unistd.h> | |
+ | |
+#include "openbsd-compat/sys-queue.h" | |
+#include "xmalloc.h" | |
+#include "ssh.h" | |
+#include "ssh1.h" | |
+#include "ssh2.h" | |
+#include "sshpty.h" | |
+#include "packet.h" | |
+#include "buffer.h" | |
+#include "match.h" | |
+#include "uidswap.h" | |
+#include "compat.h" | |
+#include "channels.h" | |
+#include "key.h" | |
+#include "cipher.h" | |
+#ifdef GSSAPI | |
+#include "ssh-gss.h" | |
+#endif | |
+#include "hostfile.h" | |
+#include "auth.h" | |
+#include "auth-options.h" | |
+#include "authfd.h" | |
+#include "pathnames.h" | |
+#include "log.h" | |
+#include "misc.h" | |
+#include "servconf.h" | |
+#include "sshlogin.h" | |
+#include "serverloop.h" | |
+#include "canohost.h" | |
+#include "session.h" | |
+#include "kex.h" | |
+#include "monitor_wrap.h" | |
+#include "sftp.h" | |
+ | |
+#if defined(KRB5) && defined(USE_AFS) | |
+#include <kafs.h> | |
+#endif | |
+ | |
+#ifdef WITH_SELINUX | |
+#include <selinux/selinux.h> | |
+#endif | |
+ | |
+#define IS_INTERNAL_SFTP(c) \ | |
+ (!strncmp(c, INTERNAL_SFTP_NAME, sizeof(INTERNAL_SFTP_NAME) - 1) && \ | |
+ (c[sizeof(INTERNAL_SFTP_NAME) - 1] == '\0' || \ | |
+ c[sizeof(INTERNAL_SFTP_NAME) - 1] == ' ' || \ | |
+ c[sizeof(INTERNAL_SFTP_NAME) - 1] == '\t')) | |
+ | |
+/* func */ | |
+ | |
+Session *session_new(void); | |
+void session_set_fds(Session *, int, int, int, int, int); | |
+void session_pty_cleanup(Session *); | |
+void session_proctitle(Session *); | |
+int session_setup_x11fwd(Session *); | |
+int do_exec_pty(Session *, const char *); | |
+int do_exec_no_pty(Session *, const char *); | |
+int do_exec(Session *, const char *); | |
+void do_login(Session *, const char *); | |
+#ifdef LOGIN_NEEDS_UTMPX | |
+static void do_pre_login(Session *s); | |
+#endif | |
+void do_child(Session *, const char *); | |
+void do_motd(void); | |
+int check_quietlogin(Session *, const char *); | |
+ | |
+static void do_authenticated1(Authctxt *); | |
+static void do_authenticated2(Authctxt *); | |
+ | |
+static int session_pty_req(Session *); | |
+ | |
+/* import */ | |
+extern ServerOptions options; | |
+extern char *__progname; | |
+extern int log_stderr; | |
+extern int debug_flag; | |
+extern u_int utmp_len; | |
+extern int startup_pipe; | |
+extern void destroy_sensitive_data(void); | |
+extern Buffer loginmsg; | |
+ | |
+/* original command from peer. */ | |
+const char *original_command = NULL; | |
+ | |
+/* data */ | |
+static int sessions_first_unused = -1; | |
+static int sessions_nalloc = 0; | |
+static Session *sessions = NULL; | |
+ | |
+#define SUBSYSTEM_NONE 0 | |
+#define SUBSYSTEM_EXT 1 | |
+#define SUBSYSTEM_INT_SFTP 2 | |
+#define SUBSYSTEM_INT_SFTP_ERROR 3 | |
+ | |
+#ifdef HAVE_LOGIN_CAP | |
+login_cap_t *lc; | |
+#endif | |
+ | |
+static int is_child = 0; | |
+ | |
+/* Name and directory of socket for authentication agent forwarding. */ | |
+static char *auth_sock_name = NULL; | |
+static char *auth_sock_dir = NULL; | |
+ | |
+/* removes the agent forwarding socket */ | |
+ | |
+static void | |
+auth_sock_cleanup_proc(struct passwd *pw) | |
+{ | |
+ if (auth_sock_name != NULL) { | |
+ temporarily_use_uid(pw); | |
+ unlink(auth_sock_name); | |
+ rmdir(auth_sock_dir); | |
+ auth_sock_name = NULL; | |
+ restore_uid(); | |
+ } | |
+} | |
+ | |
+static int | |
+auth_input_request_forwarding(struct passwd * pw) | |
+{ | |
+ Channel *nc; | |
+ int sock = -1; | |
+ | |
+ if (auth_sock_name != NULL) { | |
+ error("authentication forwarding requested twice."); | |
+ return 0; | |
+ } | |
+ | |
+ /* Temporarily drop privileged uid for mkdir/bind. */ | |
+ temporarily_use_uid(pw); | |
+ | |
+ /* Allocate a buffer for the socket name, and format the name. */ | |
+ auth_sock_dir = xstrdup("/tmp/ssh-XXXXXXXXXX"); | |
+ | |
+ /* Create private directory for socket */ | |
+ if (mkdtemp(auth_sock_dir) == NULL) { | |
+ packet_send_debug("Agent forwarding disabled: " | |
+ "mkdtemp() failed: %.100s", strerror(errno)); | |
+ restore_uid(); | |
+ free(auth_sock_dir); | |
+ auth_sock_dir = NULL; | |
+ goto authsock_err; | |
+ } | |
+ | |
+ xasprintf(&auth_sock_name, "%s/agent.%ld", | |
+ auth_sock_dir, (long) getpid()); | |
+ | |
+ /* Start a Unix listener on auth_sock_name. */ | |
+ sock = unix_listener(auth_sock_name, SSH_LISTEN_BACKLOG, 0); | |
+ | |
+ /* Restore the privileged uid. */ | |
+ restore_uid(); | |
+ | |
+ /* Check for socket/bind/listen failure. */ | |
+ if (sock < 0) | |
+ goto authsock_err; | |
+ | |
+ /* Allocate a channel for the authentication agent socket. */ | |
+ nc = channel_new("auth socket", | |
+ SSH_CHANNEL_AUTH_SOCKET, sock, sock, -1, | |
+ CHAN_X11_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT, | |
+ 0, "auth socket", 1); | |
+ nc->path = xstrdup(auth_sock_name); | |
+ return 1; | |
+ | |
+ authsock_err: | |
+ free(auth_sock_name); | |
+ if (auth_sock_dir != NULL) { | |
+ rmdir(auth_sock_dir); | |
+ free(auth_sock_dir); | |
+ } | |
+ if (sock != -1) | |
+ close(sock); | |
+ auth_sock_name = NULL; | |
+ auth_sock_dir = NULL; | |
+ return 0; | |
+} | |
+ | |
+static void | |
+display_loginmsg(void) | |
+{ | |
+ if (buffer_len(&loginmsg) > 0) { | |
+ buffer_append(&loginmsg, "\0", 1); | |
+ printf("%s", (char *)buffer_ptr(&loginmsg)); | |
+ buffer_clear(&loginmsg); | |
+ } | |
+} | |
+ | |
+void | |
+do_authenticated(Authctxt *authctxt) | |
+{ | |
+ setproctitle("%s", authctxt->pw->pw_name); | |
+ | |
+ /* setup the channel layer */ | |
+ /* XXX - streamlocal? */ | |
+ if (no_port_forwarding_flag || | |
+ (options.allow_tcp_forwarding & FORWARD_LOCAL) == 0) | |
+ channel_disable_adm_local_opens(); | |
+ else | |
+ channel_permit_all_opens(); | |
+ | |
+ auth_debug_send(); | |
+ | |
+ if (compat20) | |
+ do_authenticated2(authctxt); | |
+ else | |
+ do_authenticated1(authctxt); | |
+ | |
+ do_cleanup(authctxt); | |
+} | |
+ | |
+/* | |
+ * Prepares for an interactive session. This is called after the user has | |
+ * been successfully authenticated. During this message exchange, pseudo | |
+ * terminals are allocated, X11, TCP/IP, and authentication agent forwardings | |
+ * are requested, etc. | |
+ */ | |
+static void | |
+do_authenticated1(Authctxt *authctxt) | |
+{ | |
+ Session *s; | |
+ char *command; | |
+ int success, type, screen_flag; | |
+ int enable_compression_after_reply = 0; | |
+ u_int proto_len, data_len, dlen, compression_level = 0; | |
+ | |
+ s = session_new(); | |
+ if (s == NULL) { | |
+ error("no more sessions"); | |
+ return; | |
+ } | |
+ s->authctxt = authctxt; | |
+ s->pw = authctxt->pw; | |
+ | |
+ /* | |
+ * We stay in this loop until the client requests to execute a shell | |
+ * or a command. | |
+ */ | |
+ for (;;) { | |
+ success = 0; | |
+ | |
+ /* Get a packet from the client. */ | |
+ type = packet_read(); | |
+ | |
+ /* Process the packet. */ | |
+ switch (type) { | |
+ case SSH_CMSG_REQUEST_COMPRESSION: | |
+ compression_level = packet_get_int(); | |
+ packet_check_eom(); | |
+ if (compression_level < 1 || compression_level > 9) { | |
+ packet_send_debug("Received invalid compression level %d.", | |
+ compression_level); | |
+ break; | |
+ } | |
+ if (options.compression == COMP_NONE) { | |
+ debug2("compression disabled"); | |
+ break; | |
+ } | |
+ /* Enable compression after we have responded with SUCCESS. */ | |
+ enable_compression_after_reply = 1; | |
+ success = 1; | |
+ break; | |
+ | |
+ case SSH_CMSG_REQUEST_PTY: | |
+ success = session_pty_req(s); | |
+ break; | |
+ | |
+ case SSH_CMSG_X11_REQUEST_FORWARDING: | |
+ s->auth_proto = packet_get_string(&proto_len); | |
+ s->auth_data = packet_get_string(&data_len); | |
+ | |
+ screen_flag = packet_get_protocol_flags() & | |
+ SSH_PROTOFLAG_SCREEN_NUMBER; | |
+ debug2("SSH_PROTOFLAG_SCREEN_NUMBER: %d", screen_flag); | |
+ | |
+ if (packet_remaining() == 4) { | |
+ if (!screen_flag) | |
+ debug2("Buggy client: " | |
+ "X11 screen flag missing"); | |
+ s->screen = packet_get_int(); | |
+ } else { | |
+ s->screen = 0; | |
+ } | |
+ packet_check_eom(); | |
+ success = session_setup_x11fwd(s); | |
+ if (!success) { | |
+ free(s->auth_proto); | |
+ free(s->auth_data); | |
+ s->auth_proto = NULL; | |
+ s->auth_data = NULL; | |
+ } | |
+ break; | |
+ | |
+ case SSH_CMSG_AGENT_REQUEST_FORWARDING: | |
+ if (!options.allow_agent_forwarding || | |
+ no_agent_forwarding_flag || compat13) { | |
+ debug("Authentication agent forwarding not permitted for this authentication."); | |
+ break; | |
+ } | |
+ debug("Received authentication agent forwarding request."); | |
+ success = auth_input_request_forwarding(s->pw); | |
+ break; | |
+ | |
+ case SSH_CMSG_PORT_FORWARD_REQUEST: | |
+ if (no_port_forwarding_flag) { | |
+ debug("Port forwarding not permitted for this authentication."); | |
+ break; | |
+ } | |
+ if (!(options.allow_tcp_forwarding & FORWARD_REMOTE)) { | |
+ debug("Port forwarding not permitted."); | |
+ break; | |
+ } | |
+ debug("Received TCP/IP port forwarding request."); | |
+ if (channel_input_port_forward_request(s->pw->pw_uid == 0, | |
+ &options.fwd_opts) < 0) { | |
+ debug("Port forwarding failed."); | |
+ break; | |
+ } | |
+ success = 1; | |
+ break; | |
+ | |
+ case SSH_CMSG_MAX_PACKET_SIZE: | |
+ if (packet_set_maxsize(packet_get_int()) > 0) | |
+ success = 1; | |
+ break; | |
+ | |
+ case SSH_CMSG_EXEC_SHELL: | |
+ case SSH_CMSG_EXEC_CMD: | |
+ if (type == SSH_CMSG_EXEC_CMD) { | |
+ command = packet_get_string(&dlen); | |
+ debug("Exec command '%.500s'", command); | |
+ if (do_exec(s, command) != 0) | |
+ packet_disconnect( | |
+ "command execution failed"); | |
+ free(command); | |
+ } else { | |
+ if (do_exec(s, NULL) != 0) | |
+ packet_disconnect( | |
+ "shell execution failed"); | |
+ } | |
+ packet_check_eom(); | |
+ session_close(s); | |
+ return; | |
+ | |
+ default: | |
+ /* | |
+ * Any unknown messages in this phase are ignored, | |
+ * and a failure message is returned. | |
+ */ | |
+ logit("Unknown packet type received after authentication: %d", type); | |
+ } | |
+ packet_start(success ? SSH_SMSG_SUCCESS : SSH_SMSG_FAILURE); | |
+ packet_send(); | |
+ packet_write_wait(); | |
+ | |
+ /* Enable compression now that we have replied if appropriate. */ | |
+ if (enable_compression_after_reply) { | |
+ enable_compression_after_reply = 0; | |
+ packet_start_compression(compression_level); | |
+ } | |
+ } | |
+} | |
+ | |
+#define USE_PIPES 1 | |
+/* | |
+ * This is called to fork and execute a command when we have no tty. This | |
+ * will call do_child from the child, and server_loop from the parent after | |
+ * setting up file descriptors and such. | |
+ */ | |
+int | |
+do_exec_no_pty(Session *s, const char *command) | |
+{ | |
+ pid_t pid; | |
+ | |
+#ifdef USE_PIPES | |
+ int pin[2], pout[2], perr[2]; | |
+ | |
+ if (s == NULL) | |
+ fatal("do_exec_no_pty: no session"); | |
+ | |
+ /* Allocate pipes for communicating with the program. */ | |
+ if (pipe(pin) < 0) { | |
+ error("%s: pipe in: %.100s", __func__, strerror(errno)); | |
+ return -1; | |
+ } | |
+ if (pipe(pout) < 0) { | |
+ error("%s: pipe out: %.100s", __func__, strerror(errno)); | |
+ close(pin[0]); | |
+ close(pin[1]); | |
+ return -1; | |
+ } | |
+ if (pipe(perr) < 0) { | |
+ error("%s: pipe err: %.100s", __func__, | |
+ strerror(errno)); | |
+ close(pin[0]); | |
+ close(pin[1]); | |
+ close(pout[0]); | |
+ close(pout[1]); | |
+ return -1; | |
+ } | |
+#else | |
+ int inout[2], err[2]; | |
+ | |
+ if (s == NULL) | |
+ fatal("do_exec_no_pty: no session"); | |
+ | |
+ /* Uses socket pairs to communicate with the program. */ | |
+ if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) < 0) { | |
+ error("%s: socketpair #1: %.100s", __func__, strerror(errno)); | |
+ return -1; | |
+ } | |
+ if (socketpair(AF_UNIX, SOCK_STREAM, 0, err) < 0) { | |
+ error("%s: socketpair #2: %.100s", __func__, | |
+ strerror(errno)); | |
+ close(inout[0]); | |
+ close(inout[1]); | |
+ return -1; | |
+ } | |
+#endif | |
+ | |
+ session_proctitle(s); | |
+ | |
+ /* Fork the child. */ | |
+ switch ((pid = fork())) { | |
+ case -1: | |
+ error("%s: fork: %.100s", __func__, strerror(errno)); | |
+#ifdef USE_PIPES | |
+ close(pin[0]); | |
+ close(pin[1]); | |
+ close(pout[0]); | |
+ close(pout[1]); | |
+ close(perr[0]); | |
+ close(perr[1]); | |
+#else | |
+ close(inout[0]); | |
+ close(inout[1]); | |
+ close(err[0]); | |
+ close(err[1]); | |
+#endif | |
+ return -1; | |
+ case 0: | |
+ is_child = 1; | |
+ | |
+ /* Child. Reinitialize the log since the pid has changed. */ | |
+ log_init(__progname, options.log_level, | |
+ options.log_facility, log_stderr); | |
+ | |
+ /* | |
+ * Create a new session and process group since the 4.4BSD | |
+ * setlogin() affects the entire process group. | |
+ */ | |
+ if (setsid() < 0) | |
+ error("setsid failed: %.100s", strerror(errno)); | |
+ | |
+#ifdef USE_PIPES | |
+ /* | |
+ * Redirect stdin. We close the parent side of the socket | |
+ * pair, and make the child side the standard input. | |
+ */ | |
+ close(pin[1]); | |
+ if (dup2(pin[0], 0) < 0) | |
+ perror("dup2 stdin"); | |
+ close(pin[0]); | |
+ | |
+ /* Redirect stdout. */ | |
+ close(pout[0]); | |
+ if (dup2(pout[1], 1) < 0) | |
+ perror("dup2 stdout"); | |
+ close(pout[1]); | |
+ | |
+ /* Redirect stderr. */ | |
+ close(perr[0]); | |
+ if (dup2(perr[1], 2) < 0) | |
+ perror("dup2 stderr"); | |
+ close(perr[1]); | |
+#else | |
+ /* | |
+ * Redirect stdin, stdout, and stderr. Stdin and stdout will | |
+ * use the same socket, as some programs (particularly rdist) | |
+ * seem to depend on it. | |
+ */ | |
+ close(inout[1]); | |
+ close(err[1]); | |
+ if (dup2(inout[0], 0) < 0) /* stdin */ | |
+ perror("dup2 stdin"); | |
+ if (dup2(inout[0], 1) < 0) /* stdout (same as stdin) */ | |
+ perror("dup2 stdout"); | |
+ close(inout[0]); | |
+ if (dup2(err[0], 2) < 0) /* stderr */ | |
+ perror("dup2 stderr"); | |
+ close(err[0]); | |
+#endif | |
+ | |
+ | |
+#ifdef _UNICOS | |
+ cray_init_job(s->pw); /* set up cray jid and tmpdir */ | |
+#endif | |
+ | |
+ /* Do processing for the child (exec command etc). */ | |
+ do_child(s, command); | |
+ /* NOTREACHED */ | |
+ default: | |
+ break; | |
+ } | |
+ | |
+#ifdef _UNICOS | |
+ signal(WJSIGNAL, cray_job_termination_handler); | |
+#endif /* _UNICOS */ | |
+#ifdef HAVE_CYGWIN | |
+ cygwin_set_impersonation_token(INVALID_HANDLE_VALUE); | |
+#endif | |
+ | |
+ s->pid = pid; | |
+ /* Set interactive/non-interactive mode. */ | |
+ packet_set_interactive(s->display != NULL, | |
+ options.ip_qos_interactive, options.ip_qos_bulk); | |
+ | |
+ /* | |
+ * Clear loginmsg, since it's the child's responsibility to display | |
+ * it to the user, otherwise multiple sessions may accumulate | |
+ * multiple copies of the login messages. | |
+ */ | |
+ buffer_clear(&loginmsg); | |
+ | |
+#ifdef USE_PIPES | |
+ /* We are the parent. Close the child sides of the pipes. */ | |
+ close(pin[0]); | |
+ close(pout[1]); | |
+ close(perr[1]); | |
+ | |
+ if (compat20) { | |
+ session_set_fds(s, pin[1], pout[0], perr[0], | |
+ s->is_subsystem, 0); | |
+ } else { | |
+ /* Enter the interactive session. */ | |
+ server_loop(pid, pin[1], pout[0], perr[0]); | |
+ /* server_loop has closed pin[1], pout[0], and perr[0]. */ | |
+ } | |
+#else | |
+ /* We are the parent. Close the child sides of the socket pairs. */ | |
+ close(inout[0]); | |
+ close(err[0]); | |
+ | |
+ /* | |
+ * Enter the interactive session. Note: server_loop must be able to | |
+ * handle the case that fdin and fdout are the same. | |
+ */ | |
+ if (compat20) { | |
+ session_set_fds(s, inout[1], inout[1], err[1], | |
+ s->is_subsystem, 0); | |
+ } else { | |
+ server_loop(pid, inout[1], inout[1], err[1]); | |
+ /* server_loop has closed inout[1] and err[1]. */ | |
+ } | |
+#endif | |
+ return 0; | |
+} | |
+ | |
+/* | |
+ * This is called to fork and execute a command when we have a tty. This | |
+ * will call do_child from the child, and server_loop from the parent after | |
+ * setting up file descriptors, controlling tty, updating wtmp, utmp, | |
+ * lastlog, and other such operations. | |
+ */ | |
+int | |
+do_exec_pty(Session *s, const char *command) | |
+{ | |
+ int fdout, ptyfd, ttyfd, ptymaster; | |
+ pid_t pid; | |
+ | |
+ if (s == NULL) | |
+ fatal("do_exec_pty: no session"); | |
+ ptyfd = s->ptyfd; | |
+ ttyfd = s->ttyfd; | |
+ | |
+ /* | |
+ * Create another descriptor of the pty master side for use as the | |
+ * standard input. We could use the original descriptor, but this | |
+ * simplifies code in server_loop. The descriptor is bidirectional. | |
+ * Do this before forking (and cleanup in the child) so as to | |
+ * detect and gracefully fail out-of-fd conditions. | |
+ */ | |
+ if ((fdout = dup(ptyfd)) < 0) { | |
+ error("%s: dup #1: %s", __func__, strerror(errno)); | |
+ close(ttyfd); | |
+ close(ptyfd); | |
+ return -1; | |
+ } | |
+ /* we keep a reference to the pty master */ | |
+ if ((ptymaster = dup(ptyfd)) < 0) { | |
+ error("%s: dup #2: %s", __func__, strerror(errno)); | |
+ close(ttyfd); | |
+ close(ptyfd); | |
+ close(fdout); | |
+ return -1; | |
+ } | |
+ | |
+ /* Fork the child. */ | |
+ switch ((pid = fork())) { | |
+ case -1: | |
+ error("%s: fork: %.100s", __func__, strerror(errno)); | |
+ close(fdout); | |
+ close(ptymaster); | |
+ close(ttyfd); | |
+ close(ptyfd); | |
+ return -1; | |
+ case 0: | |
+ is_child = 1; | |
+ | |
+ close(fdout); | |
+ close(ptymaster); | |
+ | |
+ /* Child. Reinitialize the log because the pid has changed. */ | |
+ log_init(__progname, options.log_level, | |
+ options.log_facility, log_stderr); | |
+ /* Close the master side of the pseudo tty. */ | |
+ close(ptyfd); | |
+ | |
+ /* Make the pseudo tty our controlling tty. */ | |
+ pty_make_controlling_tty(&ttyfd, s->tty); | |
+ | |
+ /* Redirect stdin/stdout/stderr from the pseudo tty. */ | |
+ if (dup2(ttyfd, 0) < 0) | |
+ error("dup2 stdin: %s", strerror(errno)); | |
+ if (dup2(ttyfd, 1) < 0) | |
+ error("dup2 stdout: %s", strerror(errno)); | |
+ if (dup2(ttyfd, 2) < 0) | |
+ error("dup2 stderr: %s", strerror(errno)); | |
+ | |
+ /* Close the extra descriptor for the pseudo tty. */ | |
+ close(ttyfd); | |
+ | |
+ /* record login, etc. similar to login(1) */ | |
+#ifndef HAVE_OSF_SIA | |
+ if (!(options.use_login && command == NULL)) { | |
+#ifdef _UNICOS | |
+ cray_init_job(s->pw); /* set up cray jid and tmpdir */ | |
+#endif /* _UNICOS */ | |
+ do_login(s, command); | |
+ } | |
+# ifdef LOGIN_NEEDS_UTMPX | |
+ else | |
+ do_pre_login(s); | |
+# endif | |
+#endif | |
+ /* | |
+ * Do common processing for the child, such as execing | |
+ * the command. | |
+ */ | |
+ do_child(s, command); | |
+ /* NOTREACHED */ | |
+ default: | |
+ break; | |
+ } | |
+ | |
+#ifdef _UNICOS | |
+ signal(WJSIGNAL, cray_job_termination_handler); | |
+#endif /* _UNICOS */ | |
+#ifdef HAVE_CYGWIN | |
+ cygwin_set_impersonation_token(INVALID_HANDLE_VALUE); | |
+#endif | |
+ | |
+ s->pid = pid; | |
+ | |
+ /* Parent. Close the slave side of the pseudo tty. */ | |
+ close(ttyfd); | |
+ | |
+ /* Enter interactive session. */ | |
+ s->ptymaster = ptymaster; | |
+ packet_set_interactive(1, | |
+ options.ip_qos_interactive, options.ip_qos_bulk); | |
+ if (compat20) { | |
+ session_set_fds(s, ptyfd, fdout, -1, 1, 1); | |
+ } else { | |
+ server_loop(pid, ptyfd, fdout, -1); | |
+ /* server_loop _has_ closed ptyfd and fdout. */ | |
+ } | |
+ return 0; | |
+} | |
+ | |
+#ifdef LOGIN_NEEDS_UTMPX | |
+static void | |
+do_pre_login(Session *s) | |
+{ | |
+ socklen_t fromlen; | |
+ struct sockaddr_storage from; | |
+ pid_t pid = getpid(); | |
+ | |
+ /* | |
+ * Get IP address of client. If the connection is not a socket, let | |
+ * the address be 0.0.0.0. | |
+ */ | |
+ memset(&from, 0, sizeof(from)); | |
+ fromlen = sizeof(from); | |
+ if (packet_connection_is_on_socket()) { | |
+ if (getpeername(packet_get_connection_in(), | |
+ (struct sockaddr *)&from, &fromlen) < 0) { | |
+ debug("getpeername: %.100s", strerror(errno)); | |
+ cleanup_exit(255); | |
+ } | |
+ } | |
+ | |
+ record_utmp_only(pid, s->tty, s->pw->pw_name, | |
+ get_remote_name_or_ip(utmp_len, options.use_dns), | |
+ (struct sockaddr *)&from, fromlen); | |
+} | |
+#endif | |
+ | |
+/* | |
+ * This is called to fork and execute a command. If another command is | |
+ * to be forced, execute that instead. | |
+ */ | |
+int | |
+do_exec(Session *s, const char *command) | |
+{ | |
+ int ret; | |
+ const char *forced = NULL; | |
+ char session_type[1024], *tty = NULL; | |
+ | |
+ if (options.adm_forced_command) { | |
+ original_command = command; | |
+ command = options.adm_forced_command; | |
+ forced = "(config)"; | |
+ } else if (forced_command) { | |
+ original_command = command; | |
+ command = forced_command; | |
+ forced = "(key-option)"; | |
+ } | |
+ if (forced != NULL) { | |
+ if (IS_INTERNAL_SFTP(command)) { | |
+ s->is_subsystem = s->is_subsystem ? | |
+ SUBSYSTEM_INT_SFTP : SUBSYSTEM_INT_SFTP_ERROR; | |
+ } else if (s->is_subsystem) | |
+ s->is_subsystem = SUBSYSTEM_EXT; | |
+ snprintf(session_type, sizeof(session_type), | |
+ "forced-command %s '%.900s'", forced, command); | |
+ } else if (s->is_subsystem) { | |
+ snprintf(session_type, sizeof(session_type), | |
+ "subsystem '%.900s'", s->subsys); | |
+ } else if (command == NULL) { | |
+ snprintf(session_type, sizeof(session_type), "shell"); | |
+ } else { | |
+ /* NB. we don't log unforced commands to preserve privacy */ | |
+ snprintf(session_type, sizeof(session_type), "command"); | |
+ } | |
+ | |
+ if (s->ttyfd != -1) { | |
+ tty = s->tty; | |
+ if (strncmp(tty, "/dev/", 5) == 0) | |
+ tty += 5; | |
+ } | |
+ | |
+ verbose("Starting session: %s%s%s for %s from %.200s port %d", | |
+ session_type, | |
+ tty == NULL ? "" : " on ", | |
+ tty == NULL ? "" : tty, | |
+ s->pw->pw_name, | |
+ get_remote_ipaddr(), | |
+ get_remote_port()); | |
+ | |
+#ifdef SSH_AUDIT_EVENTS | |
+ if (command != NULL) | |
+ PRIVSEP(audit_run_command(command)); | |
+ else if (s->ttyfd == -1) { | |
+ char *shell = s->pw->pw_shell; | |
+ | |
+ if (shell[0] == '\0') /* empty shell means /bin/sh */ | |
+ shell =_PATH_BSHELL; | |
+ PRIVSEP(audit_run_command(shell)); | |
+ } | |
+#endif | |
+ if (s->ttyfd != -1) | |
+ ret = do_exec_pty(s, command); | |
+ else | |
+ ret = do_exec_no_pty(s, command); | |
+ | |
+ original_command = NULL; | |
+ | |
+ /* | |
+ * Clear loginmsg: it's the child's responsibility to display | |
+ * it to the user, otherwise multiple sessions may accumulate | |
+ * multiple copies of the login messages. | |
+ */ | |
+ buffer_clear(&loginmsg); | |
+ | |
+ return ret; | |
+} | |
+ | |
+/* administrative, login(1)-like work */ | |
+void | |
+do_login(Session *s, const char *command) | |
+{ | |
+ socklen_t fromlen; | |
+ struct sockaddr_storage from; | |
+ struct passwd * pw = s->pw; | |
+ pid_t pid = getpid(); | |
+ | |
+ /* | |
+ * Get IP address of client. If the connection is not a socket, let | |
+ * the address be 0.0.0.0. | |
+ */ | |
+ memset(&from, 0, sizeof(from)); | |
+ fromlen = sizeof(from); | |
+ if (packet_connection_is_on_socket()) { | |
+ if (getpeername(packet_get_connection_in(), | |
+ (struct sockaddr *)&from, &fromlen) < 0) { | |
+ debug("getpeername: %.100s", strerror(errno)); | |
+ cleanup_exit(255); | |
+ } | |
+ } | |
+ | |
+ /* Record that there was a login on that tty from the remote host. */ | |
+ if (!use_privsep) | |
+ record_login(pid, s->tty, pw->pw_name, pw->pw_uid, | |
+ get_remote_name_or_ip(utmp_len, | |
+ options.use_dns), | |
+ (struct sockaddr *)&from, fromlen); | |
+ | |
+#ifdef USE_PAM | |
+ /* | |
+ * If password change is needed, do it now. | |
+ * This needs to occur before the ~/.hushlogin check. | |
+ */ | |
+ if (options.use_pam && !use_privsep && s->authctxt->force_pwchange) { | |
+ display_loginmsg(); | |
+ do_pam_chauthtok(); | |
+ s->authctxt->force_pwchange = 0; | |
+ /* XXX - signal [net] parent to enable forwardings */ | |
+ } | |
+#endif | |
+ | |
+ if (check_quietlogin(s, command)) | |
+ return; | |
+ | |
+ display_loginmsg(); | |
+ | |
+ do_motd(); | |
+} | |
+ | |
+/* | |
+ * Display the message of the day. | |
+ */ | |
+void | |
+do_motd(void) | |
+{ | |
+ FILE *f; | |
+ char buf[256]; | |
+ | |
+ if (options.print_motd) { | |
+#ifdef HAVE_LOGIN_CAP | |
+ f = fopen(login_getcapstr(lc, "welcome", "/etc/motd", | |
+ "/etc/motd"), "r"); | |
+#else | |
+ f = fopen("/etc/motd", "r"); | |
+#endif | |
+ if (f) { | |
+ while (fgets(buf, sizeof(buf), f)) | |
+ fputs(buf, stdout); | |
+ fclose(f); | |
+ } | |
+ } | |
+} | |
+ | |
+ | |
+/* | |
+ * Check for quiet login, either .hushlogin or command given. | |
+ */ | |
+int | |
+check_quietlogin(Session *s, const char *command) | |
+{ | |
+ char buf[256]; | |
+ struct passwd *pw = s->pw; | |
+ struct stat st; | |
+ | |
+ /* Return 1 if .hushlogin exists or a command given. */ | |
+ if (command != NULL) | |
+ return 1; | |
+ snprintf(buf, sizeof(buf), "%.200s/.hushlogin", pw->pw_dir); | |
+#ifdef HAVE_LOGIN_CAP | |
+ if (login_getcapbool(lc, "hushlogin", 0) || stat(buf, &st) >= 0) | |
+ return 1; | |
+#else | |
+ if (stat(buf, &st) >= 0) | |
+ return 1; | |
+#endif | |
+ return 0; | |
+} | |
+ | |
+/* | |
+ * Sets the value of the given variable in the environment. If the variable | |
+ * already exists, its value is overridden. | |
+ */ | |
+void | |
+child_set_env(char ***envp, u_int *envsizep, const char *name, | |
+ const char *value) | |
+{ | |
+ char **env; | |
+ u_int envsize; | |
+ u_int i, namelen; | |
+ | |
+ if (strchr(name, '=') != NULL) { | |
+ error("Invalid environment variable \"%.100s\"", name); | |
+ return; | |
+ } | |
+ | |
+ /* | |
+ * If we're passed an uninitialized list, allocate a single null | |
+ * entry before continuing. | |
+ */ | |
+ if (*envp == NULL && *envsizep == 0) { | |
+ *envp = xmalloc(sizeof(char *)); | |
+ *envp[0] = NULL; | |
+ *envsizep = 1; | |
+ } | |
+ | |
+ /* | |
+ * Find the slot where the value should be stored. If the variable | |
+ * already exists, we reuse the slot; otherwise we append a new slot | |
+ * at the end of the array, expanding if necessary. | |
+ */ | |
+ env = *envp; | |
+ namelen = strlen(name); | |
+ for (i = 0; env[i]; i++) | |
+ if (strncmp(env[i], name, namelen) == 0 && env[i][namelen] == '=') | |
+ break; | |
+ if (env[i]) { | |
+ /* Reuse the slot. */ | |
+ free(env[i]); | |
+ } else { | |
+ /* New variable. Expand if necessary. */ | |
+ envsize = *envsizep; | |
+ if (i >= envsize - 1) { | |
+ if (envsize >= 1000) | |
+ fatal("child_set_env: too many env vars"); | |
+ envsize += 50; | |
+ env = (*envp) = xrealloc(env, envsize, sizeof(char *)); | |
+ *envsizep = envsize; | |
+ } | |
+ /* Need to set the NULL pointer at end of array beyond the new slot. */ | |
+ env[i + 1] = NULL; | |
+ } | |
+ | |
+ /* Allocate space and format the variable in the appropriate slot. */ | |
+ env[i] = xmalloc(strlen(name) + 1 + strlen(value) + 1); | |
+ snprintf(env[i], strlen(name) + 1 + strlen(value) + 1, "%s=%s", name, value); | |
+} | |
+ | |
+/* | |
+ * Reads environment variables from the given file and adds/overrides them | |
+ * into the environment. If the file does not exist, this does nothing. | |
+ * Otherwise, it must consist of empty lines, comments (line starts with '#') | |
+ * and assignments of the form name=value. No other forms are allowed. | |
+ */ | |
+static void | |
+read_environment_file(char ***env, u_int *envsize, | |
+ const char *filename) | |
+{ | |
+ FILE *f; | |
+ char buf[4096]; | |
+ char *cp, *value; | |
+ u_int lineno = 0; | |
+ | |
+ f = fopen(filename, "r"); | |
+ if (!f) | |
+ return; | |
+ | |
+ while (fgets(buf, sizeof(buf), f)) { | |
+ if (++lineno > 1000) | |
+ fatal("Too many lines in environment file %s", filename); | |
+ for (cp = buf; *cp == ' ' || *cp == '\t'; cp++) | |
+ ; | |
+ if (!*cp || *cp == '#' || *cp == '\n') | |
+ continue; | |
+ | |
+ cp[strcspn(cp, "\n")] = '\0'; | |
+ | |
+ value = strchr(cp, '='); | |
+ if (value == NULL) { | |
+ fprintf(stderr, "Bad line %u in %.100s\n", lineno, | |
+ filename); | |
+ continue; | |
+ } | |
+ /* | |
+ * Replace the equals sign by nul, and advance value to | |
+ * the value string. | |
+ */ | |
+ *value = '\0'; | |
+ value++; | |
+ child_set_env(env, envsize, cp, value); | |
+ } | |
+ fclose(f); | |
+} | |
+ | |
+#ifdef HAVE_ETC_DEFAULT_LOGIN | |
+/* | |
+ * Return named variable from specified environment, or NULL if not present. | |
+ */ | |
+static char * | |
+child_get_env(char **env, const char *name) | |
+{ | |
+ int i; | |
+ size_t len; | |
+ | |
+ len = strlen(name); | |
+ for (i=0; env[i] != NULL; i++) | |
+ if (strncmp(name, env[i], len) == 0 && env[i][len] == '=') | |
+ return(env[i] + len + 1); | |
+ return NULL; | |
+} | |
+ | |
+/* | |
+ * Read /etc/default/login. | |
+ * We pick up the PATH (or SUPATH for root) and UMASK. | |
+ */ | |
+static void | |
+read_etc_default_login(char ***env, u_int *envsize, uid_t uid) | |
+{ | |
+ char **tmpenv = NULL, *var; | |
+ u_int i, tmpenvsize = 0; | |
+ u_long mask; | |
+ | |
+ /* | |
+ * We don't want to copy the whole file to the child's environment, | |
+ * so we use a temporary environment and copy the variables we're | |
+ * interested in. | |
+ */ | |
+ read_environment_file(&tmpenv, &tmpenvsize, "/etc/default/login"); | |
+ | |
+ if (tmpenv == NULL) | |
+ return; | |
+ | |
+ if (uid == 0) | |
+ var = child_get_env(tmpenv, "SUPATH"); | |
+ else | |
+ var = child_get_env(tmpenv, "PATH"); | |
+ if (var != NULL) | |
+ child_set_env(env, envsize, "PATH", var); | |
+ | |
+ if ((var = child_get_env(tmpenv, "UMASK")) != NULL) | |
+ if (sscanf(var, "%5lo", &mask) == 1) | |
+ umask((mode_t)mask); | |
+ | |
+ for (i = 0; tmpenv[i] != NULL; i++) | |
+ free(tmpenv[i]); | |
+ free(tmpenv); | |
+} | |
+#endif /* HAVE_ETC_DEFAULT_LOGIN */ | |
+ | |
+void | |
+copy_environment(char **source, char ***env, u_int *envsize) | |
+{ | |
+ char *var_name, *var_val; | |
+ int i; | |
+ | |
+ if (source == NULL) | |
+ return; | |
+ | |
+ for(i = 0; source[i] != NULL; i++) { | |
+ var_name = xstrdup(source[i]); | |
+ if ((var_val = strstr(var_name, "=")) == NULL) { | |
+ free(var_name); | |
+ continue; | |
+ } | |
+ *var_val++ = '\0'; | |
+ | |
+ debug3("Copy environment: %s=%s", var_name, var_val); | |
+ child_set_env(env, envsize, var_name, var_val); | |
+ | |
+ free(var_name); | |
+ } | |
+} | |
+ | |
+static char ** | |
+do_setup_env(Session *s, const char *shell) | |
+{ | |
+ char buf[256]; | |
+ u_int i, envsize; | |
+ char **env, *laddr; | |
+ struct passwd *pw = s->pw; | |
+#if !defined (HAVE_LOGIN_CAP) && !defined (HAVE_CYGWIN) | |
+ char *path = NULL; | |
+#endif | |
+ | |
+ /* Initialize the environment. */ | |
+ envsize = 100; | |
+ env = xcalloc(envsize, sizeof(char *)); | |
+ env[0] = NULL; | |
+ | |
+#ifdef HAVE_CYGWIN | |
+ /* | |
+ * The Windows environment contains some setting which are | |
+ * important for a running system. They must not be dropped. | |
+ */ | |
+ { | |
+ char **p; | |
+ | |
+ p = fetch_windows_environment(); | |
+ copy_environment(p, &env, &envsize); | |
+ free_windows_environment(p); | |
+ } | |
+#endif | |
+ | |
+#ifdef GSSAPI | |
+ /* Allow any GSSAPI methods that we've used to alter | |
+ * the childs environment as they see fit | |
+ */ | |
+ ssh_gssapi_do_child(&env, &envsize); | |
+#endif | |
+ | |
+ if (!options.use_login) { | |
+ /* Set basic environment. */ | |
+ for (i = 0; i < s->num_env; i++) | |
+ child_set_env(&env, &envsize, s->env[i].name, | |
+ s->env[i].val); | |
+ | |
+ child_set_env(&env, &envsize, "USER", pw->pw_name); | |
+ child_set_env(&env, &envsize, "LOGNAME", pw->pw_name); | |
+#ifdef _AIX | |
+ child_set_env(&env, &envsize, "LOGIN", pw->pw_name); | |
+#endif | |
+ child_set_env(&env, &envsize, "HOME", pw->pw_dir); | |
+#ifdef HAVE_LOGIN_CAP | |
+ if (setusercontext(lc, pw, pw->pw_uid, LOGIN_SETPATH) < 0) | |
+ child_set_env(&env, &envsize, "PATH", _PATH_STDPATH); | |
+ else | |
+ child_set_env(&env, &envsize, "PATH", getenv("PATH")); | |
+#else /* HAVE_LOGIN_CAP */ | |
+# ifndef HAVE_CYGWIN | |
+ /* | |
+ * There's no standard path on Windows. The path contains | |
+ * important components pointing to the system directories, | |
+ * needed for loading shared libraries. So the path better | |
+ * remains intact here. | |
+ */ | |
+# ifdef HAVE_ETC_DEFAULT_LOGIN | |
+ read_etc_default_login(&env, &envsize, pw->pw_uid); | |
+ path = child_get_env(env, "PATH"); | |
+# endif /* HAVE_ETC_DEFAULT_LOGIN */ | |
+ if (path == NULL || *path == '\0') { | |
+ child_set_env(&env, &envsize, "PATH", | |
+ s->pw->pw_uid == 0 ? | |
+ SUPERUSER_PATH : _PATH_STDPATH); | |
+ } | |
+# endif /* HAVE_CYGWIN */ | |
+#endif /* HAVE_LOGIN_CAP */ | |
+ | |
+ snprintf(buf, sizeof buf, "%.200s/%.50s", | |
+ _PATH_MAILDIR, pw->pw_name); | |
+ child_set_env(&env, &envsize, "MAIL", buf); | |
+ | |
+ /* Normal systems set SHELL by default. */ | |
+ child_set_env(&env, &envsize, "SHELL", shell); | |
+ } | |
+ if (getenv("TZ")) | |
+ child_set_env(&env, &envsize, "TZ", getenv("TZ")); | |
+ | |
+ /* Set custom environment options from RSA authentication. */ | |
+ if (!options.use_login) { | |
+ while (custom_environment) { | |
+ struct envstring *ce = custom_environment; | |
+ char *str = ce->s; | |
+ | |
+ for (i = 0; str[i] != '=' && str[i]; i++) | |
+ ; | |
+ if (str[i] == '=') { | |
+ str[i] = 0; | |
+ child_set_env(&env, &envsize, str, str + i + 1); | |
+ } | |
+ custom_environment = ce->next; | |
+ free(ce->s); | |
+ free(ce); | |
+ } | |
+ } | |
+ | |
+ /* SSH_CLIENT deprecated */ | |
+ snprintf(buf, sizeof buf, "%.50s %d %d", | |
+ get_remote_ipaddr(), get_remote_port(), get_local_port()); | |
+ child_set_env(&env, &envsize, "SSH_CLIENT", buf); | |
+ | |
+ laddr = get_local_ipaddr(packet_get_connection_in()); | |
+ snprintf(buf, sizeof buf, "%.50s %d %.50s %d", | |
+ get_remote_ipaddr(), get_remote_port(), laddr, get_local_port()); | |
+ free(laddr); | |
+ child_set_env(&env, &envsize, "SSH_CONNECTION", buf); | |
+ | |
+ if (s->ttyfd != -1) | |
+ child_set_env(&env, &envsize, "SSH_TTY", s->tty); | |
+ if (s->term) | |
+ child_set_env(&env, &envsize, "TERM", s->term); | |
+ if (s->display) | |
+ child_set_env(&env, &envsize, "DISPLAY", s->display); | |
+ if (original_command) | |
+ child_set_env(&env, &envsize, "SSH_ORIGINAL_COMMAND", | |
+ original_command); | |
+ | |
+#ifdef _UNICOS | |
+ if (cray_tmpdir[0] != '\0') | |
+ child_set_env(&env, &envsize, "TMPDIR", cray_tmpdir); | |
+#endif /* _UNICOS */ | |
+ | |
+ /* | |
+ * Since we clear KRB5CCNAME at startup, if it's set now then it | |
+ * must have been set by a native authentication method (eg AIX or | |
+ * SIA), so copy it to the child. | |
+ */ | |
+ { | |
+ char *cp; | |
+ | |
+ if ((cp = getenv("KRB5CCNAME")) != NULL) | |
+ child_set_env(&env, &envsize, "KRB5CCNAME", cp); | |
+ } | |
+ | |
+#ifdef _AIX | |
+ { | |
+ char *cp; | |
+ | |
+ if ((cp = getenv("AUTHSTATE")) != NULL) | |
+ child_set_env(&env, &envsize, "AUTHSTATE", cp); | |
+ read_environment_file(&env, &envsize, "/etc/environment"); | |
+ } | |
+#endif | |
+#ifdef KRB5 | |
+ if (s->authctxt->krb5_ccname) | |
+ child_set_env(&env, &envsize, "KRB5CCNAME", | |
+ s->authctxt->krb5_ccname); | |
+#endif | |
+#ifdef USE_PAM | |
+ /* | |
+ * Pull in any environment variables that may have | |
+ * been set by PAM. | |
+ */ | |
+ if (options.use_pam) { | |
+ char **p; | |
+ | |
+ p = fetch_pam_child_environment(); | |
+ copy_environment(p, &env, &envsize); | |
+ free_pam_environment(p); | |
+ | |
+ p = fetch_pam_environment(); | |
+ copy_environment(p, &env, &envsize); | |
+ free_pam_environment(p); | |
+ } | |
+#endif /* USE_PAM */ | |
+ | |
+ if (auth_sock_name != NULL) | |
+ child_set_env(&env, &envsize, SSH_AUTHSOCKET_ENV_NAME, | |
+ auth_sock_name); | |
+ | |
+ /* read $HOME/.ssh/environment. */ | |
+ if (options.permit_user_env && !options.use_login) { | |
+ snprintf(buf, sizeof buf, "%.200s/.ssh/environment", | |
+ strcmp(pw->pw_dir, "/") ? pw->pw_dir : ""); | |
+ read_environment_file(&env, &envsize, buf); | |
+ } | |
+ if (debug_flag) { | |
+ /* dump the environment */ | |
+ fprintf(stderr, "Environment:\n"); | |
+ for (i = 0; env[i]; i++) | |
+ fprintf(stderr, " %.200s\n", env[i]); | |
+ } | |
+ return env; | |
+} | |
+ | |
+/* | |
+ * Run $HOME/.ssh/rc, /etc/ssh/sshrc, or xauth (whichever is found | |
+ * first in this order). | |
+ */ | |
+static void | |
+do_rc_files(Session *s, const char *shell) | |
+{ | |
+ FILE *f = NULL; | |
+ char cmd[1024]; | |
+ int do_xauth; | |
+ struct stat st; | |
+ | |
+ do_xauth = | |
+ s->display != NULL && s->auth_proto != NULL && s->auth_data != NULL; | |
+ | |
+ /* ignore _PATH_SSH_USER_RC for subsystems and admin forced commands */ | |
+ if (!s->is_subsystem && options.adm_forced_command == NULL && | |
+ !no_user_rc && options.permit_user_rc && | |
+ stat(_PATH_SSH_USER_RC, &st) >= 0) { | |
+ snprintf(cmd, sizeof cmd, "%s -c '%s %s'", | |
+ shell, _PATH_BSHELL, _PATH_SSH_USER_RC); | |
+ if (debug_flag) | |
+ fprintf(stderr, "Running %s\n", cmd); | |
+ f = popen(cmd, "w"); | |
+ if (f) { | |
+ if (do_xauth) | |
+ fprintf(f, "%s %s\n", s->auth_proto, | |
+ s->auth_data); | |
+ pclose(f); | |
+ } else | |
+ fprintf(stderr, "Could not run %s\n", | |
+ _PATH_SSH_USER_RC); | |
+ } else if (stat(_PATH_SSH_SYSTEM_RC, &st) >= 0) { | |
+ if (debug_flag) | |
+ fprintf(stderr, "Running %s %s\n", _PATH_BSHELL, | |
+ _PATH_SSH_SYSTEM_RC); | |
+ f = popen(_PATH_BSHELL " " _PATH_SSH_SYSTEM_RC, "w"); | |
+ if (f) { | |
+ if (do_xauth) | |
+ fprintf(f, "%s %s\n", s->auth_proto, | |
+ s->auth_data); | |
+ pclose(f); | |
+ } else | |
+ fprintf(stderr, "Could not run %s\n", | |
+ _PATH_SSH_SYSTEM_RC); | |
+ } else if (do_xauth && options.xauth_location != NULL) { | |
+ /* Add authority data to .Xauthority if appropriate. */ | |
+ if (debug_flag) { | |
+ fprintf(stderr, | |
+ "Running %.500s remove %.100s\n", | |
+ options.xauth_location, s->auth_display); | |
+ fprintf(stderr, | |
+ "%.500s add %.100s %.100s %.100s\n", | |
+ options.xauth_location, s->auth_display, | |
+ s->auth_proto, s->auth_data); | |
+ } | |
+ snprintf(cmd, sizeof cmd, "%s -q -", | |
+ options.xauth_location); | |
+ f = popen(cmd, "w"); | |
+ if (f) { | |
+ fprintf(f, "remove %s\n", | |
+ s->auth_display); | |
+ fprintf(f, "add %s %s %s\n", | |
+ s->auth_display, s->auth_proto, | |
+ s->auth_data); | |
+ pclose(f); | |
+ } else { | |
+ fprintf(stderr, "Could not run %s\n", | |
+ cmd); | |
+ } | |
+ } | |
+} | |
+ | |
+static void | |
+do_nologin(struct passwd *pw) | |
+{ | |
+ FILE *f = NULL; | |
+ char buf[1024], *nl, *def_nl = _PATH_NOLOGIN; | |
+ struct stat sb; | |
+ | |
+#ifdef HAVE_LOGIN_CAP | |
+ if (login_getcapbool(lc, "ignorenologin", 0) || pw->pw_uid == 0) | |
+ return; | |
+ nl = login_getcapstr(lc, "nologin", def_nl, def_nl); | |
+#else | |
+ if (pw->pw_uid == 0) | |
+ return; | |
+ nl = def_nl; | |
+#endif | |
+ if (stat(nl, &sb) == -1) { | |
+ if (nl != def_nl) | |
+ free(nl); | |
+ return; | |
+ } | |
+ | |
+ /* /etc/nologin exists. Print its contents if we can and exit. */ | |
+ logit("User %.100s not allowed because %s exists", pw->pw_name, nl); | |
+ if ((f = fopen(nl, "r")) != NULL) { | |
+ while (fgets(buf, sizeof(buf), f)) | |
+ fputs(buf, stderr); | |
+ fclose(f); | |
+ } | |
+ exit(254); | |
+} | |
+ | |
+/* | |
+ * Chroot into a directory after checking it for safety: all path components | |
+ * must be root-owned directories with strict permissions. | |
+ */ | |
+static void | |
+safely_chroot(const char *path, uid_t uid) | |
+{ | |
+ const char *cp; | |
+ char component[MAXPATHLEN]; | |
+ struct stat st; | |
+ | |
+ if (*path != '/') | |
+ fatal("chroot path does not begin at root"); | |
+ if (strlen(path) >= sizeof(component)) | |
+ fatal("chroot path too long"); | |
+ | |
+ /* | |
+ * Descend the path, checking that each component is a | |
+ * root-owned directory with strict permissions. | |
+ */ | |
+ for (cp = path; cp != NULL;) { | |
+ if ((cp = strchr(cp, '/')) == NULL) | |
+ strlcpy(component, path, sizeof(component)); | |
+ else { | |
+ cp++; | |
+ memcpy(component, path, cp - path); | |
+ component[cp - path] = '\0'; | |
+ } | |
+ | |
+ debug3("%s: checking '%s'", __func__, component); | |
+ | |
+ if (stat(component, &st) != 0) | |
+ fatal("%s: stat(\"%s\"): %s", __func__, | |
+ component, strerror(errno)); | |
+ if (st.st_uid != 0 || (st.st_mode & 022) != 0) | |
+ fatal("bad ownership or modes for chroot " | |
+ "directory %s\"%s\"", | |
+ cp == NULL ? "" : "component ", component); | |
+ if (!S_ISDIR(st.st_mode)) | |
+ fatal("chroot path %s\"%s\" is not a directory", | |
+ cp == NULL ? "" : "component ", component); | |
+ | |
+ } | |
+ | |
+ if (chdir(path) == -1) | |
+ fatal("Unable to chdir to chroot path \"%s\": " | |
+ "%s", path, strerror(errno)); | |
+ if (chroot(path) == -1) | |
+ fatal("chroot(\"%s\"): %s", path, strerror(errno)); | |
+ if (chdir("/") == -1) | |
+ fatal("%s: chdir(/) after chroot: %s", | |
+ __func__, strerror(errno)); | |
+ verbose("Changed root directory to \"%s\"", path); | |
+} | |
+ | |
+/* Set login name, uid, gid, and groups. */ | |
+void | |
+do_setusercontext(struct passwd *pw) | |
+{ | |
+ char *chroot_path, *tmp; | |
+#ifdef USE_LIBIAF | |
+ int doing_chroot = 0; | |
+#endif | |
+ | |
+ platform_setusercontext(pw); | |
+ | |
+ if (platform_privileged_uidswap()) { | |
+#ifdef HAVE_LOGIN_CAP | |
+ if (setusercontext(lc, pw, pw->pw_uid, | |
+ (LOGIN_SETALL & ~(LOGIN_SETPATH|LOGIN_SETUSER))) < 0) { | |
+ perror("unable to set user context"); | |
+ exit(1); | |
+ } | |
+#else | |
+ if (setlogin(pw->pw_name) < 0) | |
+ error("setlogin failed: %s", strerror(errno)); | |
+ if (setgid(pw->pw_gid) < 0) { | |
+ perror("setgid"); | |
+ exit(1); | |
+ } | |
+ /* Initialize the group list. */ | |
+ if (initgroups(pw->pw_name, pw->pw_gid) < 0) { | |
+ perror("initgroups"); | |
+ exit(1); | |
+ } | |
+ endgrent(); | |
+#endif | |
+ | |
+ platform_setusercontext_post_groups(pw); | |
+ | |
+ if (options.chroot_directory != NULL && | |
+ strcasecmp(options.chroot_directory, "none") != 0) { | |
+ tmp = tilde_expand_filename(options.chroot_directory, | |
+ pw->pw_uid); | |
+ chroot_path = percent_expand(tmp, "h", pw->pw_dir, | |
+ "u", pw->pw_name, (char *)NULL); | |
+ safely_chroot(chroot_path, pw->pw_uid); | |
+ free(tmp); | |
+ free(chroot_path); | |
+ /* Make sure we don't attempt to chroot again */ | |
+ free(options.chroot_directory); | |
+ options.chroot_directory = NULL; | |
+#ifdef USE_LIBIAF | |
+ doing_chroot = 1; | |
+#endif | |
+ } | |
+ | |
+#ifdef HAVE_LOGIN_CAP | |
+ if (setusercontext(lc, pw, pw->pw_uid, LOGIN_SETUSER) < 0) { | |
+ perror("unable to set user context (setuser)"); | |
+ exit(1); | |
+ } | |
+ /* | |
+ * FreeBSD's setusercontext() will not apply the user's | |
+ * own umask setting unless running with the user's UID. | |
+ */ | |
+ (void) setusercontext(lc, pw, pw->pw_uid, LOGIN_SETUMASK); | |
+#else | |
+# ifdef USE_LIBIAF | |
+/* In a chroot environment, the set_id() will always fail; typically | |
+ * because of the lack of necessary authentication services and runtime | |
+ * such as ./usr/lib/libiaf.so, ./usr/lib/libpam.so.1, and ./etc/passwd | |
+ * We skip it in the internal sftp chroot case. | |
+ * We'll lose auditing and ACLs but permanently_set_uid will | |
+ * take care of the rest. | |
+ */ | |
+ if ((doing_chroot == 0) && set_id(pw->pw_name) != 0) { | |
+ fatal("set_id(%s) Failed", pw->pw_name); | |
+ } | |
+# endif /* USE_LIBIAF */ | |
+ /* Permanently switch to the desired uid. */ | |
+ permanently_set_uid(pw); | |
+#endif | |
+ } else if (options.chroot_directory != NULL && | |
+ strcasecmp(options.chroot_directory, "none") != 0) { | |
+ fatal("server lacks privileges to chroot to ChrootDirectory"); | |
+ } | |
+ | |
+ if (getuid() != pw->pw_uid || geteuid() != pw->pw_uid) | |
+ fatal("Failed to set uids to %u.", (u_int) pw->pw_uid); | |
+} | |
+ | |
+static void | |
+do_pwchange(Session *s) | |
+{ | |
+ fflush(NULL); | |
+ fprintf(stderr, "WARNING: Your password has expired.\n"); | |
+ if (s->ttyfd != -1) { | |
+ fprintf(stderr, | |
+ "You must change your password now and login again!\n"); | |
+#ifdef WITH_SELINUX | |
+ setexeccon(NULL); | |
+#endif | |
+#ifdef PASSWD_NEEDS_USERNAME | |
+ execl(_PATH_PASSWD_PROG, "passwd", s->pw->pw_name, | |
+ (char *)NULL); | |
+#else | |
+ execl(_PATH_PASSWD_PROG, "passwd", (char *)NULL); | |
+#endif | |
+ perror("passwd"); | |
+ } else { | |
+ fprintf(stderr, | |
+ "Password change required but no TTY available.\n"); | |
+ } | |
+ exit(1); | |
+} | |
+ | |
+static void | |
+launch_login(struct passwd *pw, const char *hostname) | |
+{ | |
+ /* Launch login(1). */ | |
+ | |
+ execl(LOGIN_PROGRAM, "login", "-h", hostname, | |
+#ifdef xxxLOGIN_NEEDS_TERM | |
+ (s->term ? s->term : "unknown"), | |
+#endif /* LOGIN_NEEDS_TERM */ | |
+#ifdef LOGIN_NO_ENDOPT | |
+ "-p", "-f", pw->pw_name, (char *)NULL); | |
+#else | |
+ "-p", "-f", "--", pw->pw_name, (char *)NULL); | |
+#endif | |
+ | |
+ /* Login couldn't be executed, die. */ | |
+ | |
+ perror("login"); | |
+ exit(1); | |
+} | |
+ | |
+static void | |
+child_close_fds(void) | |
+{ | |
+ extern AuthenticationConnection *auth_conn; | |
+ | |
+ if (auth_conn) { | |
+ ssh_close_authentication_connection(auth_conn); | |
+ auth_conn = NULL; | |
+ } | |
+ | |
+ if (packet_get_connection_in() == packet_get_connection_out()) | |
+ close(packet_get_connection_in()); | |
+ else { | |
+ close(packet_get_connection_in()); | |
+ close(packet_get_connection_out()); | |
+ } | |
+ /* | |
+ * Close all descriptors related to channels. They will still remain | |
+ * open in the parent. | |
+ */ | |
+ /* XXX better use close-on-exec? -markus */ | |
+ channel_close_all(); | |
+ | |
+ /* | |
+ * Close any extra file descriptors. Note that there may still be | |
+ * descriptors left by system functions. They will be closed later. | |
+ */ | |
+ endpwent(); | |
+ | |
+ /* | |
+ * Close any extra open file descriptors so that we don't have them | |
+ * hanging around in clients. Note that we want to do this after | |
+ * initgroups, because at least on Solaris 2.3 it leaves file | |
+ * descriptors open. | |
+ */ | |
+ closefrom(STDERR_FILENO + 1); | |
+} | |
+ | |
+/* | |
+ * Performs common processing for the child, such as setting up the | |
+ * environment, closing extra file descriptors, setting the user and group | |
+ * ids, and executing the command or shell. | |
+ */ | |
+#define ARGV_MAX 10 | |
+void | |
+do_child(Session *s, const char *command) | |
+{ | |
+ extern char **environ; | |
+ char **env; | |
+ char *argv[ARGV_MAX]; | |
+ const char *shell, *shell0, *hostname = NULL; | |
+ struct passwd *pw = s->pw; | |
+ int r = 0; | |
+ | |
+ /* remove hostkey from the child's memory */ | |
+ destroy_sensitive_data(); | |
+ | |
+ /* Force a password change */ | |
+ if (s->authctxt->force_pwchange) { | |
+ do_setusercontext(pw); | |
+ child_close_fds(); | |
+ do_pwchange(s); | |
+ exit(1); | |
+ } | |
+ | |
+ /* login(1) is only called if we execute the login shell */ | |
+ if (options.use_login && command != NULL) | |
+ options.use_login = 0; | |
+ | |
+#ifdef _UNICOS | |
+ cray_setup(pw->pw_uid, pw->pw_name, command); | |
+#endif /* _UNICOS */ | |
+ | |
+ /* | |
+ * Login(1) does this as well, and it needs uid 0 for the "-h" | |
+ * switch, so we let login(1) to this for us. | |
+ */ | |
+ if (!options.use_login) { | |
+#ifdef HAVE_OSF_SIA | |
+ session_setup_sia(pw, s->ttyfd == -1 ? NULL : s->tty); | |
+ if (!check_quietlogin(s, command)) | |
+ do_motd(); | |
+#else /* HAVE_OSF_SIA */ | |
+ /* When PAM is enabled we rely on it to do the nologin check */ | |
+ if (!options.use_pam) | |
+ do_nologin(pw); | |
+ do_setusercontext(pw); | |
+ /* | |
+ * PAM session modules in do_setusercontext may have | |
+ * generated messages, so if this in an interactive | |
+ * login then display them too. | |
+ */ | |
+ if (!check_quietlogin(s, command)) | |
+ display_loginmsg(); | |
+#endif /* HAVE_OSF_SIA */ | |
+ } | |
+ | |
+#ifdef USE_PAM | |
+ if (options.use_pam && !options.use_login && !is_pam_session_open()) { | |
+ debug3("PAM session not opened, exiting"); | |
+ display_loginmsg(); | |
+ exit(254); | |
+ } | |
+#endif | |
+ | |
+ /* | |
+ * Get the shell from the password data. An empty shell field is | |
+ * legal, and means /bin/sh. | |
+ */ | |
+ shell = (pw->pw_shell[0] == '\0') ? _PATH_BSHELL : pw->pw_shell; | |
+ | |
+ /* | |
+ * Make sure $SHELL points to the shell from the password file, | |
+ * even if shell is overridden from login.conf | |
+ */ | |
+ env = do_setup_env(s, shell); | |
+ | |
+#ifdef HAVE_LOGIN_CAP | |
+ shell = login_getcapstr(lc, "shell", (char *)shell, (char *)shell); | |
+#endif | |
+ | |
+ /* we have to stash the hostname before we close our socket. */ | |
+ if (options.use_login) | |
+ hostname = get_remote_name_or_ip(utmp_len, | |
+ options.use_dns); | |
+ /* | |
+ * Close the connection descriptors; note that this is the child, and | |
+ * the server will still have the socket open, and it is important | |
+ * that we do not shutdown it. Note that the descriptors cannot be | |
+ * closed before building the environment, as we call | |
+ * get_remote_ipaddr there. | |
+ */ | |
+ child_close_fds(); | |
+ | |
+ /* | |
+ * Must take new environment into use so that .ssh/rc, | |
+ * /etc/ssh/sshrc and xauth are run in the proper environment. | |
+ */ | |
+ environ = env; | |
+ | |
+#if defined(KRB5) && defined(USE_AFS) | |
+ /* | |
+ * At this point, we check to see if AFS is active and if we have | |
+ * a valid Kerberos 5 TGT. If so, it seems like a good idea to see | |
+ * if we can (and need to) extend the ticket into an AFS token. If | |
+ * we don't do this, we run into potential problems if the user's | |
+ * home directory is in AFS and it's not world-readable. | |
+ */ | |
+ | |
+ if (options.kerberos_get_afs_token && k_hasafs() && | |
+ (s->authctxt->krb5_ctx != NULL)) { | |
+ char cell[64]; | |
+ | |
+ debug("Getting AFS token"); | |
+ | |
+ k_setpag(); | |
+ | |
+ if (k_afs_cell_of_file(pw->pw_dir, cell, sizeof(cell)) == 0) | |
+ krb5_afslog(s->authctxt->krb5_ctx, | |
+ s->authctxt->krb5_fwd_ccache, cell, NULL); | |
+ | |
+ krb5_afslog_home(s->authctxt->krb5_ctx, | |
+ s->authctxt->krb5_fwd_ccache, NULL, NULL, pw->pw_dir); | |
+ } | |
+#endif | |
+ | |
+ /* Change current directory to the user's home directory. */ | |
+ if (chdir(pw->pw_dir) < 0) { | |
+ /* Suppress missing homedir warning for chroot case */ | |
+#ifdef HAVE_LOGIN_CAP | |
+ r = login_getcapbool(lc, "requirehome", 0); | |
+#endif | |
+ if (r || options.chroot_directory == NULL || | |
+ strcasecmp(options.chroot_directory, "none") == 0) | |
+ fprintf(stderr, "Could not chdir to home " | |
+ "directory %s: %s\n", pw->pw_dir, | |
+ strerror(errno)); | |
+ if (r) | |
+ exit(1); | |
+ } | |
+ | |
+ closefrom(STDERR_FILENO + 1); | |
+ | |
+ if (!options.use_login) | |
+ do_rc_files(s, shell); | |
+ | |
+ /* restore SIGPIPE for child */ | |
+ signal(SIGPIPE, SIG_DFL); | |
+ | |
+ if (s->is_subsystem == SUBSYSTEM_INT_SFTP_ERROR) { | |
+ printf("This service allows sftp connections only.\n"); | |
+ fflush(NULL); | |
+ exit(1); | |
+ } else if (s->is_subsystem == SUBSYSTEM_INT_SFTP) { | |
+ extern int optind, optreset; | |
+ int i; | |
+ char *p, *args; | |
+ | |
+ setproctitle("%s@%s", s->pw->pw_name, INTERNAL_SFTP_NAME); | |
+ args = xstrdup(command ? command : "sftp-server"); | |
+ for (i = 0, (p = strtok(args, " ")); p; (p = strtok(NULL, " "))) | |
+ if (i < ARGV_MAX - 1) | |
+ argv[i++] = p; | |
+ argv[i] = NULL; | |
+ optind = optreset = 1; | |
+ __progname = argv[0]; | |
+#ifdef WITH_SELINUX | |
+ ssh_selinux_change_context("sftpd_t"); | |
+#endif | |
+ exit(sftp_server_main(i, argv, s->pw)); | |
+ } | |
+ | |
+ fflush(NULL); | |
+ | |
+ if (options.use_login) { | |
+ launch_login(pw, hostname); | |
+ /* NEVERREACHED */ | |
+ } | |
+ | |
+ /* Get the last component of the shell name. */ | |
+ if ((shell0 = strrchr(shell, '/')) != NULL) | |
+ shell0++; | |
+ else | |
+ shell0 = shell; | |
+ | |
+ /* | |
+ * If we have no command, execute the shell. In this case, the shell | |
+ * name to be passed in argv[0] is preceded by '-' to indicate that | |
+ * this is a login shell. | |
+ */ | |
+ if (!command) { | |
+ char argv0[256]; | |
+ | |
+ /* Start the shell. Set initial character to '-'. */ | |
+ argv0[0] = '-'; | |
+ | |
+ if (strlcpy(argv0 + 1, shell0, sizeof(argv0) - 1) | |
+ >= sizeof(argv0) - 1) { | |
+ errno = EINVAL; | |
+ perror(shell); | |
+ exit(1); | |
+ } | |
+ | |
+ /* Execute the shell. */ | |
+ argv[0] = argv0; | |
+ argv[1] = NULL; | |
+ execve(shell, argv, env); | |
+ | |
+ /* Executing the shell failed. */ | |
+ perror(shell); | |
+ exit(1); | |
+ } | |
+ /* | |
+ * Execute the command using the user's shell. This uses the -c | |
+ * option to execute the command. | |
+ */ | |
+ argv[0] = (char *) shell0; | |
+ argv[1] = "-c"; | |
+ argv[2] = (char *) command; | |
+ argv[3] = NULL; | |
+ execve(shell, argv, env); | |
+ perror(shell); | |
+ exit(1); | |
+} | |
+ | |
+void | |
+session_unused(int id) | |
+{ | |
+ debug3("%s: session id %d unused", __func__, id); | |
+ if (id >= options.max_sessions || | |
+ id >= sessions_nalloc) { | |
+ fatal("%s: insane session id %d (max %d nalloc %d)", | |
+ __func__, id, options.max_sessions, sessions_nalloc); | |
+ } | |
+ memset(&sessions[id], 0, sizeof(*sessions)); | |
+ sessions[id].self = id; | |
+ sessions[id].used = 0; | |
+ sessions[id].chanid = -1; | |
+ sessions[id].ptyfd = -1; | |
+ sessions[id].ttyfd = -1; | |
+ sessions[id].ptymaster = -1; | |
+ sessions[id].x11_chanids = NULL; | |
+ sessions[id].next_unused = sessions_first_unused; | |
+ sessions_first_unused = id; | |
+} | |
+ | |
+Session * | |
+session_new(void) | |
+{ | |
+ Session *s, *tmp; | |
+ | |
+ if (sessions_first_unused == -1) { | |
+ if (sessions_nalloc >= options.max_sessions) | |
+ return NULL; | |
+ debug2("%s: allocate (allocated %d max %d)", | |
+ __func__, sessions_nalloc, options.max_sessions); | |
+ tmp = xrealloc(sessions, sessions_nalloc + 1, | |
+ sizeof(*sessions)); | |
+ if (tmp == NULL) { | |
+ error("%s: cannot allocate %d sessions", | |
+ __func__, sessions_nalloc + 1); | |
+ return NULL; | |
+ } | |
+ sessions = tmp; | |
+ session_unused(sessions_nalloc++); | |
+ } | |
+ | |
+ if (sessions_first_unused >= sessions_nalloc || | |
+ sessions_first_unused < 0) { | |
+ fatal("%s: insane first_unused %d max %d nalloc %d", | |
+ __func__, sessions_first_unused, options.max_sessions, | |
+ sessions_nalloc); | |
+ } | |
+ | |
+ s = &sessions[sessions_first_unused]; | |
+ if (s->used) { | |
+ fatal("%s: session %d already used", | |
+ __func__, sessions_first_unused); | |
+ } | |
+ sessions_first_unused = s->next_unused; | |
+ s->used = 1; | |
+ s->next_unused = -1; | |
+ debug("session_new: session %d", s->self); | |
+ | |
+ return s; | |
+} | |
+ | |
+static void | |
+session_dump(void) | |
+{ | |
+ int i; | |
+ for (i = 0; i < sessions_nalloc; i++) { | |
+ Session *s = &sessions[i]; | |
+ | |
+ debug("dump: used %d next_unused %d session %d %p " | |
+ "channel %d pid %ld", | |
+ s->used, | |
+ s->next_unused, | |
+ s->self, | |
+ s, | |
+ s->chanid, | |
+ (long)s->pid); | |
+ } | |
+} | |
+ | |
+int | |
+session_open(Authctxt *authctxt, int chanid) | |
+{ | |
+ Session *s = session_new(); | |
+ debug("session_open: channel %d", chanid); | |
+ if (s == NULL) { | |
+ error("no more sessions"); | |
+ return 0; | |
+ } | |
+ s->authctxt = authctxt; | |
+ s->pw = authctxt->pw; | |
+ if (s->pw == NULL || !authctxt->valid) | |
+ fatal("no user for session %d", s->self); | |
+ debug("session_open: session %d: link with channel %d", s->self, chanid); | |
+ s->chanid = chanid; | |
+ return 1; | |
+} | |
+ | |
+Session * | |
+session_by_tty(char *tty) | |
+{ | |
+ int i; | |
+ for (i = 0; i < sessions_nalloc; i++) { | |
+ Session *s = &sessions[i]; | |
+ if (s->used && s->ttyfd != -1 && strcmp(s->tty, tty) == 0) { | |
+ debug("session_by_tty: session %d tty %s", i, tty); | |
+ return s; | |
+ } | |
+ } | |
+ debug("session_by_tty: unknown tty %.100s", tty); | |
+ session_dump(); | |
+ return NULL; | |
+} | |
+ | |
+static Session * | |
+session_by_channel(int id) | |
+{ | |
+ int i; | |
+ for (i = 0; i < sessions_nalloc; i++) { | |
+ Session *s = &sessions[i]; | |
+ if (s->used && s->chanid == id) { | |
+ debug("session_by_channel: session %d channel %d", | |
+ i, id); | |
+ return s; | |
+ } | |
+ } | |
+ debug("session_by_channel: unknown channel %d", id); | |
+ session_dump(); | |
+ return NULL; | |
+} | |
+ | |
+static Session * | |
+session_by_x11_channel(int id) | |
+{ | |
+ int i, j; | |
+ | |
+ for (i = 0; i < sessions_nalloc; i++) { | |
+ Session *s = &sessions[i]; | |
+ | |
+ if (s->x11_chanids == NULL || !s->used) | |
+ continue; | |
+ for (j = 0; s->x11_chanids[j] != -1; j++) { | |
+ if (s->x11_chanids[j] == id) { | |
+ debug("session_by_x11_channel: session %d " | |
+ "channel %d", s->self, id); | |
+ return s; | |
+ } | |
+ } | |
+ } | |
+ debug("session_by_x11_channel: unknown channel %d", id); | |
+ session_dump(); | |
+ return NULL; | |
+} | |
+ | |
+static Session * | |
+session_by_pid(pid_t pid) | |
+{ | |
+ int i; | |
+ debug("session_by_pid: pid %ld", (long)pid); | |
+ for (i = 0; i < sessions_nalloc; i++) { | |
+ Session *s = &sessions[i]; | |
+ if (s->used && s->pid == pid) | |
+ return s; | |
+ } | |
+ error("session_by_pid: unknown pid %ld", (long)pid); | |
+ session_dump(); | |
+ return NULL; | |
+} | |
+ | |
+static int | |
+session_window_change_req(Session *s) | |
+{ | |
+ s->col = packet_get_int(); | |
+ s->row = packet_get_int(); | |
+ s->xpixel = packet_get_int(); | |
+ s->ypixel = packet_get_int(); | |
+ packet_check_eom(); | |
+ pty_change_window_size(s->ptyfd, s->row, s->col, s->xpixel, s->ypixel); | |
+ return 1; | |
+} | |
+ | |
+static int | |
+session_pty_req(Session *s) | |
+{ | |
+ u_int len; | |
+ int n_bytes; | |
+ | |
+ if (no_pty_flag || !options.permit_tty) { | |
+ debug("Allocating a pty not permitted for this authentication."); | |
+ return 0; | |
+ } | |
+ if (s->ttyfd != -1) { | |
+ packet_disconnect("Protocol error: you already have a pty."); | |
+ return 0; | |
+ } | |
+ | |
+ s->term = packet_get_string(&len); | |
+ | |
+ if (compat20) { | |
+ s->col = packet_get_int(); | |
+ s->row = packet_get_int(); | |
+ } else { | |
+ s->row = packet_get_int(); | |
+ s->col = packet_get_int(); | |
+ } | |
+ s->xpixel = packet_get_int(); | |
+ s->ypixel = packet_get_int(); | |
+ | |
+ if (strcmp(s->term, "") == 0) { | |
+ free(s->term); | |
+ s->term = NULL; | |
+ } | |
+ | |
+ /* Allocate a pty and open it. */ | |
+ debug("Allocating pty."); | |
+ if (!PRIVSEP(pty_allocate(&s->ptyfd, &s->ttyfd, s->tty, | |
+ sizeof(s->tty)))) { | |
+ free(s->term); | |
+ s->term = NULL; | |
+ s->ptyfd = -1; | |
+ s->ttyfd = -1; | |
+ error("session_pty_req: session %d alloc failed", s->self); | |
+ return 0; | |
+ } | |
+ debug("session_pty_req: session %d alloc %s", s->self, s->tty); | |
+ | |
+ /* for SSH1 the tty modes length is not given */ | |
+ if (!compat20) | |
+ n_bytes = packet_remaining(); | |
+ tty_parse_modes(s->ttyfd, &n_bytes); | |
+ | |
+ if (!use_privsep) | |
+ pty_setowner(s->pw, s->tty); | |
+ | |
+ /* Set window size from the packet. */ | |
+ pty_change_window_size(s->ptyfd, s->row, s->col, s->xpixel, s->ypixel); | |
+ | |
+ packet_check_eom(); | |
+ session_proctitle(s); | |
+ return 1; | |
+} | |
+ | |
+static int | |
+session_subsystem_req(Session *s) | |
+{ | |
+ struct stat st; | |
+ u_int len; | |
+ int success = 0; | |
+ char *prog, *cmd; | |
+ u_int i; | |
+ | |
+ s->subsys = packet_get_string(&len); | |
+ packet_check_eom(); | |
+ debug2("subsystem request for %.100s by user %s", s->subsys, | |
+ s->pw->pw_name); | |
+ | |
+ for (i = 0; i < options.num_subsystems; i++) { | |
+ if (strcmp(s->subsys, options.subsystem_name[i]) == 0) { | |
+ prog = options.subsystem_command[i]; | |
+ cmd = options.subsystem_args[i]; | |
+ if (strcmp(INTERNAL_SFTP_NAME, prog) == 0) { | |
+ s->is_subsystem = SUBSYSTEM_INT_SFTP; | |
+ debug("subsystem: %s", prog); | |
+ } else { | |
+ if (stat(prog, &st) < 0) | |
+ debug("subsystem: cannot stat %s: %s", | |
+ prog, strerror(errno)); | |
+ s->is_subsystem = SUBSYSTEM_EXT; | |
+ debug("subsystem: exec() %s", cmd); | |
+ } | |
+ success = do_exec(s, cmd) == 0; | |
+ break; | |
+ } | |
+ } | |
+ | |
+ if (!success) | |
+ logit("subsystem request for %.100s by user %s failed, " | |
+ "subsystem not found", s->subsys, s->pw->pw_name); | |
+ | |
+ return success; | |
+} | |
+ | |
+static int | |
+session_x11_req(Session *s) | |
+{ | |
+ int success; | |
+ | |
+ if (s->auth_proto != NULL || s->auth_data != NULL) { | |
+ error("session_x11_req: session %d: " | |
+ "x11 forwarding already active", s->self); | |
+ return 0; | |
+ } | |
+ s->single_connection = packet_get_char(); | |
+ s->auth_proto = packet_get_string(NULL); | |
+ s->auth_data = packet_get_string(NULL); | |
+ s->screen = packet_get_int(); | |
+ packet_check_eom(); | |
+ | |
+ success = session_setup_x11fwd(s); | |
+ if (!success) { | |
+ free(s->auth_proto); | |
+ free(s->auth_data); | |
+ s->auth_proto = NULL; | |
+ s->auth_data = NULL; | |
+ } | |
+ return success; | |
+} | |
+ | |
+static int | |
+session_shell_req(Session *s) | |
+{ | |
+ packet_check_eom(); | |
+ return do_exec(s, NULL) == 0; | |
+} | |
+ | |
+static int | |
+session_exec_req(Session *s) | |
+{ | |
+ u_int len, success; | |
+ | |
+ char *command = packet_get_string(&len); | |
+ packet_check_eom(); | |
+ success = do_exec(s, command) == 0; | |
+ free(command); | |
+ return success; | |
+} | |
+ | |
+static int | |
+session_break_req(Session *s) | |
+{ | |
+ | |
+ packet_get_int(); /* ignored */ | |
+ packet_check_eom(); | |
+ | |
+ if (s->ptymaster == -1 || tcsendbreak(s->ptymaster, 0) < 0) | |
+ return 0; | |
+ return 1; | |
+} | |
+ | |
+static int | |
+session_env_req(Session *s) | |
+{ | |
+ char *name, *val; | |
+ u_int name_len, val_len, i; | |
+ | |
+ name = packet_get_cstring(&name_len); | |
+ val = packet_get_cstring(&val_len); | |
+ packet_check_eom(); | |
+ | |
+ /* Don't set too many environment variables */ | |
+ if (s->num_env > 128) { | |
+ debug2("Ignoring env request %s: too many env vars", name); | |
+ goto fail; | |
+ } | |
+ | |
+ for (i = 0; i < options.num_accept_env; i++) { | |
+ if (match_pattern(name, options.accept_env[i])) { | |
+ debug2("Setting env %d: %s=%s", s->num_env, name, val); | |
+ s->env = xrealloc(s->env, s->num_env + 1, | |
+ sizeof(*s->env)); | |
+ s->env[s->num_env].name = name; | |
+ s->env[s->num_env].val = val; | |
+ s->num_env++; | |
+ return (1); | |
+ } | |
+ } | |
+ debug2("Ignoring env request %s: disallowed name", name); | |
+ | |
+ fail: | |
+ free(name); | |
+ free(val); | |
+ return (0); | |
+} | |
+ | |
+static int | |
+session_auth_agent_req(Session *s) | |
+{ | |
+ static int called = 0; | |
+ packet_check_eom(); | |
+ if (no_agent_forwarding_flag || !options.allow_agent_forwarding) { | |
+ debug("session_auth_agent_req: no_agent_forwarding_flag"); | |
+ return 0; | |
+ } | |
+ if (called) { | |
+ return 0; | |
+ } else { | |
+ called = 1; | |
+ return auth_input_request_forwarding(s->pw); | |
+ } | |
+} | |
+ | |
+int | |
+session_input_channel_req(Channel *c, const char *rtype) | |
+{ | |
+ int success = 0; | |
+ Session *s; | |
+ | |
+ if ((s = session_by_channel(c->self)) == NULL) { | |
+ logit("session_input_channel_req: no session %d req %.100s", | |
+ c->self, rtype); | |
+ return 0; | |
+ } | |
+ debug("session_input_channel_req: session %d req %s", s->self, rtype); | |
+ | |
+ /* | |
+ * a session is in LARVAL state until a shell, a command | |
+ * or a subsystem is executed | |
+ */ | |
+ if (c->type == SSH_CHANNEL_LARVAL) { | |
+ if (strcmp(rtype, "shell") == 0) { | |
+ success = session_shell_req(s); | |
+ } else if (strcmp(rtype, "exec") == 0) { | |
+ success = session_exec_req(s); | |
+ } else if (strcmp(rtype, "pty-req") == 0) { | |
+ success = session_pty_req(s); | |
+ } else if (strcmp(rtype, "x11-req") == 0) { | |
+ success = session_x11_req(s); | |
+ } else if (strcmp(rtype, "auth-agent-req@openssh.com") == 0) { | |
+ success = session_auth_agent_req(s); | |
+ } else if (strcmp(rtype, "subsystem") == 0) { | |
+ success = session_subsystem_req(s); | |
+ } else if (strcmp(rtype, "env") == 0) { | |
+ success = session_env_req(s); | |
+ } | |
+ } | |
+ if (strcmp(rtype, "window-change") == 0) { | |
+ success = session_window_change_req(s); | |
+ } else if (strcmp(rtype, "break") == 0) { | |
+ success = session_break_req(s); | |
+ } | |
+ | |
+ return success; | |
+} | |
+ | |
+void | |
+session_set_fds(Session *s, int fdin, int fdout, int fderr, int ignore_fderr, | |
+ int is_tty) | |
+{ | |
+ if (!compat20) | |
+ fatal("session_set_fds: called for proto != 2.0"); | |
+ /* | |
+ * now that have a child and a pipe to the child, | |
+ * we can activate our channel and register the fd's | |
+ */ | |
+ if (s->chanid == -1) | |
+ fatal("no channel for session %d", s->self); | |
+ channel_set_fds(s->chanid, | |
+ fdout, fdin, fderr, | |
+ ignore_fderr ? CHAN_EXTENDED_IGNORE : CHAN_EXTENDED_READ, | |
+ 1, is_tty, CHAN_SES_WINDOW_DEFAULT); | |
+} | |
+ | |
+/* | |
+ * Function to perform pty cleanup. Also called if we get aborted abnormally | |
+ * (e.g., due to a dropped connection). | |
+ */ | |
+void | |
+session_pty_cleanup2(Session *s) | |
+{ | |
+ if (s == NULL) { | |
+ error("session_pty_cleanup: no session"); | |
+ return; | |
+ } | |
+ if (s->ttyfd == -1) | |
+ return; | |
+ | |
+ debug("session_pty_cleanup: session %d release %s", s->self, s->tty); | |
+ | |
+ /* Record that the user has logged out. */ | |
+ if (s->pid != 0) | |
+ record_logout(s->pid, s->tty, s->pw->pw_name); | |
+ | |
+ /* Release the pseudo-tty. */ | |
+ if (getuid() == 0) | |
+ pty_release(s->tty); | |
+ | |
+ /* | |
+ * Close the server side of the socket pairs. We must do this after | |
+ * the pty cleanup, so that another process doesn't get this pty | |
+ * while we're still cleaning up. | |
+ */ | |
+ if (s->ptymaster != -1 && close(s->ptymaster) < 0) | |
+ error("close(s->ptymaster/%d): %s", | |
+ s->ptymaster, strerror(errno)); | |
+ | |
+ /* unlink pty from session */ | |
+ s->ttyfd = -1; | |
+} | |
+ | |
+void | |
+session_pty_cleanup(Session *s) | |
+{ | |
+ PRIVSEP(session_pty_cleanup2(s)); | |
+} | |
+ | |
+static char * | |
+sig2name(int sig) | |
+{ | |
+#define SSH_SIG(x) if (sig == SIG ## x) return #x | |
+ SSH_SIG(ABRT); | |
+ SSH_SIG(ALRM); | |
+ SSH_SIG(FPE); | |
+ SSH_SIG(HUP); | |
+ SSH_SIG(ILL); | |
+ SSH_SIG(INT); | |
+ SSH_SIG(KILL); | |
+ SSH_SIG(PIPE); | |
+ SSH_SIG(QUIT); | |
+ SSH_SIG(SEGV); | |
+ SSH_SIG(TERM); | |
+ SSH_SIG(USR1); | |
+ SSH_SIG(USR2); | |
+#undef SSH_SIG | |
+ return "SIG@openssh.com"; | |
+} | |
+ | |
+static void | |
+session_close_x11(int id) | |
+{ | |
+ Channel *c; | |
+ | |
+ if ((c = channel_by_id(id)) == NULL) { | |
+ debug("session_close_x11: x11 channel %d missing", id); | |
+ } else { | |
+ /* Detach X11 listener */ | |
+ debug("session_close_x11: detach x11 channel %d", id); | |
+ channel_cancel_cleanup(id); | |
+ if (c->ostate != CHAN_OUTPUT_CLOSED) | |
+ chan_mark_dead(c); | |
+ } | |
+} | |
+ | |
+static void | |
+session_close_single_x11(int id, void *arg) | |
+{ | |
+ Session *s; | |
+ u_int i; | |
+ | |
+ debug3("session_close_single_x11: channel %d", id); | |
+ channel_cancel_cleanup(id); | |
+ if ((s = session_by_x11_channel(id)) == NULL) | |
+ fatal("session_close_single_x11: no x11 channel %d", id); | |
+ for (i = 0; s->x11_chanids[i] != -1; i++) { | |
+ debug("session_close_single_x11: session %d: " | |
+ "closing channel %d", s->self, s->x11_chanids[i]); | |
+ /* | |
+ * The channel "id" is already closing, but make sure we | |
+ * close all of its siblings. | |
+ */ | |
+ if (s->x11_chanids[i] != id) | |
+ session_close_x11(s->x11_chanids[i]); | |
+ } | |
+ free(s->x11_chanids); | |
+ s->x11_chanids = NULL; | |
+ free(s->display); | |
+ s->display = NULL; | |
+ free(s->auth_proto); | |
+ s->auth_proto = NULL; | |
+ free(s->auth_data); | |
+ s->auth_data = NULL; | |
+ free(s->auth_display); | |
+ s->auth_display = NULL; | |
+} | |
+ | |
+static void | |
+session_exit_message(Session *s, int status) | |
+{ | |
+ Channel *c; | |
+ | |
+ if ((c = channel_lookup(s->chanid)) == NULL) | |
+ fatal("session_exit_message: session %d: no channel %d", | |
+ s->self, s->chanid); | |
+ debug("session_exit_message: session %d channel %d pid %ld", | |
+ s->self, s->chanid, (long)s->pid); | |
+ | |
+ if (WIFEXITED(status)) { | |
+ channel_request_start(s->chanid, "exit-status", 0); | |
+ packet_put_int(WEXITSTATUS(status)); | |
+ packet_send(); | |
+ } else if (WIFSIGNALED(status)) { | |
+ channel_request_start(s->chanid, "exit-signal", 0); | |
+ packet_put_cstring(sig2name(WTERMSIG(status))); | |
+#ifdef WCOREDUMP | |
+ packet_put_char(WCOREDUMP(status)? 1 : 0); | |
+#else /* WCOREDUMP */ | |
+ packet_put_char(0); | |
+#endif /* WCOREDUMP */ | |
+ packet_put_cstring(""); | |
+ packet_put_cstring(""); | |
+ packet_send(); | |
+ } else { | |
+ /* Some weird exit cause. Just exit. */ | |
+ packet_disconnect("wait returned status %04x.", status); | |
+ } | |
+ | |
+ /* disconnect channel */ | |
+ debug("session_exit_message: release channel %d", s->chanid); | |
+ | |
+ /* | |
+ * Adjust cleanup callback attachment to send close messages when | |
+ * the channel gets EOF. The session will be then be closed | |
+ * by session_close_by_channel when the childs close their fds. | |
+ */ | |
+ channel_register_cleanup(c->self, session_close_by_channel, 1); | |
+ | |
+ /* | |
+ * emulate a write failure with 'chan_write_failed', nobody will be | |
+ * interested in data we write. | |
+ * Note that we must not call 'chan_read_failed', since there could | |
+ * be some more data waiting in the pipe. | |
+ */ | |
+ if (c->ostate != CHAN_OUTPUT_CLOSED) | |
+ chan_write_failed(c); | |
+} | |
+ | |
+void | |
+session_close(Session *s) | |
+{ | |
+ u_int i; | |
+ | |
+ debug("session_close: session %d pid %ld", s->self, (long)s->pid); | |
+ if (s->ttyfd != -1) | |
+ session_pty_cleanup(s); | |
+ free(s->term); | |
+ free(s->display); | |
+ free(s->x11_chanids); | |
+ free(s->auth_display); | |
+ free(s->auth_data); | |
+ free(s->auth_proto); | |
+ free(s->subsys); | |
+ if (s->env != NULL) { | |
+ for (i = 0; i < s->num_env; i++) { | |
+ free(s->env[i].name); | |
+ free(s->env[i].val); | |
+ } | |
+ free(s->env); | |
+ } | |
+ session_proctitle(s); | |
+ session_unused(s->self); | |
+} | |
+ | |
+void | |
+session_close_by_pid(pid_t pid, int status) | |
+{ | |
+ Session *s = session_by_pid(pid); | |
+ if (s == NULL) { | |
+ debug("session_close_by_pid: no session for pid %ld", | |
+ (long)pid); | |
+ return; | |
+ } | |
+ if (s->chanid != -1) | |
+ session_exit_message(s, status); | |
+ if (s->ttyfd != -1) | |
+ session_pty_cleanup(s); | |
+ s->pid = 0; | |
+} | |
+ | |
+/* | |
+ * this is called when a channel dies before | |
+ * the session 'child' itself dies | |
+ */ | |
+void | |
+session_close_by_channel(int id, void *arg) | |
+{ | |
+ Session *s = session_by_channel(id); | |
+ u_int i; | |
+ | |
+ if (s == NULL) { | |
+ debug("session_close_by_channel: no session for id %d", id); | |
+ return; | |
+ } | |
+ debug("session_close_by_channel: channel %d child %ld", | |
+ id, (long)s->pid); | |
+ if (s->pid != 0) { | |
+ debug("session_close_by_channel: channel %d: has child", id); | |
+ /* | |
+ * delay detach of session, but release pty, since | |
+ * the fd's to the child are already closed | |
+ */ | |
+ if (s->ttyfd != -1) | |
+ session_pty_cleanup(s); | |
+ return; | |
+ } | |
+ /* detach by removing callback */ | |
+ channel_cancel_cleanup(s->chanid); | |
+ | |
+ /* Close any X11 listeners associated with this session */ | |
+ if (s->x11_chanids != NULL) { | |
+ for (i = 0; s->x11_chanids[i] != -1; i++) { | |
+ session_close_x11(s->x11_chanids[i]); | |
+ s->x11_chanids[i] = -1; | |
+ } | |
+ } | |
+ | |
+ s->chanid = -1; | |
+ session_close(s); | |
+} | |
+ | |
+void | |
+session_destroy_all(void (*closefunc)(Session *)) | |
+{ | |
+ int i; | |
+ for (i = 0; i < sessions_nalloc; i++) { | |
+ Session *s = &sessions[i]; | |
+ if (s->used) { | |
+ if (closefunc != NULL) | |
+ closefunc(s); | |
+ else | |
+ session_close(s); | |
+ } | |
+ } | |
+} | |
+ | |
+static char * | |
+session_tty_list(void) | |
+{ | |
+ static char buf[1024]; | |
+ int i; | |
+ char *cp; | |
+ | |
+ buf[0] = '\0'; | |
+ for (i = 0; i < sessions_nalloc; i++) { | |
+ Session *s = &sessions[i]; | |
+ if (s->used && s->ttyfd != -1) { | |
+ | |
+ if (strncmp(s->tty, "/dev/", 5) != 0) { | |
+ cp = strrchr(s->tty, '/'); | |
+ cp = (cp == NULL) ? s->tty : cp + 1; | |
+ } else | |
+ cp = s->tty + 5; | |
+ | |
+ if (buf[0] != '\0') | |
+ strlcat(buf, ",", sizeof buf); | |
+ strlcat(buf, cp, sizeof buf); | |
+ } | |
+ } | |
+ if (buf[0] == '\0') | |
+ strlcpy(buf, "notty", sizeof buf); | |
+ return buf; | |
+} | |
+ | |
+void | |
+session_proctitle(Session *s) | |
+{ | |
+ if (s->pw == NULL) | |
+ error("no user for session %d", s->self); | |
+ else | |
+ setproctitle("%s@%s", s->pw->pw_name, session_tty_list()); | |
+} | |
+ | |
+int | |
+session_setup_x11fwd(Session *s) | |
+{ | |
+ struct stat st; | |
+ char display[512], auth_display[512]; | |
+ char hostname[NI_MAXHOST]; | |
+ u_int i; | |
+ | |
+ if (no_x11_forwarding_flag) { | |
+ packet_send_debug("X11 forwarding disabled in user configuration file."); | |
+ return 0; | |
+ } | |
+ if (!options.x11_forwarding) { | |
+ debug("X11 forwarding disabled in server configuration file."); | |
+ return 0; | |
+ } | |
+ if (!options.xauth_location || | |
+ (stat(options.xauth_location, &st) == -1)) { | |
+ packet_send_debug("No xauth program; cannot forward with spoofing."); | |
+ return 0; | |
+ } | |
+ if (options.use_login) { | |
+ packet_send_debug("X11 forwarding disabled; " | |
+ "not compatible with UseLogin=yes."); | |
+ return 0; | |
+ } | |
+ if (s->display != NULL) { | |
+ debug("X11 display already set."); | |
+ return 0; | |
+ } | |
+ if (x11_create_display_inet(options.x11_display_offset, | |
+ options.x11_use_localhost, s->single_connection, | |
+ &s->display_number, &s->x11_chanids) == -1) { | |
+ debug("x11_create_display_inet failed."); | |
+ return 0; | |
+ } | |
+ for (i = 0; s->x11_chanids[i] != -1; i++) { | |
+ channel_register_cleanup(s->x11_chanids[i], | |
+ session_close_single_x11, 0); | |
+ } | |
+ | |
+ /* Set up a suitable value for the DISPLAY variable. */ | |
+ if (gethostname(hostname, sizeof(hostname)) < 0) | |
+ fatal("gethostname: %.100s", strerror(errno)); | |
+ /* | |
+ * auth_display must be used as the displayname when the | |
+ * authorization entry is added with xauth(1). This will be | |
+ * different than the DISPLAY string for localhost displays. | |
+ */ | |
+ if (options.x11_use_localhost) { | |
+ snprintf(display, sizeof display, "localhost:%u.%u", | |
+ s->display_number, s->screen); | |
+ snprintf(auth_display, sizeof auth_display, "unix:%u.%u", | |
+ s->display_number, s->screen); | |
+ s->display = xstrdup(display); | |
+ s->auth_display = xstrdup(auth_display); | |
+ } else { | |
+#ifdef IPADDR_IN_DISPLAY | |
+ struct hostent *he; | |
+ struct in_addr my_addr; | |
+ | |
+ he = gethostbyname(hostname); | |
+ if (he == NULL) { | |
+ error("Can't get IP address for X11 DISPLAY."); | |
+ packet_send_debug("Can't get IP address for X11 DISPLAY."); | |
+ return 0; | |
+ } | |
+ memcpy(&my_addr, he->h_addr_list[0], sizeof(struct in_addr)); | |
+ snprintf(display, sizeof display, "%.50s:%u.%u", inet_ntoa(my_addr), | |
+ s->display_number, s->screen); | |
+#else | |
+ snprintf(display, sizeof display, "%.400s:%u.%u", hostname, | |
+ s->display_number, s->screen); | |
+#endif | |
+ s->display = xstrdup(display); | |
+ s->auth_display = xstrdup(display); | |
+ } | |
+ | |
+ return 1; | |
+} | |
+ | |
+static void | |
+do_authenticated2(Authctxt *authctxt) | |
+{ | |
+ server_loop2(authctxt); | |
+} | |
+ | |
+void | |
+do_cleanup(Authctxt *authctxt) | |
+{ | |
+ static int called = 0; | |
+ | |
+ debug("do_cleanup"); | |
+ | |
+ /* no cleanup if we're in the child for login shell */ | |
+ if (is_child) | |
+ return; | |
+ | |
+ /* avoid double cleanup */ | |
+ if (called) | |
+ return; | |
+ called = 1; | |
+ | |
+ if (authctxt == NULL) | |
+ return; | |
+ | |
+#ifdef USE_PAM | |
+ if (options.use_pam) { | |
+ sshpam_cleanup(); | |
+ sshpam_thread_cleanup(); | |
+ } | |
+#endif | |
+ | |
+ if (!authctxt->authenticated) | |
+ return; | |
+ | |
+#ifdef KRB5 | |
+ if (options.kerberos_ticket_cleanup && | |
+ authctxt->krb5_ctx) | |
+ krb5_cleanup_proc(authctxt); | |
+#endif | |
+ | |
+#ifdef GSSAPI | |
+ if (compat20 && options.gss_cleanup_creds) | |
+ ssh_gssapi_cleanup_creds(); | |
+#endif | |
+ | |
+ /* remove agent socket */ | |
+ auth_sock_cleanup_proc(authctxt->pw); | |
+ | |
+ /* | |
+ * Cleanup ptys/utmp only if privsep is disabled, | |
+ * or if running in monitor. | |
+ */ | |
+ if (!use_privsep || mm_is_monitor()) | |
+ session_destroy_all(session_pty_cleanup2); | |
+} | |
diff -urp --new-file openssh-6.7p1/ssh-add.0 openssh-6.7p1.patched/ssh-add.0 | |
--- openssh-6.7p1/ssh-add.0 2014-10-05 20:39:36.000000000 -0700 | |
+++ openssh-6.7p1.patched/ssh-add.0 2014-10-11 21:18:37.000000000 -0700 | |
@@ -4,7 +4,7 @@ NAME | |
ssh-add - adds private key identities to the authentication agent | |
SYNOPSIS | |
- ssh-add [-cDdkLlXx] [-t life] [file ...] | |
+ ssh-add [-cDdkKLlXx] [-t life] [file ...] | |
ssh-add -s pkcs11 | |
ssh-add -e pkcs11 | |
@@ -55,6 +55,13 @@ DESCRIPTION | |
-l Lists fingerprints of all identities currently represented by the | |
agent. | |
+ -m Add identities to the agent using any passphrases stored in your | |
+ Mac OS X keychain. | |
+ | |
+ -M When adding identities, each passphrase will also be stored in | |
+ your Mac OS X keychain. When removing identities with -d, each | |
+ passphrase will be removed from your Mac OS X keychain. | |
+ | |
-s pkcs11 | |
Add keys provided by the PKCS#11 shared library pkcs11. | |
diff -urp --new-file openssh-6.7p1/ssh-add.1 openssh-6.7p1.patched/ssh-add.1 | |
--- openssh-6.7p1/ssh-add.1 2013-12-17 22:46:28.000000000 -0800 | |
+++ openssh-6.7p1.patched/ssh-add.1 2014-10-11 21:18:37.000000000 -0700 | |
@@ -43,7 +43,7 @@ | |
.Nd adds private key identities to the authentication agent | |
.Sh SYNOPSIS | |
.Nm ssh-add | |
-.Op Fl cDdkLlXx | |
+.Op Fl cDdkLlMmXx | |
.Op Fl t Ar life | |
.Op Ar | |
.Nm ssh-add | |
@@ -119,6 +119,13 @@ Lists public key parameters of all ident | |
by the agent. | |
.It Fl l | |
Lists fingerprints of all identities currently represented by the agent. | |
+.It Fl m | |
+Add identities to the agent using any passphrases stored in your Mac OS | |
+X keychain. | |
+.It Fl M | |
+When adding identities, each passphrase will also be stored in your Mac OS | |
+Xkeychain. When removing identities with -d, each passphrase will be removed | |
+from your Mac OS X keychain. | |
.It Fl s Ar pkcs11 | |
Add keys provided by the PKCS#11 shared library | |
.Ar pkcs11 . | |
diff -urp --new-file openssh-6.7p1/ssh-add.c openssh-6.7p1.patched/ssh-add.c | |
--- openssh-6.7p1/ssh-add.c 2014-07-10 16:19:05.000000000 -0700 | |
+++ openssh-6.7p1.patched/ssh-add.c 2014-10-11 21:18:37.000000000 -0700 | |
@@ -62,6 +62,7 @@ | |
#include "authfile.h" | |
#include "pathnames.h" | |
#include "misc.h" | |
+#include "keychain.h" | |
#include "ssherr.h" | |
/* argv0 */ | |
@@ -98,12 +99,24 @@ clear_pass(void) | |
} | |
static int | |
-delete_file(AuthenticationConnection *ac, const char *filename, int key_only) | |
+add_from_keychain(AuthenticationConnection *ac) | |
+{ | |
+ if (ssh_add_from_keychain(ac) == 0) | |
+ return -1; | |
+ | |
+ fprintf(stderr, "Added keychain identities.\n"); | |
+ return 0; | |
+} | |
+ | |
+static int | |
+delete_file(AuthenticationConnection *ac, int keychain, const char *filename, int key_only) | |
{ | |
Key *public = NULL, *cert = NULL; | |
char *certpath = NULL, *comment = NULL; | |
int ret = -1; | |
+ if (keychain) | |
+ remove_from_keychain(filename); | |
public = key_load_public(filename, &comment); | |
if (public == NULL) { | |
printf("Bad key file %s\n", filename); | |
@@ -166,7 +179,7 @@ delete_all(AuthenticationConnection *ac) | |
} | |
static int | |
-add_file(AuthenticationConnection *ac, const char *filename, int key_only) | |
+add_file(AuthenticationConnection *ac, int keychain, const char *filename, int key_only) | |
{ | |
Key *private, *cert; | |
char *comment = NULL; | |
@@ -205,12 +218,16 @@ add_file(AuthenticationConnection *ac, c | |
if ((r = sshkey_parse_private_fileblob(&keyblob, "", filename, | |
&private, &comment)) != 0 && r != SSH_ERR_KEY_WRONG_PASSPHRASE) | |
fatal("Cannot parse %s: %s", filename, ssh_err(r)); | |
+ if (keychain && private != NULL) | |
+ store_in_keychain(filename, ""); | |
/* try last */ | |
if (private == NULL && pass != NULL) { | |
if ((r = sshkey_parse_private_fileblob(&keyblob, pass, filename, | |
&private, &comment)) != 0 && | |
r != SSH_ERR_KEY_WRONG_PASSPHRASE) | |
fatal("Cannot parse %s: %s", filename, ssh_err(r)); | |
+ if (keychain && private != NULL) | |
+ store_in_keychain(filename, pass); | |
} | |
if (comment == NULL) | |
comment = xstrdup(filename); | |
@@ -232,8 +249,11 @@ add_file(AuthenticationConnection *ac, c | |
r != SSH_ERR_KEY_WRONG_PASSPHRASE) | |
fatal("Cannot parse %s: %s", | |
filename, ssh_err(r)); | |
- if (private != NULL) | |
+ if (private != NULL) { | |
+ if (keychain) | |
+ store_in_keychain(filename, pass); | |
break; | |
+ } | |
clear_pass(); | |
snprintf(msg, sizeof msg, | |
"Bad passphrase, try again for %.200s: ", comment); | |
@@ -390,13 +410,13 @@ lock_agent(AuthenticationConnection *ac, | |
} | |
static int | |
-do_file(AuthenticationConnection *ac, int deleting, int key_only, char *file) | |
+do_file(AuthenticationConnection *ac, int deleting, int keychain, int key_only, char *file) | |
{ | |
if (deleting) { | |
- if (delete_file(ac, file, key_only) == -1) | |
+ if (delete_file(ac, keychain, file, key_only) == -1) | |
return -1; | |
} else { | |
- if (add_file(ac, file, key_only) == -1) | |
+ if (add_file(ac, keychain, file, key_only) == -1) | |
return -1; | |
} | |
return 0; | |
@@ -418,6 +438,11 @@ usage(void) | |
fprintf(stderr, " -X Unlock agent.\n"); | |
fprintf(stderr, " -s pkcs11 Add keys from PKCS#11 provider.\n"); | |
fprintf(stderr, " -e pkcs11 Remove keys provided by PKCS#11 provider.\n"); | |
+#ifdef KEYCHAIN | |
+ fprintf(stderr, " -m Add all identities stored in your Mac OS X keychain.\n"); | |
+ fprintf(stderr, " -M Store passphrases in your Mac OS X keychain.\n"); | |
+ fprintf(stderr, " With -d, remove passphrases from your Mac OS X keychain.\n"); | |
+#endif | |
} | |
int | |
@@ -428,6 +453,7 @@ main(int argc, char **argv) | |
AuthenticationConnection *ac = NULL; | |
char *pkcs11provider = NULL; | |
int i, ch, deleting = 0, ret = 0, key_only = 0; | |
+ int keychain = 0; | |
/* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ | |
sanitise_stdfd(); | |
@@ -446,7 +472,7 @@ main(int argc, char **argv) | |
"Could not open a connection to your authentication agent.\n"); | |
exit(2); | |
} | |
- while ((ch = getopt(argc, argv, "klLcdDxXe:s:t:")) != -1) { | |
+ while ((ch = getopt(argc, argv, "kKlLcdDxXmMe:s:t:")) != -1) { | |
switch (ch) { | |
case 'k': | |
key_only = 1; | |
@@ -485,6 +511,13 @@ main(int argc, char **argv) | |
goto done; | |
} | |
break; | |
+ case 'm': | |
+ if (add_from_keychain(ac) == -1) | |
+ ret = 1; | |
+ goto done; | |
+ case 'M': | |
+ keychain = 1; | |
+ break; | |
default: | |
usage(); | |
ret = 1; | |
@@ -516,7 +549,7 @@ main(int argc, char **argv) | |
default_files[i]); | |
if (stat(buf, &st) < 0) | |
continue; | |
- if (do_file(ac, deleting, key_only, buf) == -1) | |
+ if (do_file(ac, deleting, keychain, key_only, buf) == -1) | |
ret = 1; | |
else | |
count++; | |
@@ -525,7 +558,7 @@ main(int argc, char **argv) | |
ret = 1; | |
} else { | |
for (i = 0; i < argc; i++) { | |
- if (do_file(ac, deleting, key_only, argv[i]) == -1) | |
+ if (do_file(ac, deleting, keychain, key_only, argv[i]) == -1) | |
ret = 1; | |
} | |
} | |
diff -urp --new-file openssh-6.7p1/ssh-add.c.orig openssh-6.7p1.patched/ssh-add.c.orig | |
--- openssh-6.7p1/ssh-add.c.orig 1969-12-31 16:00:00.000000000 -0800 | |
+++ openssh-6.7p1.patched/ssh-add.c.orig 2014-10-11 21:16:18.000000000 -0700 | |
@@ -0,0 +1,541 @@ | |
+/* $OpenBSD: ssh-add.c,v 1.113 2014/07/09 14:15:56 benno Exp $ */ | |
+/* | |
+ * Author: Tatu Ylonen <ylo@cs.hut.fi> | |
+ * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | |
+ * All rights reserved | |
+ * Adds an identity to the authentication server, or removes an identity. | |
+ * | |
+ * As far as I am concerned, the code I have written for this software | |
+ * can be used freely for any purpose. Any derived versions of this | |
+ * software must be clearly marked as such, and if the derived work is | |
+ * incompatible with the protocol description in the RFC file, it must be | |
+ * called by a name other than "ssh" or "Secure Shell". | |
+ * | |
+ * SSH2 implementation, | |
+ * Copyright (c) 2000, 2001 Markus Friedl. 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 <sys/types.h> | |
+#include <sys/stat.h> | |
+#include <sys/param.h> | |
+ | |
+#include <openssl/evp.h> | |
+#include "openbsd-compat/openssl-compat.h" | |
+ | |
+#include <fcntl.h> | |
+#include <pwd.h> | |
+#include <stdarg.h> | |
+#include <stdio.h> | |
+#include <stdlib.h> | |
+#include <string.h> | |
+#include <unistd.h> | |
+ | |
+#include "xmalloc.h" | |
+#include "ssh.h" | |
+#include "rsa.h" | |
+#include "log.h" | |
+#include "key.h" | |
+#include "buffer.h" | |
+#include "authfd.h" | |
+#include "authfile.h" | |
+#include "pathnames.h" | |
+#include "misc.h" | |
+#include "ssherr.h" | |
+ | |
+/* argv0 */ | |
+extern char *__progname; | |
+ | |
+/* Default files to add */ | |
+static char *default_files[] = { | |
+ _PATH_SSH_CLIENT_ID_RSA, | |
+ _PATH_SSH_CLIENT_ID_DSA, | |
+#ifdef OPENSSL_HAS_ECC | |
+ _PATH_SSH_CLIENT_ID_ECDSA, | |
+#endif | |
+ _PATH_SSH_CLIENT_ID_ED25519, | |
+ _PATH_SSH_CLIENT_IDENTITY, | |
+ NULL | |
+}; | |
+ | |
+/* Default lifetime (0 == forever) */ | |
+static int lifetime = 0; | |
+ | |
+/* User has to confirm key use */ | |
+static int confirm = 0; | |
+ | |
+/* we keep a cache of one passphrases */ | |
+static char *pass = NULL; | |
+static void | |
+clear_pass(void) | |
+{ | |
+ if (pass) { | |
+ explicit_bzero(pass, strlen(pass)); | |
+ free(pass); | |
+ pass = NULL; | |
+ } | |
+} | |
+ | |
+static int | |
+delete_file(AuthenticationConnection *ac, const char *filename, int key_only) | |
+{ | |
+ Key *public = NULL, *cert = NULL; | |
+ char *certpath = NULL, *comment = NULL; | |
+ int ret = -1; | |
+ | |
+ public = key_load_public(filename, &comment); | |
+ if (public == NULL) { | |
+ printf("Bad key file %s\n", filename); | |
+ return -1; | |
+ } | |
+ if (ssh_remove_identity(ac, public)) { | |
+ fprintf(stderr, "Identity removed: %s (%s)\n", filename, comment); | |
+ ret = 0; | |
+ } else | |
+ fprintf(stderr, "Could not remove identity: %s\n", filename); | |
+ | |
+ if (key_only) | |
+ goto out; | |
+ | |
+ /* Now try to delete the corresponding certificate too */ | |
+ free(comment); | |
+ comment = NULL; | |
+ xasprintf(&certpath, "%s-cert.pub", filename); | |
+ if ((cert = key_load_public(certpath, &comment)) == NULL) | |
+ goto out; | |
+ if (!key_equal_public(cert, public)) | |
+ fatal("Certificate %s does not match private key %s", | |
+ certpath, filename); | |
+ | |
+ if (ssh_remove_identity(ac, cert)) { | |
+ fprintf(stderr, "Identity removed: %s (%s)\n", certpath, | |
+ comment); | |
+ ret = 0; | |
+ } else | |
+ fprintf(stderr, "Could not remove identity: %s\n", certpath); | |
+ | |
+ out: | |
+ if (cert != NULL) | |
+ key_free(cert); | |
+ if (public != NULL) | |
+ key_free(public); | |
+ free(certpath); | |
+ free(comment); | |
+ | |
+ return ret; | |
+} | |
+ | |
+/* Send a request to remove all identities. */ | |
+static int | |
+delete_all(AuthenticationConnection *ac) | |
+{ | |
+ int ret = -1; | |
+ | |
+ if (ssh_remove_all_identities(ac, 1)) | |
+ ret = 0; | |
+ /* ignore error-code for ssh2 */ | |
+ ssh_remove_all_identities(ac, 2); | |
+ | |
+ if (ret == 0) | |
+ fprintf(stderr, "All identities removed.\n"); | |
+ else | |
+ fprintf(stderr, "Failed to remove all identities.\n"); | |
+ | |
+ return ret; | |
+} | |
+ | |
+static int | |
+add_file(AuthenticationConnection *ac, const char *filename, int key_only) | |
+{ | |
+ Key *private, *cert; | |
+ char *comment = NULL; | |
+ char msg[1024], *certpath = NULL; | |
+ int r, fd, perms_ok, ret = -1; | |
+ Buffer keyblob; | |
+ | |
+ if (strcmp(filename, "-") == 0) { | |
+ fd = STDIN_FILENO; | |
+ filename = "(stdin)"; | |
+ } else if ((fd = open(filename, O_RDONLY)) < 0) { | |
+ perror(filename); | |
+ return -1; | |
+ } | |
+ | |
+ /* | |
+ * Since we'll try to load a keyfile multiple times, permission errors | |
+ * will occur multiple times, so check perms first and bail if wrong. | |
+ */ | |
+ if (fd != STDIN_FILENO) { | |
+ perms_ok = key_perm_ok(fd, filename); | |
+ if (!perms_ok) { | |
+ close(fd); | |
+ return -1; | |
+ } | |
+ } | |
+ buffer_init(&keyblob); | |
+ if (!key_load_file(fd, filename, &keyblob)) { | |
+ buffer_free(&keyblob); | |
+ close(fd); | |
+ return -1; | |
+ } | |
+ close(fd); | |
+ | |
+ /* At first, try empty passphrase */ | |
+ if ((r = sshkey_parse_private_fileblob(&keyblob, "", filename, | |
+ &private, &comment)) != 0 && r != SSH_ERR_KEY_WRONG_PASSPHRASE) | |
+ fatal("Cannot parse %s: %s", filename, ssh_err(r)); | |
+ if (keychain && private != NULL) | |
+ store_in_keychain(filename, ""); | |
+ /* try last */ | |
+ if (private == NULL && pass != NULL) { | |
+ if ((r = sshkey_parse_private_fileblob(&keyblob, pass, filename, | |
+ &private, &comment)) != 0 && | |
+ r != SSH_ERR_KEY_WRONG_PASSPHRASE) | |
+ fatal("Cannot parse %s: %s", filename, ssh_err(r)); | |
+ if (keychain && private != NULL) | |
+ store_in_keychain(filename, pass); | |
+ } | |
+ if (comment == NULL) | |
+ comment = xstrdup(filename); | |
+ if (private == NULL) { | |
+ /* clear passphrase since it did not work */ | |
+ clear_pass(); | |
+ snprintf(msg, sizeof msg, "Enter passphrase for %.200s: ", | |
+ comment); | |
+ for (;;) { | |
+ pass = read_passphrase(msg, RP_ALLOW_STDIN); | |
+ if (strcmp(pass, "") == 0) { | |
+ clear_pass(); | |
+ free(comment); | |
+ buffer_free(&keyblob); | |
+ return -1; | |
+ } | |
+ if ((r = sshkey_parse_private_fileblob(&keyblob, | |
+ pass, filename, &private, NULL)) != 0 && | |
+ r != SSH_ERR_KEY_WRONG_PASSPHRASE) | |
+ fatal("Cannot parse %s: %s", | |
+ filename, ssh_err(r)); | |
+ if (private != NULL) | |
+ break; | |
+ clear_pass(); | |
+ snprintf(msg, sizeof msg, | |
+ "Bad passphrase, try again for %.200s: ", comment); | |
+ } | |
+ } | |
+ buffer_free(&keyblob); | |
+ | |
+ if (ssh_add_identity_constrained(ac, private, comment, lifetime, | |
+ confirm)) { | |
+ fprintf(stderr, "Identity added: %s (%s)\n", filename, comment); | |
+ ret = 0; | |
+ if (lifetime != 0) | |
+ fprintf(stderr, | |
+ "Lifetime set to %d seconds\n", lifetime); | |
+ if (confirm != 0) | |
+ fprintf(stderr, | |
+ "The user must confirm each use of the key\n"); | |
+ } else { | |
+ fprintf(stderr, "Could not add identity: %s\n", filename); | |
+ } | |
+ | |
+ /* Skip trying to load the cert if requested */ | |
+ if (key_only) | |
+ goto out; | |
+ | |
+ /* Now try to add the certificate flavour too */ | |
+ xasprintf(&certpath, "%s-cert.pub", filename); | |
+ if ((cert = key_load_public(certpath, NULL)) == NULL) | |
+ goto out; | |
+ | |
+ if (!key_equal_public(cert, private)) { | |
+ error("Certificate %s does not match private key %s", | |
+ certpath, filename); | |
+ key_free(cert); | |
+ goto out; | |
+ } | |
+ | |
+ /* Graft with private bits */ | |
+ if (key_to_certified(private, key_cert_is_legacy(cert)) != 0) { | |
+ error("%s: key_to_certified failed", __func__); | |
+ key_free(cert); | |
+ goto out; | |
+ } | |
+ key_cert_copy(cert, private); | |
+ key_free(cert); | |
+ | |
+ if (!ssh_add_identity_constrained(ac, private, comment, | |
+ lifetime, confirm)) { | |
+ error("Certificate %s (%s) add failed", certpath, | |
+ private->cert->key_id); | |
+ } | |
+ fprintf(stderr, "Certificate added: %s (%s)\n", certpath, | |
+ private->cert->key_id); | |
+ if (lifetime != 0) | |
+ fprintf(stderr, "Lifetime set to %d seconds\n", lifetime); | |
+ if (confirm != 0) | |
+ fprintf(stderr, "The user must confirm each use of the key\n"); | |
+ out: | |
+ if (certpath != NULL) | |
+ free(certpath); | |
+ free(comment); | |
+ key_free(private); | |
+ | |
+ return ret; | |
+} | |
+ | |
+static int | |
+update_card(AuthenticationConnection *ac, int add, const char *id) | |
+{ | |
+ char *pin = NULL; | |
+ int ret = -1; | |
+ | |
+ if (add) { | |
+ if ((pin = read_passphrase("Enter passphrase for PKCS#11: ", | |
+ RP_ALLOW_STDIN)) == NULL) | |
+ return -1; | |
+ } | |
+ | |
+ if (ssh_update_card(ac, add, id, pin == NULL ? "" : pin, | |
+ lifetime, confirm)) { | |
+ fprintf(stderr, "Card %s: %s\n", | |
+ add ? "added" : "removed", id); | |
+ ret = 0; | |
+ } else { | |
+ fprintf(stderr, "Could not %s card: %s\n", | |
+ add ? "add" : "remove", id); | |
+ ret = -1; | |
+ } | |
+ free(pin); | |
+ return ret; | |
+} | |
+ | |
+static int | |
+list_identities(AuthenticationConnection *ac, int do_fp) | |
+{ | |
+ Key *key; | |
+ char *comment, *fp; | |
+ int had_identities = 0; | |
+ int version; | |
+ | |
+ for (version = 1; version <= 2; version++) { | |
+ for (key = ssh_get_first_identity(ac, &comment, version); | |
+ key != NULL; | |
+ key = ssh_get_next_identity(ac, &comment, version)) { | |
+ had_identities = 1; | |
+ if (do_fp) { | |
+ fp = key_fingerprint(key, SSH_FP_MD5, | |
+ SSH_FP_HEX); | |
+ printf("%d %s %s (%s)\n", | |
+ key_size(key), fp, comment, key_type(key)); | |
+ free(fp); | |
+ } else { | |
+ if (!key_write(key, stdout)) | |
+ fprintf(stderr, "key_write failed"); | |
+ fprintf(stdout, " %s\n", comment); | |
+ } | |
+ key_free(key); | |
+ free(comment); | |
+ } | |
+ } | |
+ if (!had_identities) { | |
+ printf("The agent has no identities.\n"); | |
+ return -1; | |
+ } | |
+ return 0; | |
+} | |
+ | |
+static int | |
+lock_agent(AuthenticationConnection *ac, int lock) | |
+{ | |
+ char prompt[100], *p1, *p2; | |
+ int passok = 1, ret = -1; | |
+ | |
+ strlcpy(prompt, "Enter lock password: ", sizeof(prompt)); | |
+ p1 = read_passphrase(prompt, RP_ALLOW_STDIN); | |
+ if (lock) { | |
+ strlcpy(prompt, "Again: ", sizeof prompt); | |
+ p2 = read_passphrase(prompt, RP_ALLOW_STDIN); | |
+ if (strcmp(p1, p2) != 0) { | |
+ fprintf(stderr, "Passwords do not match.\n"); | |
+ passok = 0; | |
+ } | |
+ explicit_bzero(p2, strlen(p2)); | |
+ free(p2); | |
+ } | |
+ if (passok && ssh_lock_agent(ac, lock, p1)) { | |
+ fprintf(stderr, "Agent %slocked.\n", lock ? "" : "un"); | |
+ ret = 0; | |
+ } else | |
+ fprintf(stderr, "Failed to %slock agent.\n", lock ? "" : "un"); | |
+ explicit_bzero(p1, strlen(p1)); | |
+ free(p1); | |
+ return (ret); | |
+} | |
+ | |
+static int | |
+do_file(AuthenticationConnection *ac, int deleting, int key_only, char *file) | |
+{ | |
+ if (deleting) { | |
+ if (delete_file(ac, file, key_only) == -1) | |
+ return -1; | |
+ } else { | |
+ if (add_file(ac, file, key_only) == -1) | |
+ return -1; | |
+ } | |
+ return 0; | |
+} | |
+ | |
+static void | |
+usage(void) | |
+{ | |
+ fprintf(stderr, "usage: %s [options] [file ...]\n", __progname); | |
+ fprintf(stderr, "Options:\n"); | |
+ fprintf(stderr, " -l List fingerprints of all identities.\n"); | |
+ fprintf(stderr, " -L List public key parameters of all identities.\n"); | |
+ fprintf(stderr, " -k Load only keys and not certificates.\n"); | |
+ fprintf(stderr, " -c Require confirmation to sign using identities\n"); | |
+ fprintf(stderr, " -t life Set lifetime (in seconds) when adding identities.\n"); | |
+ fprintf(stderr, " -d Delete identity.\n"); | |
+ fprintf(stderr, " -D Delete all identities.\n"); | |
+ fprintf(stderr, " -x Lock agent.\n"); | |
+ fprintf(stderr, " -X Unlock agent.\n"); | |
+ fprintf(stderr, " -s pkcs11 Add keys from PKCS#11 provider.\n"); | |
+ fprintf(stderr, " -e pkcs11 Remove keys provided by PKCS#11 provider.\n"); | |
+} | |
+ | |
+int | |
+main(int argc, char **argv) | |
+{ | |
+ extern char *optarg; | |
+ extern int optind; | |
+ AuthenticationConnection *ac = NULL; | |
+ char *pkcs11provider = NULL; | |
+ int i, ch, deleting = 0, ret = 0, key_only = 0; | |
+ | |
+ /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ | |
+ sanitise_stdfd(); | |
+ | |
+ __progname = ssh_get_progname(argv[0]); | |
+ seed_rng(); | |
+ | |
+ OpenSSL_add_all_algorithms(); | |
+ | |
+ setlinebuf(stdout); | |
+ | |
+ /* At first, get a connection to the authentication agent. */ | |
+ ac = ssh_get_authentication_connection(); | |
+ if (ac == NULL) { | |
+ fprintf(stderr, | |
+ "Could not open a connection to your authentication agent.\n"); | |
+ exit(2); | |
+ } | |
+ while ((ch = getopt(argc, argv, "klLcdDxXe:s:t:")) != -1) { | |
+ switch (ch) { | |
+ case 'k': | |
+ key_only = 1; | |
+ break; | |
+ case 'l': | |
+ case 'L': | |
+ if (list_identities(ac, ch == 'l' ? 1 : 0) == -1) | |
+ ret = 1; | |
+ goto done; | |
+ case 'x': | |
+ case 'X': | |
+ if (lock_agent(ac, ch == 'x' ? 1 : 0) == -1) | |
+ ret = 1; | |
+ goto done; | |
+ case 'c': | |
+ confirm = 1; | |
+ break; | |
+ case 'd': | |
+ deleting = 1; | |
+ break; | |
+ case 'D': | |
+ if (delete_all(ac) == -1) | |
+ ret = 1; | |
+ goto done; | |
+ case 's': | |
+ pkcs11provider = optarg; | |
+ break; | |
+ case 'e': | |
+ deleting = 1; | |
+ pkcs11provider = optarg; | |
+ break; | |
+ case 't': | |
+ if ((lifetime = convtime(optarg)) == -1) { | |
+ fprintf(stderr, "Invalid lifetime\n"); | |
+ ret = 1; | |
+ goto done; | |
+ } | |
+ break; | |
+ default: | |
+ usage(); | |
+ ret = 1; | |
+ goto done; | |
+ } | |
+ } | |
+ argc -= optind; | |
+ argv += optind; | |
+ if (pkcs11provider != NULL) { | |
+ if (update_card(ac, !deleting, pkcs11provider) == -1) | |
+ ret = 1; | |
+ goto done; | |
+ } | |
+ if (argc == 0) { | |
+ char buf[MAXPATHLEN]; | |
+ struct passwd *pw; | |
+ struct stat st; | |
+ int count = 0; | |
+ | |
+ if ((pw = getpwuid(getuid())) == NULL) { | |
+ fprintf(stderr, "No user found with uid %u\n", | |
+ (u_int)getuid()); | |
+ ret = 1; | |
+ goto done; | |
+ } | |
+ | |
+ for (i = 0; default_files[i]; i++) { | |
+ snprintf(buf, sizeof(buf), "%s/%s", pw->pw_dir, | |
+ default_files[i]); | |
+ if (stat(buf, &st) < 0) | |
+ continue; | |
+ if (do_file(ac, deleting, key_only, buf) == -1) | |
+ ret = 1; | |
+ else | |
+ count++; | |
+ } | |
+ if (count == 0) | |
+ ret = 1; | |
+ } else { | |
+ for (i = 0; i < argc; i++) { | |
+ if (do_file(ac, deleting, key_only, argv[i]) == -1) | |
+ ret = 1; | |
+ } | |
+ } | |
+ clear_pass(); | |
+ | |
+done: | |
+ ssh_close_authentication_connection(ac); | |
+ return ret; | |
+} | |
diff -urp --new-file openssh-6.7p1/ssh-agent.c openssh-6.7p1.patched/ssh-agent.c | |
--- openssh-6.7p1/ssh-agent.c 2014-07-29 19:32:46.000000000 -0700 | |
+++ openssh-6.7p1.patched/ssh-agent.c 2014-10-11 21:18:37.000000000 -0700 | |
@@ -66,6 +66,9 @@ | |
#include <time.h> | |
#include <string.h> | |
#include <unistd.h> | |
+#ifdef __APPLE_LAUNCHD__ | |
+#include <launch.h> | |
+#endif | |
#include "xmalloc.h" | |
#include "ssh.h" | |
@@ -73,10 +76,12 @@ | |
#include "buffer.h" | |
#include "key.h" | |
#include "authfd.h" | |
+#include "authfile.h" | |
#include "compat.h" | |
#include "log.h" | |
#include "misc.h" | |
#include "digest.h" | |
+#include "keychain.h" | |
#ifdef ENABLE_PKCS11 | |
#include "ssh-pkcs11.h" | |
@@ -701,6 +706,61 @@ process_remove_smartcard_key(SocketEntry | |
} | |
#endif /* ENABLE_PKCS11 */ | |
+static int | |
+add_identity_callback(const char *filename, const char *passphrase) | |
+{ | |
+ Key *k; | |
+ int version; | |
+ Idtab *tab; | |
+ | |
+ if ((k = key_load_private(filename, passphrase, NULL)) == NULL) | |
+ return 1; | |
+ switch (k->type) { | |
+ case KEY_RSA: | |
+ case KEY_RSA1: | |
+ if (RSA_blinding_on(k->rsa, NULL) != 1) { | |
+ key_free(k); | |
+ return 1; | |
+ } | |
+ break; | |
+ } | |
+ version = k->type == KEY_RSA1 ? 1 : 2; | |
+ tab = idtab_lookup(version); | |
+ if (lookup_identity(k, version) == NULL) { | |
+ Identity *id = xmalloc(sizeof(Identity)); | |
+ id->key = k; | |
+ id->comment = xstrdup(filename); | |
+ if (id->comment == NULL) { | |
+ key_free(k); | |
+ return 1; | |
+ } | |
+ id->death = 0; | |
+ id->confirm = 0; | |
+ TAILQ_INSERT_TAIL(&tab->idlist, id, next); | |
+ tab->nentries++; | |
+ } else { | |
+ key_free(k); | |
+ return 1; | |
+ } | |
+ | |
+ return 0; | |
+} | |
+ | |
+static void | |
+process_add_from_keychain(SocketEntry *e) | |
+{ | |
+ int result; | |
+ | |
+ result = add_identities_using_keychain(&add_identity_callback); | |
+ | |
+ /* e will be NULL when ssh-agent adds keys on its own at startup */ | |
+ if (e) { | |
+ buffer_put_int(&e->output, 1); | |
+ buffer_put_char(&e->output, | |
+ result ? SSH_AGENT_FAILURE : SSH_AGENT_SUCCESS); | |
+ } | |
+} | |
+ | |
/* dispatch incoming messages */ | |
static void | |
@@ -795,6 +855,9 @@ process_message(SocketEntry *e) | |
process_remove_smartcard_key(e); | |
break; | |
#endif /* ENABLE_PKCS11 */ | |
+ case SSH_AGENTC_ADD_FROM_KEYCHAIN: | |
+ process_add_from_keychain(e); | |
+ break; | |
default: | |
/* Unknown message. Respond with failure. */ | |
error("Unknown message %d", type); | |
@@ -1034,7 +1097,11 @@ usage(void) | |
int | |
main(int ac, char **av) | |
{ | |
+#ifdef __APPLE_LAUNCHD__ | |
+ int c_flag = 0, d_flag = 0, k_flag = 0, s_flag = 0, l_flag = 0; | |
+#else | |
int c_flag = 0, d_flag = 0, k_flag = 0, s_flag = 0; | |
+#endif | |
int sock, fd, ch, result, saved_errno; | |
u_int nalloc; | |
char *shell, *format, *pidstr, *agentsocket = NULL; | |
@@ -1069,7 +1136,11 @@ main(int ac, char **av) | |
__progname = ssh_get_progname(av[0]); | |
seed_rng(); | |
+#ifdef __APPLE_LAUNCHD__ | |
+ while ((ch = getopt(ac, av, "cdklsa:t:")) != -1) { | |
+#else | |
while ((ch = getopt(ac, av, "cdksa:t:")) != -1) { | |
+#endif | |
switch (ch) { | |
case 'c': | |
if (s_flag) | |
@@ -1079,6 +1150,11 @@ main(int ac, char **av) | |
case 'k': | |
k_flag++; | |
break; | |
+#ifdef __APPLE_LAUNCHD__ | |
+ case 'l': | |
+ l_flag++; | |
+ break; | |
+#endif | |
case 's': | |
if (c_flag) | |
usage(); | |
@@ -1105,7 +1181,11 @@ main(int ac, char **av) | |
ac -= optind; | |
av += optind; | |
+#ifdef __APPPLE_LAUNCHD__ | |
+ if (ac > 0 && (c_flag || k_flag || s_flag || d_flag || l_flag)) | |
+#else | |
if (ac > 0 && (c_flag || k_flag || s_flag || d_flag)) | |
+#endif | |
usage(); | |
if (ac == 0 && !c_flag && !s_flag) { | |
@@ -1161,6 +1241,53 @@ main(int ac, char **av) | |
* Create socket early so it will exist before command gets run from | |
* the parent. | |
*/ | |
+#ifdef __APPLE_LAUNCHD__ | |
+ if (l_flag) { | |
+ launch_data_t resp, msg, tmp; | |
+ size_t listeners_i; | |
+ | |
+ msg = launch_data_new_string(LAUNCH_KEY_CHECKIN); | |
+ | |
+ resp = launch_msg(msg); | |
+ | |
+ if (NULL == resp) { | |
+ perror("launch_msg"); | |
+ exit(1); | |
+ } | |
+ launch_data_free(msg); | |
+ switch (launch_data_get_type(resp)) { | |
+ case LAUNCH_DATA_ERRNO: | |
+ errno = launch_data_get_errno(resp); | |
+ perror("launch_msg response"); | |
+ exit(1); | |
+ case LAUNCH_DATA_DICTIONARY: | |
+ break; | |
+ default: | |
+ fprintf(stderr, "launch_msg unknown response"); | |
+ exit(1); | |
+ } | |
+ tmp = launch_data_dict_lookup(resp, LAUNCH_JOBKEY_SOCKETS); | |
+ | |
+ if (NULL == tmp) { | |
+ fprintf(stderr, "no sockets\n"); | |
+ exit(1); | |
+ } | |
+ | |
+ tmp = launch_data_dict_lookup(tmp, "Listeners"); | |
+ | |
+ if (NULL == tmp) { | |
+ fprintf(stderr, "no known listeners\n"); | |
+ exit(1); | |
+ } | |
+ | |
+ for (listeners_i = 0; listeners_i < launch_data_array_get_count(tmp); listeners_i++) { | |
+ launch_data_t obj_at_ind = launch_data_array_get_index(tmp, listeners_i); | |
+ new_socket(AUTH_SOCKET, launch_data_get_fd(obj_at_ind)); | |
+ } | |
+ | |
+ launch_data_free(resp); | |
+ } else { | |
+#endif | |
prev_mask = umask(0177); | |
sock = unix_listener(socket_name, SSH_LISTEN_BACKLOG, 0); | |
if (sock < 0) { | |
@@ -1215,6 +1342,14 @@ main(int ac, char **av) | |
error("setsid: %s", strerror(errno)); | |
cleanup_exit(1); | |
} | |
+#ifdef __APPLE_LAUNCHD__ | |
+ } | |
+#endif | |
+ | |
+#ifdef __APPLE_LAUNCHD__ | |
+ if (l_flag) | |
+ goto skip2; | |
+#endif | |
(void)chdir("/"); | |
if ((fd = open(_PATH_DEVNULL, O_RDWR, 0)) != -1) { | |
@@ -1243,6 +1378,7 @@ skip: | |
pkcs11_init(0); | |
#endif | |
new_socket(AUTH_SOCKET, sock); | |
+skip2: | |
if (ac > 0) | |
parent_alive_interval = 10; | |
idtab_init(); | |
@@ -1252,6 +1388,10 @@ skip: | |
signal(SIGTERM, cleanup_handler); | |
nalloc = 0; | |
+#ifdef KEYCHAIN | |
+ process_add_from_keychain(NULL); | |
+#endif | |
+ | |
while (1) { | |
prepare_select(&readsetp, &writesetp, &max_fd, &nalloc, &tvp); | |
result = select(max_fd + 1, readsetp, writesetp, NULL, tvp); | |
diff -urp --new-file openssh-6.7p1/ssh-agent.c.orig openssh-6.7p1.patched/ssh-agent.c.orig | |
--- openssh-6.7p1/ssh-agent.c.orig 1969-12-31 16:00:00.000000000 -0800 | |
+++ openssh-6.7p1.patched/ssh-agent.c.orig 2014-07-29 19:32:46.000000000 -0700 | |
@@ -0,0 +1,1270 @@ | |
+/* $OpenBSD: ssh-agent.c,v 1.190 2014/07/25 21:22:03 dtucker Exp $ */ | |
+/* | |
+ * Author: Tatu Ylonen <ylo@cs.hut.fi> | |
+ * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | |
+ * All rights reserved | |
+ * The authentication agent program. | |
+ * | |
+ * As far as I am concerned, the code I have written for this software | |
+ * can be used freely for any purpose. Any derived versions of this | |
+ * software must be clearly marked as such, and if the derived work is | |
+ * incompatible with the protocol description in the RFC file, it must be | |
+ * called by a name other than "ssh" or "Secure Shell". | |
+ * | |
+ * Copyright (c) 2000, 2001 Markus Friedl. 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 <sys/types.h> | |
+#include <sys/param.h> | |
+#include <sys/resource.h> | |
+#include <sys/stat.h> | |
+#include <sys/socket.h> | |
+#ifdef HAVE_SYS_TIME_H | |
+# include <sys/time.h> | |
+#endif | |
+#ifdef HAVE_SYS_UN_H | |
+# include <sys/un.h> | |
+#endif | |
+#include "openbsd-compat/sys-queue.h" | |
+ | |
+#ifdef WITH_OPENSSL | |
+#include <openssl/evp.h> | |
+#include "openbsd-compat/openssl-compat.h" | |
+#endif | |
+ | |
+#include <errno.h> | |
+#include <fcntl.h> | |
+#ifdef HAVE_PATHS_H | |
+# include <paths.h> | |
+#endif | |
+#include <signal.h> | |
+#include <stdarg.h> | |
+#include <stdio.h> | |
+#include <stdlib.h> | |
+#include <time.h> | |
+#include <string.h> | |
+#include <unistd.h> | |
+ | |
+#include "xmalloc.h" | |
+#include "ssh.h" | |
+#include "rsa.h" | |
+#include "buffer.h" | |
+#include "key.h" | |
+#include "authfd.h" | |
+#include "compat.h" | |
+#include "log.h" | |
+#include "misc.h" | |
+#include "digest.h" | |
+ | |
+#ifdef ENABLE_PKCS11 | |
+#include "ssh-pkcs11.h" | |
+#endif | |
+ | |
+#if defined(HAVE_SYS_PRCTL_H) | |
+#include <sys/prctl.h> /* For prctl() and PR_SET_DUMPABLE */ | |
+#endif | |
+ | |
+typedef enum { | |
+ AUTH_UNUSED, | |
+ AUTH_SOCKET, | |
+ AUTH_CONNECTION | |
+} sock_type; | |
+ | |
+typedef struct { | |
+ int fd; | |
+ sock_type type; | |
+ Buffer input; | |
+ Buffer output; | |
+ Buffer request; | |
+} SocketEntry; | |
+ | |
+u_int sockets_alloc = 0; | |
+SocketEntry *sockets = NULL; | |
+ | |
+typedef struct identity { | |
+ TAILQ_ENTRY(identity) next; | |
+ Key *key; | |
+ char *comment; | |
+ char *provider; | |
+ time_t death; | |
+ u_int confirm; | |
+} Identity; | |
+ | |
+typedef struct { | |
+ int nentries; | |
+ TAILQ_HEAD(idqueue, identity) idlist; | |
+} Idtab; | |
+ | |
+/* private key table, one per protocol version */ | |
+Idtab idtable[3]; | |
+ | |
+int max_fd = 0; | |
+ | |
+/* pid of shell == parent of agent */ | |
+pid_t parent_pid = -1; | |
+time_t parent_alive_interval = 0; | |
+ | |
+/* pid of process for which cleanup_socket is applicable */ | |
+pid_t cleanup_pid = 0; | |
+ | |
+/* pathname and directory for AUTH_SOCKET */ | |
+char socket_name[MAXPATHLEN]; | |
+char socket_dir[MAXPATHLEN]; | |
+ | |
+/* locking */ | |
+int locked = 0; | |
+char *lock_passwd = NULL; | |
+ | |
+extern char *__progname; | |
+ | |
+/* Default lifetime in seconds (0 == forever) */ | |
+static long lifetime = 0; | |
+ | |
+static void | |
+close_socket(SocketEntry *e) | |
+{ | |
+ close(e->fd); | |
+ e->fd = -1; | |
+ e->type = AUTH_UNUSED; | |
+ buffer_free(&e->input); | |
+ buffer_free(&e->output); | |
+ buffer_free(&e->request); | |
+} | |
+ | |
+static void | |
+idtab_init(void) | |
+{ | |
+ int i; | |
+ | |
+ for (i = 0; i <=2; i++) { | |
+ TAILQ_INIT(&idtable[i].idlist); | |
+ idtable[i].nentries = 0; | |
+ } | |
+} | |
+ | |
+/* return private key table for requested protocol version */ | |
+static Idtab * | |
+idtab_lookup(int version) | |
+{ | |
+ if (version < 1 || version > 2) | |
+ fatal("internal error, bad protocol version %d", version); | |
+ return &idtable[version]; | |
+} | |
+ | |
+static void | |
+free_identity(Identity *id) | |
+{ | |
+ key_free(id->key); | |
+ free(id->provider); | |
+ free(id->comment); | |
+ free(id); | |
+} | |
+ | |
+/* return matching private key for given public key */ | |
+static Identity * | |
+lookup_identity(Key *key, int version) | |
+{ | |
+ Identity *id; | |
+ | |
+ Idtab *tab = idtab_lookup(version); | |
+ TAILQ_FOREACH(id, &tab->idlist, next) { | |
+ if (key_equal(key, id->key)) | |
+ return (id); | |
+ } | |
+ return (NULL); | |
+} | |
+ | |
+/* Check confirmation of keysign request */ | |
+static int | |
+confirm_key(Identity *id) | |
+{ | |
+ char *p; | |
+ int ret = -1; | |
+ | |
+ p = key_fingerprint(id->key, SSH_FP_MD5, SSH_FP_HEX); | |
+ if (ask_permission("Allow use of key %s?\nKey fingerprint %s.", | |
+ id->comment, p)) | |
+ ret = 0; | |
+ free(p); | |
+ | |
+ return (ret); | |
+} | |
+ | |
+/* send list of supported public keys to 'client' */ | |
+static void | |
+process_request_identities(SocketEntry *e, int version) | |
+{ | |
+ Idtab *tab = idtab_lookup(version); | |
+ Identity *id; | |
+ Buffer msg; | |
+ | |
+ buffer_init(&msg); | |
+ buffer_put_char(&msg, (version == 1) ? | |
+ SSH_AGENT_RSA_IDENTITIES_ANSWER : SSH2_AGENT_IDENTITIES_ANSWER); | |
+ buffer_put_int(&msg, tab->nentries); | |
+ TAILQ_FOREACH(id, &tab->idlist, next) { | |
+ if (id->key->type == KEY_RSA1) { | |
+#ifdef WITH_SSH1 | |
+ buffer_put_int(&msg, BN_num_bits(id->key->rsa->n)); | |
+ buffer_put_bignum(&msg, id->key->rsa->e); | |
+ buffer_put_bignum(&msg, id->key->rsa->n); | |
+#endif | |
+ } else { | |
+ u_char *blob; | |
+ u_int blen; | |
+ key_to_blob(id->key, &blob, &blen); | |
+ buffer_put_string(&msg, blob, blen); | |
+ free(blob); | |
+ } | |
+ buffer_put_cstring(&msg, id->comment); | |
+ } | |
+ buffer_put_int(&e->output, buffer_len(&msg)); | |
+ buffer_append(&e->output, buffer_ptr(&msg), buffer_len(&msg)); | |
+ buffer_free(&msg); | |
+} | |
+ | |
+#ifdef WITH_SSH1 | |
+/* ssh1 only */ | |
+static void | |
+process_authentication_challenge1(SocketEntry *e) | |
+{ | |
+ u_char buf[32], mdbuf[16], session_id[16]; | |
+ u_int response_type; | |
+ BIGNUM *challenge; | |
+ Identity *id; | |
+ int i, len; | |
+ Buffer msg; | |
+ struct ssh_digest_ctx *md; | |
+ Key *key; | |
+ | |
+ buffer_init(&msg); | |
+ key = key_new(KEY_RSA1); | |
+ if ((challenge = BN_new()) == NULL) | |
+ fatal("process_authentication_challenge1: BN_new failed"); | |
+ | |
+ (void) buffer_get_int(&e->request); /* ignored */ | |
+ buffer_get_bignum(&e->request, key->rsa->e); | |
+ buffer_get_bignum(&e->request, key->rsa->n); | |
+ buffer_get_bignum(&e->request, challenge); | |
+ | |
+ /* Only protocol 1.1 is supported */ | |
+ if (buffer_len(&e->request) == 0) | |
+ goto failure; | |
+ buffer_get(&e->request, session_id, 16); | |
+ response_type = buffer_get_int(&e->request); | |
+ if (response_type != 1) | |
+ goto failure; | |
+ | |
+ id = lookup_identity(key, 1); | |
+ if (id != NULL && (!id->confirm || confirm_key(id) == 0)) { | |
+ Key *private = id->key; | |
+ /* Decrypt the challenge using the private key. */ | |
+ if (rsa_private_decrypt(challenge, challenge, private->rsa) != 0) | |
+ goto failure; | |
+ | |
+ /* The response is MD5 of decrypted challenge plus session id. */ | |
+ len = BN_num_bytes(challenge); | |
+ if (len <= 0 || len > 32) { | |
+ logit("process_authentication_challenge: bad challenge length %d", len); | |
+ goto failure; | |
+ } | |
+ memset(buf, 0, 32); | |
+ BN_bn2bin(challenge, buf + 32 - len); | |
+ if ((md = ssh_digest_start(SSH_DIGEST_MD5)) == NULL || | |
+ ssh_digest_update(md, buf, 32) < 0 || | |
+ ssh_digest_update(md, session_id, 16) < 0 || | |
+ ssh_digest_final(md, mdbuf, sizeof(mdbuf)) < 0) | |
+ fatal("%s: md5 failed", __func__); | |
+ ssh_digest_free(md); | |
+ | |
+ /* Send the response. */ | |
+ buffer_put_char(&msg, SSH_AGENT_RSA_RESPONSE); | |
+ for (i = 0; i < 16; i++) | |
+ buffer_put_char(&msg, mdbuf[i]); | |
+ goto send; | |
+ } | |
+ | |
+failure: | |
+ /* Unknown identity or protocol error. Send failure. */ | |
+ buffer_put_char(&msg, SSH_AGENT_FAILURE); | |
+send: | |
+ buffer_put_int(&e->output, buffer_len(&msg)); | |
+ buffer_append(&e->output, buffer_ptr(&msg), buffer_len(&msg)); | |
+ key_free(key); | |
+ BN_clear_free(challenge); | |
+ buffer_free(&msg); | |
+} | |
+#endif | |
+ | |
+/* ssh2 only */ | |
+static void | |
+process_sign_request2(SocketEntry *e) | |
+{ | |
+ u_char *blob, *data, *signature = NULL; | |
+ u_int blen, dlen, slen = 0; | |
+ extern int datafellows; | |
+ int odatafellows; | |
+ int ok = -1, flags; | |
+ Buffer msg; | |
+ Key *key; | |
+ | |
+ datafellows = 0; | |
+ | |
+ blob = buffer_get_string(&e->request, &blen); | |
+ data = buffer_get_string(&e->request, &dlen); | |
+ | |
+ flags = buffer_get_int(&e->request); | |
+ odatafellows = datafellows; | |
+ if (flags & SSH_AGENT_OLD_SIGNATURE) | |
+ datafellows = SSH_BUG_SIGBLOB; | |
+ | |
+ key = key_from_blob(blob, blen); | |
+ if (key != NULL) { | |
+ Identity *id = lookup_identity(key, 2); | |
+ if (id != NULL && (!id->confirm || confirm_key(id) == 0)) | |
+ ok = key_sign(id->key, &signature, &slen, data, dlen); | |
+ key_free(key); | |
+ } | |
+ buffer_init(&msg); | |
+ if (ok == 0) { | |
+ buffer_put_char(&msg, SSH2_AGENT_SIGN_RESPONSE); | |
+ buffer_put_string(&msg, signature, slen); | |
+ } else { | |
+ buffer_put_char(&msg, SSH_AGENT_FAILURE); | |
+ } | |
+ buffer_put_int(&e->output, buffer_len(&msg)); | |
+ buffer_append(&e->output, buffer_ptr(&msg), | |
+ buffer_len(&msg)); | |
+ buffer_free(&msg); | |
+ free(data); | |
+ free(blob); | |
+ free(signature); | |
+ datafellows = odatafellows; | |
+} | |
+ | |
+/* shared */ | |
+static void | |
+process_remove_identity(SocketEntry *e, int version) | |
+{ | |
+ u_int blen; | |
+ int success = 0; | |
+ Key *key = NULL; | |
+ u_char *blob; | |
+#ifdef WITH_SSH1 | |
+ u_int bits; | |
+#endif /* WITH_SSH1 */ | |
+ | |
+ switch (version) { | |
+#ifdef WITH_SSH1 | |
+ case 1: | |
+ key = key_new(KEY_RSA1); | |
+ bits = buffer_get_int(&e->request); | |
+ buffer_get_bignum(&e->request, key->rsa->e); | |
+ buffer_get_bignum(&e->request, key->rsa->n); | |
+ | |
+ if (bits != key_size(key)) | |
+ logit("Warning: identity keysize mismatch: actual %u, announced %u", | |
+ key_size(key), bits); | |
+ break; | |
+#endif /* WITH_SSH1 */ | |
+ case 2: | |
+ blob = buffer_get_string(&e->request, &blen); | |
+ key = key_from_blob(blob, blen); | |
+ free(blob); | |
+ break; | |
+ } | |
+ if (key != NULL) { | |
+ Identity *id = lookup_identity(key, version); | |
+ if (id != NULL) { | |
+ /* | |
+ * We have this key. Free the old key. Since we | |
+ * don't want to leave empty slots in the middle of | |
+ * the array, we actually free the key there and move | |
+ * all the entries between the empty slot and the end | |
+ * of the array. | |
+ */ | |
+ Idtab *tab = idtab_lookup(version); | |
+ if (tab->nentries < 1) | |
+ fatal("process_remove_identity: " | |
+ "internal error: tab->nentries %d", | |
+ tab->nentries); | |
+ TAILQ_REMOVE(&tab->idlist, id, next); | |
+ free_identity(id); | |
+ tab->nentries--; | |
+ success = 1; | |
+ } | |
+ key_free(key); | |
+ } | |
+ buffer_put_int(&e->output, 1); | |
+ buffer_put_char(&e->output, | |
+ success ? SSH_AGENT_SUCCESS : SSH_AGENT_FAILURE); | |
+} | |
+ | |
+static void | |
+process_remove_all_identities(SocketEntry *e, int version) | |
+{ | |
+ Idtab *tab = idtab_lookup(version); | |
+ Identity *id; | |
+ | |
+ /* Loop over all identities and clear the keys. */ | |
+ for (id = TAILQ_FIRST(&tab->idlist); id; | |
+ id = TAILQ_FIRST(&tab->idlist)) { | |
+ TAILQ_REMOVE(&tab->idlist, id, next); | |
+ free_identity(id); | |
+ } | |
+ | |
+ /* Mark that there are no identities. */ | |
+ tab->nentries = 0; | |
+ | |
+ /* Send success. */ | |
+ buffer_put_int(&e->output, 1); | |
+ buffer_put_char(&e->output, SSH_AGENT_SUCCESS); | |
+} | |
+ | |
+/* removes expired keys and returns number of seconds until the next expiry */ | |
+static time_t | |
+reaper(void) | |
+{ | |
+ time_t deadline = 0, now = monotime(); | |
+ Identity *id, *nxt; | |
+ int version; | |
+ Idtab *tab; | |
+ | |
+ for (version = 1; version < 3; version++) { | |
+ tab = idtab_lookup(version); | |
+ for (id = TAILQ_FIRST(&tab->idlist); id; id = nxt) { | |
+ nxt = TAILQ_NEXT(id, next); | |
+ if (id->death == 0) | |
+ continue; | |
+ if (now >= id->death) { | |
+ debug("expiring key '%s'", id->comment); | |
+ TAILQ_REMOVE(&tab->idlist, id, next); | |
+ free_identity(id); | |
+ tab->nentries--; | |
+ } else | |
+ deadline = (deadline == 0) ? id->death : | |
+ MIN(deadline, id->death); | |
+ } | |
+ } | |
+ if (deadline == 0 || deadline <= now) | |
+ return 0; | |
+ else | |
+ return (deadline - now); | |
+} | |
+ | |
+static void | |
+process_add_identity(SocketEntry *e, int version) | |
+{ | |
+ Idtab *tab = idtab_lookup(version); | |
+ Identity *id; | |
+ int type, success = 0, confirm = 0; | |
+ char *comment; | |
+ time_t death = 0; | |
+ Key *k = NULL; | |
+ | |
+ switch (version) { | |
+#ifdef WITH_SSH1 | |
+ case 1: | |
+ k = key_new_private(KEY_RSA1); | |
+ (void) buffer_get_int(&e->request); /* ignored */ | |
+ buffer_get_bignum(&e->request, k->rsa->n); | |
+ buffer_get_bignum(&e->request, k->rsa->e); | |
+ buffer_get_bignum(&e->request, k->rsa->d); | |
+ buffer_get_bignum(&e->request, k->rsa->iqmp); | |
+ | |
+ /* SSH and SSL have p and q swapped */ | |
+ buffer_get_bignum(&e->request, k->rsa->q); /* p */ | |
+ buffer_get_bignum(&e->request, k->rsa->p); /* q */ | |
+ | |
+ /* Generate additional parameters */ | |
+ if (rsa_generate_additional_parameters(k->rsa) != 0) | |
+ fatal("%s: rsa_generate_additional_parameters " | |
+ "error", __func__); | |
+ | |
+ /* enable blinding */ | |
+ if (RSA_blinding_on(k->rsa, NULL) != 1) { | |
+ error("process_add_identity: RSA_blinding_on failed"); | |
+ key_free(k); | |
+ goto send; | |
+ } | |
+ break; | |
+#endif /* WITH_SSH1 */ | |
+ case 2: | |
+ k = key_private_deserialize(&e->request); | |
+ if (k == NULL) { | |
+ buffer_clear(&e->request); | |
+ goto send; | |
+ } | |
+ break; | |
+ } | |
+ if (k == NULL) | |
+ goto send; | |
+ comment = buffer_get_string(&e->request, NULL); | |
+ | |
+ while (buffer_len(&e->request)) { | |
+ switch ((type = buffer_get_char(&e->request))) { | |
+ case SSH_AGENT_CONSTRAIN_LIFETIME: | |
+ death = monotime() + buffer_get_int(&e->request); | |
+ break; | |
+ case SSH_AGENT_CONSTRAIN_CONFIRM: | |
+ confirm = 1; | |
+ break; | |
+ default: | |
+ error("process_add_identity: " | |
+ "Unknown constraint type %d", type); | |
+ free(comment); | |
+ key_free(k); | |
+ goto send; | |
+ } | |
+ } | |
+ success = 1; | |
+ if (lifetime && !death) | |
+ death = monotime() + lifetime; | |
+ if ((id = lookup_identity(k, version)) == NULL) { | |
+ id = xcalloc(1, sizeof(Identity)); | |
+ id->key = k; | |
+ TAILQ_INSERT_TAIL(&tab->idlist, id, next); | |
+ /* Increment the number of identities. */ | |
+ tab->nentries++; | |
+ } else { | |
+ key_free(k); | |
+ free(id->comment); | |
+ } | |
+ id->comment = comment; | |
+ id->death = death; | |
+ id->confirm = confirm; | |
+send: | |
+ buffer_put_int(&e->output, 1); | |
+ buffer_put_char(&e->output, | |
+ success ? SSH_AGENT_SUCCESS : SSH_AGENT_FAILURE); | |
+} | |
+ | |
+/* XXX todo: encrypt sensitive data with passphrase */ | |
+static void | |
+process_lock_agent(SocketEntry *e, int lock) | |
+{ | |
+ int success = 0; | |
+ char *passwd; | |
+ | |
+ passwd = buffer_get_string(&e->request, NULL); | |
+ if (locked && !lock && strcmp(passwd, lock_passwd) == 0) { | |
+ locked = 0; | |
+ explicit_bzero(lock_passwd, strlen(lock_passwd)); | |
+ free(lock_passwd); | |
+ lock_passwd = NULL; | |
+ success = 1; | |
+ } else if (!locked && lock) { | |
+ locked = 1; | |
+ lock_passwd = xstrdup(passwd); | |
+ success = 1; | |
+ } | |
+ explicit_bzero(passwd, strlen(passwd)); | |
+ free(passwd); | |
+ | |
+ buffer_put_int(&e->output, 1); | |
+ buffer_put_char(&e->output, | |
+ success ? SSH_AGENT_SUCCESS : SSH_AGENT_FAILURE); | |
+} | |
+ | |
+static void | |
+no_identities(SocketEntry *e, u_int type) | |
+{ | |
+ Buffer msg; | |
+ | |
+ buffer_init(&msg); | |
+ buffer_put_char(&msg, | |
+ (type == SSH_AGENTC_REQUEST_RSA_IDENTITIES) ? | |
+ SSH_AGENT_RSA_IDENTITIES_ANSWER : SSH2_AGENT_IDENTITIES_ANSWER); | |
+ buffer_put_int(&msg, 0); | |
+ buffer_put_int(&e->output, buffer_len(&msg)); | |
+ buffer_append(&e->output, buffer_ptr(&msg), buffer_len(&msg)); | |
+ buffer_free(&msg); | |
+} | |
+ | |
+#ifdef ENABLE_PKCS11 | |
+static void | |
+process_add_smartcard_key(SocketEntry *e) | |
+{ | |
+ char *provider = NULL, *pin; | |
+ int i, type, version, count = 0, success = 0, confirm = 0; | |
+ time_t death = 0; | |
+ Key **keys = NULL, *k; | |
+ Identity *id; | |
+ Idtab *tab; | |
+ | |
+ provider = buffer_get_string(&e->request, NULL); | |
+ pin = buffer_get_string(&e->request, NULL); | |
+ | |
+ while (buffer_len(&e->request)) { | |
+ switch ((type = buffer_get_char(&e->request))) { | |
+ case SSH_AGENT_CONSTRAIN_LIFETIME: | |
+ death = monotime() + buffer_get_int(&e->request); | |
+ break; | |
+ case SSH_AGENT_CONSTRAIN_CONFIRM: | |
+ confirm = 1; | |
+ break; | |
+ default: | |
+ error("process_add_smartcard_key: " | |
+ "Unknown constraint type %d", type); | |
+ goto send; | |
+ } | |
+ } | |
+ if (lifetime && !death) | |
+ death = monotime() + lifetime; | |
+ | |
+ count = pkcs11_add_provider(provider, pin, &keys); | |
+ for (i = 0; i < count; i++) { | |
+ k = keys[i]; | |
+ version = k->type == KEY_RSA1 ? 1 : 2; | |
+ tab = idtab_lookup(version); | |
+ if (lookup_identity(k, version) == NULL) { | |
+ id = xcalloc(1, sizeof(Identity)); | |
+ id->key = k; | |
+ id->provider = xstrdup(provider); | |
+ id->comment = xstrdup(provider); /* XXX */ | |
+ id->death = death; | |
+ id->confirm = confirm; | |
+ TAILQ_INSERT_TAIL(&tab->idlist, id, next); | |
+ tab->nentries++; | |
+ success = 1; | |
+ } else { | |
+ key_free(k); | |
+ } | |
+ keys[i] = NULL; | |
+ } | |
+send: | |
+ free(pin); | |
+ free(provider); | |
+ free(keys); | |
+ buffer_put_int(&e->output, 1); | |
+ buffer_put_char(&e->output, | |
+ success ? SSH_AGENT_SUCCESS : SSH_AGENT_FAILURE); | |
+} | |
+ | |
+static void | |
+process_remove_smartcard_key(SocketEntry *e) | |
+{ | |
+ char *provider = NULL, *pin = NULL; | |
+ int version, success = 0; | |
+ Identity *id, *nxt; | |
+ Idtab *tab; | |
+ | |
+ provider = buffer_get_string(&e->request, NULL); | |
+ pin = buffer_get_string(&e->request, NULL); | |
+ free(pin); | |
+ | |
+ for (version = 1; version < 3; version++) { | |
+ tab = idtab_lookup(version); | |
+ for (id = TAILQ_FIRST(&tab->idlist); id; id = nxt) { | |
+ nxt = TAILQ_NEXT(id, next); | |
+ /* Skip file--based keys */ | |
+ if (id->provider == NULL) | |
+ continue; | |
+ if (!strcmp(provider, id->provider)) { | |
+ TAILQ_REMOVE(&tab->idlist, id, next); | |
+ free_identity(id); | |
+ tab->nentries--; | |
+ } | |
+ } | |
+ } | |
+ if (pkcs11_del_provider(provider) == 0) | |
+ success = 1; | |
+ else | |
+ error("process_remove_smartcard_key:" | |
+ " pkcs11_del_provider failed"); | |
+ free(provider); | |
+ buffer_put_int(&e->output, 1); | |
+ buffer_put_char(&e->output, | |
+ success ? SSH_AGENT_SUCCESS : SSH_AGENT_FAILURE); | |
+} | |
+#endif /* ENABLE_PKCS11 */ | |
+ | |
+/* dispatch incoming messages */ | |
+ | |
+static void | |
+process_message(SocketEntry *e) | |
+{ | |
+ u_int msg_len, type; | |
+ u_char *cp; | |
+ | |
+ if (buffer_len(&e->input) < 5) | |
+ return; /* Incomplete message. */ | |
+ cp = buffer_ptr(&e->input); | |
+ msg_len = get_u32(cp); | |
+ if (msg_len > 256 * 1024) { | |
+ close_socket(e); | |
+ return; | |
+ } | |
+ if (buffer_len(&e->input) < msg_len + 4) | |
+ return; | |
+ | |
+ /* move the current input to e->request */ | |
+ buffer_consume(&e->input, 4); | |
+ buffer_clear(&e->request); | |
+ buffer_append(&e->request, buffer_ptr(&e->input), msg_len); | |
+ buffer_consume(&e->input, msg_len); | |
+ type = buffer_get_char(&e->request); | |
+ | |
+ /* check wheter agent is locked */ | |
+ if (locked && type != SSH_AGENTC_UNLOCK) { | |
+ buffer_clear(&e->request); | |
+ switch (type) { | |
+ case SSH_AGENTC_REQUEST_RSA_IDENTITIES: | |
+ case SSH2_AGENTC_REQUEST_IDENTITIES: | |
+ /* send empty lists */ | |
+ no_identities(e, type); | |
+ break; | |
+ default: | |
+ /* send a fail message for all other request types */ | |
+ buffer_put_int(&e->output, 1); | |
+ buffer_put_char(&e->output, SSH_AGENT_FAILURE); | |
+ } | |
+ return; | |
+ } | |
+ | |
+ debug("type %d", type); | |
+ switch (type) { | |
+ case SSH_AGENTC_LOCK: | |
+ case SSH_AGENTC_UNLOCK: | |
+ process_lock_agent(e, type == SSH_AGENTC_LOCK); | |
+ break; | |
+#ifdef WITH_SSH1 | |
+ /* ssh1 */ | |
+ case SSH_AGENTC_RSA_CHALLENGE: | |
+ process_authentication_challenge1(e); | |
+ break; | |
+ case SSH_AGENTC_REQUEST_RSA_IDENTITIES: | |
+ process_request_identities(e, 1); | |
+ break; | |
+ case SSH_AGENTC_ADD_RSA_IDENTITY: | |
+ case SSH_AGENTC_ADD_RSA_ID_CONSTRAINED: | |
+ process_add_identity(e, 1); | |
+ break; | |
+ case SSH_AGENTC_REMOVE_RSA_IDENTITY: | |
+ process_remove_identity(e, 1); | |
+ break; | |
+ case SSH_AGENTC_REMOVE_ALL_RSA_IDENTITIES: | |
+ process_remove_all_identities(e, 1); | |
+ break; | |
+#endif | |
+ /* ssh2 */ | |
+ case SSH2_AGENTC_SIGN_REQUEST: | |
+ process_sign_request2(e); | |
+ break; | |
+ case SSH2_AGENTC_REQUEST_IDENTITIES: | |
+ process_request_identities(e, 2); | |
+ break; | |
+ case SSH2_AGENTC_ADD_IDENTITY: | |
+ case SSH2_AGENTC_ADD_ID_CONSTRAINED: | |
+ process_add_identity(e, 2); | |
+ break; | |
+ case SSH2_AGENTC_REMOVE_IDENTITY: | |
+ process_remove_identity(e, 2); | |
+ break; | |
+ case SSH2_AGENTC_REMOVE_ALL_IDENTITIES: | |
+ process_remove_all_identities(e, 2); | |
+ break; | |
+#ifdef ENABLE_PKCS11 | |
+ case SSH_AGENTC_ADD_SMARTCARD_KEY: | |
+ case SSH_AGENTC_ADD_SMARTCARD_KEY_CONSTRAINED: | |
+ process_add_smartcard_key(e); | |
+ break; | |
+ case SSH_AGENTC_REMOVE_SMARTCARD_KEY: | |
+ process_remove_smartcard_key(e); | |
+ break; | |
+#endif /* ENABLE_PKCS11 */ | |
+ default: | |
+ /* Unknown message. Respond with failure. */ | |
+ error("Unknown message %d", type); | |
+ buffer_clear(&e->request); | |
+ buffer_put_int(&e->output, 1); | |
+ buffer_put_char(&e->output, SSH_AGENT_FAILURE); | |
+ break; | |
+ } | |
+} | |
+ | |
+static void | |
+new_socket(sock_type type, int fd) | |
+{ | |
+ u_int i, old_alloc, new_alloc; | |
+ | |
+ set_nonblock(fd); | |
+ | |
+ if (fd > max_fd) | |
+ max_fd = fd; | |
+ | |
+ for (i = 0; i < sockets_alloc; i++) | |
+ if (sockets[i].type == AUTH_UNUSED) { | |
+ sockets[i].fd = fd; | |
+ buffer_init(&sockets[i].input); | |
+ buffer_init(&sockets[i].output); | |
+ buffer_init(&sockets[i].request); | |
+ sockets[i].type = type; | |
+ return; | |
+ } | |
+ old_alloc = sockets_alloc; | |
+ new_alloc = sockets_alloc + 10; | |
+ sockets = xrealloc(sockets, new_alloc, sizeof(sockets[0])); | |
+ for (i = old_alloc; i < new_alloc; i++) | |
+ sockets[i].type = AUTH_UNUSED; | |
+ sockets_alloc = new_alloc; | |
+ sockets[old_alloc].fd = fd; | |
+ buffer_init(&sockets[old_alloc].input); | |
+ buffer_init(&sockets[old_alloc].output); | |
+ buffer_init(&sockets[old_alloc].request); | |
+ sockets[old_alloc].type = type; | |
+} | |
+ | |
+static int | |
+prepare_select(fd_set **fdrp, fd_set **fdwp, int *fdl, u_int *nallocp, | |
+ struct timeval **tvpp) | |
+{ | |
+ u_int i, sz; | |
+ int n = 0; | |
+ static struct timeval tv; | |
+ time_t deadline; | |
+ | |
+ for (i = 0; i < sockets_alloc; i++) { | |
+ switch (sockets[i].type) { | |
+ case AUTH_SOCKET: | |
+ case AUTH_CONNECTION: | |
+ n = MAX(n, sockets[i].fd); | |
+ break; | |
+ case AUTH_UNUSED: | |
+ break; | |
+ default: | |
+ fatal("Unknown socket type %d", sockets[i].type); | |
+ break; | |
+ } | |
+ } | |
+ | |
+ sz = howmany(n+1, NFDBITS) * sizeof(fd_mask); | |
+ if (*fdrp == NULL || sz > *nallocp) { | |
+ free(*fdrp); | |
+ free(*fdwp); | |
+ *fdrp = xmalloc(sz); | |
+ *fdwp = xmalloc(sz); | |
+ *nallocp = sz; | |
+ } | |
+ if (n < *fdl) | |
+ debug("XXX shrink: %d < %d", n, *fdl); | |
+ *fdl = n; | |
+ memset(*fdrp, 0, sz); | |
+ memset(*fdwp, 0, sz); | |
+ | |
+ for (i = 0; i < sockets_alloc; i++) { | |
+ switch (sockets[i].type) { | |
+ case AUTH_SOCKET: | |
+ case AUTH_CONNECTION: | |
+ FD_SET(sockets[i].fd, *fdrp); | |
+ if (buffer_len(&sockets[i].output) > 0) | |
+ FD_SET(sockets[i].fd, *fdwp); | |
+ break; | |
+ default: | |
+ break; | |
+ } | |
+ } | |
+ deadline = reaper(); | |
+ if (parent_alive_interval != 0) | |
+ deadline = (deadline == 0) ? parent_alive_interval : | |
+ MIN(deadline, parent_alive_interval); | |
+ if (deadline == 0) { | |
+ *tvpp = NULL; | |
+ } else { | |
+ tv.tv_sec = deadline; | |
+ tv.tv_usec = 0; | |
+ *tvpp = &tv; | |
+ } | |
+ return (1); | |
+} | |
+ | |
+static void | |
+after_select(fd_set *readset, fd_set *writeset) | |
+{ | |
+ struct sockaddr_un sunaddr; | |
+ socklen_t slen; | |
+ char buf[1024]; | |
+ int len, sock; | |
+ u_int i, orig_alloc; | |
+ uid_t euid; | |
+ gid_t egid; | |
+ | |
+ for (i = 0, orig_alloc = sockets_alloc; i < orig_alloc; i++) | |
+ switch (sockets[i].type) { | |
+ case AUTH_UNUSED: | |
+ break; | |
+ case AUTH_SOCKET: | |
+ if (FD_ISSET(sockets[i].fd, readset)) { | |
+ slen = sizeof(sunaddr); | |
+ sock = accept(sockets[i].fd, | |
+ (struct sockaddr *)&sunaddr, &slen); | |
+ if (sock < 0) { | |
+ error("accept from AUTH_SOCKET: %s", | |
+ strerror(errno)); | |
+ break; | |
+ } | |
+ if (getpeereid(sock, &euid, &egid) < 0) { | |
+ error("getpeereid %d failed: %s", | |
+ sock, strerror(errno)); | |
+ close(sock); | |
+ break; | |
+ } | |
+ if ((euid != 0) && (getuid() != euid)) { | |
+ error("uid mismatch: " | |
+ "peer euid %u != uid %u", | |
+ (u_int) euid, (u_int) getuid()); | |
+ close(sock); | |
+ break; | |
+ } | |
+ new_socket(AUTH_CONNECTION, sock); | |
+ } | |
+ break; | |
+ case AUTH_CONNECTION: | |
+ if (buffer_len(&sockets[i].output) > 0 && | |
+ FD_ISSET(sockets[i].fd, writeset)) { | |
+ len = write(sockets[i].fd, | |
+ buffer_ptr(&sockets[i].output), | |
+ buffer_len(&sockets[i].output)); | |
+ if (len == -1 && (errno == EAGAIN || | |
+ errno == EWOULDBLOCK || | |
+ errno == EINTR)) | |
+ continue; | |
+ if (len <= 0) { | |
+ close_socket(&sockets[i]); | |
+ break; | |
+ } | |
+ buffer_consume(&sockets[i].output, len); | |
+ } | |
+ if (FD_ISSET(sockets[i].fd, readset)) { | |
+ len = read(sockets[i].fd, buf, sizeof(buf)); | |
+ if (len == -1 && (errno == EAGAIN || | |
+ errno == EWOULDBLOCK || | |
+ errno == EINTR)) | |
+ continue; | |
+ if (len <= 0) { | |
+ close_socket(&sockets[i]); | |
+ break; | |
+ } | |
+ buffer_append(&sockets[i].input, buf, len); | |
+ explicit_bzero(buf, sizeof(buf)); | |
+ process_message(&sockets[i]); | |
+ } | |
+ break; | |
+ default: | |
+ fatal("Unknown type %d", sockets[i].type); | |
+ } | |
+} | |
+ | |
+static void | |
+cleanup_socket(void) | |
+{ | |
+ if (cleanup_pid != 0 && getpid() != cleanup_pid) | |
+ return; | |
+ debug("%s: cleanup", __func__); | |
+ if (socket_name[0]) | |
+ unlink(socket_name); | |
+ if (socket_dir[0]) | |
+ rmdir(socket_dir); | |
+} | |
+ | |
+void | |
+cleanup_exit(int i) | |
+{ | |
+ cleanup_socket(); | |
+ _exit(i); | |
+} | |
+ | |
+/*ARGSUSED*/ | |
+static void | |
+cleanup_handler(int sig) | |
+{ | |
+ cleanup_socket(); | |
+#ifdef ENABLE_PKCS11 | |
+ pkcs11_terminate(); | |
+#endif | |
+ _exit(2); | |
+} | |
+ | |
+static void | |
+check_parent_exists(void) | |
+{ | |
+ /* | |
+ * If our parent has exited then getppid() will return (pid_t)1, | |
+ * so testing for that should be safe. | |
+ */ | |
+ if (parent_pid != -1 && getppid() != parent_pid) { | |
+ /* printf("Parent has died - Authentication agent exiting.\n"); */ | |
+ cleanup_socket(); | |
+ _exit(2); | |
+ } | |
+} | |
+ | |
+static void | |
+usage(void) | |
+{ | |
+ fprintf(stderr, | |
+ "usage: ssh-agent [-c | -s] [-d] [-a bind_address] [-t life]\n" | |
+ " [command [arg ...]]\n" | |
+ " ssh-agent [-c | -s] -k\n"); | |
+ exit(1); | |
+} | |
+ | |
+int | |
+main(int ac, char **av) | |
+{ | |
+ int c_flag = 0, d_flag = 0, k_flag = 0, s_flag = 0; | |
+ int sock, fd, ch, result, saved_errno; | |
+ u_int nalloc; | |
+ char *shell, *format, *pidstr, *agentsocket = NULL; | |
+ fd_set *readsetp = NULL, *writesetp = NULL; | |
+#ifdef HAVE_SETRLIMIT | |
+ struct rlimit rlim; | |
+#endif | |
+ extern int optind; | |
+ extern char *optarg; | |
+ pid_t pid; | |
+ char pidstrbuf[1 + 3 * sizeof pid]; | |
+ struct timeval *tvp = NULL; | |
+ size_t len; | |
+ mode_t prev_mask; | |
+ | |
+ /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ | |
+ sanitise_stdfd(); | |
+ | |
+ /* drop */ | |
+ setegid(getgid()); | |
+ setgid(getgid()); | |
+ | |
+#if defined(HAVE_PRCTL) && defined(PR_SET_DUMPABLE) | |
+ /* Disable ptrace on Linux without sgid bit */ | |
+ prctl(PR_SET_DUMPABLE, 0); | |
+#endif | |
+ | |
+#ifdef WITH_OPENSSL | |
+ OpenSSL_add_all_algorithms(); | |
+#endif | |
+ | |
+ __progname = ssh_get_progname(av[0]); | |
+ seed_rng(); | |
+ | |
+ while ((ch = getopt(ac, av, "cdksa:t:")) != -1) { | |
+ switch (ch) { | |
+ case 'c': | |
+ if (s_flag) | |
+ usage(); | |
+ c_flag++; | |
+ break; | |
+ case 'k': | |
+ k_flag++; | |
+ break; | |
+ case 's': | |
+ if (c_flag) | |
+ usage(); | |
+ s_flag++; | |
+ break; | |
+ case 'd': | |
+ if (d_flag) | |
+ usage(); | |
+ d_flag++; | |
+ break; | |
+ case 'a': | |
+ agentsocket = optarg; | |
+ break; | |
+ case 't': | |
+ if ((lifetime = convtime(optarg)) == -1) { | |
+ fprintf(stderr, "Invalid lifetime\n"); | |
+ usage(); | |
+ } | |
+ break; | |
+ default: | |
+ usage(); | |
+ } | |
+ } | |
+ ac -= optind; | |
+ av += optind; | |
+ | |
+ if (ac > 0 && (c_flag || k_flag || s_flag || d_flag)) | |
+ usage(); | |
+ | |
+ if (ac == 0 && !c_flag && !s_flag) { | |
+ shell = getenv("SHELL"); | |
+ if (shell != NULL && (len = strlen(shell)) > 2 && | |
+ strncmp(shell + len - 3, "csh", 3) == 0) | |
+ c_flag = 1; | |
+ } | |
+ if (k_flag) { | |
+ const char *errstr = NULL; | |
+ | |
+ pidstr = getenv(SSH_AGENTPID_ENV_NAME); | |
+ if (pidstr == NULL) { | |
+ fprintf(stderr, "%s not set, cannot kill agent\n", | |
+ SSH_AGENTPID_ENV_NAME); | |
+ exit(1); | |
+ } | |
+ pid = (int)strtonum(pidstr, 2, INT_MAX, &errstr); | |
+ if (errstr) { | |
+ fprintf(stderr, | |
+ "%s=\"%s\", which is not a good PID: %s\n", | |
+ SSH_AGENTPID_ENV_NAME, pidstr, errstr); | |
+ exit(1); | |
+ } | |
+ if (kill(pid, SIGTERM) == -1) { | |
+ perror("kill"); | |
+ exit(1); | |
+ } | |
+ format = c_flag ? "unsetenv %s;\n" : "unset %s;\n"; | |
+ printf(format, SSH_AUTHSOCKET_ENV_NAME); | |
+ printf(format, SSH_AGENTPID_ENV_NAME); | |
+ printf("echo Agent pid %ld killed;\n", (long)pid); | |
+ exit(0); | |
+ } | |
+ parent_pid = getpid(); | |
+ | |
+ if (agentsocket == NULL) { | |
+ /* Create private directory for agent socket */ | |
+ mktemp_proto(socket_dir, sizeof(socket_dir)); | |
+ if (mkdtemp(socket_dir) == NULL) { | |
+ perror("mkdtemp: private socket dir"); | |
+ exit(1); | |
+ } | |
+ snprintf(socket_name, sizeof socket_name, "%s/agent.%ld", socket_dir, | |
+ (long)parent_pid); | |
+ } else { | |
+ /* Try to use specified agent socket */ | |
+ socket_dir[0] = '\0'; | |
+ strlcpy(socket_name, agentsocket, sizeof socket_name); | |
+ } | |
+ | |
+ /* | |
+ * Create socket early so it will exist before command gets run from | |
+ * the parent. | |
+ */ | |
+ prev_mask = umask(0177); | |
+ sock = unix_listener(socket_name, SSH_LISTEN_BACKLOG, 0); | |
+ if (sock < 0) { | |
+ /* XXX - unix_listener() calls error() not perror() */ | |
+ *socket_name = '\0'; /* Don't unlink any existing file */ | |
+ cleanup_exit(1); | |
+ } | |
+ umask(prev_mask); | |
+ | |
+ /* | |
+ * Fork, and have the parent execute the command, if any, or present | |
+ * the socket data. The child continues as the authentication agent. | |
+ */ | |
+ if (d_flag) { | |
+ log_init(__progname, SYSLOG_LEVEL_DEBUG1, SYSLOG_FACILITY_AUTH, 1); | |
+ format = c_flag ? "setenv %s %s;\n" : "%s=%s; export %s;\n"; | |
+ printf(format, SSH_AUTHSOCKET_ENV_NAME, socket_name, | |
+ SSH_AUTHSOCKET_ENV_NAME); | |
+ printf("echo Agent pid %ld;\n", (long)parent_pid); | |
+ goto skip; | |
+ } | |
+ pid = fork(); | |
+ if (pid == -1) { | |
+ perror("fork"); | |
+ cleanup_exit(1); | |
+ } | |
+ if (pid != 0) { /* Parent - execute the given command. */ | |
+ close(sock); | |
+ snprintf(pidstrbuf, sizeof pidstrbuf, "%ld", (long)pid); | |
+ if (ac == 0) { | |
+ format = c_flag ? "setenv %s %s;\n" : "%s=%s; export %s;\n"; | |
+ printf(format, SSH_AUTHSOCKET_ENV_NAME, socket_name, | |
+ SSH_AUTHSOCKET_ENV_NAME); | |
+ printf(format, SSH_AGENTPID_ENV_NAME, pidstrbuf, | |
+ SSH_AGENTPID_ENV_NAME); | |
+ printf("echo Agent pid %ld;\n", (long)pid); | |
+ exit(0); | |
+ } | |
+ if (setenv(SSH_AUTHSOCKET_ENV_NAME, socket_name, 1) == -1 || | |
+ setenv(SSH_AGENTPID_ENV_NAME, pidstrbuf, 1) == -1) { | |
+ perror("setenv"); | |
+ exit(1); | |
+ } | |
+ execvp(av[0], av); | |
+ perror(av[0]); | |
+ exit(1); | |
+ } | |
+ /* child */ | |
+ log_init(__progname, SYSLOG_LEVEL_INFO, SYSLOG_FACILITY_AUTH, 0); | |
+ | |
+ if (setsid() == -1) { | |
+ error("setsid: %s", strerror(errno)); | |
+ cleanup_exit(1); | |
+ } | |
+ | |
+ (void)chdir("/"); | |
+ if ((fd = open(_PATH_DEVNULL, O_RDWR, 0)) != -1) { | |
+ /* XXX might close listen socket */ | |
+ (void)dup2(fd, STDIN_FILENO); | |
+ (void)dup2(fd, STDOUT_FILENO); | |
+ (void)dup2(fd, STDERR_FILENO); | |
+ if (fd > 2) | |
+ close(fd); | |
+ } | |
+ | |
+#ifdef HAVE_SETRLIMIT | |
+ /* deny core dumps, since memory contains unencrypted private keys */ | |
+ rlim.rlim_cur = rlim.rlim_max = 0; | |
+ if (setrlimit(RLIMIT_CORE, &rlim) < 0) { | |
+ error("setrlimit RLIMIT_CORE: %s", strerror(errno)); | |
+ cleanup_exit(1); | |
+ } | |
+#endif | |
+ | |
+skip: | |
+ | |
+ cleanup_pid = getpid(); | |
+ | |
+#ifdef ENABLE_PKCS11 | |
+ pkcs11_init(0); | |
+#endif | |
+ new_socket(AUTH_SOCKET, sock); | |
+ if (ac > 0) | |
+ parent_alive_interval = 10; | |
+ idtab_init(); | |
+ signal(SIGPIPE, SIG_IGN); | |
+ signal(SIGINT, d_flag ? cleanup_handler : SIG_IGN); | |
+ signal(SIGHUP, cleanup_handler); | |
+ signal(SIGTERM, cleanup_handler); | |
+ nalloc = 0; | |
+ | |
+ while (1) { | |
+ prepare_select(&readsetp, &writesetp, &max_fd, &nalloc, &tvp); | |
+ result = select(max_fd + 1, readsetp, writesetp, NULL, tvp); | |
+ saved_errno = errno; | |
+ if (parent_alive_interval != 0) | |
+ check_parent_exists(); | |
+ (void) reaper(); /* remove expired keys */ | |
+ if (result < 0) { | |
+ if (saved_errno == EINTR) | |
+ continue; | |
+ fatal("select: %s", strerror(saved_errno)); | |
+ } else if (result > 0) | |
+ after_select(readsetp, writesetp); | |
+ } | |
+ /* NOTREACHED */ | |
+} | |
diff -urp --new-file openssh-6.7p1/ssh-keysign.8 openssh-6.7p1.patched/ssh-keysign.8 | |
--- openssh-6.7p1/ssh-keysign.8 2013-12-17 22:46:28.000000000 -0800 | |
+++ openssh-6.7p1.patched/ssh-keysign.8 2014-10-11 21:18:37.000000000 -0700 | |
@@ -72,6 +72,9 @@ accessible to others. | |
Since they are readable only by root, | |
.Nm | |
must be set-uid root if host-based authentication is used. | |
+Note that | |
+.Nm | |
+is not set-uid by default on Mac OS X. | |
.Pp | |
.It Pa /etc/ssh/ssh_host_dsa_key-cert.pub | |
.It Pa /etc/ssh/ssh_host_ecdsa_key-cert.pub | |
diff -urp --new-file openssh-6.7p1/sshconnect1.c openssh-6.7p1.patched/sshconnect1.c | |
--- openssh-6.7p1/sshconnect1.c 2014-07-17 21:11:26.000000000 -0700 | |
+++ openssh-6.7p1.patched/sshconnect1.c 2014-10-11 21:18:37.000000000 -0700 | |
@@ -47,6 +47,7 @@ | |
#include "hostfile.h" | |
#include "auth.h" | |
#include "digest.h" | |
+#include "keychain.h" | |
/* Session id for the current session. */ | |
u_char session_id[16]; | |
@@ -262,6 +263,10 @@ try_rsa_authentication(int idx) | |
snprintf(buf, sizeof(buf), | |
"Enter passphrase for RSA key '%.100s': ", comment); | |
for (i = 0; i < options.number_of_password_prompts; i++) { | |
+#ifdef __APPLE_KEYCHAIN__ | |
+ passphrase = keychain_read_passphrase(comment, options.ask_pass_gui); | |
+ if (passphrase == NULL) | |
+#endif | |
passphrase = read_passphrase(buf, 0); | |
if (strcmp(passphrase, "") != 0) { | |
private = key_load_private_type(KEY_RSA1, | |
diff -urp --new-file openssh-6.7p1/sshconnect2.c openssh-6.7p1.patched/sshconnect2.c | |
--- openssh-6.7p1/sshconnect2.c 2014-07-17 21:11:27.000000000 -0700 | |
+++ openssh-6.7p1.patched/sshconnect2.c 2014-10-11 21:18:37.000000000 -0700 | |
@@ -70,6 +70,7 @@ | |
#include "pathnames.h" | |
#include "uidswap.h" | |
#include "hostfile.h" | |
+#include "keychain.h" | |
#ifdef GSSAPI | |
#include "ssh-gss.h" | |
@@ -1122,6 +1123,10 @@ load_identity_file(char *filename, int u | |
snprintf(prompt, sizeof prompt, | |
"Enter passphrase for key '%.100s': ", filename); | |
for (i = 0; i < options.number_of_password_prompts; i++) { | |
+#ifdef __APPLE_KEYCHAIN__ | |
+ passphrase = keychain_read_passphrase(filename, options.ask_pass_gui); | |
+ if (passphrase == NULL) | |
+#endif | |
passphrase = read_passphrase(prompt, 0); | |
if (strcmp(passphrase, "") != 0) { | |
private = key_load_private_type(KEY_UNSPEC, | |
diff -urp --new-file openssh-6.7p1/sshconnect2.c.orig openssh-6.7p1.patched/sshconnect2.c.orig | |
--- openssh-6.7p1/sshconnect2.c.orig 1969-12-31 16:00:00.000000000 -0800 | |
+++ openssh-6.7p1.patched/sshconnect2.c.orig 2014-07-17 21:11:27.000000000 -0700 | |
@@ -0,0 +1,1675 @@ | |
+/* $OpenBSD: sshconnect2.c,v 1.210 2014/07/15 15:54:14 millert Exp $ */ | |
+/* | |
+ * Copyright (c) 2000 Markus Friedl. All rights reserved. | |
+ * Copyright (c) 2008 Damien Miller. 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 <sys/types.h> | |
+#include <sys/socket.h> | |
+#include <sys/wait.h> | |
+#include <sys/stat.h> | |
+ | |
+#include <errno.h> | |
+#include <fcntl.h> | |
+#include <netdb.h> | |
+#include <pwd.h> | |
+#include <signal.h> | |
+#include <stdarg.h> | |
+#include <stdio.h> | |
+#include <string.h> | |
+#include <unistd.h> | |
+#if defined(HAVE_STRNVIS) && defined(HAVE_VIS_H) && !defined(BROKEN_STRNVIS) | |
+#include <vis.h> | |
+#endif | |
+ | |
+#include "openbsd-compat/sys-queue.h" | |
+ | |
+#include "xmalloc.h" | |
+#include "ssh.h" | |
+#include "ssh2.h" | |
+#include "buffer.h" | |
+#include "packet.h" | |
+#include "compat.h" | |
+#include "cipher.h" | |
+#include "key.h" | |
+#include "kex.h" | |
+#include "myproposal.h" | |
+#include "sshconnect.h" | |
+#include "authfile.h" | |
+#include "dh.h" | |
+#include "authfd.h" | |
+#include "log.h" | |
+#include "misc.h" | |
+#include "readconf.h" | |
+#include "match.h" | |
+#include "dispatch.h" | |
+#include "canohost.h" | |
+#include "msg.h" | |
+#include "pathnames.h" | |
+#include "uidswap.h" | |
+#include "hostfile.h" | |
+ | |
+#ifdef GSSAPI | |
+#include "ssh-gss.h" | |
+#endif | |
+ | |
+/* import */ | |
+extern char *client_version_string; | |
+extern char *server_version_string; | |
+extern Options options; | |
+ | |
+/* | |
+ * SSH2 key exchange | |
+ */ | |
+ | |
+u_char *session_id2 = NULL; | |
+u_int session_id2_len = 0; | |
+ | |
+char *xxx_host; | |
+struct sockaddr *xxx_hostaddr; | |
+ | |
+Kex *xxx_kex = NULL; | |
+ | |
+static int | |
+verify_host_key_callback(Key *hostkey) | |
+{ | |
+ if (verify_host_key(xxx_host, xxx_hostaddr, hostkey) == -1) | |
+ fatal("Host key verification failed."); | |
+ return 0; | |
+} | |
+ | |
+static char * | |
+order_hostkeyalgs(char *host, struct sockaddr *hostaddr, u_short port) | |
+{ | |
+ char *oavail, *avail, *first, *last, *alg, *hostname, *ret; | |
+ size_t maxlen; | |
+ struct hostkeys *hostkeys; | |
+ int ktype; | |
+ u_int i; | |
+ | |
+ /* Find all hostkeys for this hostname */ | |
+ get_hostfile_hostname_ipaddr(host, hostaddr, port, &hostname, NULL); | |
+ hostkeys = init_hostkeys(); | |
+ for (i = 0; i < options.num_user_hostfiles; i++) | |
+ load_hostkeys(hostkeys, hostname, options.user_hostfiles[i]); | |
+ for (i = 0; i < options.num_system_hostfiles; i++) | |
+ load_hostkeys(hostkeys, hostname, options.system_hostfiles[i]); | |
+ | |
+ oavail = avail = xstrdup(KEX_DEFAULT_PK_ALG); | |
+ maxlen = strlen(avail) + 1; | |
+ first = xmalloc(maxlen); | |
+ last = xmalloc(maxlen); | |
+ *first = *last = '\0'; | |
+ | |
+#define ALG_APPEND(to, from) \ | |
+ do { \ | |
+ if (*to != '\0') \ | |
+ strlcat(to, ",", maxlen); \ | |
+ strlcat(to, from, maxlen); \ | |
+ } while (0) | |
+ | |
+ while ((alg = strsep(&avail, ",")) && *alg != '\0') { | |
+ if ((ktype = key_type_from_name(alg)) == KEY_UNSPEC) | |
+ fatal("%s: unknown alg %s", __func__, alg); | |
+ if (lookup_key_in_hostkeys_by_type(hostkeys, | |
+ key_type_plain(ktype), NULL)) | |
+ ALG_APPEND(first, alg); | |
+ else | |
+ ALG_APPEND(last, alg); | |
+ } | |
+#undef ALG_APPEND | |
+ xasprintf(&ret, "%s%s%s", first, *first == '\0' ? "" : ",", last); | |
+ if (*first != '\0') | |
+ debug3("%s: prefer hostkeyalgs: %s", __func__, first); | |
+ | |
+ free(first); | |
+ free(last); | |
+ free(hostname); | |
+ free(oavail); | |
+ free_hostkeys(hostkeys); | |
+ | |
+ return ret; | |
+} | |
+ | |
+void | |
+ssh_kex2(char *host, struct sockaddr *hostaddr, u_short port) | |
+{ | |
+ char *myproposal[PROPOSAL_MAX] = { KEX_CLIENT }; | |
+ Kex *kex; | |
+ | |
+ xxx_host = host; | |
+ xxx_hostaddr = hostaddr; | |
+ | |
+ if (options.ciphers == (char *)-1) { | |
+ logit("No valid ciphers for protocol version 2 given, using defaults."); | |
+ options.ciphers = NULL; | |
+ } | |
+ if (options.ciphers != NULL) { | |
+ myproposal[PROPOSAL_ENC_ALGS_CTOS] = | |
+ myproposal[PROPOSAL_ENC_ALGS_STOC] = options.ciphers; | |
+ } | |
+ myproposal[PROPOSAL_ENC_ALGS_CTOS] = | |
+ compat_cipher_proposal(myproposal[PROPOSAL_ENC_ALGS_CTOS]); | |
+ myproposal[PROPOSAL_ENC_ALGS_STOC] = | |
+ compat_cipher_proposal(myproposal[PROPOSAL_ENC_ALGS_STOC]); | |
+ if (options.compression) { | |
+ myproposal[PROPOSAL_COMP_ALGS_CTOS] = | |
+ myproposal[PROPOSAL_COMP_ALGS_STOC] = "zlib@openssh.com,zlib,none"; | |
+ } else { | |
+ myproposal[PROPOSAL_COMP_ALGS_CTOS] = | |
+ myproposal[PROPOSAL_COMP_ALGS_STOC] = "none,zlib@openssh.com,zlib"; | |
+ } | |
+ if (options.macs != NULL) { | |
+ myproposal[PROPOSAL_MAC_ALGS_CTOS] = | |
+ myproposal[PROPOSAL_MAC_ALGS_STOC] = options.macs; | |
+ } | |
+ if (options.hostkeyalgorithms != NULL) | |
+ myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = | |
+ compat_pkalg_proposal(options.hostkeyalgorithms); | |
+ else { | |
+ /* Prefer algorithms that we already have keys for */ | |
+ myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = | |
+ compat_pkalg_proposal( | |
+ order_hostkeyalgs(host, hostaddr, port)); | |
+ } | |
+ if (options.kex_algorithms != NULL) | |
+ myproposal[PROPOSAL_KEX_ALGS] = options.kex_algorithms; | |
+ myproposal[PROPOSAL_KEX_ALGS] = compat_kex_proposal( | |
+ myproposal[PROPOSAL_KEX_ALGS]); | |
+ | |
+ if (options.rekey_limit || options.rekey_interval) | |
+ packet_set_rekey_limits((u_int32_t)options.rekey_limit, | |
+ (time_t)options.rekey_interval); | |
+ | |
+ /* start key exchange */ | |
+ kex = kex_setup(myproposal); | |
+#ifdef WITH_OPENSSL | |
+ kex->kex[KEX_DH_GRP1_SHA1] = kexdh_client; | |
+ kex->kex[KEX_DH_GRP14_SHA1] = kexdh_client; | |
+ kex->kex[KEX_DH_GEX_SHA1] = kexgex_client; | |
+ kex->kex[KEX_DH_GEX_SHA256] = kexgex_client; | |
+ kex->kex[KEX_ECDH_SHA2] = kexecdh_client; | |
+#endif | |
+ kex->kex[KEX_C25519_SHA256] = kexc25519_client; | |
+ kex->client_version_string=client_version_string; | |
+ kex->server_version_string=server_version_string; | |
+ kex->verify_host_key=&verify_host_key_callback; | |
+ | |
+ xxx_kex = kex; | |
+ | |
+ dispatch_run(DISPATCH_BLOCK, &kex->done, kex); | |
+ | |
+ if (options.use_roaming && !kex->roaming) { | |
+ debug("Roaming not allowed by server"); | |
+ options.use_roaming = 0; | |
+ } | |
+ | |
+ session_id2 = kex->session_id; | |
+ session_id2_len = kex->session_id_len; | |
+ | |
+#ifdef DEBUG_KEXDH | |
+ /* send 1st encrypted/maced/compressed message */ | |
+ packet_start(SSH2_MSG_IGNORE); | |
+ packet_put_cstring("markus"); | |
+ packet_send(); | |
+ packet_write_wait(); | |
+#endif | |
+} | |
+ | |
+/* | |
+ * Authenticate user | |
+ */ | |
+ | |
+typedef struct Authctxt Authctxt; | |
+typedef struct Authmethod Authmethod; | |
+typedef struct identity Identity; | |
+typedef struct idlist Idlist; | |
+ | |
+struct identity { | |
+ TAILQ_ENTRY(identity) next; | |
+ AuthenticationConnection *ac; /* set if agent supports key */ | |
+ Key *key; /* public/private key */ | |
+ char *filename; /* comment for agent-only keys */ | |
+ int tried; | |
+ int isprivate; /* key points to the private key */ | |
+ int userprovided; | |
+}; | |
+TAILQ_HEAD(idlist, identity); | |
+ | |
+struct Authctxt { | |
+ const char *server_user; | |
+ const char *local_user; | |
+ const char *host; | |
+ const char *service; | |
+ Authmethod *method; | |
+ sig_atomic_t success; | |
+ char *authlist; | |
+ /* pubkey */ | |
+ Idlist keys; | |
+ AuthenticationConnection *agent; | |
+ /* hostbased */ | |
+ Sensitive *sensitive; | |
+ /* kbd-interactive */ | |
+ int info_req_seen; | |
+ /* generic */ | |
+ void *methoddata; | |
+}; | |
+struct Authmethod { | |
+ char *name; /* string to compare against server's list */ | |
+ int (*userauth)(Authctxt *authctxt); | |
+ void (*cleanup)(Authctxt *authctxt); | |
+ int *enabled; /* flag in option struct that enables method */ | |
+ int *batch_flag; /* flag in option struct that disables method */ | |
+}; | |
+ | |
+void input_userauth_success(int, u_int32_t, void *); | |
+void input_userauth_success_unexpected(int, u_int32_t, void *); | |
+void input_userauth_failure(int, u_int32_t, void *); | |
+void input_userauth_banner(int, u_int32_t, void *); | |
+void input_userauth_error(int, u_int32_t, void *); | |
+void input_userauth_info_req(int, u_int32_t, void *); | |
+void input_userauth_pk_ok(int, u_int32_t, void *); | |
+void input_userauth_passwd_changereq(int, u_int32_t, void *); | |
+ | |
+int userauth_none(Authctxt *); | |
+int userauth_pubkey(Authctxt *); | |
+int userauth_passwd(Authctxt *); | |
+int userauth_kbdint(Authctxt *); | |
+int userauth_hostbased(Authctxt *); | |
+ | |
+#ifdef GSSAPI | |
+int userauth_gssapi(Authctxt *authctxt); | |
+void input_gssapi_response(int type, u_int32_t, void *); | |
+void input_gssapi_token(int type, u_int32_t, void *); | |
+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 *); | |
+#endif | |
+ | |
+void userauth(Authctxt *, char *); | |
+ | |
+static int sign_and_send_pubkey(Authctxt *, Identity *); | |
+static void pubkey_prepare(Authctxt *); | |
+static void pubkey_cleanup(Authctxt *); | |
+static Key *load_identity_file(char *, int); | |
+ | |
+static Authmethod *authmethod_get(char *authlist); | |
+static Authmethod *authmethod_lookup(const char *name); | |
+static char *authmethods_get(void); | |
+ | |
+Authmethod authmethods[] = { | |
+#ifdef GSSAPI | |
+ {"gssapi-with-mic", | |
+ userauth_gssapi, | |
+ NULL, | |
+ &options.gss_authentication, | |
+ NULL}, | |
+#endif | |
+ {"hostbased", | |
+ userauth_hostbased, | |
+ NULL, | |
+ &options.hostbased_authentication, | |
+ NULL}, | |
+ {"publickey", | |
+ userauth_pubkey, | |
+ NULL, | |
+ &options.pubkey_authentication, | |
+ NULL}, | |
+ {"keyboard-interactive", | |
+ userauth_kbdint, | |
+ NULL, | |
+ &options.kbd_interactive_authentication, | |
+ &options.batch_mode}, | |
+ {"password", | |
+ userauth_passwd, | |
+ NULL, | |
+ &options.password_authentication, | |
+ &options.batch_mode}, | |
+ {"none", | |
+ userauth_none, | |
+ NULL, | |
+ NULL, | |
+ NULL}, | |
+ {NULL, NULL, NULL, NULL, NULL} | |
+}; | |
+ | |
+void | |
+ssh_userauth2(const char *local_user, const char *server_user, char *host, | |
+ Sensitive *sensitive) | |
+{ | |
+ Authctxt authctxt; | |
+ int type; | |
+ | |
+ if (options.challenge_response_authentication) | |
+ options.kbd_interactive_authentication = 1; | |
+ | |
+ packet_start(SSH2_MSG_SERVICE_REQUEST); | |
+ packet_put_cstring("ssh-userauth"); | |
+ packet_send(); | |
+ debug("SSH2_MSG_SERVICE_REQUEST sent"); | |
+ packet_write_wait(); | |
+ type = packet_read(); | |
+ if (type != SSH2_MSG_SERVICE_ACCEPT) | |
+ fatal("Server denied authentication request: %d", type); | |
+ if (packet_remaining() > 0) { | |
+ char *reply = packet_get_string(NULL); | |
+ debug2("service_accept: %s", reply); | |
+ free(reply); | |
+ } else { | |
+ debug2("buggy server: service_accept w/o service"); | |
+ } | |
+ packet_check_eom(); | |
+ debug("SSH2_MSG_SERVICE_ACCEPT received"); | |
+ | |
+ if (options.preferred_authentications == NULL) | |
+ options.preferred_authentications = authmethods_get(); | |
+ | |
+ /* setup authentication context */ | |
+ memset(&authctxt, 0, sizeof(authctxt)); | |
+ pubkey_prepare(&authctxt); | |
+ authctxt.server_user = server_user; | |
+ authctxt.local_user = local_user; | |
+ authctxt.host = host; | |
+ authctxt.service = "ssh-connection"; /* service name */ | |
+ authctxt.success = 0; | |
+ authctxt.method = authmethod_lookup("none"); | |
+ authctxt.authlist = NULL; | |
+ authctxt.methoddata = NULL; | |
+ authctxt.sensitive = sensitive; | |
+ authctxt.info_req_seen = 0; | |
+ if (authctxt.method == NULL) | |
+ fatal("ssh_userauth2: internal error: cannot send userauth none request"); | |
+ | |
+ /* initial userauth request */ | |
+ userauth_none(&authctxt); | |
+ | |
+ dispatch_init(&input_userauth_error); | |
+ dispatch_set(SSH2_MSG_USERAUTH_SUCCESS, &input_userauth_success); | |
+ dispatch_set(SSH2_MSG_USERAUTH_FAILURE, &input_userauth_failure); | |
+ dispatch_set(SSH2_MSG_USERAUTH_BANNER, &input_userauth_banner); | |
+ dispatch_run(DISPATCH_BLOCK, &authctxt.success, &authctxt); /* loop until success */ | |
+ | |
+ pubkey_cleanup(&authctxt); | |
+ dispatch_range(SSH2_MSG_USERAUTH_MIN, SSH2_MSG_USERAUTH_MAX, NULL); | |
+ | |
+ debug("Authentication succeeded (%s).", authctxt.method->name); | |
+} | |
+ | |
+void | |
+userauth(Authctxt *authctxt, char *authlist) | |
+{ | |
+ if (authctxt->method != NULL && authctxt->method->cleanup != NULL) | |
+ authctxt->method->cleanup(authctxt); | |
+ | |
+ free(authctxt->methoddata); | |
+ authctxt->methoddata = NULL; | |
+ if (authlist == NULL) { | |
+ authlist = authctxt->authlist; | |
+ } else { | |
+ free(authctxt->authlist); | |
+ authctxt->authlist = authlist; | |
+ } | |
+ for (;;) { | |
+ Authmethod *method = authmethod_get(authlist); | |
+ if (method == NULL) | |
+ fatal("Permission denied (%s).", authlist); | |
+ authctxt->method = method; | |
+ | |
+ /* reset the per method handler */ | |
+ dispatch_range(SSH2_MSG_USERAUTH_PER_METHOD_MIN, | |
+ SSH2_MSG_USERAUTH_PER_METHOD_MAX, NULL); | |
+ | |
+ /* and try new method */ | |
+ if (method->userauth(authctxt) != 0) { | |
+ debug2("we sent a %s packet, wait for reply", method->name); | |
+ break; | |
+ } else { | |
+ debug2("we did not send a packet, disable method"); | |
+ method->enabled = NULL; | |
+ } | |
+ } | |
+} | |
+ | |
+/* ARGSUSED */ | |
+void | |
+input_userauth_error(int type, u_int32_t seq, void *ctxt) | |
+{ | |
+ fatal("input_userauth_error: bad message during authentication: " | |
+ "type %d", type); | |
+} | |
+ | |
+/* ARGSUSED */ | |
+void | |
+input_userauth_banner(int type, u_int32_t seq, void *ctxt) | |
+{ | |
+ char *msg, *raw, *lang; | |
+ u_int len; | |
+ | |
+ debug3("input_userauth_banner"); | |
+ raw = packet_get_string(&len); | |
+ lang = packet_get_string(NULL); | |
+ if (len > 0 && options.log_level >= SYSLOG_LEVEL_INFO) { | |
+ if (len > 65536) | |
+ len = 65536; | |
+ msg = xmalloc(len * 4 + 1); /* max expansion from strnvis() */ | |
+ strnvis(msg, raw, len * 4 + 1, VIS_SAFE|VIS_OCTAL|VIS_NOSLASH); | |
+ fprintf(stderr, "%s", msg); | |
+ free(msg); | |
+ } | |
+ free(raw); | |
+ free(lang); | |
+} | |
+ | |
+/* ARGSUSED */ | |
+void | |
+input_userauth_success(int type, u_int32_t seq, void *ctxt) | |
+{ | |
+ Authctxt *authctxt = ctxt; | |
+ | |
+ if (authctxt == NULL) | |
+ fatal("input_userauth_success: no authentication context"); | |
+ free(authctxt->authlist); | |
+ authctxt->authlist = NULL; | |
+ if (authctxt->method != NULL && authctxt->method->cleanup != NULL) | |
+ authctxt->method->cleanup(authctxt); | |
+ free(authctxt->methoddata); | |
+ authctxt->methoddata = NULL; | |
+ authctxt->success = 1; /* break out */ | |
+} | |
+ | |
+void | |
+input_userauth_success_unexpected(int type, u_int32_t seq, void *ctxt) | |
+{ | |
+ Authctxt *authctxt = ctxt; | |
+ | |
+ if (authctxt == NULL) | |
+ fatal("%s: no authentication context", __func__); | |
+ | |
+ fatal("Unexpected authentication success during %s.", | |
+ authctxt->method->name); | |
+} | |
+ | |
+/* ARGSUSED */ | |
+void | |
+input_userauth_failure(int type, u_int32_t seq, void *ctxt) | |
+{ | |
+ Authctxt *authctxt = ctxt; | |
+ char *authlist = NULL; | |
+ int partial; | |
+ | |
+ if (authctxt == NULL) | |
+ fatal("input_userauth_failure: no authentication context"); | |
+ | |
+ authlist = packet_get_string(NULL); | |
+ partial = packet_get_char(); | |
+ packet_check_eom(); | |
+ | |
+ if (partial != 0) { | |
+ logit("Authenticated with partial success."); | |
+ /* reset state */ | |
+ pubkey_cleanup(authctxt); | |
+ pubkey_prepare(authctxt); | |
+ } | |
+ debug("Authentications that can continue: %s", authlist); | |
+ | |
+ userauth(authctxt, authlist); | |
+} | |
+ | |
+/* ARGSUSED */ | |
+void | |
+input_userauth_pk_ok(int type, u_int32_t seq, void *ctxt) | |
+{ | |
+ Authctxt *authctxt = ctxt; | |
+ Key *key = NULL; | |
+ Identity *id = NULL; | |
+ Buffer b; | |
+ int pktype, sent = 0; | |
+ u_int alen, blen; | |
+ char *pkalg, *fp; | |
+ u_char *pkblob; | |
+ | |
+ if (authctxt == NULL) | |
+ fatal("input_userauth_pk_ok: no authentication context"); | |
+ if (datafellows & SSH_BUG_PKOK) { | |
+ /* this is similar to SSH_BUG_PKAUTH */ | |
+ debug2("input_userauth_pk_ok: SSH_BUG_PKOK"); | |
+ pkblob = packet_get_string(&blen); | |
+ buffer_init(&b); | |
+ buffer_append(&b, pkblob, blen); | |
+ pkalg = buffer_get_string(&b, &alen); | |
+ buffer_free(&b); | |
+ } else { | |
+ pkalg = packet_get_string(&alen); | |
+ pkblob = packet_get_string(&blen); | |
+ } | |
+ packet_check_eom(); | |
+ | |
+ debug("Server accepts key: pkalg %s blen %u", pkalg, blen); | |
+ | |
+ if ((pktype = key_type_from_name(pkalg)) == KEY_UNSPEC) { | |
+ debug("unknown pkalg %s", pkalg); | |
+ goto done; | |
+ } | |
+ if ((key = key_from_blob(pkblob, blen)) == NULL) { | |
+ debug("no key from blob. pkalg %s", pkalg); | |
+ goto done; | |
+ } | |
+ if (key->type != pktype) { | |
+ error("input_userauth_pk_ok: type mismatch " | |
+ "for decoded key (received %d, expected %d)", | |
+ key->type, pktype); | |
+ goto done; | |
+ } | |
+ fp = key_fingerprint(key, SSH_FP_MD5, SSH_FP_HEX); | |
+ debug2("input_userauth_pk_ok: fp %s", fp); | |
+ free(fp); | |
+ | |
+ /* | |
+ * search keys in the reverse order, because last candidate has been | |
+ * moved to the end of the queue. this also avoids confusion by | |
+ * duplicate keys | |
+ */ | |
+ TAILQ_FOREACH_REVERSE(id, &authctxt->keys, idlist, next) { | |
+ if (key_equal(key, id->key)) { | |
+ sent = sign_and_send_pubkey(authctxt, id); | |
+ break; | |
+ } | |
+ } | |
+done: | |
+ if (key != NULL) | |
+ key_free(key); | |
+ free(pkalg); | |
+ free(pkblob); | |
+ | |
+ /* try another method if we did not send a packet */ | |
+ if (sent == 0) | |
+ userauth(authctxt, NULL); | |
+} | |
+ | |
+#ifdef GSSAPI | |
+int | |
+userauth_gssapi(Authctxt *authctxt) | |
+{ | |
+ Gssctxt *gssctxt = NULL; | |
+ static gss_OID_set gss_supported = NULL; | |
+ static u_int mech = 0; | |
+ OM_uint32 min; | |
+ int ok = 0; | |
+ | |
+ /* Try one GSSAPI method at a time, rather than sending them all at | |
+ * once. */ | |
+ | |
+ if (gss_supported == NULL) | |
+ gss_indicate_mechs(&min, &gss_supported); | |
+ | |
+ /* 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)) { | |
+ ok = 1; /* Mechanism works */ | |
+ } else { | |
+ mech++; | |
+ } | |
+ } | |
+ | |
+ if (!ok) | |
+ return 0; | |
+ | |
+ authctxt->methoddata=(void *)gssctxt; | |
+ | |
+ 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_int(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); | |
+ | |
+ packet_send(); | |
+ | |
+ dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_RESPONSE, &input_gssapi_response); | |
+ dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, &input_gssapi_token); | |
+ dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_ERROR, &input_gssapi_error); | |
+ dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK, &input_gssapi_errtok); | |
+ | |
+ mech++; /* Move along to next candidate */ | |
+ | |
+ return 1; | |
+} | |
+ | |
+static OM_uint32 | |
+process_gssapi_token(void *ctxt, gss_buffer_t recv_tok) | |
+{ | |
+ Authctxt *authctxt = ctxt; | |
+ Gssctxt *gssctxt = authctxt->methoddata; | |
+ gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER; | |
+ gss_buffer_desc mic = GSS_C_EMPTY_BUFFER; | |
+ gss_buffer_desc gssbuf; | |
+ OM_uint32 status, ms, flags; | |
+ Buffer b; | |
+ | |
+ status = ssh_gssapi_init_ctx(gssctxt, options.gss_deleg_creds, | |
+ recv_tok, &send_tok, &flags); | |
+ | |
+ if (send_tok.length > 0) { | |
+ if (GSS_ERROR(status)) | |
+ packet_start(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK); | |
+ else | |
+ packet_start(SSH2_MSG_USERAUTH_GSSAPI_TOKEN); | |
+ | |
+ packet_put_string(send_tok.value, send_tok.length); | |
+ packet_send(); | |
+ gss_release_buffer(&ms, &send_tok); | |
+ } | |
+ | |
+ if (status == GSS_S_COMPLETE) { | |
+ /* send either complete or MIC, depending on mechanism */ | |
+ if (!(flags & GSS_C_INTEG_FLAG)) { | |
+ packet_start(SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE); | |
+ packet_send(); | |
+ } else { | |
+ ssh_gssapi_buildmic(&b, authctxt->server_user, | |
+ authctxt->service, "gssapi-with-mic"); | |
+ | |
+ gssbuf.value = buffer_ptr(&b); | |
+ gssbuf.length = buffer_len(&b); | |
+ | |
+ status = ssh_gssapi_sign(gssctxt, &gssbuf, &mic); | |
+ | |
+ if (!GSS_ERROR(status)) { | |
+ packet_start(SSH2_MSG_USERAUTH_GSSAPI_MIC); | |
+ packet_put_string(mic.value, mic.length); | |
+ | |
+ packet_send(); | |
+ } | |
+ | |
+ buffer_free(&b); | |
+ gss_release_buffer(&ms, &mic); | |
+ } | |
+ } | |
+ | |
+ return status; | |
+} | |
+ | |
+/* ARGSUSED */ | |
+void | |
+input_gssapi_response(int type, u_int32_t plen, void *ctxt) | |
+{ | |
+ Authctxt *authctxt = ctxt; | |
+ Gssctxt *gssctxt; | |
+ int oidlen; | |
+ char *oidv; | |
+ | |
+ if (authctxt == NULL) | |
+ fatal("input_gssapi_response: no authentication context"); | |
+ gssctxt = authctxt->methoddata; | |
+ | |
+ /* Setup our OID */ | |
+ oidv = packet_get_string(&oidlen); | |
+ | |
+ if (oidlen <= 2 || | |
+ oidv[0] != SSH_GSS_OIDTYPE || | |
+ oidv[1] != oidlen - 2) { | |
+ free(oidv); | |
+ debug("Badly encoded mechanism OID received"); | |
+ userauth(authctxt, NULL); | |
+ return; | |
+ } | |
+ | |
+ if (!ssh_gssapi_check_oid(gssctxt, oidv + 2, oidlen - 2)) | |
+ fatal("Server returned different OID than expected"); | |
+ | |
+ packet_check_eom(); | |
+ | |
+ free(oidv); | |
+ | |
+ if (GSS_ERROR(process_gssapi_token(ctxt, GSS_C_NO_BUFFER))) { | |
+ /* Start again with next method on list */ | |
+ debug("Trying to start again"); | |
+ userauth(authctxt, NULL); | |
+ return; | |
+ } | |
+} | |
+ | |
+/* ARGSUSED */ | |
+void | |
+input_gssapi_token(int type, u_int32_t plen, void *ctxt) | |
+{ | |
+ Authctxt *authctxt = ctxt; | |
+ gss_buffer_desc recv_tok; | |
+ OM_uint32 status; | |
+ u_int slen; | |
+ | |
+ if (authctxt == NULL) | |
+ fatal("input_gssapi_response: no authentication context"); | |
+ | |
+ recv_tok.value = packet_get_string(&slen); | |
+ recv_tok.length = slen; /* safe typecast */ | |
+ | |
+ packet_check_eom(); | |
+ | |
+ status = process_gssapi_token(ctxt, &recv_tok); | |
+ | |
+ free(recv_tok.value); | |
+ | |
+ if (GSS_ERROR(status)) { | |
+ /* Start again with the next method in the list */ | |
+ userauth(authctxt, NULL); | |
+ return; | |
+ } | |
+} | |
+ | |
+/* ARGSUSED */ | |
+void | |
+input_gssapi_errtok(int type, u_int32_t plen, void *ctxt) | |
+{ | |
+ Authctxt *authctxt = ctxt; | |
+ Gssctxt *gssctxt; | |
+ gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER; | |
+ gss_buffer_desc recv_tok; | |
+ OM_uint32 ms; | |
+ u_int len; | |
+ | |
+ if (authctxt == NULL) | |
+ fatal("input_gssapi_response: no authentication context"); | |
+ gssctxt = authctxt->methoddata; | |
+ | |
+ recv_tok.value = packet_get_string(&len); | |
+ recv_tok.length = len; | |
+ | |
+ packet_check_eom(); | |
+ | |
+ /* Stick it into GSSAPI and see what it says */ | |
+ (void)ssh_gssapi_init_ctx(gssctxt, options.gss_deleg_creds, | |
+ &recv_tok, &send_tok, NULL); | |
+ | |
+ free(recv_tok.value); | |
+ gss_release_buffer(&ms, &send_tok); | |
+ | |
+ /* Server will be returning a failed packet after this one */ | |
+} | |
+ | |
+/* ARGSUSED */ | |
+void | |
+input_gssapi_error(int type, u_int32_t plen, void *ctxt) | |
+{ | |
+ char *msg; | |
+ char *lang; | |
+ | |
+ /* maj */(void)packet_get_int(); | |
+ /* min */(void)packet_get_int(); | |
+ msg=packet_get_string(NULL); | |
+ lang=packet_get_string(NULL); | |
+ | |
+ packet_check_eom(); | |
+ | |
+ debug("Server GSSAPI Error:\n%s", msg); | |
+ free(msg); | |
+ free(lang); | |
+} | |
+#endif /* GSSAPI */ | |
+ | |
+int | |
+userauth_none(Authctxt *authctxt) | |
+{ | |
+ /* initial userauth request */ | |
+ packet_start(SSH2_MSG_USERAUTH_REQUEST); | |
+ packet_put_cstring(authctxt->server_user); | |
+ packet_put_cstring(authctxt->service); | |
+ packet_put_cstring(authctxt->method->name); | |
+ packet_send(); | |
+ return 1; | |
+} | |
+ | |
+int | |
+userauth_passwd(Authctxt *authctxt) | |
+{ | |
+ static int attempt = 0; | |
+ char prompt[150]; | |
+ char *password; | |
+ const char *host = options.host_key_alias ? options.host_key_alias : | |
+ authctxt->host; | |
+ | |
+ if (attempt++ >= options.number_of_password_prompts) | |
+ return 0; | |
+ | |
+ if (attempt != 1) | |
+ error("Permission denied, please try again."); | |
+ | |
+ snprintf(prompt, sizeof(prompt), "%.30s@%.128s's password: ", | |
+ authctxt->server_user, host); | |
+ password = read_passphrase(prompt, 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_char(0); | |
+ packet_put_cstring(password); | |
+ explicit_bzero(password, strlen(password)); | |
+ free(password); | |
+ packet_add_padding(64); | |
+ packet_send(); | |
+ | |
+ dispatch_set(SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ, | |
+ &input_userauth_passwd_changereq); | |
+ | |
+ return 1; | |
+} | |
+ | |
+/* | |
+ * parse PASSWD_CHANGEREQ, prompt user and send SSH2_MSG_USERAUTH_REQUEST | |
+ */ | |
+/* ARGSUSED */ | |
+void | |
+input_userauth_passwd_changereq(int type, u_int32_t seqnr, void *ctxt) | |
+{ | |
+ Authctxt *authctxt = ctxt; | |
+ char *info, *lang, *password = NULL, *retype = NULL; | |
+ char prompt[150]; | |
+ const char *host = options.host_key_alias ? options.host_key_alias : | |
+ authctxt->host; | |
+ | |
+ debug2("input_userauth_passwd_changereq"); | |
+ | |
+ if (authctxt == NULL) | |
+ fatal("input_userauth_passwd_changereq: " | |
+ "no authentication context"); | |
+ | |
+ info = packet_get_string(NULL); | |
+ lang = packet_get_string(NULL); | |
+ if (strlen(info) > 0) | |
+ logit("%s", info); | |
+ free(info); | |
+ free(lang); | |
+ 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_char(1); /* additional info */ | |
+ snprintf(prompt, sizeof(prompt), | |
+ "Enter %.30s@%.128s's old password: ", | |
+ authctxt->server_user, host); | |
+ password = read_passphrase(prompt, 0); | |
+ packet_put_cstring(password); | |
+ explicit_bzero(password, strlen(password)); | |
+ free(password); | |
+ password = NULL; | |
+ while (password == NULL) { | |
+ snprintf(prompt, sizeof(prompt), | |
+ "Enter %.30s@%.128s's new password: ", | |
+ authctxt->server_user, host); | |
+ password = read_passphrase(prompt, RP_ALLOW_EOF); | |
+ if (password == NULL) { | |
+ /* bail out */ | |
+ return; | |
+ } | |
+ snprintf(prompt, sizeof(prompt), | |
+ "Retype %.30s@%.128s's new password: ", | |
+ authctxt->server_user, host); | |
+ retype = read_passphrase(prompt, 0); | |
+ if (strcmp(password, retype) != 0) { | |
+ explicit_bzero(password, strlen(password)); | |
+ free(password); | |
+ logit("Mismatch; try again, EOF to quit."); | |
+ password = NULL; | |
+ } | |
+ explicit_bzero(retype, strlen(retype)); | |
+ free(retype); | |
+ } | |
+ packet_put_cstring(password); | |
+ explicit_bzero(password, strlen(password)); | |
+ free(password); | |
+ packet_add_padding(64); | |
+ packet_send(); | |
+ | |
+ dispatch_set(SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ, | |
+ &input_userauth_passwd_changereq); | |
+} | |
+ | |
+static int | |
+identity_sign(Identity *id, u_char **sigp, u_int *lenp, | |
+ u_char *data, u_int datalen) | |
+{ | |
+ Key *prv; | |
+ int ret; | |
+ | |
+ /* the agent supports this key */ | |
+ if (id->ac) | |
+ return (ssh_agent_sign(id->ac, id->key, sigp, lenp, | |
+ data, datalen)); | |
+ /* | |
+ * we have already loaded the private key or | |
+ * the private key is stored in external hardware | |
+ */ | |
+ if (id->isprivate || (id->key->flags & SSHKEY_FLAG_EXT)) | |
+ return (key_sign(id->key, sigp, lenp, data, datalen)); | |
+ /* load the private key from the file */ | |
+ if ((prv = load_identity_file(id->filename, id->userprovided)) == NULL) | |
+ return (-1); | |
+ ret = key_sign(prv, sigp, lenp, data, datalen); | |
+ key_free(prv); | |
+ return (ret); | |
+} | |
+ | |
+static int | |
+sign_and_send_pubkey(Authctxt *authctxt, Identity *id) | |
+{ | |
+ Buffer b; | |
+ u_char *blob, *signature; | |
+ u_int bloblen, slen; | |
+ u_int skip = 0; | |
+ int ret = -1; | |
+ int have_sig = 1; | |
+ char *fp; | |
+ | |
+ fp = key_fingerprint(id->key, SSH_FP_MD5, SSH_FP_HEX); | |
+ debug3("sign_and_send_pubkey: %s %s", key_type(id->key), fp); | |
+ free(fp); | |
+ | |
+ if (key_to_blob(id->key, &blob, &bloblen) == 0) { | |
+ /* we cannot handle this key */ | |
+ debug3("sign_and_send_pubkey: cannot handle key"); | |
+ return 0; | |
+ } | |
+ /* data to be signed */ | |
+ buffer_init(&b); | |
+ if (datafellows & SSH_OLD_SESSIONID) { | |
+ buffer_append(&b, session_id2, session_id2_len); | |
+ skip = session_id2_len; | |
+ } else { | |
+ buffer_put_string(&b, session_id2, session_id2_len); | |
+ skip = buffer_len(&b); | |
+ } | |
+ buffer_put_char(&b, SSH2_MSG_USERAUTH_REQUEST); | |
+ buffer_put_cstring(&b, authctxt->server_user); | |
+ buffer_put_cstring(&b, | |
+ datafellows & SSH_BUG_PKSERVICE ? | |
+ "ssh-userauth" : | |
+ authctxt->service); | |
+ if (datafellows & SSH_BUG_PKAUTH) { | |
+ buffer_put_char(&b, have_sig); | |
+ } else { | |
+ buffer_put_cstring(&b, authctxt->method->name); | |
+ buffer_put_char(&b, have_sig); | |
+ buffer_put_cstring(&b, key_ssh_name(id->key)); | |
+ } | |
+ buffer_put_string(&b, blob, bloblen); | |
+ | |
+ /* generate signature */ | |
+ ret = identity_sign(id, &signature, &slen, | |
+ buffer_ptr(&b), buffer_len(&b)); | |
+ if (ret == -1) { | |
+ free(blob); | |
+ buffer_free(&b); | |
+ return 0; | |
+ } | |
+#ifdef DEBUG_PK | |
+ buffer_dump(&b); | |
+#endif | |
+ if (datafellows & SSH_BUG_PKSERVICE) { | |
+ buffer_clear(&b); | |
+ buffer_append(&b, session_id2, session_id2_len); | |
+ skip = session_id2_len; | |
+ buffer_put_char(&b, SSH2_MSG_USERAUTH_REQUEST); | |
+ buffer_put_cstring(&b, authctxt->server_user); | |
+ buffer_put_cstring(&b, authctxt->service); | |
+ buffer_put_cstring(&b, authctxt->method->name); | |
+ buffer_put_char(&b, have_sig); | |
+ if (!(datafellows & SSH_BUG_PKAUTH)) | |
+ buffer_put_cstring(&b, key_ssh_name(id->key)); | |
+ buffer_put_string(&b, blob, bloblen); | |
+ } | |
+ free(blob); | |
+ | |
+ /* append signature */ | |
+ buffer_put_string(&b, signature, slen); | |
+ free(signature); | |
+ | |
+ /* skip session id and packet type */ | |
+ if (buffer_len(&b) < skip + 1) | |
+ fatal("userauth_pubkey: internal error"); | |
+ buffer_consume(&b, skip + 1); | |
+ | |
+ /* put remaining data from buffer into packet */ | |
+ packet_start(SSH2_MSG_USERAUTH_REQUEST); | |
+ packet_put_raw(buffer_ptr(&b), buffer_len(&b)); | |
+ buffer_free(&b); | |
+ packet_send(); | |
+ | |
+ return 1; | |
+} | |
+ | |
+static int | |
+send_pubkey_test(Authctxt *authctxt, Identity *id) | |
+{ | |
+ u_char *blob; | |
+ u_int bloblen, have_sig = 0; | |
+ | |
+ debug3("send_pubkey_test"); | |
+ | |
+ if (key_to_blob(id->key, &blob, &bloblen) == 0) { | |
+ /* we cannot handle this key */ | |
+ debug3("send_pubkey_test: cannot handle key"); | |
+ return 0; | |
+ } | |
+ /* register callback for USERAUTH_PK_OK message */ | |
+ dispatch_set(SSH2_MSG_USERAUTH_PK_OK, &input_userauth_pk_ok); | |
+ | |
+ 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_char(have_sig); | |
+ if (!(datafellows & SSH_BUG_PKAUTH)) | |
+ packet_put_cstring(key_ssh_name(id->key)); | |
+ packet_put_string(blob, bloblen); | |
+ free(blob); | |
+ packet_send(); | |
+ return 1; | |
+} | |
+ | |
+static Key * | |
+load_identity_file(char *filename, int userprovided) | |
+{ | |
+ Key *private; | |
+ char prompt[300], *passphrase; | |
+ int perm_ok = 0, quit, i; | |
+ struct stat st; | |
+ | |
+ if (stat(filename, &st) < 0) { | |
+ (userprovided ? logit : debug3)("no such identity: %s: %s", | |
+ filename, strerror(errno)); | |
+ return NULL; | |
+ } | |
+ private = key_load_private_type(KEY_UNSPEC, filename, "", NULL, &perm_ok); | |
+ if (!perm_ok) { | |
+ if (private != NULL) | |
+ key_free(private); | |
+ return NULL; | |
+ } | |
+ if (private == NULL) { | |
+ if (options.batch_mode) | |
+ return NULL; | |
+ snprintf(prompt, sizeof prompt, | |
+ "Enter passphrase for key '%.100s': ", filename); | |
+ for (i = 0; i < options.number_of_password_prompts; i++) { | |
+ passphrase = read_passphrase(prompt, 0); | |
+ if (strcmp(passphrase, "") != 0) { | |
+ private = key_load_private_type(KEY_UNSPEC, | |
+ filename, passphrase, NULL, NULL); | |
+ quit = 0; | |
+ } else { | |
+ debug2("no passphrase given, try next key"); | |
+ quit = 1; | |
+ } | |
+ explicit_bzero(passphrase, strlen(passphrase)); | |
+ free(passphrase); | |
+ if (private != NULL || quit) | |
+ break; | |
+ debug2("bad passphrase given, try again..."); | |
+ } | |
+ } | |
+ return private; | |
+} | |
+ | |
+/* | |
+ * try keys in the following order: | |
+ * 1. agent keys that are found in the config file | |
+ * 2. other agent keys | |
+ * 3. keys that are only listed in the config file | |
+ */ | |
+static void | |
+pubkey_prepare(Authctxt *authctxt) | |
+{ | |
+ Identity *id, *id2, *tmp; | |
+ Idlist agent, files, *preferred; | |
+ Key *key; | |
+ AuthenticationConnection *ac; | |
+ char *comment; | |
+ int i, found; | |
+ | |
+ TAILQ_INIT(&agent); /* keys from the agent */ | |
+ TAILQ_INIT(&files); /* keys from the config file */ | |
+ preferred = &authctxt->keys; | |
+ TAILQ_INIT(preferred); /* preferred order of keys */ | |
+ | |
+ /* list of keys stored in the filesystem and PKCS#11 */ | |
+ for (i = 0; i < options.num_identity_files; i++) { | |
+ key = options.identity_keys[i]; | |
+ if (key && key->type == KEY_RSA1) | |
+ continue; | |
+ if (key && key->cert && key->cert->type != SSH2_CERT_TYPE_USER) | |
+ continue; | |
+ options.identity_keys[i] = NULL; | |
+ id = xcalloc(1, sizeof(*id)); | |
+ id->key = key; | |
+ id->filename = xstrdup(options.identity_files[i]); | |
+ id->userprovided = options.identity_file_userprovided[i]; | |
+ TAILQ_INSERT_TAIL(&files, id, next); | |
+ } | |
+ /* Prefer PKCS11 keys that are explicitly listed */ | |
+ TAILQ_FOREACH_SAFE(id, &files, next, tmp) { | |
+ if (id->key == NULL || (id->key->flags & SSHKEY_FLAG_EXT) == 0) | |
+ continue; | |
+ found = 0; | |
+ TAILQ_FOREACH(id2, &files, next) { | |
+ if (id2->key == NULL || | |
+ (id2->key->flags & SSHKEY_FLAG_EXT) == 0) | |
+ continue; | |
+ if (key_equal(id->key, id2->key)) { | |
+ TAILQ_REMOVE(&files, id, next); | |
+ TAILQ_INSERT_TAIL(preferred, id, next); | |
+ found = 1; | |
+ break; | |
+ } | |
+ } | |
+ /* If IdentitiesOnly set and key not found then don't use it */ | |
+ if (!found && options.identities_only) { | |
+ TAILQ_REMOVE(&files, id, next); | |
+ explicit_bzero(id, sizeof(*id)); | |
+ free(id); | |
+ } | |
+ } | |
+ /* list of keys supported by the agent */ | |
+ if ((ac = ssh_get_authentication_connection())) { | |
+ for (key = ssh_get_first_identity(ac, &comment, 2); | |
+ key != NULL; | |
+ key = ssh_get_next_identity(ac, &comment, 2)) { | |
+ found = 0; | |
+ TAILQ_FOREACH(id, &files, next) { | |
+ /* agent keys from the config file are preferred */ | |
+ if (key_equal(key, id->key)) { | |
+ key_free(key); | |
+ free(comment); | |
+ TAILQ_REMOVE(&files, id, next); | |
+ TAILQ_INSERT_TAIL(preferred, id, next); | |
+ id->ac = ac; | |
+ found = 1; | |
+ break; | |
+ } | |
+ } | |
+ if (!found && !options.identities_only) { | |
+ id = xcalloc(1, sizeof(*id)); | |
+ id->key = key; | |
+ id->filename = comment; | |
+ id->ac = ac; | |
+ TAILQ_INSERT_TAIL(&agent, id, next); | |
+ } | |
+ } | |
+ /* append remaining agent keys */ | |
+ for (id = TAILQ_FIRST(&agent); id; id = TAILQ_FIRST(&agent)) { | |
+ TAILQ_REMOVE(&agent, id, next); | |
+ TAILQ_INSERT_TAIL(preferred, id, next); | |
+ } | |
+ authctxt->agent = ac; | |
+ } | |
+ /* append remaining keys from the config file */ | |
+ for (id = TAILQ_FIRST(&files); id; id = TAILQ_FIRST(&files)) { | |
+ TAILQ_REMOVE(&files, id, next); | |
+ TAILQ_INSERT_TAIL(preferred, id, next); | |
+ } | |
+ TAILQ_FOREACH(id, preferred, next) { | |
+ debug2("key: %s (%p),%s", id->filename, id->key, | |
+ id->userprovided ? " explicit" : ""); | |
+ } | |
+} | |
+ | |
+static void | |
+pubkey_cleanup(Authctxt *authctxt) | |
+{ | |
+ Identity *id; | |
+ | |
+ if (authctxt->agent != NULL) | |
+ ssh_close_authentication_connection(authctxt->agent); | |
+ for (id = TAILQ_FIRST(&authctxt->keys); id; | |
+ id = TAILQ_FIRST(&authctxt->keys)) { | |
+ TAILQ_REMOVE(&authctxt->keys, id, next); | |
+ if (id->key) | |
+ key_free(id->key); | |
+ free(id->filename); | |
+ free(id); | |
+ } | |
+} | |
+ | |
+int | |
+userauth_pubkey(Authctxt *authctxt) | |
+{ | |
+ Identity *id; | |
+ int sent = 0; | |
+ | |
+ while ((id = TAILQ_FIRST(&authctxt->keys))) { | |
+ if (id->tried++) | |
+ return (0); | |
+ /* move key to the end of the queue */ | |
+ TAILQ_REMOVE(&authctxt->keys, id, next); | |
+ TAILQ_INSERT_TAIL(&authctxt->keys, id, next); | |
+ /* | |
+ * send a test message if we have the public key. for | |
+ * encrypted keys we cannot do this and have to load the | |
+ * private key instead | |
+ */ | |
+ if (id->key != NULL) { | |
+ if (key_type_plain(id->key->type) == KEY_RSA && | |
+ (datafellows & SSH_BUG_RSASIGMD5) != 0) { | |
+ debug("Skipped %s key %s for RSA/MD5 server", | |
+ key_type(id->key), id->filename); | |
+ } else if (id->key->type != KEY_RSA1) { | |
+ debug("Offering %s public key: %s", | |
+ key_type(id->key), id->filename); | |
+ sent = send_pubkey_test(authctxt, id); | |
+ } | |
+ } else { | |
+ debug("Trying private key: %s", id->filename); | |
+ id->key = load_identity_file(id->filename, | |
+ id->userprovided); | |
+ if (id->key != NULL) { | |
+ id->isprivate = 1; | |
+ if (key_type_plain(id->key->type) == KEY_RSA && | |
+ (datafellows & SSH_BUG_RSASIGMD5) != 0) { | |
+ debug("Skipped %s key %s for RSA/MD5 " | |
+ "server", key_type(id->key), | |
+ id->filename); | |
+ } else { | |
+ sent = sign_and_send_pubkey( | |
+ authctxt, id); | |
+ } | |
+ key_free(id->key); | |
+ id->key = NULL; | |
+ } | |
+ } | |
+ if (sent) | |
+ return (sent); | |
+ } | |
+ return (0); | |
+} | |
+ | |
+/* | |
+ * Send userauth request message specifying keyboard-interactive method. | |
+ */ | |
+int | |
+userauth_kbdint(Authctxt *authctxt) | |
+{ | |
+ static int attempt = 0; | |
+ | |
+ if (attempt++ >= options.number_of_password_prompts) | |
+ return 0; | |
+ /* disable if no SSH2_MSG_USERAUTH_INFO_REQUEST has been seen */ | |
+ if (attempt > 1 && !authctxt->info_req_seen) { | |
+ debug3("userauth_kbdint: disable: no info_req_seen"); | |
+ dispatch_set(SSH2_MSG_USERAUTH_INFO_REQUEST, NULL); | |
+ return 0; | |
+ } | |
+ | |
+ debug2("userauth_kbdint"); | |
+ 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_cstring(""); /* lang */ | |
+ packet_put_cstring(options.kbd_interactive_devices ? | |
+ options.kbd_interactive_devices : ""); | |
+ packet_send(); | |
+ | |
+ dispatch_set(SSH2_MSG_USERAUTH_INFO_REQUEST, &input_userauth_info_req); | |
+ return 1; | |
+} | |
+ | |
+/* | |
+ * parse INFO_REQUEST, prompt user and send INFO_RESPONSE | |
+ */ | |
+void | |
+input_userauth_info_req(int type, u_int32_t seq, void *ctxt) | |
+{ | |
+ Authctxt *authctxt = ctxt; | |
+ char *name, *inst, *lang, *prompt, *response; | |
+ u_int num_prompts, i; | |
+ int echo = 0; | |
+ | |
+ debug2("input_userauth_info_req"); | |
+ | |
+ if (authctxt == NULL) | |
+ fatal("input_userauth_info_req: no authentication context"); | |
+ | |
+ authctxt->info_req_seen = 1; | |
+ | |
+ name = packet_get_string(NULL); | |
+ inst = packet_get_string(NULL); | |
+ lang = packet_get_string(NULL); | |
+ if (strlen(name) > 0) | |
+ logit("%s", name); | |
+ if (strlen(inst) > 0) | |
+ logit("%s", inst); | |
+ free(name); | |
+ free(inst); | |
+ free(lang); | |
+ | |
+ num_prompts = packet_get_int(); | |
+ /* | |
+ * Begin to build info response packet based on prompts requested. | |
+ * We commit to providing the correct number of responses, so if | |
+ * further on we run into a problem that prevents this, we have to | |
+ * be sure and clean this up and send a correct error response. | |
+ */ | |
+ packet_start(SSH2_MSG_USERAUTH_INFO_RESPONSE); | |
+ packet_put_int(num_prompts); | |
+ | |
+ debug2("input_userauth_info_req: num_prompts %d", num_prompts); | |
+ for (i = 0; i < num_prompts; i++) { | |
+ prompt = packet_get_string(NULL); | |
+ echo = packet_get_char(); | |
+ | |
+ response = read_passphrase(prompt, echo ? RP_ECHO : 0); | |
+ | |
+ packet_put_cstring(response); | |
+ explicit_bzero(response, strlen(response)); | |
+ free(response); | |
+ free(prompt); | |
+ } | |
+ packet_check_eom(); /* done with parsing incoming message. */ | |
+ | |
+ packet_add_padding(64); | |
+ packet_send(); | |
+} | |
+ | |
+static int | |
+ssh_keysign(Key *key, u_char **sigp, u_int *lenp, | |
+ u_char *data, u_int datalen) | |
+{ | |
+ Buffer b; | |
+ struct stat st; | |
+ pid_t pid; | |
+ int to[2], from[2], status, version = 2; | |
+ | |
+ debug2("ssh_keysign called"); | |
+ | |
+ if (stat(_PATH_SSH_KEY_SIGN, &st) < 0) { | |
+ error("ssh_keysign: not installed: %s", strerror(errno)); | |
+ return -1; | |
+ } | |
+ if (fflush(stdout) != 0) | |
+ error("ssh_keysign: fflush: %s", strerror(errno)); | |
+ if (pipe(to) < 0) { | |
+ error("ssh_keysign: pipe: %s", strerror(errno)); | |
+ return -1; | |
+ } | |
+ if (pipe(from) < 0) { | |
+ error("ssh_keysign: pipe: %s", strerror(errno)); | |
+ return -1; | |
+ } | |
+ if ((pid = fork()) < 0) { | |
+ error("ssh_keysign: fork: %s", strerror(errno)); | |
+ return -1; | |
+ } | |
+ if (pid == 0) { | |
+ /* keep the socket on exec */ | |
+ fcntl(packet_get_connection_in(), F_SETFD, 0); | |
+ permanently_drop_suid(getuid()); | |
+ close(from[0]); | |
+ if (dup2(from[1], STDOUT_FILENO) < 0) | |
+ fatal("ssh_keysign: dup2: %s", strerror(errno)); | |
+ close(to[1]); | |
+ if (dup2(to[0], STDIN_FILENO) < 0) | |
+ fatal("ssh_keysign: dup2: %s", strerror(errno)); | |
+ close(from[1]); | |
+ close(to[0]); | |
+ execl(_PATH_SSH_KEY_SIGN, _PATH_SSH_KEY_SIGN, (char *) 0); | |
+ fatal("ssh_keysign: exec(%s): %s", _PATH_SSH_KEY_SIGN, | |
+ strerror(errno)); | |
+ } | |
+ close(from[1]); | |
+ close(to[0]); | |
+ | |
+ buffer_init(&b); | |
+ buffer_put_int(&b, packet_get_connection_in()); /* send # of socket */ | |
+ buffer_put_string(&b, data, datalen); | |
+ if (ssh_msg_send(to[1], version, &b) == -1) | |
+ fatal("ssh_keysign: couldn't send request"); | |
+ | |
+ if (ssh_msg_recv(from[0], &b) < 0) { | |
+ error("ssh_keysign: no reply"); | |
+ buffer_free(&b); | |
+ return -1; | |
+ } | |
+ close(from[0]); | |
+ close(to[1]); | |
+ | |
+ while (waitpid(pid, &status, 0) < 0) | |
+ if (errno != EINTR) | |
+ break; | |
+ | |
+ if (buffer_get_char(&b) != version) { | |
+ error("ssh_keysign: bad version"); | |
+ buffer_free(&b); | |
+ return -1; | |
+ } | |
+ *sigp = buffer_get_string(&b, lenp); | |
+ buffer_free(&b); | |
+ | |
+ return 0; | |
+} | |
+ | |
+int | |
+userauth_hostbased(Authctxt *authctxt) | |
+{ | |
+ Key *private = NULL; | |
+ Sensitive *sensitive = authctxt->sensitive; | |
+ Buffer b; | |
+ u_char *signature, *blob; | |
+ char *chost, *pkalg, *p; | |
+ const char *service; | |
+ u_int blen, slen; | |
+ int ok, i, found = 0; | |
+ | |
+ /* check for a useful key */ | |
+ for (i = 0; i < sensitive->nkeys; i++) { | |
+ private = sensitive->keys[i]; | |
+ if (private && private->type != KEY_RSA1) { | |
+ found = 1; | |
+ /* we take and free the key */ | |
+ sensitive->keys[i] = NULL; | |
+ break; | |
+ } | |
+ } | |
+ if (!found) { | |
+ debug("No more client hostkeys for hostbased authentication."); | |
+ return 0; | |
+ } | |
+ if (key_to_blob(private, &blob, &blen) == 0) { | |
+ key_free(private); | |
+ return 0; | |
+ } | |
+ /* figure out a name for the client host */ | |
+ p = get_local_name(packet_get_connection_in()); | |
+ if (p == NULL) { | |
+ error("userauth_hostbased: cannot get local ipaddr/name"); | |
+ key_free(private); | |
+ free(blob); | |
+ return 0; | |
+ } | |
+ xasprintf(&chost, "%s.", p); | |
+ debug2("userauth_hostbased: chost %s", chost); | |
+ free(p); | |
+ | |
+ service = datafellows & SSH_BUG_HBSERVICE ? "ssh-userauth" : | |
+ authctxt->service; | |
+ pkalg = xstrdup(key_ssh_name(private)); | |
+ buffer_init(&b); | |
+ /* construct data */ | |
+ buffer_put_string(&b, session_id2, session_id2_len); | |
+ buffer_put_char(&b, SSH2_MSG_USERAUTH_REQUEST); | |
+ buffer_put_cstring(&b, authctxt->server_user); | |
+ buffer_put_cstring(&b, service); | |
+ buffer_put_cstring(&b, authctxt->method->name); | |
+ buffer_put_cstring(&b, pkalg); | |
+ buffer_put_string(&b, blob, blen); | |
+ buffer_put_cstring(&b, chost); | |
+ buffer_put_cstring(&b, authctxt->local_user); | |
+#ifdef DEBUG_PK | |
+ buffer_dump(&b); | |
+#endif | |
+ if (sensitive->external_keysign) | |
+ ok = ssh_keysign(private, &signature, &slen, | |
+ buffer_ptr(&b), buffer_len(&b)); | |
+ else | |
+ ok = key_sign(private, &signature, &slen, | |
+ buffer_ptr(&b), buffer_len(&b)); | |
+ key_free(private); | |
+ buffer_free(&b); | |
+ if (ok != 0) { | |
+ error("key_sign failed"); | |
+ free(chost); | |
+ free(pkalg); | |
+ free(blob); | |
+ 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_cstring(pkalg); | |
+ packet_put_string(blob, blen); | |
+ packet_put_cstring(chost); | |
+ packet_put_cstring(authctxt->local_user); | |
+ packet_put_string(signature, slen); | |
+ explicit_bzero(signature, slen); | |
+ free(signature); | |
+ free(chost); | |
+ free(pkalg); | |
+ free(blob); | |
+ | |
+ packet_send(); | |
+ return 1; | |
+} | |
+ | |
+/* find auth method */ | |
+ | |
+/* | |
+ * given auth method name, if configurable options permit this method fill | |
+ * in auth_ident field and return true, otherwise return false. | |
+ */ | |
+static int | |
+authmethod_is_enabled(Authmethod *method) | |
+{ | |
+ if (method == NULL) | |
+ return 0; | |
+ /* return false if options indicate this method is disabled */ | |
+ if (method->enabled == NULL || *method->enabled == 0) | |
+ return 0; | |
+ /* return false if batch mode is enabled but method needs interactive mode */ | |
+ if (method->batch_flag != NULL && *method->batch_flag != 0) | |
+ return 0; | |
+ return 1; | |
+} | |
+ | |
+static Authmethod * | |
+authmethod_lookup(const char *name) | |
+{ | |
+ Authmethod *method = NULL; | |
+ if (name != NULL) | |
+ for (method = authmethods; method->name != NULL; method++) | |
+ if (strcmp(name, method->name) == 0) | |
+ return method; | |
+ debug2("Unrecognized authentication method name: %s", name ? name : "NULL"); | |
+ return NULL; | |
+} | |
+ | |
+/* XXX internal state */ | |
+static Authmethod *current = NULL; | |
+static char *supported = NULL; | |
+static char *preferred = NULL; | |
+ | |
+/* | |
+ * Given the authentication method list sent by the server, return the | |
+ * next method we should try. If the server initially sends a nil list, | |
+ * use a built-in default list. | |
+ */ | |
+static Authmethod * | |
+authmethod_get(char *authlist) | |
+{ | |
+ char *name = NULL; | |
+ u_int next; | |
+ | |
+ /* Use a suitable default if we're passed a nil list. */ | |
+ if (authlist == NULL || strlen(authlist) == 0) | |
+ authlist = options.preferred_authentications; | |
+ | |
+ if (supported == NULL || strcmp(authlist, supported) != 0) { | |
+ debug3("start over, passed a different list %s", authlist); | |
+ free(supported); | |
+ supported = xstrdup(authlist); | |
+ preferred = options.preferred_authentications; | |
+ debug3("preferred %s", preferred); | |
+ current = NULL; | |
+ } else if (current != NULL && authmethod_is_enabled(current)) | |
+ return current; | |
+ | |
+ for (;;) { | |
+ if ((name = match_list(preferred, supported, &next)) == NULL) { | |
+ debug("No more authentication methods to try."); | |
+ current = NULL; | |
+ return NULL; | |
+ } | |
+ preferred += next; | |
+ debug3("authmethod_lookup %s", name); | |
+ debug3("remaining preferred: %s", preferred); | |
+ if ((current = authmethod_lookup(name)) != NULL && | |
+ authmethod_is_enabled(current)) { | |
+ debug3("authmethod_is_enabled %s", name); | |
+ debug("Next authentication method: %s", name); | |
+ free(name); | |
+ return current; | |
+ } | |
+ free(name); | |
+ } | |
+} | |
+ | |
+static char * | |
+authmethods_get(void) | |
+{ | |
+ Authmethod *method = NULL; | |
+ Buffer b; | |
+ char *list; | |
+ | |
+ buffer_init(&b); | |
+ for (method = authmethods; method->name != NULL; method++) { | |
+ if (authmethod_is_enabled(method)) { | |
+ if (buffer_len(&b) > 0) | |
+ buffer_append(&b, ",", 1); | |
+ buffer_append(&b, method->name, strlen(method->name)); | |
+ } | |
+ } | |
+ buffer_append(&b, "\0", 1); | |
+ list = xstrdup(buffer_ptr(&b)); | |
+ buffer_free(&b); | |
+ return list; | |
+} | |
+ | |
diff -urp --new-file openssh-6.7p1/sshd.8 openssh-6.7p1.patched/sshd.8 | |
--- openssh-6.7p1/sshd.8 2014-07-03 16:00:04.000000000 -0700 | |
+++ openssh-6.7p1.patched/sshd.8 2014-10-11 21:18:37.000000000 -0700 | |
@@ -954,10 +954,7 @@ The content of this file is not sensitiv | |
.Xr ssh-keygen 1 , | |
.Xr ssh-keyscan 1 , | |
.Xr chroot 2 , | |
-.Xr login.conf 5 , | |
-.Xr moduli 5 , | |
.Xr sshd_config 5 , | |
-.Xr inetd 8 , | |
.Xr sftp-server 8 | |
.Sh AUTHORS | |
OpenSSH is a derivative of the original and free | |
diff -urp --new-file openssh-6.7p1/sshd.8.orig openssh-6.7p1.patched/sshd.8.orig | |
--- openssh-6.7p1/sshd.8.orig 1969-12-31 16:00:00.000000000 -0800 | |
+++ openssh-6.7p1.patched/sshd.8.orig 2014-07-03 16:00:04.000000000 -0700 | |
@@ -0,0 +1,972 @@ | |
+.\" | |
+.\" Author: Tatu Ylonen <ylo@cs.hut.fi> | |
+.\" Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | |
+.\" All rights reserved | |
+.\" | |
+.\" As far as I am concerned, the code I have written for this software | |
+.\" can be used freely for any purpose. Any derived versions of this | |
+.\" software must be clearly marked as such, and if the derived work is | |
+.\" incompatible with the protocol description in the RFC file, it must be | |
+.\" called by a name other than "ssh" or "Secure Shell". | |
+.\" | |
+.\" Copyright (c) 1999,2000 Markus Friedl. All rights reserved. | |
+.\" Copyright (c) 1999 Aaron Campbell. All rights reserved. | |
+.\" Copyright (c) 1999 Theo de Raadt. 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. | |
+.\" | |
+.\" $OpenBSD: sshd.8,v 1.276 2014/07/03 22:40:43 djm Exp $ | |
+.Dd $Mdocdate: July 3 2014 $ | |
+.Dt SSHD 8 | |
+.Os | |
+.Sh NAME | |
+.Nm sshd | |
+.Nd OpenSSH SSH daemon | |
+.Sh SYNOPSIS | |
+.Nm sshd | |
+.Bk -words | |
+.Op Fl 46DdeiqTt | |
+.Op Fl b Ar bits | |
+.Op Fl C Ar connection_spec | |
+.Op Fl c Ar host_certificate_file | |
+.Op Fl E Ar log_file | |
+.Op Fl f Ar config_file | |
+.Op Fl g Ar login_grace_time | |
+.Op Fl h Ar host_key_file | |
+.Op Fl k Ar key_gen_time | |
+.Op Fl o Ar option | |
+.Op Fl p Ar port | |
+.Op Fl u Ar len | |
+.Ek | |
+.Sh DESCRIPTION | |
+.Nm | |
+(OpenSSH Daemon) is the daemon program for | |
+.Xr ssh 1 . | |
+Together these programs replace rlogin and rsh, | |
+and provide secure encrypted communications between two untrusted hosts | |
+over an insecure network. | |
+.Pp | |
+.Nm | |
+listens for connections from clients. | |
+It is normally started at boot from | |
+.Pa /etc/rc . | |
+It forks a new | |
+daemon for each incoming connection. | |
+The forked daemons handle | |
+key exchange, encryption, authentication, command execution, | |
+and data exchange. | |
+.Pp | |
+.Nm | |
+can be configured using command-line options or a configuration file | |
+(by default | |
+.Xr sshd_config 5 ) ; | |
+command-line options override values specified in the | |
+configuration file. | |
+.Nm | |
+rereads its configuration file when it receives a hangup signal, | |
+.Dv SIGHUP , | |
+by executing itself with the name and options it was started with, e.g.\& | |
+.Pa /usr/sbin/sshd . | |
+.Pp | |
+The options are as follows: | |
+.Bl -tag -width Ds | |
+.It Fl 4 | |
+Forces | |
+.Nm | |
+to use IPv4 addresses only. | |
+.It Fl 6 | |
+Forces | |
+.Nm | |
+to use IPv6 addresses only. | |
+.It Fl b Ar bits | |
+Specifies the number of bits in the ephemeral protocol version 1 | |
+server key (default 1024). | |
+.It Fl C Ar connection_spec | |
+Specify the connection parameters to use for the | |
+.Fl T | |
+extended test mode. | |
+If provided, any | |
+.Cm Match | |
+directives in the configuration file | |
+that would apply to the specified user, host, and address will be set before | |
+the configuration is written to standard output. | |
+The connection parameters are supplied as keyword=value pairs. | |
+The keywords are | |
+.Dq user , | |
+.Dq host , | |
+.Dq laddr , | |
+.Dq lport , | |
+and | |
+.Dq addr . | |
+All are required and may be supplied in any order, either with multiple | |
+.Fl C | |
+options or as a comma-separated list. | |
+.It Fl c Ar host_certificate_file | |
+Specifies a path to a certificate file to identify | |
+.Nm | |
+during key exchange. | |
+The certificate file must match a host key file specified using the | |
+.Fl h | |
+option or the | |
+.Cm HostKey | |
+configuration directive. | |
+.It Fl D | |
+When this option is specified, | |
+.Nm | |
+will not detach and does not become a daemon. | |
+This allows easy monitoring of | |
+.Nm sshd . | |
+.It Fl d | |
+Debug mode. | |
+The server sends verbose debug output to standard error, | |
+and does not put itself in the background. | |
+The server also will not fork and will only process one connection. | |
+This option is only intended for debugging for the server. | |
+Multiple | |
+.Fl d | |
+options increase the debugging level. | |
+Maximum is 3. | |
+.It Fl E Ar log_file | |
+Append debug logs to | |
+.Ar log_file | |
+instead of the system log. | |
+.It Fl e | |
+Write debug logs to standard error instead of the system log. | |
+.It Fl f Ar config_file | |
+Specifies the name of the configuration file. | |
+The default is | |
+.Pa /etc/ssh/sshd_config . | |
+.Nm | |
+refuses to start if there is no configuration file. | |
+.It Fl g Ar login_grace_time | |
+Gives the grace time for clients to authenticate themselves (default | |
+120 seconds). | |
+If the client fails to authenticate the user within | |
+this many seconds, the server disconnects and exits. | |
+A value of zero indicates no limit. | |
+.It Fl h Ar host_key_file | |
+Specifies a file from which a host key is read. | |
+This option must be given if | |
+.Nm | |
+is not run as root (as the normal | |
+host key files are normally not readable by anyone but root). | |
+The default is | |
+.Pa /etc/ssh/ssh_host_key | |
+for protocol version 1, and | |
+.Pa /etc/ssh/ssh_host_dsa_key , | |
+.Pa /etc/ssh/ssh_host_ecdsa_key . | |
+.Pa /etc/ssh/ssh_host_ed25519_key | |
+and | |
+.Pa /etc/ssh/ssh_host_rsa_key | |
+for protocol version 2. | |
+It is possible to have multiple host key files for | |
+the different protocol versions and host key algorithms. | |
+.It Fl i | |
+Specifies that | |
+.Nm | |
+is being run from | |
+.Xr inetd 8 . | |
+.Nm | |
+is normally not run | |
+from inetd because it needs to generate the server key before it can | |
+respond to the client, and this may take tens of seconds. | |
+Clients would have to wait too long if the key was regenerated every time. | |
+However, with small key sizes (e.g. 512) using | |
+.Nm | |
+from inetd may | |
+be feasible. | |
+.It Fl k Ar key_gen_time | |
+Specifies how often the ephemeral protocol version 1 server key is | |
+regenerated (default 3600 seconds, or one hour). | |
+The motivation for regenerating the key fairly | |
+often is that the key is not stored anywhere, and after about an hour | |
+it becomes impossible to recover the key for decrypting intercepted | |
+communications even if the machine is cracked into or physically | |
+seized. | |
+A value of zero indicates that the key will never be regenerated. | |
+.It Fl o Ar option | |
+Can be used to give options in the format used in the configuration file. | |
+This is useful for specifying options for which there is no separate | |
+command-line flag. | |
+For full details of the options, and their values, see | |
+.Xr sshd_config 5 . | |
+.It Fl p Ar port | |
+Specifies the port on which the server listens for connections | |
+(default 22). | |
+Multiple port options are permitted. | |
+Ports specified in the configuration file with the | |
+.Cm Port | |
+option are ignored when a command-line port is specified. | |
+Ports specified using the | |
+.Cm ListenAddress | |
+option override command-line ports. | |
+.It Fl q | |
+Quiet mode. | |
+Nothing is sent to the system log. | |
+Normally the beginning, | |
+authentication, and termination of each connection is logged. | |
+.It Fl T | |
+Extended test mode. | |
+Check the validity of the configuration file, output the effective configuration | |
+to stdout and then exit. | |
+Optionally, | |
+.Cm Match | |
+rules may be applied by specifying the connection parameters using one or more | |
+.Fl C | |
+options. | |
+.It Fl t | |
+Test mode. | |
+Only check the validity of the configuration file and sanity of the keys. | |
+This is useful for updating | |
+.Nm | |
+reliably as configuration options may change. | |
+.It Fl u Ar len | |
+This option is used to specify the size of the field | |
+in the | |
+.Li utmp | |
+structure that holds the remote host name. | |
+If the resolved host name is longer than | |
+.Ar len , | |
+the dotted decimal value will be used instead. | |
+This allows hosts with very long host names that | |
+overflow this field to still be uniquely identified. | |
+Specifying | |
+.Fl u0 | |
+indicates that only dotted decimal addresses | |
+should be put into the | |
+.Pa utmp | |
+file. | |
+.Fl u0 | |
+may also be used to prevent | |
+.Nm | |
+from making DNS requests unless the authentication | |
+mechanism or configuration requires it. | |
+Authentication mechanisms that may require DNS include | |
+.Cm RhostsRSAAuthentication , | |
+.Cm HostbasedAuthentication , | |
+and using a | |
+.Cm from="pattern-list" | |
+option in a key file. | |
+Configuration options that require DNS include using a | |
+USER@HOST pattern in | |
+.Cm AllowUsers | |
+or | |
+.Cm DenyUsers . | |
+.El | |
+.Sh AUTHENTICATION | |
+The OpenSSH SSH daemon supports SSH protocols 1 and 2. | |
+The default is to use protocol 2 only, | |
+though this can be changed via the | |
+.Cm Protocol | |
+option in | |
+.Xr sshd_config 5 . | |
+Protocol 2 supports DSA, ECDSA, ED25519 and RSA keys; | |
+protocol 1 only supports RSA keys. | |
+For both protocols, | |
+each host has a host-specific key, | |
+normally 2048 bits, | |
+used to identify the host. | |
+.Pp | |
+Forward security for protocol 1 is provided through | |
+an additional server key, | |
+normally 768 bits, | |
+generated when the server starts. | |
+This key is normally regenerated every hour if it has been used, and | |
+is never stored on disk. | |
+Whenever a client connects, the daemon responds with its public | |
+host and server keys. | |
+The client compares the | |
+RSA host key against its own database to verify that it has not changed. | |
+The client then generates a 256-bit random number. | |
+It encrypts this | |
+random number using both the host key and the server key, and sends | |
+the encrypted number to the server. | |
+Both sides then use this | |
+random number as a session key which is used to encrypt all further | |
+communications in the session. | |
+The rest of the session is encrypted | |
+using a conventional cipher, currently Blowfish or 3DES, with 3DES | |
+being used by default. | |
+The client selects the encryption algorithm | |
+to use from those offered by the server. | |
+.Pp | |
+For protocol 2, | |
+forward security is provided through a Diffie-Hellman key agreement. | |
+This key agreement results in a shared session key. | |
+The rest of the session is encrypted using a symmetric cipher, currently | |
+128-bit AES, Blowfish, 3DES, CAST128, Arcfour, 192-bit AES, or 256-bit AES. | |
+The client selects the encryption algorithm | |
+to use from those offered by the server. | |
+Additionally, session integrity is provided | |
+through a cryptographic message authentication code | |
+(hmac-md5, hmac-sha1, umac-64, umac-128, hmac-ripemd160, | |
+hmac-sha2-256 or hmac-sha2-512). | |
+.Pp | |
+Finally, the server and the client enter an authentication dialog. | |
+The client tries to authenticate itself using | |
+host-based authentication, | |
+public key authentication, | |
+challenge-response authentication, | |
+or password authentication. | |
+.Pp | |
+Regardless of the authentication type, the account is checked to | |
+ensure that it is accessible. An account is not accessible if it is | |
+locked, listed in | |
+.Cm DenyUsers | |
+or its group is listed in | |
+.Cm DenyGroups | |
+\&. The definition of a locked account is system dependant. Some platforms | |
+have their own account database (eg AIX) and some modify the passwd field ( | |
+.Ql \&*LK\&* | |
+on Solaris and UnixWare, | |
+.Ql \&* | |
+on HP-UX, containing | |
+.Ql Nologin | |
+on Tru64, | |
+a leading | |
+.Ql \&*LOCKED\&* | |
+on FreeBSD and a leading | |
+.Ql \&! | |
+on most Linuxes). | |
+If there is a requirement to disable password authentication | |
+for the account while allowing still public-key, then the passwd field | |
+should be set to something other than these values (eg | |
+.Ql NP | |
+or | |
+.Ql \&*NP\&* | |
+). | |
+.Pp | |
+If the client successfully authenticates itself, a dialog for | |
+preparing the session is entered. | |
+At this time the client may request | |
+things like allocating a pseudo-tty, forwarding X11 connections, | |
+forwarding TCP connections, or forwarding the authentication agent | |
+connection over the secure channel. | |
+.Pp | |
+After this, the client either requests a shell or execution of a command. | |
+The sides then enter session mode. | |
+In this mode, either side may send | |
+data at any time, and such data is forwarded to/from the shell or | |
+command on the server side, and the user terminal in the client side. | |
+.Pp | |
+When the user program terminates and all forwarded X11 and other | |
+connections have been closed, the server sends command exit status to | |
+the client, and both sides exit. | |
+.Sh LOGIN PROCESS | |
+When a user successfully logs in, | |
+.Nm | |
+does the following: | |
+.Bl -enum -offset indent | |
+.It | |
+If the login is on a tty, and no command has been specified, | |
+prints last login time and | |
+.Pa /etc/motd | |
+(unless prevented in the configuration file or by | |
+.Pa ~/.hushlogin ; | |
+see the | |
+.Sx FILES | |
+section). | |
+.It | |
+If the login is on a tty, records login time. | |
+.It | |
+Checks | |
+.Pa /etc/nologin ; | |
+if it exists, prints contents and quits | |
+(unless root). | |
+.It | |
+Changes to run with normal user privileges. | |
+.It | |
+Sets up basic environment. | |
+.It | |
+Reads the file | |
+.Pa ~/.ssh/environment , | |
+if it exists, and users are allowed to change their environment. | |
+See the | |
+.Cm PermitUserEnvironment | |
+option in | |
+.Xr sshd_config 5 . | |
+.It | |
+Changes to user's home directory. | |
+.It | |
+If | |
+.Pa ~/.ssh/rc | |
+exists and the | |
+.Xr sshd_config 5 | |
+.Cm PermitUserRC | |
+option is set, runs it; else if | |
+.Pa /etc/ssh/sshrc | |
+exists, runs | |
+it; otherwise runs xauth. | |
+The | |
+.Dq rc | |
+files are given the X11 | |
+authentication protocol and cookie in standard input. | |
+See | |
+.Sx SSHRC , | |
+below. | |
+.It | |
+Runs user's shell or command. | |
+.El | |
+.Sh SSHRC | |
+If the file | |
+.Pa ~/.ssh/rc | |
+exists, | |
+.Xr sh 1 | |
+runs it after reading the | |
+environment files but before starting the user's shell or command. | |
+It must not produce any output on stdout; stderr must be used | |
+instead. | |
+If X11 forwarding is in use, it will receive the "proto cookie" pair in | |
+its standard input (and | |
+.Ev DISPLAY | |
+in its environment). | |
+The script must call | |
+.Xr xauth 1 | |
+because | |
+.Nm | |
+will not run xauth automatically to add X11 cookies. | |
+.Pp | |
+The primary purpose of this file is to run any initialization routines | |
+which may be needed before the user's home directory becomes | |
+accessible; AFS is a particular example of such an environment. | |
+.Pp | |
+This file will probably contain some initialization code followed by | |
+something similar to: | |
+.Bd -literal -offset 3n | |
+if read proto cookie && [ -n "$DISPLAY" ]; then | |
+ if [ `echo $DISPLAY | cut -c1-10` = 'localhost:' ]; then | |
+ # X11UseLocalhost=yes | |
+ echo add unix:`echo $DISPLAY | | |
+ cut -c11-` $proto $cookie | |
+ else | |
+ # X11UseLocalhost=no | |
+ echo add $DISPLAY $proto $cookie | |
+ fi | xauth -q - | |
+fi | |
+.Ed | |
+.Pp | |
+If this file does not exist, | |
+.Pa /etc/ssh/sshrc | |
+is run, and if that | |
+does not exist either, xauth is used to add the cookie. | |
+.Sh AUTHORIZED_KEYS FILE FORMAT | |
+.Cm AuthorizedKeysFile | |
+specifies the files containing public keys for | |
+public key authentication; | |
+if none is specified, the default is | |
+.Pa ~/.ssh/authorized_keys | |
+and | |
+.Pa ~/.ssh/authorized_keys2 . | |
+Each line of the file contains one | |
+key (empty lines and lines starting with a | |
+.Ql # | |
+are ignored as | |
+comments). | |
+Protocol 1 public keys consist of the following space-separated fields: | |
+options, bits, exponent, modulus, comment. | |
+Protocol 2 public key consist of: | |
+options, keytype, base64-encoded key, comment. | |
+The options field is optional; | |
+its presence is determined by whether the line starts | |
+with a number or not (the options field never starts with a number). | |
+The bits, exponent, modulus, and comment fields give the RSA key for | |
+protocol version 1; the | |
+comment field is not used for anything (but may be convenient for the | |
+user to identify the key). | |
+For protocol version 2 the keytype is | |
+.Dq ecdsa-sha2-nistp256 , | |
+.Dq ecdsa-sha2-nistp384 , | |
+.Dq ecdsa-sha2-nistp521 , | |
+.Dq ssh-ed25519 , | |
+.Dq ssh-dss | |
+or | |
+.Dq ssh-rsa . | |
+.Pp | |
+Note that lines in this file are usually several hundred bytes long | |
+(because of the size of the public key encoding) up to a limit of | |
+8 kilobytes, which permits DSA keys up to 8 kilobits and RSA | |
+keys up to 16 kilobits. | |
+You don't want to type them in; instead, copy the | |
+.Pa identity.pub , | |
+.Pa id_dsa.pub , | |
+.Pa id_ecdsa.pub , | |
+.Pa id_ed25519.pub , | |
+or the | |
+.Pa id_rsa.pub | |
+file and edit it. | |
+.Pp | |
+.Nm | |
+enforces a minimum RSA key modulus size for protocol 1 | |
+and protocol 2 keys of 768 bits. | |
+.Pp | |
+The options (if present) consist of comma-separated option | |
+specifications. | |
+No spaces are permitted, except within double quotes. | |
+The following option specifications are supported (note | |
+that option keywords are case-insensitive): | |
+.Bl -tag -width Ds | |
+.It Cm cert-authority | |
+Specifies that the listed key is a certification authority (CA) that is | |
+trusted to validate signed certificates for user authentication. | |
+.Pp | |
+Certificates may encode access restrictions similar to these key options. | |
+If both certificate restrictions and key options are present, the most | |
+restrictive union of the two is applied. | |
+.It Cm command="command" | |
+Specifies that the command is executed whenever this key is used for | |
+authentication. | |
+The command supplied by the user (if any) is ignored. | |
+The command is run on a pty if the client requests a pty; | |
+otherwise it is run without a tty. | |
+If an 8-bit clean channel is required, | |
+one must not request a pty or should specify | |
+.Cm no-pty . | |
+A quote may be included in the command by quoting it with a backslash. | |
+This option might be useful | |
+to restrict certain public keys to perform just a specific operation. | |
+An example might be a key that permits remote backups but nothing else. | |
+Note that the client may specify TCP and/or X11 | |
+forwarding unless they are explicitly prohibited. | |
+The command originally supplied by the client is available in the | |
+.Ev SSH_ORIGINAL_COMMAND | |
+environment variable. | |
+Note that this option applies to shell, command or subsystem execution. | |
+Also note that this command may be superseded by either a | |
+.Xr sshd_config 5 | |
+.Cm ForceCommand | |
+directive or a command embedded in a certificate. | |
+.It Cm environment="NAME=value" | |
+Specifies that the string is to be added to the environment when | |
+logging in using this key. | |
+Environment variables set this way | |
+override other default environment values. | |
+Multiple options of this type are permitted. | |
+Environment processing is disabled by default and is | |
+controlled via the | |
+.Cm PermitUserEnvironment | |
+option. | |
+This option is automatically disabled if | |
+.Cm UseLogin | |
+is enabled. | |
+.It Cm from="pattern-list" | |
+Specifies that in addition to public key authentication, either the canonical | |
+name of the remote host or its IP address must be present in the | |
+comma-separated list of patterns. | |
+See PATTERNS in | |
+.Xr ssh_config 5 | |
+for more information on patterns. | |
+.Pp | |
+In addition to the wildcard matching that may be applied to hostnames or | |
+addresses, a | |
+.Cm from | |
+stanza may match IP addresses using CIDR address/masklen notation. | |
+.Pp | |
+The purpose of this option is to optionally increase security: public key | |
+authentication by itself does not trust the network or name servers or | |
+anything (but the key); however, if somebody somehow steals the key, the key | |
+permits an intruder to log in from anywhere in the world. | |
+This additional option makes using a stolen key more difficult (name | |
+servers and/or routers would have to be compromised in addition to | |
+just the key). | |
+.It Cm no-agent-forwarding | |
+Forbids authentication agent forwarding when this key is used for | |
+authentication. | |
+.It Cm no-port-forwarding | |
+Forbids TCP forwarding when this key is used for authentication. | |
+Any port forward requests by the client will return an error. | |
+This might be used, e.g. in connection with the | |
+.Cm command | |
+option. | |
+.It Cm no-pty | |
+Prevents tty allocation (a request to allocate a pty will fail). | |
+.It Cm no-user-rc | |
+Disables execution of | |
+.Pa ~/.ssh/rc . | |
+.It Cm no-X11-forwarding | |
+Forbids X11 forwarding when this key is used for authentication. | |
+Any X11 forward requests by the client will return an error. | |
+.It Cm permitopen="host:port" | |
+Limit local | |
+.Li ``ssh -L'' | |
+port forwarding such that it may only connect to the specified host and | |
+port. | |
+IPv6 addresses can be specified by enclosing the address in square brackets. | |
+Multiple | |
+.Cm permitopen | |
+options may be applied separated by commas. | |
+No pattern matching is performed on the specified hostnames, | |
+they must be literal domains or addresses. | |
+A port specification of | |
+.Cm * | |
+matches any port. | |
+.It Cm principals="principals" | |
+On a | |
+.Cm cert-authority | |
+line, specifies allowed principals for certificate authentication as a | |
+comma-separated list. | |
+At least one name from the list must appear in the certificate's | |
+list of principals for the certificate to be accepted. | |
+This option is ignored for keys that are not marked as trusted certificate | |
+signers using the | |
+.Cm cert-authority | |
+option. | |
+.It Cm tunnel="n" | |
+Force a | |
+.Xr tun 4 | |
+device on the server. | |
+Without this option, the next available device will be used if | |
+the client requests a tunnel. | |
+.El | |
+.Pp | |
+An example authorized_keys file: | |
+.Bd -literal -offset 3n | |
+# Comments allowed at start of line | |
+ssh-rsa AAAAB3Nza...LiPk== user@example.net | |
+from="*.sales.example.net,!pc.sales.example.net" ssh-rsa | |
+AAAAB2...19Q== john@example.net | |
+command="dump /home",no-pty,no-port-forwarding ssh-dss | |
+AAAAC3...51R== example.net | |
+permitopen="192.0.2.1:80",permitopen="192.0.2.2:25" ssh-dss | |
+AAAAB5...21S== | |
+tunnel="0",command="sh /etc/netstart tun0" ssh-rsa AAAA...== | |
+jane@example.net | |
+.Ed | |
+.Sh SSH_KNOWN_HOSTS FILE FORMAT | |
+The | |
+.Pa /etc/ssh/ssh_known_hosts | |
+and | |
+.Pa ~/.ssh/known_hosts | |
+files contain host public keys for all known hosts. | |
+The global file should | |
+be prepared by the administrator (optional), and the per-user file is | |
+maintained automatically: whenever the user connects from an unknown host, | |
+its key is added to the per-user file. | |
+.Pp | |
+Each line in these files contains the following fields: markers (optional), | |
+hostnames, bits, exponent, modulus, comment. | |
+The fields are separated by spaces. | |
+.Pp | |
+The marker is optional, but if it is present then it must be one of | |
+.Dq @cert-authority , | |
+to indicate that the line contains a certification authority (CA) key, | |
+or | |
+.Dq @revoked , | |
+to indicate that the key contained on the line is revoked and must not ever | |
+be accepted. | |
+Only one marker should be used on a key line. | |
+.Pp | |
+Hostnames is a comma-separated list of patterns | |
+.Pf ( Ql * | |
+and | |
+.Ql \&? | |
+act as | |
+wildcards); each pattern in turn is matched against the canonical host | |
+name (when authenticating a client) or against the user-supplied | |
+name (when authenticating a server). | |
+A pattern may also be preceded by | |
+.Ql \&! | |
+to indicate negation: if the host name matches a negated | |
+pattern, it is not accepted (by that line) even if it matched another | |
+pattern on the line. | |
+A hostname or address may optionally be enclosed within | |
+.Ql \&[ | |
+and | |
+.Ql \&] | |
+brackets then followed by | |
+.Ql \&: | |
+and a non-standard port number. | |
+.Pp | |
+Alternately, hostnames may be stored in a hashed form which hides host names | |
+and addresses should the file's contents be disclosed. | |
+Hashed hostnames start with a | |
+.Ql | | |
+character. | |
+Only one hashed hostname may appear on a single line and none of the above | |
+negation or wildcard operators may be applied. | |
+.Pp | |
+Bits, exponent, and modulus are taken directly from the RSA host key; they | |
+can be obtained, for example, from | |
+.Pa /etc/ssh/ssh_host_key.pub . | |
+The optional comment field continues to the end of the line, and is not used. | |
+.Pp | |
+Lines starting with | |
+.Ql # | |
+and empty lines are ignored as comments. | |
+.Pp | |
+When performing host authentication, authentication is accepted if any | |
+matching line has the proper key; either one that matches exactly or, | |
+if the server has presented a certificate for authentication, the key | |
+of the certification authority that signed the certificate. | |
+For a key to be trusted as a certification authority, it must use the | |
+.Dq @cert-authority | |
+marker described above. | |
+.Pp | |
+The known hosts file also provides a facility to mark keys as revoked, | |
+for example when it is known that the associated private key has been | |
+stolen. | |
+Revoked keys are specified by including the | |
+.Dq @revoked | |
+marker at the beginning of the key line, and are never accepted for | |
+authentication or as certification authorities, but instead will | |
+produce a warning from | |
+.Xr ssh 1 | |
+when they are encountered. | |
+.Pp | |
+It is permissible (but not | |
+recommended) to have several lines or different host keys for the same | |
+names. | |
+This will inevitably happen when short forms of host names | |
+from different domains are put in the file. | |
+It is possible | |
+that the files contain conflicting information; authentication is | |
+accepted if valid information can be found from either file. | |
+.Pp | |
+Note that the lines in these files are typically hundreds of characters | |
+long, and you definitely don't want to type in the host keys by hand. | |
+Rather, generate them by a script, | |
+.Xr ssh-keyscan 1 | |
+or by taking | |
+.Pa /etc/ssh/ssh_host_key.pub | |
+and adding the host names at the front. | |
+.Xr ssh-keygen 1 | |
+also offers some basic automated editing for | |
+.Pa ~/.ssh/known_hosts | |
+including removing hosts matching a host name and converting all host | |
+names to their hashed representations. | |
+.Pp | |
+An example ssh_known_hosts file: | |
+.Bd -literal -offset 3n | |
+# Comments allowed at start of line | |
+closenet,...,192.0.2.53 1024 37 159...93 closenet.example.net | |
+cvs.example.net,192.0.2.10 ssh-rsa AAAA1234.....= | |
+# A hashed hostname | |
+|1|JfKTdBh7rNbXkVAQCRp4OQoPfmI=|USECr3SWf1JUPsms5AqfD5QfxkM= ssh-rsa | |
+AAAA1234.....= | |
+# A revoked key | |
+@revoked * ssh-rsa AAAAB5W... | |
+# A CA key, accepted for any host in *.mydomain.com or *.mydomain.org | |
+@cert-authority *.mydomain.org,*.mydomain.com ssh-rsa AAAAB5W... | |
+.Ed | |
+.Sh FILES | |
+.Bl -tag -width Ds -compact | |
+.It Pa ~/.hushlogin | |
+This file is used to suppress printing the last login time and | |
+.Pa /etc/motd , | |
+if | |
+.Cm PrintLastLog | |
+and | |
+.Cm PrintMotd , | |
+respectively, | |
+are enabled. | |
+It does not suppress printing of the banner specified by | |
+.Cm Banner . | |
+.Pp | |
+.It Pa ~/.rhosts | |
+This file is used for host-based authentication (see | |
+.Xr ssh 1 | |
+for more information). | |
+On some machines this file may need to be | |
+world-readable if the user's home directory is on an NFS partition, | |
+because | |
+.Nm | |
+reads it as root. | |
+Additionally, this file must be owned by the user, | |
+and must not have write permissions for anyone else. | |
+The recommended | |
+permission for most machines is read/write for the user, and not | |
+accessible by others. | |
+.Pp | |
+.It Pa ~/.shosts | |
+This file is used in exactly the same way as | |
+.Pa .rhosts , | |
+but allows host-based authentication without permitting login with | |
+rlogin/rsh. | |
+.Pp | |
+.It Pa ~/.ssh/ | |
+This directory is the default location for all user-specific configuration | |
+and authentication information. | |
+There is no general requirement to keep the entire contents of this directory | |
+secret, but the recommended permissions are read/write/execute for the user, | |
+and not accessible by others. | |
+.Pp | |
+.It Pa ~/.ssh/authorized_keys | |
+Lists the public keys (DSA, ECDSA, ED25519, RSA) | |
+that can be used for logging in as this user. | |
+The format of this file is described above. | |
+The content of the file is not highly sensitive, but the recommended | |
+permissions are read/write for the user, and not accessible by others. | |
+.Pp | |
+If this file, the | |
+.Pa ~/.ssh | |
+directory, or the user's home directory are writable | |
+by other users, then the file could be modified or replaced by unauthorized | |
+users. | |
+In this case, | |
+.Nm | |
+will not allow it to be used unless the | |
+.Cm StrictModes | |
+option has been set to | |
+.Dq no . | |
+.Pp | |
+.It Pa ~/.ssh/environment | |
+This file is read into the environment at login (if it exists). | |
+It can only contain empty lines, comment lines (that start with | |
+.Ql # ) , | |
+and assignment lines of the form name=value. | |
+The file should be writable | |
+only by the user; it need not be readable by anyone else. | |
+Environment processing is disabled by default and is | |
+controlled via the | |
+.Cm PermitUserEnvironment | |
+option. | |
+.Pp | |
+.It Pa ~/.ssh/known_hosts | |
+Contains a list of host keys for all hosts the user has logged into | |
+that are not already in the systemwide list of known host keys. | |
+The format of this file is described above. | |
+This file should be writable only by root/the owner and | |
+can, but need not be, world-readable. | |
+.Pp | |
+.It Pa ~/.ssh/rc | |
+Contains initialization routines to be run before | |
+the user's home directory becomes accessible. | |
+This file should be writable only by the user, and need not be | |
+readable by anyone else. | |
+.Pp | |
+.It Pa /etc/hosts.equiv | |
+This file is for host-based authentication (see | |
+.Xr ssh 1 ) . | |
+It should only be writable by root. | |
+.Pp | |
+.It Pa /etc/moduli | |
+Contains Diffie-Hellman groups used for the "Diffie-Hellman Group Exchange". | |
+The file format is described in | |
+.Xr moduli 5 . | |
+.Pp | |
+.It Pa /etc/motd | |
+See | |
+.Xr motd 5 . | |
+.Pp | |
+.It Pa /etc/nologin | |
+If this file exists, | |
+.Nm | |
+refuses to let anyone except root log in. | |
+The contents of the file | |
+are displayed to anyone trying to log in, and non-root connections are | |
+refused. | |
+The file should be world-readable. | |
+.Pp | |
+.It Pa /etc/shosts.equiv | |
+This file is used in exactly the same way as | |
+.Pa hosts.equiv , | |
+but allows host-based authentication without permitting login with | |
+rlogin/rsh. | |
+.Pp | |
+.It Pa /etc/ssh/ssh_host_key | |
+.It Pa /etc/ssh/ssh_host_dsa_key | |
+.It Pa /etc/ssh/ssh_host_ecdsa_key | |
+.It Pa /etc/ssh/ssh_host_ed25519_key | |
+.It Pa /etc/ssh/ssh_host_rsa_key | |
+These files contain the private parts of the host keys. | |
+These files should only be owned by root, readable only by root, and not | |
+accessible to others. | |
+Note that | |
+.Nm | |
+does not start if these files are group/world-accessible. | |
+.Pp | |
+.It Pa /etc/ssh/ssh_host_key.pub | |
+.It Pa /etc/ssh/ssh_host_dsa_key.pub | |
+.It Pa /etc/ssh/ssh_host_ecdsa_key.pub | |
+.It Pa /etc/ssh/ssh_host_ed25519_key.pub | |
+.It Pa /etc/ssh/ssh_host_rsa_key.pub | |
+These files contain the public parts of the host keys. | |
+These files should be world-readable but writable only by | |
+root. | |
+Their contents should match the respective private parts. | |
+These files are not | |
+really used for anything; they are provided for the convenience of | |
+the user so their contents can be copied to known hosts files. | |
+These files are created using | |
+.Xr ssh-keygen 1 . | |
+.Pp | |
+.It Pa /etc/ssh/ssh_known_hosts | |
+Systemwide list of known host keys. | |
+This file should be prepared by the | |
+system administrator to contain the public host keys of all machines in the | |
+organization. | |
+The format of this file is described above. | |
+This file should be writable only by root/the owner and | |
+should be world-readable. | |
+.Pp | |
+.It Pa /etc/ssh/sshd_config | |
+Contains configuration data for | |
+.Nm sshd . | |
+The file format and configuration options are described in | |
+.Xr sshd_config 5 . | |
+.Pp | |
+.It Pa /etc/ssh/sshrc | |
+Similar to | |
+.Pa ~/.ssh/rc , | |
+it can be used to specify | |
+machine-specific login-time initializations globally. | |
+This file should be writable only by root, and should be world-readable. | |
+.Pp | |
+.It Pa /var/empty | |
+.Xr chroot 2 | |
+directory used by | |
+.Nm | |
+during privilege separation in the pre-authentication phase. | |
+The directory should not contain any files and must be owned by root | |
+and not group or world-writable. | |
+.Pp | |
+.It Pa /var/run/sshd.pid | |
+Contains the process ID of the | |
+.Nm | |
+listening for connections (if there are several daemons running | |
+concurrently for different ports, this contains the process ID of the one | |
+started last). | |
+The content of this file is not sensitive; it can be world-readable. | |
+.El | |
+.Sh SEE ALSO | |
+.Xr scp 1 , | |
+.Xr sftp 1 , | |
+.Xr ssh 1 , | |
+.Xr ssh-add 1 , | |
+.Xr ssh-agent 1 , | |
+.Xr ssh-keygen 1 , | |
+.Xr ssh-keyscan 1 , | |
+.Xr chroot 2 , | |
+.Xr login.conf 5 , | |
+.Xr moduli 5 , | |
+.Xr sshd_config 5 , | |
+.Xr inetd 8 , | |
+.Xr sftp-server 8 | |
+.Sh AUTHORS | |
+OpenSSH is a derivative of the original and free | |
+ssh 1.2.12 release by Tatu Ylonen. | |
+Aaron Campbell, Bob Beck, Markus Friedl, Niels Provos, | |
+Theo de Raadt and Dug Song | |
+removed many bugs, re-added newer features and | |
+created OpenSSH. | |
+Markus Friedl contributed the support for SSH | |
+protocol versions 1.5 and 2.0. | |
+Niels Provos and Markus Friedl contributed support | |
+for privilege separation. | |
diff -urp --new-file openssh-6.7p1/sshd.c openssh-6.7p1.patched/sshd.c | |
--- openssh-6.7p1/sshd.c 2014-08-26 11:11:55.000000000 -0700 | |
+++ openssh-6.7p1.patched/sshd.c 2014-10-11 21:18:37.000000000 -0700 | |
@@ -2137,6 +2137,12 @@ main(int ac, char **av) | |
audit_event(SSH_AUTH_SUCCESS); | |
#endif | |
+#ifdef USE_PAM | |
+ if (options.use_pam) { | |
+ do_pam_setcred(1); | |
+ do_pam_session(); | |
+ } | |
+#endif | |
#ifdef GSSAPI | |
if (options.gss_authentication) { | |
temporarily_use_uid(authctxt->pw); | |
@@ -2144,12 +2150,6 @@ main(int ac, char **av) | |
restore_uid(); | |
} | |
#endif | |
-#ifdef USE_PAM | |
- if (options.use_pam) { | |
- do_pam_setcred(1); | |
- do_pam_session(); | |
- } | |
-#endif | |
/* | |
* In privilege separation, we fork another child and prepare | |
diff -urp --new-file openssh-6.7p1/sshd.c.orig openssh-6.7p1.patched/sshd.c.orig | |
--- openssh-6.7p1/sshd.c.orig 1969-12-31 16:00:00.000000000 -0800 | |
+++ openssh-6.7p1.patched/sshd.c.orig 2014-08-26 11:11:55.000000000 -0700 | |
@@ -0,0 +1,2541 @@ | |
+/* $OpenBSD: sshd.c,v 1.428 2014/07/15 15:54:14 millert Exp $ */ | |
+/* | |
+ * Author: Tatu Ylonen <ylo@cs.hut.fi> | |
+ * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | |
+ * All rights reserved | |
+ * This program is the ssh daemon. It listens for connections from clients, | |
+ * and performs authentication, executes use commands or shell, and forwards | |
+ * information to/from the application to the user client over an encrypted | |
+ * connection. This can also handle forwarding of X11, TCP/IP, and | |
+ * authentication agent connections. | |
+ * | |
+ * As far as I am concerned, the code I have written for this software | |
+ * can be used freely for any purpose. Any derived versions of this | |
+ * software must be clearly marked as such, and if the derived work is | |
+ * incompatible with the protocol description in the RFC file, it must be | |
+ * called by a name other than "ssh" or "Secure Shell". | |
+ * | |
+ * SSH2 implementation: | |
+ * Privilege Separation: | |
+ * | |
+ * Copyright (c) 2000, 2001, 2002 Markus Friedl. All rights reserved. | |
+ * Copyright (c) 2002 Niels Provos. 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 <sys/types.h> | |
+#include <sys/ioctl.h> | |
+#include <sys/socket.h> | |
+#ifdef HAVE_SYS_STAT_H | |
+# include <sys/stat.h> | |
+#endif | |
+#ifdef HAVE_SYS_TIME_H | |
+# include <sys/time.h> | |
+#endif | |
+#include "openbsd-compat/sys-tree.h" | |
+#include "openbsd-compat/sys-queue.h" | |
+#include <sys/wait.h> | |
+ | |
+#include <errno.h> | |
+#include <fcntl.h> | |
+#include <netdb.h> | |
+#ifdef HAVE_PATHS_H | |
+#include <paths.h> | |
+#endif | |
+#include <grp.h> | |
+#include <pwd.h> | |
+#include <signal.h> | |
+#include <stdarg.h> | |
+#include <stdio.h> | |
+#include <stdlib.h> | |
+#include <string.h> | |
+#include <unistd.h> | |
+ | |
+#ifdef WITH_OPENSSL | |
+#include <openssl/dh.h> | |
+#include <openssl/bn.h> | |
+#include <openssl/rand.h> | |
+#include "openbsd-compat/openssl-compat.h" | |
+#endif | |
+ | |
+#ifdef HAVE_SECUREWARE | |
+#include <sys/security.h> | |
+#include <prot.h> | |
+#endif | |
+ | |
+#include "xmalloc.h" | |
+#include "ssh.h" | |
+#include "ssh1.h" | |
+#include "ssh2.h" | |
+#include "rsa.h" | |
+#include "sshpty.h" | |
+#include "packet.h" | |
+#include "log.h" | |
+#include "buffer.h" | |
+#include "misc.h" | |
+#include "servconf.h" | |
+#include "uidswap.h" | |
+#include "compat.h" | |
+#include "cipher.h" | |
+#include "digest.h" | |
+#include "key.h" | |
+#include "kex.h" | |
+#include "myproposal.h" | |
+#include "authfile.h" | |
+#include "pathnames.h" | |
+#include "atomicio.h" | |
+#include "canohost.h" | |
+#include "hostfile.h" | |
+#include "auth.h" | |
+#include "authfd.h" | |
+#include "msg.h" | |
+#include "dispatch.h" | |
+#include "channels.h" | |
+#include "session.h" | |
+#include "monitor_mm.h" | |
+#include "monitor.h" | |
+#ifdef GSSAPI | |
+#include "ssh-gss.h" | |
+#endif | |
+#include "monitor_wrap.h" | |
+#include "roaming.h" | |
+#include "ssh-sandbox.h" | |
+#include "version.h" | |
+ | |
+#ifndef O_NOCTTY | |
+#define O_NOCTTY 0 | |
+#endif | |
+ | |
+/* Re-exec fds */ | |
+#define REEXEC_DEVCRYPTO_RESERVED_FD (STDERR_FILENO + 1) | |
+#define REEXEC_STARTUP_PIPE_FD (STDERR_FILENO + 2) | |
+#define REEXEC_CONFIG_PASS_FD (STDERR_FILENO + 3) | |
+#define REEXEC_MIN_FREE_FD (STDERR_FILENO + 4) | |
+ | |
+extern char *__progname; | |
+ | |
+/* Server configuration options. */ | |
+ServerOptions options; | |
+ | |
+/* Name of the server configuration file. */ | |
+char *config_file_name = _PATH_SERVER_CONFIG_FILE; | |
+ | |
+/* | |
+ * Debug mode flag. This can be set on the command line. If debug | |
+ * mode is enabled, extra debugging output will be sent to the system | |
+ * log, the daemon will not go to background, and will exit after processing | |
+ * the first connection. | |
+ */ | |
+int debug_flag = 0; | |
+ | |
+/* Flag indicating that the daemon should only test the configuration and keys. */ | |
+int test_flag = 0; | |
+ | |
+/* Flag indicating that the daemon is being started from inetd. */ | |
+int inetd_flag = 0; | |
+ | |
+/* Flag indicating that sshd should not detach and become a daemon. */ | |
+int no_daemon_flag = 0; | |
+ | |
+/* debug goes to stderr unless inetd_flag is set */ | |
+int log_stderr = 0; | |
+ | |
+/* Saved arguments to main(). */ | |
+char **saved_argv; | |
+int saved_argc; | |
+ | |
+/* re-exec */ | |
+int rexeced_flag = 0; | |
+int rexec_flag = 1; | |
+int rexec_argc = 0; | |
+char **rexec_argv; | |
+ | |
+/* | |
+ * The sockets that the server is listening; this is used in the SIGHUP | |
+ * signal handler. | |
+ */ | |
+#define MAX_LISTEN_SOCKS 16 | |
+int listen_socks[MAX_LISTEN_SOCKS]; | |
+int num_listen_socks = 0; | |
+ | |
+/* | |
+ * the client's version string, passed by sshd2 in compat mode. if != NULL, | |
+ * sshd will skip the version-number exchange | |
+ */ | |
+char *client_version_string = NULL; | |
+char *server_version_string = NULL; | |
+ | |
+/* for rekeying XXX fixme */ | |
+Kex *xxx_kex; | |
+ | |
+/* Daemon's agent connection */ | |
+AuthenticationConnection *auth_conn = NULL; | |
+int have_agent = 0; | |
+ | |
+/* | |
+ * Any really sensitive data in the application is contained in this | |
+ * structure. The idea is that this structure could be locked into memory so | |
+ * that the pages do not get written into swap. However, there are some | |
+ * problems. The private key contains BIGNUMs, and we do not (in principle) | |
+ * have access to the internals of them, and locking just the structure is | |
+ * not very useful. Currently, memory locking is not implemented. | |
+ */ | |
+struct { | |
+ Key *server_key; /* ephemeral server key */ | |
+ Key *ssh1_host_key; /* ssh1 host key */ | |
+ Key **host_keys; /* all private host keys */ | |
+ Key **host_pubkeys; /* all public host keys */ | |
+ Key **host_certificates; /* all public host certificates */ | |
+ int have_ssh1_key; | |
+ int have_ssh2_key; | |
+ u_char ssh1_cookie[SSH_SESSION_KEY_LENGTH]; | |
+} sensitive_data; | |
+ | |
+/* | |
+ * Flag indicating whether the RSA server key needs to be regenerated. | |
+ * Is set in the SIGALRM handler and cleared when the key is regenerated. | |
+ */ | |
+static volatile sig_atomic_t key_do_regen = 0; | |
+ | |
+/* This is set to true when a signal is received. */ | |
+static volatile sig_atomic_t received_sighup = 0; | |
+static volatile sig_atomic_t received_sigterm = 0; | |
+ | |
+/* session identifier, used by RSA-auth */ | |
+u_char session_id[16]; | |
+ | |
+/* same for ssh2 */ | |
+u_char *session_id2 = NULL; | |
+u_int session_id2_len = 0; | |
+ | |
+/* record remote hostname or ip */ | |
+u_int utmp_len = MAXHOSTNAMELEN; | |
+ | |
+/* options.max_startup sized array of fd ints */ | |
+int *startup_pipes = NULL; | |
+int startup_pipe; /* in child */ | |
+ | |
+/* variables used for privilege separation */ | |
+int use_privsep = -1; | |
+struct monitor *pmonitor = NULL; | |
+int privsep_is_preauth = 1; | |
+ | |
+/* global authentication context */ | |
+Authctxt *the_authctxt = NULL; | |
+ | |
+/* sshd_config buffer */ | |
+Buffer cfg; | |
+ | |
+/* message to be displayed after login */ | |
+Buffer loginmsg; | |
+ | |
+/* Unprivileged user */ | |
+struct passwd *privsep_pw = NULL; | |
+ | |
+/* Prototypes for various functions defined later in this file. */ | |
+void destroy_sensitive_data(void); | |
+void demote_sensitive_data(void); | |
+ | |
+#ifdef WITH_SSH1 | |
+static void do_ssh1_kex(void); | |
+#endif | |
+static void do_ssh2_kex(void); | |
+ | |
+/* | |
+ * Close all listening sockets | |
+ */ | |
+static void | |
+close_listen_socks(void) | |
+{ | |
+ int i; | |
+ | |
+ for (i = 0; i < num_listen_socks; i++) | |
+ close(listen_socks[i]); | |
+ num_listen_socks = -1; | |
+} | |
+ | |
+static void | |
+close_startup_pipes(void) | |
+{ | |
+ int i; | |
+ | |
+ if (startup_pipes) | |
+ for (i = 0; i < options.max_startups; i++) | |
+ if (startup_pipes[i] != -1) | |
+ close(startup_pipes[i]); | |
+} | |
+ | |
+/* | |
+ * Signal handler for SIGHUP. Sshd execs itself when it receives SIGHUP; | |
+ * the effect is to reread the configuration file (and to regenerate | |
+ * the server key). | |
+ */ | |
+ | |
+/*ARGSUSED*/ | |
+static void | |
+sighup_handler(int sig) | |
+{ | |
+ int save_errno = errno; | |
+ | |
+ received_sighup = 1; | |
+ signal(SIGHUP, sighup_handler); | |
+ errno = save_errno; | |
+} | |
+ | |
+/* | |
+ * Called from the main program after receiving SIGHUP. | |
+ * Restarts the server. | |
+ */ | |
+static void | |
+sighup_restart(void) | |
+{ | |
+ logit("Received SIGHUP; restarting."); | |
+ platform_pre_restart(); | |
+ 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)); | |
+ exit(1); | |
+} | |
+ | |
+/* | |
+ * Generic signal handler for terminating signals in the master daemon. | |
+ */ | |
+/*ARGSUSED*/ | |
+static void | |
+sigterm_handler(int sig) | |
+{ | |
+ received_sigterm = sig; | |
+} | |
+ | |
+/* | |
+ * SIGCHLD handler. This is called whenever a child dies. This will then | |
+ * reap any zombies left by exited children. | |
+ */ | |
+/*ARGSUSED*/ | |
+static void | |
+main_sigchld_handler(int sig) | |
+{ | |
+ int save_errno = errno; | |
+ pid_t pid; | |
+ int status; | |
+ | |
+ while ((pid = waitpid(-1, &status, WNOHANG)) > 0 || | |
+ (pid < 0 && errno == EINTR)) | |
+ ; | |
+ | |
+ signal(SIGCHLD, main_sigchld_handler); | |
+ errno = save_errno; | |
+} | |
+ | |
+/* | |
+ * Signal handler for the alarm after the login grace period has expired. | |
+ */ | |
+/*ARGSUSED*/ | |
+static void | |
+grace_alarm_handler(int sig) | |
+{ | |
+ if (use_privsep && pmonitor != NULL && pmonitor->m_pid > 0) | |
+ kill(pmonitor->m_pid, SIGALRM); | |
+ | |
+ /* | |
+ * Try to kill any processes that we have spawned, E.g. authorized | |
+ * keys command helpers. | |
+ */ | |
+ if (getpgid(0) == getpid()) { | |
+ signal(SIGTERM, SIG_IGN); | |
+ kill(0, SIGTERM); | |
+ } | |
+ | |
+ /* Log error and exit. */ | |
+ sigdie("Timeout before authentication for %s", get_remote_ipaddr()); | |
+} | |
+ | |
+/* | |
+ * Signal handler for the key regeneration alarm. Note that this | |
+ * alarm only occurs in the daemon waiting for connections, and it does not | |
+ * do anything with the private key or random state before forking. | |
+ * Thus there should be no concurrency control/asynchronous execution | |
+ * problems. | |
+ */ | |
+static void | |
+generate_ephemeral_server_key(void) | |
+{ | |
+ verbose("Generating %s%d bit RSA key.", | |
+ sensitive_data.server_key ? "new " : "", options.server_key_bits); | |
+ if (sensitive_data.server_key != NULL) | |
+ key_free(sensitive_data.server_key); | |
+ sensitive_data.server_key = key_generate(KEY_RSA1, | |
+ options.server_key_bits); | |
+ verbose("RSA key generation complete."); | |
+ | |
+ arc4random_buf(sensitive_data.ssh1_cookie, SSH_SESSION_KEY_LENGTH); | |
+} | |
+ | |
+/*ARGSUSED*/ | |
+static void | |
+key_regeneration_alarm(int sig) | |
+{ | |
+ int save_errno = errno; | |
+ | |
+ signal(SIGALRM, SIG_DFL); | |
+ errno = save_errno; | |
+ key_do_regen = 1; | |
+} | |
+ | |
+static void | |
+sshd_exchange_identification(int sock_in, int sock_out) | |
+{ | |
+ u_int i; | |
+ int mismatch; | |
+ int remote_major, remote_minor; | |
+ int major, minor; | |
+ char *s, *newline = "\n"; | |
+ char buf[256]; /* Must not be larger than remote_version. */ | |
+ char remote_version[256]; /* Must be at least as big as buf. */ | |
+ | |
+ if ((options.protocol & SSH_PROTO_1) && | |
+ (options.protocol & SSH_PROTO_2)) { | |
+ major = PROTOCOL_MAJOR_1; | |
+ minor = 99; | |
+ } else if (options.protocol & SSH_PROTO_2) { | |
+ major = PROTOCOL_MAJOR_2; | |
+ minor = PROTOCOL_MINOR_2; | |
+ newline = "\r\n"; | |
+ } else { | |
+ major = PROTOCOL_MAJOR_1; | |
+ minor = PROTOCOL_MINOR_1; | |
+ } | |
+ | |
+ xasprintf(&server_version_string, "SSH-%d.%d-%.100s%s%s%s", | |
+ major, minor, SSH_VERSION, | |
+ *options.version_addendum == '\0' ? "" : " ", | |
+ options.version_addendum, newline); | |
+ | |
+ /* Send our protocol version identification. */ | |
+ if (roaming_atomicio(vwrite, sock_out, server_version_string, | |
+ strlen(server_version_string)) | |
+ != strlen(server_version_string)) { | |
+ logit("Could not write ident string to %s", get_remote_ipaddr()); | |
+ cleanup_exit(255); | |
+ } | |
+ | |
+ /* Read other sides version identification. */ | |
+ memset(buf, 0, sizeof(buf)); | |
+ for (i = 0; i < sizeof(buf) - 1; i++) { | |
+ if (roaming_atomicio(read, sock_in, &buf[i], 1) != 1) { | |
+ logit("Did not receive identification string from %s", | |
+ get_remote_ipaddr()); | |
+ cleanup_exit(255); | |
+ } | |
+ if (buf[i] == '\r') { | |
+ buf[i] = 0; | |
+ /* Kludge for F-Secure Macintosh < 1.0.2 */ | |
+ if (i == 12 && | |
+ strncmp(buf, "SSH-1.5-W1.0", 12) == 0) | |
+ break; | |
+ continue; | |
+ } | |
+ if (buf[i] == '\n') { | |
+ buf[i] = 0; | |
+ break; | |
+ } | |
+ } | |
+ buf[sizeof(buf) - 1] = 0; | |
+ client_version_string = xstrdup(buf); | |
+ | |
+ /* | |
+ * Check that the versions match. In future this might accept | |
+ * several versions and set appropriate flags to handle them. | |
+ */ | |
+ if (sscanf(client_version_string, "SSH-%d.%d-%[^\n]\n", | |
+ &remote_major, &remote_minor, remote_version) != 3) { | |
+ s = "Protocol mismatch.\n"; | |
+ (void) atomicio(vwrite, sock_out, s, strlen(s)); | |
+ logit("Bad protocol version identification '%.100s' " | |
+ "from %s port %d", client_version_string, | |
+ get_remote_ipaddr(), get_remote_port()); | |
+ close(sock_in); | |
+ close(sock_out); | |
+ cleanup_exit(255); | |
+ } | |
+ debug("Client protocol version %d.%d; client software version %.100s", | |
+ remote_major, remote_minor, remote_version); | |
+ | |
+ compat_datafellows(remote_version); | |
+ | |
+ if ((datafellows & SSH_BUG_PROBE) != 0) { | |
+ logit("probed from %s with %s. Don't panic.", | |
+ get_remote_ipaddr(), client_version_string); | |
+ cleanup_exit(255); | |
+ } | |
+ if ((datafellows & SSH_BUG_SCANNER) != 0) { | |
+ logit("scanned from %s with %s. Don't panic.", | |
+ get_remote_ipaddr(), client_version_string); | |
+ cleanup_exit(255); | |
+ } | |
+ if ((datafellows & SSH_BUG_RSASIGMD5) != 0) { | |
+ logit("Client version \"%.100s\" uses unsafe RSA signature " | |
+ "scheme; disabling use of RSA keys", remote_version); | |
+ } | |
+ if ((datafellows & SSH_BUG_DERIVEKEY) != 0) { | |
+ fatal("Client version \"%.100s\" uses unsafe key agreement; " | |
+ "refusing connection", remote_version); | |
+ } | |
+ | |
+ mismatch = 0; | |
+ switch (remote_major) { | |
+ case 1: | |
+ if (remote_minor == 99) { | |
+ if (options.protocol & SSH_PROTO_2) | |
+ enable_compat20(); | |
+ else | |
+ mismatch = 1; | |
+ break; | |
+ } | |
+ if (!(options.protocol & SSH_PROTO_1)) { | |
+ mismatch = 1; | |
+ break; | |
+ } | |
+ if (remote_minor < 3) { | |
+ packet_disconnect("Your ssh version is too old and " | |
+ "is no longer supported. Please install a newer version."); | |
+ } else if (remote_minor == 3) { | |
+ /* note that this disables agent-forwarding */ | |
+ enable_compat13(); | |
+ } | |
+ break; | |
+ case 2: | |
+ if (options.protocol & SSH_PROTO_2) { | |
+ enable_compat20(); | |
+ break; | |
+ } | |
+ /* FALLTHROUGH */ | |
+ default: | |
+ mismatch = 1; | |
+ break; | |
+ } | |
+ chop(server_version_string); | |
+ debug("Local version string %.200s", server_version_string); | |
+ | |
+ if (mismatch) { | |
+ s = "Protocol major versions differ.\n"; | |
+ (void) atomicio(vwrite, sock_out, s, strlen(s)); | |
+ close(sock_in); | |
+ close(sock_out); | |
+ logit("Protocol major versions differ for %s: %.200s vs. %.200s", | |
+ get_remote_ipaddr(), | |
+ server_version_string, client_version_string); | |
+ cleanup_exit(255); | |
+ } | |
+} | |
+ | |
+/* Destroy the host and server keys. They will no longer be needed. */ | |
+void | |
+destroy_sensitive_data(void) | |
+{ | |
+ int i; | |
+ | |
+ if (sensitive_data.server_key) { | |
+ key_free(sensitive_data.server_key); | |
+ sensitive_data.server_key = NULL; | |
+ } | |
+ for (i = 0; i < options.num_host_key_files; i++) { | |
+ if (sensitive_data.host_keys[i]) { | |
+ key_free(sensitive_data.host_keys[i]); | |
+ sensitive_data.host_keys[i] = NULL; | |
+ } | |
+ if (sensitive_data.host_certificates[i]) { | |
+ key_free(sensitive_data.host_certificates[i]); | |
+ sensitive_data.host_certificates[i] = NULL; | |
+ } | |
+ } | |
+ sensitive_data.ssh1_host_key = NULL; | |
+ explicit_bzero(sensitive_data.ssh1_cookie, SSH_SESSION_KEY_LENGTH); | |
+} | |
+ | |
+/* Demote private to public keys for network child */ | |
+void | |
+demote_sensitive_data(void) | |
+{ | |
+ Key *tmp; | |
+ int i; | |
+ | |
+ if (sensitive_data.server_key) { | |
+ tmp = key_demote(sensitive_data.server_key); | |
+ key_free(sensitive_data.server_key); | |
+ sensitive_data.server_key = tmp; | |
+ } | |
+ | |
+ for (i = 0; i < options.num_host_key_files; i++) { | |
+ if (sensitive_data.host_keys[i]) { | |
+ tmp = key_demote(sensitive_data.host_keys[i]); | |
+ key_free(sensitive_data.host_keys[i]); | |
+ sensitive_data.host_keys[i] = tmp; | |
+ if (tmp->type == KEY_RSA1) | |
+ sensitive_data.ssh1_host_key = tmp; | |
+ } | |
+ /* Certs do not need demotion */ | |
+ } | |
+ | |
+ /* We do not clear ssh1_host key and cookie. XXX - Okay Niels? */ | |
+} | |
+ | |
+static void | |
+privsep_preauth_child(void) | |
+{ | |
+ u_int32_t rnd[256]; | |
+ gid_t gidset[1]; | |
+ | |
+ /* Enable challenge-response authentication for privilege separation */ | |
+ privsep_challenge_enable(); | |
+ | |
+#ifdef GSSAPI | |
+ /* Cache supported mechanism OIDs for later use */ | |
+ if (options.gss_authentication) | |
+ ssh_gssapi_prepare_supported_oids(); | |
+#endif | |
+ | |
+ arc4random_stir(); | |
+ arc4random_buf(rnd, sizeof(rnd)); | |
+ RAND_seed(rnd, sizeof(rnd)); | |
+ explicit_bzero(rnd, sizeof(rnd)); | |
+ | |
+ /* Demote the private keys to public keys. */ | |
+ demote_sensitive_data(); | |
+ | |
+ /* Change our root directory */ | |
+ if (chroot(_PATH_PRIVSEP_CHROOT_DIR) == -1) | |
+ fatal("chroot(\"%s\"): %s", _PATH_PRIVSEP_CHROOT_DIR, | |
+ strerror(errno)); | |
+ if (chdir("/") == -1) | |
+ fatal("chdir(\"/\"): %s", strerror(errno)); | |
+ | |
+ /* Drop our privileges */ | |
+ debug3("privsep user:group %u:%u", (u_int)privsep_pw->pw_uid, | |
+ (u_int)privsep_pw->pw_gid); | |
+#if 0 | |
+ /* XXX not ready, too heavy after chroot */ | |
+ do_setusercontext(privsep_pw); | |
+#else | |
+ gidset[0] = privsep_pw->pw_gid; | |
+ if (setgroups(1, gidset) < 0) | |
+ fatal("setgroups: %.100s", strerror(errno)); | |
+ permanently_set_uid(privsep_pw); | |
+#endif | |
+} | |
+ | |
+static int | |
+privsep_preauth(Authctxt *authctxt) | |
+{ | |
+ int status; | |
+ pid_t pid; | |
+ struct ssh_sandbox *box = NULL; | |
+ | |
+ /* Set up unprivileged child process to deal with network data */ | |
+ pmonitor = monitor_init(); | |
+ /* Store a pointer to the kex for later rekeying */ | |
+ pmonitor->m_pkex = &xxx_kex; | |
+ | |
+ if (use_privsep == PRIVSEP_ON) | |
+ box = ssh_sandbox_init(pmonitor); | |
+ pid = fork(); | |
+ if (pid == -1) { | |
+ fatal("fork of unprivileged child failed"); | |
+ } else if (pid != 0) { | |
+ debug2("Network child is on pid %ld", (long)pid); | |
+ | |
+ pmonitor->m_pid = pid; | |
+ if (have_agent) | |
+ auth_conn = ssh_get_authentication_connection(); | |
+ if (box != NULL) | |
+ ssh_sandbox_parent_preauth(box, pid); | |
+ monitor_child_preauth(authctxt, pmonitor); | |
+ | |
+ /* Sync memory */ | |
+ monitor_sync(pmonitor); | |
+ | |
+ /* Wait for the child's exit status */ | |
+ while (waitpid(pid, &status, 0) < 0) { | |
+ if (errno == EINTR) | |
+ continue; | |
+ pmonitor->m_pid = -1; | |
+ fatal("%s: waitpid: %s", __func__, strerror(errno)); | |
+ } | |
+ privsep_is_preauth = 0; | |
+ pmonitor->m_pid = -1; | |
+ if (WIFEXITED(status)) { | |
+ if (WEXITSTATUS(status) != 0) | |
+ fatal("%s: preauth child exited with status %d", | |
+ __func__, WEXITSTATUS(status)); | |
+ } else if (WIFSIGNALED(status)) | |
+ fatal("%s: preauth child terminated by signal %d", | |
+ __func__, WTERMSIG(status)); | |
+ if (box != NULL) | |
+ ssh_sandbox_parent_finish(box); | |
+ return 1; | |
+ } else { | |
+ /* child */ | |
+ close(pmonitor->m_sendfd); | |
+ close(pmonitor->m_log_recvfd); | |
+ | |
+ /* Arrange for logging to be sent to the monitor */ | |
+ set_log_handler(mm_log_handler, pmonitor); | |
+ | |
+ /* Demote the child */ | |
+ if (getuid() == 0 || geteuid() == 0) | |
+ privsep_preauth_child(); | |
+ setproctitle("%s", "[net]"); | |
+ if (box != NULL) | |
+ ssh_sandbox_child(box); | |
+ | |
+ return 0; | |
+ } | |
+} | |
+ | |
+static void | |
+privsep_postauth(Authctxt *authctxt) | |
+{ | |
+ u_int32_t rnd[256]; | |
+ | |
+#ifdef DISABLE_FD_PASSING | |
+ if (1) { | |
+#else | |
+ if (authctxt->pw->pw_uid == 0 || options.use_login) { | |
+#endif | |
+ /* File descriptor passing is broken or root login */ | |
+ use_privsep = 0; | |
+ goto skip; | |
+ } | |
+ | |
+ /* New socket pair */ | |
+ monitor_reinit(pmonitor); | |
+ | |
+ pmonitor->m_pid = fork(); | |
+ if (pmonitor->m_pid == -1) | |
+ fatal("fork of unprivileged child failed"); | |
+ else if (pmonitor->m_pid != 0) { | |
+ verbose("User child is on pid %ld", (long)pmonitor->m_pid); | |
+ buffer_clear(&loginmsg); | |
+ monitor_child_postauth(pmonitor); | |
+ | |
+ /* NEVERREACHED */ | |
+ exit(0); | |
+ } | |
+ | |
+ /* child */ | |
+ | |
+ close(pmonitor->m_sendfd); | |
+ pmonitor->m_sendfd = -1; | |
+ | |
+ /* Demote the private keys to public keys. */ | |
+ demote_sensitive_data(); | |
+ | |
+ arc4random_stir(); | |
+ arc4random_buf(rnd, sizeof(rnd)); | |
+ RAND_seed(rnd, sizeof(rnd)); | |
+ explicit_bzero(rnd, sizeof(rnd)); | |
+ | |
+ /* Drop privileges */ | |
+ do_setusercontext(authctxt->pw); | |
+ | |
+ skip: | |
+ /* It is safe now to apply the key state */ | |
+ monitor_apply_keystate(pmonitor); | |
+ | |
+ /* | |
+ * Tell the packet layer that authentication was successful, since | |
+ * this information is not part of the key state. | |
+ */ | |
+ packet_set_authenticated(); | |
+} | |
+ | |
+static char * | |
+list_hostkey_types(void) | |
+{ | |
+ Buffer b; | |
+ const char *p; | |
+ char *ret; | |
+ int i; | |
+ Key *key; | |
+ | |
+ buffer_init(&b); | |
+ for (i = 0; i < options.num_host_key_files; i++) { | |
+ key = sensitive_data.host_keys[i]; | |
+ if (key == NULL) | |
+ key = sensitive_data.host_pubkeys[i]; | |
+ if (key == NULL) | |
+ continue; | |
+ switch (key->type) { | |
+ case KEY_RSA: | |
+ case KEY_DSA: | |
+ case KEY_ECDSA: | |
+ case KEY_ED25519: | |
+ if (buffer_len(&b) > 0) | |
+ buffer_append(&b, ",", 1); | |
+ p = key_ssh_name(key); | |
+ buffer_append(&b, p, strlen(p)); | |
+ break; | |
+ } | |
+ /* If the private key has a cert peer, then list that too */ | |
+ key = sensitive_data.host_certificates[i]; | |
+ if (key == NULL) | |
+ continue; | |
+ switch (key->type) { | |
+ case KEY_RSA_CERT_V00: | |
+ case KEY_DSA_CERT_V00: | |
+ case KEY_RSA_CERT: | |
+ case KEY_DSA_CERT: | |
+ case KEY_ECDSA_CERT: | |
+ case KEY_ED25519_CERT: | |
+ if (buffer_len(&b) > 0) | |
+ buffer_append(&b, ",", 1); | |
+ p = key_ssh_name(key); | |
+ buffer_append(&b, p, strlen(p)); | |
+ break; | |
+ } | |
+ } | |
+ buffer_append(&b, "\0", 1); | |
+ ret = xstrdup(buffer_ptr(&b)); | |
+ buffer_free(&b); | |
+ debug("list_hostkey_types: %s", ret); | |
+ return ret; | |
+} | |
+ | |
+static Key * | |
+get_hostkey_by_type(int type, int need_private) | |
+{ | |
+ int i; | |
+ Key *key; | |
+ | |
+ for (i = 0; i < options.num_host_key_files; i++) { | |
+ switch (type) { | |
+ case KEY_RSA_CERT_V00: | |
+ case KEY_DSA_CERT_V00: | |
+ case KEY_RSA_CERT: | |
+ case KEY_DSA_CERT: | |
+ case KEY_ECDSA_CERT: | |
+ case KEY_ED25519_CERT: | |
+ key = sensitive_data.host_certificates[i]; | |
+ break; | |
+ default: | |
+ key = sensitive_data.host_keys[i]; | |
+ if (key == NULL && !need_private) | |
+ key = sensitive_data.host_pubkeys[i]; | |
+ break; | |
+ } | |
+ if (key != NULL && key->type == type) | |
+ return need_private ? | |
+ sensitive_data.host_keys[i] : key; | |
+ } | |
+ return NULL; | |
+} | |
+ | |
+Key * | |
+get_hostkey_public_by_type(int type) | |
+{ | |
+ return get_hostkey_by_type(type, 0); | |
+} | |
+ | |
+Key * | |
+get_hostkey_private_by_type(int type) | |
+{ | |
+ return get_hostkey_by_type(type, 1); | |
+} | |
+ | |
+Key * | |
+get_hostkey_by_index(int ind) | |
+{ | |
+ if (ind < 0 || ind >= options.num_host_key_files) | |
+ return (NULL); | |
+ return (sensitive_data.host_keys[ind]); | |
+} | |
+ | |
+Key * | |
+get_hostkey_public_by_index(int ind) | |
+{ | |
+ if (ind < 0 || ind >= options.num_host_key_files) | |
+ return (NULL); | |
+ return (sensitive_data.host_pubkeys[ind]); | |
+} | |
+ | |
+int | |
+get_hostkey_index(Key *key) | |
+{ | |
+ int i; | |
+ | |
+ for (i = 0; i < options.num_host_key_files; i++) { | |
+ if (key_is_cert(key)) { | |
+ if (key == sensitive_data.host_certificates[i]) | |
+ return (i); | |
+ } else { | |
+ if (key == sensitive_data.host_keys[i]) | |
+ return (i); | |
+ if (key == sensitive_data.host_pubkeys[i]) | |
+ return (i); | |
+ } | |
+ } | |
+ return (-1); | |
+} | |
+ | |
+/* | |
+ * returns 1 if connection should be dropped, 0 otherwise. | |
+ * dropping starts at connection #max_startups_begin with a probability | |
+ * of (max_startups_rate/100). the probability increases linearly until | |
+ * all connections are dropped for startups > max_startups | |
+ */ | |
+static int | |
+drop_connection(int startups) | |
+{ | |
+ int p, r; | |
+ | |
+ if (startups < options.max_startups_begin) | |
+ return 0; | |
+ if (startups >= options.max_startups) | |
+ return 1; | |
+ if (options.max_startups_rate == 100) | |
+ return 1; | |
+ | |
+ p = 100 - options.max_startups_rate; | |
+ p *= startups - options.max_startups_begin; | |
+ p /= options.max_startups - options.max_startups_begin; | |
+ p += options.max_startups_rate; | |
+ r = arc4random_uniform(100); | |
+ | |
+ debug("drop_connection: p %d, r %d", p, r); | |
+ return (r < p) ? 1 : 0; | |
+} | |
+ | |
+static void | |
+usage(void) | |
+{ | |
+ fprintf(stderr, "%s, %s\n", | |
+ SSH_RELEASE, | |
+#ifdef WITH_OPENSSL | |
+ SSLeay_version(SSLEAY_VERSION) | |
+#else | |
+ "without OpenSSL" | |
+#endif | |
+ ); | |
+ fprintf(stderr, | |
+"usage: sshd [-46DdeiqTt] [-b bits] [-C connection_spec] [-c host_cert_file]\n" | |
+" [-E log_file] [-f config_file] [-g login_grace_time]\n" | |
+" [-h host_key_file] [-k key_gen_time] [-o option] [-p port]\n" | |
+" [-u len]\n" | |
+ ); | |
+ exit(1); | |
+} | |
+ | |
+static void | |
+send_rexec_state(int fd, Buffer *conf) | |
+{ | |
+ Buffer m; | |
+ | |
+ debug3("%s: entering fd = %d config len %d", __func__, fd, | |
+ buffer_len(conf)); | |
+ | |
+ /* | |
+ * Protocol from reexec master to child: | |
+ * string configuration | |
+ * u_int ephemeral_key_follows | |
+ * bignum e (only if ephemeral_key_follows == 1) | |
+ * bignum n " | |
+ * bignum d " | |
+ * bignum iqmp " | |
+ * bignum p " | |
+ * bignum q " | |
+ * string rngseed (only if OpenSSL is not self-seeded) | |
+ */ | |
+ buffer_init(&m); | |
+ buffer_put_cstring(&m, buffer_ptr(conf)); | |
+ | |
+#ifdef WITH_SSH1 | |
+ if (sensitive_data.server_key != NULL && | |
+ sensitive_data.server_key->type == KEY_RSA1) { | |
+ buffer_put_int(&m, 1); | |
+ buffer_put_bignum(&m, sensitive_data.server_key->rsa->e); | |
+ buffer_put_bignum(&m, sensitive_data.server_key->rsa->n); | |
+ buffer_put_bignum(&m, sensitive_data.server_key->rsa->d); | |
+ buffer_put_bignum(&m, sensitive_data.server_key->rsa->iqmp); | |
+ buffer_put_bignum(&m, sensitive_data.server_key->rsa->p); | |
+ buffer_put_bignum(&m, sensitive_data.server_key->rsa->q); | |
+ } else | |
+#endif | |
+ buffer_put_int(&m, 0); | |
+ | |
+#ifndef OPENSSL_PRNG_ONLY | |
+ rexec_send_rng_seed(&m); | |
+#endif | |
+ | |
+ if (ssh_msg_send(fd, 0, &m) == -1) | |
+ fatal("%s: ssh_msg_send failed", __func__); | |
+ | |
+ buffer_free(&m); | |
+ | |
+ debug3("%s: done", __func__); | |
+} | |
+ | |
+static void | |
+recv_rexec_state(int fd, Buffer *conf) | |
+{ | |
+ Buffer m; | |
+ char *cp; | |
+ u_int len; | |
+ | |
+ debug3("%s: entering fd = %d", __func__, fd); | |
+ | |
+ buffer_init(&m); | |
+ | |
+ if (ssh_msg_recv(fd, &m) == -1) | |
+ fatal("%s: ssh_msg_recv failed", __func__); | |
+ if (buffer_get_char(&m) != 0) | |
+ fatal("%s: rexec version mismatch", __func__); | |
+ | |
+ cp = buffer_get_string(&m, &len); | |
+ if (conf != NULL) | |
+ buffer_append(conf, cp, len + 1); | |
+ free(cp); | |
+ | |
+ if (buffer_get_int(&m)) { | |
+#ifdef WITH_SSH1 | |
+ if (sensitive_data.server_key != NULL) | |
+ key_free(sensitive_data.server_key); | |
+ sensitive_data.server_key = key_new_private(KEY_RSA1); | |
+ buffer_get_bignum(&m, sensitive_data.server_key->rsa->e); | |
+ buffer_get_bignum(&m, sensitive_data.server_key->rsa->n); | |
+ buffer_get_bignum(&m, sensitive_data.server_key->rsa->d); | |
+ buffer_get_bignum(&m, sensitive_data.server_key->rsa->iqmp); | |
+ buffer_get_bignum(&m, sensitive_data.server_key->rsa->p); | |
+ buffer_get_bignum(&m, sensitive_data.server_key->rsa->q); | |
+ if (rsa_generate_additional_parameters( | |
+ sensitive_data.server_key->rsa) != 0) | |
+ fatal("%s: rsa_generate_additional_parameters " | |
+ "error", __func__); | |
+#else | |
+ fatal("ssh1 not supported"); | |
+#endif | |
+ } | |
+ | |
+#ifndef OPENSSL_PRNG_ONLY | |
+ rexec_recv_rng_seed(&m); | |
+#endif | |
+ | |
+ buffer_free(&m); | |
+ | |
+ debug3("%s: done", __func__); | |
+} | |
+ | |
+/* Accept a connection from inetd */ | |
+static void | |
+server_accept_inetd(int *sock_in, int *sock_out) | |
+{ | |
+ int fd; | |
+ | |
+ startup_pipe = -1; | |
+ if (rexeced_flag) { | |
+ close(REEXEC_CONFIG_PASS_FD); | |
+ *sock_in = *sock_out = dup(STDIN_FILENO); | |
+ if (!debug_flag) { | |
+ startup_pipe = dup(REEXEC_STARTUP_PIPE_FD); | |
+ close(REEXEC_STARTUP_PIPE_FD); | |
+ } | |
+ } else { | |
+ *sock_in = dup(STDIN_FILENO); | |
+ *sock_out = dup(STDOUT_FILENO); | |
+ } | |
+ /* | |
+ * We intentionally do not close the descriptors 0, 1, and 2 | |
+ * as our code for setting the descriptors won't work if | |
+ * ttyfd happens to be one of those. | |
+ */ | |
+ if ((fd = open(_PATH_DEVNULL, O_RDWR, 0)) != -1) { | |
+ dup2(fd, STDIN_FILENO); | |
+ dup2(fd, STDOUT_FILENO); | |
+ if (!log_stderr) | |
+ dup2(fd, STDERR_FILENO); | |
+ if (fd > (log_stderr ? STDERR_FILENO : STDOUT_FILENO)) | |
+ close(fd); | |
+ } | |
+ debug("inetd sockets after dupping: %d, %d", *sock_in, *sock_out); | |
+} | |
+ | |
+/* | |
+ * Listen for TCP connections | |
+ */ | |
+static void | |
+server_listen(void) | |
+{ | |
+ int ret, listen_sock, on = 1; | |
+ struct addrinfo *ai; | |
+ char ntop[NI_MAXHOST], strport[NI_MAXSERV]; | |
+ | |
+ for (ai = options.listen_addrs; ai; ai = ai->ai_next) { | |
+ if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) | |
+ continue; | |
+ if (num_listen_socks >= MAX_LISTEN_SOCKS) | |
+ fatal("Too many listen sockets. " | |
+ "Enlarge MAX_LISTEN_SOCKS"); | |
+ if ((ret = getnameinfo(ai->ai_addr, ai->ai_addrlen, | |
+ ntop, sizeof(ntop), strport, sizeof(strport), | |
+ NI_NUMERICHOST|NI_NUMERICSERV)) != 0) { | |
+ error("getnameinfo failed: %.100s", | |
+ ssh_gai_strerror(ret)); | |
+ continue; | |
+ } | |
+ /* Create socket for listening. */ | |
+ listen_sock = socket(ai->ai_family, ai->ai_socktype, | |
+ ai->ai_protocol); | |
+ if (listen_sock < 0) { | |
+ /* kernel may not support ipv6 */ | |
+ verbose("socket: %.100s", strerror(errno)); | |
+ continue; | |
+ } | |
+ if (set_nonblock(listen_sock) == -1) { | |
+ close(listen_sock); | |
+ continue; | |
+ } | |
+ /* | |
+ * Set socket options. | |
+ * Allow local port reuse in TIME_WAIT. | |
+ */ | |
+ if (setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, | |
+ &on, sizeof(on)) == -1) | |
+ error("setsockopt SO_REUSEADDR: %s", strerror(errno)); | |
+ | |
+ /* Only communicate in IPv6 over AF_INET6 sockets. */ | |
+ if (ai->ai_family == AF_INET6) | |
+ sock_set_v6only(listen_sock); | |
+ | |
+ debug("Bind to port %s on %s.", strport, ntop); | |
+ | |
+ /* Bind the socket to the desired port. */ | |
+ if (bind(listen_sock, ai->ai_addr, ai->ai_addrlen) < 0) { | |
+ error("Bind to port %s on %s failed: %.200s.", | |
+ strport, ntop, strerror(errno)); | |
+ close(listen_sock); | |
+ continue; | |
+ } | |
+ listen_socks[num_listen_socks] = listen_sock; | |
+ num_listen_socks++; | |
+ | |
+ /* Start listening on the port. */ | |
+ if (listen(listen_sock, SSH_LISTEN_BACKLOG) < 0) | |
+ fatal("listen on [%s]:%s: %.100s", | |
+ ntop, strport, strerror(errno)); | |
+ logit("Server listening on %s port %s.", ntop, strport); | |
+ } | |
+ freeaddrinfo(options.listen_addrs); | |
+ | |
+ if (!num_listen_socks) | |
+ fatal("Cannot bind any address."); | |
+} | |
+ | |
+/* | |
+ * The main TCP accept loop. Note that, for the non-debug case, returns | |
+ * from this function are in a forked subprocess. | |
+ */ | |
+static void | |
+server_accept_loop(int *sock_in, int *sock_out, int *newsock, int *config_s) | |
+{ | |
+ fd_set *fdset; | |
+ int i, j, ret, maxfd; | |
+ int key_used = 0, startups = 0; | |
+ int startup_p[2] = { -1 , -1 }; | |
+ struct sockaddr_storage from; | |
+ socklen_t fromlen; | |
+ pid_t pid; | |
+ u_char rnd[256]; | |
+ | |
+ /* setup fd set for accept */ | |
+ fdset = NULL; | |
+ maxfd = 0; | |
+ for (i = 0; i < num_listen_socks; i++) | |
+ if (listen_socks[i] > maxfd) | |
+ maxfd = listen_socks[i]; | |
+ /* pipes connected to unauthenticated childs */ | |
+ startup_pipes = xcalloc(options.max_startups, sizeof(int)); | |
+ for (i = 0; i < options.max_startups; i++) | |
+ startup_pipes[i] = -1; | |
+ | |
+ /* | |
+ * Stay listening for connections until the system crashes or | |
+ * the daemon is killed with a signal. | |
+ */ | |
+ for (;;) { | |
+ if (received_sighup) | |
+ sighup_restart(); | |
+ if (fdset != NULL) | |
+ free(fdset); | |
+ fdset = (fd_set *)xcalloc(howmany(maxfd + 1, NFDBITS), | |
+ sizeof(fd_mask)); | |
+ | |
+ for (i = 0; i < num_listen_socks; i++) | |
+ FD_SET(listen_socks[i], fdset); | |
+ for (i = 0; i < options.max_startups; i++) | |
+ if (startup_pipes[i] != -1) | |
+ FD_SET(startup_pipes[i], fdset); | |
+ | |
+ /* Wait in select until there is a connection. */ | |
+ ret = select(maxfd+1, fdset, NULL, NULL, NULL); | |
+ if (ret < 0 && errno != EINTR) | |
+ error("select: %.100s", strerror(errno)); | |
+ if (received_sigterm) { | |
+ logit("Received signal %d; terminating.", | |
+ (int) received_sigterm); | |
+ close_listen_socks(); | |
+ unlink(options.pid_file); | |
+ exit(received_sigterm == SIGTERM ? 0 : 255); | |
+ } | |
+ if (key_used && key_do_regen) { | |
+ generate_ephemeral_server_key(); | |
+ key_used = 0; | |
+ key_do_regen = 0; | |
+ } | |
+ if (ret < 0) | |
+ continue; | |
+ | |
+ for (i = 0; i < options.max_startups; i++) | |
+ if (startup_pipes[i] != -1 && | |
+ FD_ISSET(startup_pipes[i], fdset)) { | |
+ /* | |
+ * the read end of the pipe is ready | |
+ * if the child has closed the pipe | |
+ * after successful authentication | |
+ * or if the child has died | |
+ */ | |
+ close(startup_pipes[i]); | |
+ startup_pipes[i] = -1; | |
+ startups--; | |
+ } | |
+ for (i = 0; i < num_listen_socks; i++) { | |
+ if (!FD_ISSET(listen_socks[i], fdset)) | |
+ continue; | |
+ fromlen = sizeof(from); | |
+ *newsock = accept(listen_socks[i], | |
+ (struct sockaddr *)&from, &fromlen); | |
+ if (*newsock < 0) { | |
+ if (errno != EINTR && errno != EWOULDBLOCK && | |
+ errno != ECONNABORTED && errno != EAGAIN) | |
+ error("accept: %.100s", | |
+ strerror(errno)); | |
+ if (errno == EMFILE || errno == ENFILE) | |
+ usleep(100 * 1000); | |
+ continue; | |
+ } | |
+ if (unset_nonblock(*newsock) == -1) { | |
+ close(*newsock); | |
+ continue; | |
+ } | |
+ if (drop_connection(startups) == 1) { | |
+ debug("drop connection #%d", startups); | |
+ close(*newsock); | |
+ continue; | |
+ } | |
+ if (pipe(startup_p) == -1) { | |
+ close(*newsock); | |
+ continue; | |
+ } | |
+ | |
+ if (rexec_flag && socketpair(AF_UNIX, | |
+ SOCK_STREAM, 0, config_s) == -1) { | |
+ error("reexec socketpair: %s", | |
+ strerror(errno)); | |
+ close(*newsock); | |
+ close(startup_p[0]); | |
+ close(startup_p[1]); | |
+ continue; | |
+ } | |
+ | |
+ for (j = 0; j < options.max_startups; j++) | |
+ if (startup_pipes[j] == -1) { | |
+ startup_pipes[j] = startup_p[0]; | |
+ if (maxfd < startup_p[0]) | |
+ maxfd = startup_p[0]; | |
+ startups++; | |
+ break; | |
+ } | |
+ | |
+ /* | |
+ * Got connection. Fork a child to handle it, unless | |
+ * we are in debugging mode. | |
+ */ | |
+ if (debug_flag) { | |
+ /* | |
+ * In debugging mode. Close the listening | |
+ * socket, and start processing the | |
+ * connection without forking. | |
+ */ | |
+ debug("Server will not fork when running in debugging mode."); | |
+ close_listen_socks(); | |
+ *sock_in = *newsock; | |
+ *sock_out = *newsock; | |
+ close(startup_p[0]); | |
+ close(startup_p[1]); | |
+ startup_pipe = -1; | |
+ pid = getpid(); | |
+ if (rexec_flag) { | |
+ send_rexec_state(config_s[0], | |
+ &cfg); | |
+ close(config_s[0]); | |
+ } | |
+ break; | |
+ } | |
+ | |
+ /* | |
+ * Normal production daemon. Fork, and have | |
+ * the child process the connection. The | |
+ * parent continues listening. | |
+ */ | |
+ platform_pre_fork(); | |
+ if ((pid = fork()) == 0) { | |
+ /* | |
+ * Child. Close the listening and | |
+ * max_startup sockets. Start using | |
+ * the accepted socket. Reinitialize | |
+ * logging (since our pid has changed). | |
+ * We break out of the loop to handle | |
+ * the connection. | |
+ */ | |
+ platform_post_fork_child(); | |
+ startup_pipe = startup_p[1]; | |
+ close_startup_pipes(); | |
+ close_listen_socks(); | |
+ *sock_in = *newsock; | |
+ *sock_out = *newsock; | |
+ log_init(__progname, | |
+ options.log_level, | |
+ options.log_facility, | |
+ log_stderr); | |
+ if (rexec_flag) | |
+ close(config_s[0]); | |
+ break; | |
+ } | |
+ | |
+ /* Parent. Stay in the loop. */ | |
+ platform_post_fork_parent(pid); | |
+ if (pid < 0) | |
+ error("fork: %.100s", strerror(errno)); | |
+ else | |
+ debug("Forked child %ld.", (long)pid); | |
+ | |
+ close(startup_p[1]); | |
+ | |
+ if (rexec_flag) { | |
+ send_rexec_state(config_s[0], &cfg); | |
+ close(config_s[0]); | |
+ close(config_s[1]); | |
+ } | |
+ | |
+ /* | |
+ * Mark that the key has been used (it | |
+ * was "given" to the child). | |
+ */ | |
+ if ((options.protocol & SSH_PROTO_1) && | |
+ key_used == 0) { | |
+ /* Schedule server key regeneration alarm. */ | |
+ signal(SIGALRM, key_regeneration_alarm); | |
+ alarm(options.key_regeneration_time); | |
+ key_used = 1; | |
+ } | |
+ | |
+ close(*newsock); | |
+ | |
+ /* | |
+ * Ensure that our random state differs | |
+ * from that of the child | |
+ */ | |
+ arc4random_stir(); | |
+ arc4random_buf(rnd, sizeof(rnd)); | |
+ RAND_seed(rnd, sizeof(rnd)); | |
+ explicit_bzero(rnd, sizeof(rnd)); | |
+ } | |
+ | |
+ /* child process check (or debug mode) */ | |
+ if (num_listen_socks < 0) | |
+ break; | |
+ } | |
+} | |
+ | |
+ | |
+/* | |
+ * Main program for the daemon. | |
+ */ | |
+int | |
+main(int ac, char **av) | |
+{ | |
+ extern char *optarg; | |
+ extern int optind; | |
+ int opt, i, j, on = 1; | |
+ int sock_in = -1, sock_out = -1, newsock = -1; | |
+ const char *remote_ip; | |
+ int remote_port; | |
+ char *line, *logfile = NULL; | |
+ int config_s[2] = { -1 , -1 }; | |
+ u_int n; | |
+ u_int64_t ibytes, obytes; | |
+ mode_t new_umask; | |
+ Key *key; | |
+ Key *pubkey; | |
+ int keytype; | |
+ Authctxt *authctxt; | |
+ struct connection_info *connection_info = get_connection_info(0, 0); | |
+ | |
+#ifdef HAVE_SECUREWARE | |
+ (void)set_auth_parameters(ac, av); | |
+#endif | |
+ __progname = ssh_get_progname(av[0]); | |
+ | |
+ /* Save argv. Duplicate so setproctitle emulation doesn't clobber it */ | |
+ saved_argc = ac; | |
+ rexec_argc = ac; | |
+ saved_argv = xcalloc(ac + 1, sizeof(*saved_argv)); | |
+ for (i = 0; i < ac; i++) | |
+ saved_argv[i] = xstrdup(av[i]); | |
+ saved_argv[i] = NULL; | |
+ | |
+#ifndef HAVE_SETPROCTITLE | |
+ /* Prepare for later setproctitle emulation */ | |
+ compat_init_setproctitle(ac, av); | |
+ av = saved_argv; | |
+#endif | |
+ | |
+ if (geteuid() == 0 && setgroups(0, NULL) == -1) | |
+ debug("setgroups(): %.200s", strerror(errno)); | |
+ | |
+ /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ | |
+ sanitise_stdfd(); | |
+ | |
+ /* Initialize configuration options to their default values. */ | |
+ initialize_server_options(&options); | |
+ | |
+ /* Parse command-line arguments. */ | |
+ while ((opt = getopt(ac, av, "f:p:b:k:h:g:u:o:C:dDeE:iqrtQRT46")) != -1) { | |
+ switch (opt) { | |
+ case '4': | |
+ options.address_family = AF_INET; | |
+ break; | |
+ case '6': | |
+ options.address_family = AF_INET6; | |
+ break; | |
+ case 'f': | |
+ config_file_name = optarg; | |
+ break; | |
+ case 'c': | |
+ if (options.num_host_cert_files >= MAX_HOSTCERTS) { | |
+ fprintf(stderr, "too many host certificates.\n"); | |
+ exit(1); | |
+ } | |
+ options.host_cert_files[options.num_host_cert_files++] = | |
+ derelativise_path(optarg); | |
+ break; | |
+ case 'd': | |
+ if (debug_flag == 0) { | |
+ debug_flag = 1; | |
+ options.log_level = SYSLOG_LEVEL_DEBUG1; | |
+ } else if (options.log_level < SYSLOG_LEVEL_DEBUG3) | |
+ options.log_level++; | |
+ break; | |
+ case 'D': | |
+ no_daemon_flag = 1; | |
+ break; | |
+ case 'E': | |
+ logfile = xstrdup(optarg); | |
+ /* FALLTHROUGH */ | |
+ case 'e': | |
+ log_stderr = 1; | |
+ break; | |
+ case 'i': | |
+ inetd_flag = 1; | |
+ break; | |
+ case 'r': | |
+ rexec_flag = 0; | |
+ break; | |
+ case 'R': | |
+ rexeced_flag = 1; | |
+ inetd_flag = 1; | |
+ break; | |
+ case 'Q': | |
+ /* ignored */ | |
+ break; | |
+ case 'q': | |
+ options.log_level = SYSLOG_LEVEL_QUIET; | |
+ break; | |
+ case 'b': | |
+ options.server_key_bits = (int)strtonum(optarg, 256, | |
+ 32768, NULL); | |
+ break; | |
+ case 'p': | |
+ options.ports_from_cmdline = 1; | |
+ if (options.num_ports >= MAX_PORTS) { | |
+ fprintf(stderr, "too many ports.\n"); | |
+ exit(1); | |
+ } | |
+ options.ports[options.num_ports++] = a2port(optarg); | |
+ if (options.ports[options.num_ports-1] <= 0) { | |
+ fprintf(stderr, "Bad port number.\n"); | |
+ exit(1); | |
+ } | |
+ break; | |
+ case 'g': | |
+ if ((options.login_grace_time = convtime(optarg)) == -1) { | |
+ fprintf(stderr, "Invalid login grace time.\n"); | |
+ exit(1); | |
+ } | |
+ break; | |
+ case 'k': | |
+ if ((options.key_regeneration_time = convtime(optarg)) == -1) { | |
+ fprintf(stderr, "Invalid key regeneration interval.\n"); | |
+ exit(1); | |
+ } | |
+ break; | |
+ case 'h': | |
+ if (options.num_host_key_files >= MAX_HOSTKEYS) { | |
+ fprintf(stderr, "too many host keys.\n"); | |
+ exit(1); | |
+ } | |
+ options.host_key_files[options.num_host_key_files++] = | |
+ derelativise_path(optarg); | |
+ break; | |
+ case 't': | |
+ test_flag = 1; | |
+ break; | |
+ case 'T': | |
+ test_flag = 2; | |
+ break; | |
+ case 'C': | |
+ if (parse_server_match_testspec(connection_info, | |
+ optarg) == -1) | |
+ exit(1); | |
+ break; | |
+ case 'u': | |
+ utmp_len = (u_int)strtonum(optarg, 0, MAXHOSTNAMELEN+1, NULL); | |
+ if (utmp_len > MAXHOSTNAMELEN) { | |
+ fprintf(stderr, "Invalid utmp length.\n"); | |
+ exit(1); | |
+ } | |
+ break; | |
+ case 'o': | |
+ line = xstrdup(optarg); | |
+ if (process_server_config_line(&options, line, | |
+ "command-line", 0, NULL, NULL) != 0) | |
+ exit(1); | |
+ free(line); | |
+ break; | |
+ case '?': | |
+ default: | |
+ usage(); | |
+ break; | |
+ } | |
+ } | |
+ if (rexeced_flag || inetd_flag) | |
+ rexec_flag = 0; | |
+ if (!test_flag && (rexec_flag && (av[0] == NULL || *av[0] != '/'))) | |
+ fatal("sshd re-exec requires execution with an absolute path"); | |
+ if (rexeced_flag) | |
+ closefrom(REEXEC_MIN_FREE_FD); | |
+ else | |
+ closefrom(REEXEC_DEVCRYPTO_RESERVED_FD); | |
+ | |
+#ifdef WITH_OPENSSL | |
+ OpenSSL_add_all_algorithms(); | |
+#endif | |
+ | |
+ /* If requested, redirect the logs to the specified logfile. */ | |
+ if (logfile != NULL) { | |
+ log_redirect_stderr_to(logfile); | |
+ free(logfile); | |
+ } | |
+ /* | |
+ * Force logging to stderr until we have loaded the private host | |
+ * key (unless started from inetd) | |
+ */ | |
+ log_init(__progname, | |
+ options.log_level == SYSLOG_LEVEL_NOT_SET ? | |
+ SYSLOG_LEVEL_INFO : options.log_level, | |
+ options.log_facility == SYSLOG_FACILITY_NOT_SET ? | |
+ SYSLOG_FACILITY_AUTH : options.log_facility, | |
+ log_stderr || !inetd_flag); | |
+ | |
+ /* | |
+ * Unset KRB5CCNAME, otherwise the user's session may inherit it from | |
+ * root's environment | |
+ */ | |
+ if (getenv("KRB5CCNAME") != NULL) | |
+ (void) unsetenv("KRB5CCNAME"); | |
+ | |
+#ifdef _UNICOS | |
+ /* Cray can define user privs drop all privs now! | |
+ * Not needed on PRIV_SU systems! | |
+ */ | |
+ drop_cray_privs(); | |
+#endif | |
+ | |
+ sensitive_data.server_key = NULL; | |
+ sensitive_data.ssh1_host_key = NULL; | |
+ sensitive_data.have_ssh1_key = 0; | |
+ sensitive_data.have_ssh2_key = 0; | |
+ | |
+ /* | |
+ * If we're doing an extended config test, make sure we have all of | |
+ * the parameters we need. If we're not doing an extended test, | |
+ * do not silently ignore connection test params. | |
+ */ | |
+ if (test_flag >= 2 && server_match_spec_complete(connection_info) == 0) | |
+ fatal("user, host and addr are all required when testing " | |
+ "Match configs"); | |
+ if (test_flag < 2 && server_match_spec_complete(connection_info) >= 0) | |
+ fatal("Config test connection parameter (-C) provided without " | |
+ "test mode (-T)"); | |
+ | |
+ /* Fetch our configuration */ | |
+ buffer_init(&cfg); | |
+ if (rexeced_flag) | |
+ recv_rexec_state(REEXEC_CONFIG_PASS_FD, &cfg); | |
+ else | |
+ load_server_config(config_file_name, &cfg); | |
+ | |
+ parse_server_config(&options, rexeced_flag ? "rexec" : config_file_name, | |
+ &cfg, NULL); | |
+ | |
+ seed_rng(); | |
+ | |
+ /* Fill in default values for those options not explicitly set. */ | |
+ fill_default_server_options(&options); | |
+ | |
+ /* challenge-response is implemented via keyboard interactive */ | |
+ if (options.challenge_response_authentication) | |
+ options.kbd_interactive_authentication = 1; | |
+ | |
+ /* Check that options are sensible */ | |
+ if (options.authorized_keys_command_user == NULL && | |
+ (options.authorized_keys_command != NULL && | |
+ strcasecmp(options.authorized_keys_command, "none") != 0)) | |
+ fatal("AuthorizedKeysCommand set without " | |
+ "AuthorizedKeysCommandUser"); | |
+ | |
+ /* | |
+ * Check whether there is any path through configured auth methods. | |
+ * Unfortunately it is not possible to verify this generally before | |
+ * daemonisation in the presence of Match block, but this catches | |
+ * and warns for trivial misconfigurations that could break login. | |
+ */ | |
+ if (options.num_auth_methods != 0) { | |
+ if ((options.protocol & SSH_PROTO_1)) | |
+ fatal("AuthenticationMethods is not supported with " | |
+ "SSH protocol 1"); | |
+ for (n = 0; n < options.num_auth_methods; n++) { | |
+ if (auth2_methods_valid(options.auth_methods[n], | |
+ 1) == 0) | |
+ break; | |
+ } | |
+ if (n >= options.num_auth_methods) | |
+ fatal("AuthenticationMethods cannot be satisfied by " | |
+ "enabled authentication methods"); | |
+ } | |
+ | |
+ /* set default channel AF */ | |
+ channel_set_af(options.address_family); | |
+ | |
+ /* Check that there are no remaining arguments. */ | |
+ if (optind < ac) { | |
+ fprintf(stderr, "Extra argument %s.\n", av[optind]); | |
+ exit(1); | |
+ } | |
+ | |
+ debug("sshd version %s, %s", SSH_VERSION, | |
+#ifdef WITH_OPENSSL | |
+ SSLeay_version(SSLEAY_VERSION) | |
+#else | |
+ "without OpenSSL" | |
+#endif | |
+ ); | |
+ | |
+ /* Store privilege separation user for later use if required. */ | |
+ if ((privsep_pw = getpwnam(SSH_PRIVSEP_USER)) == NULL) { | |
+ if (use_privsep || options.kerberos_authentication) | |
+ fatal("Privilege separation user %s does not exist", | |
+ SSH_PRIVSEP_USER); | |
+ } else { | |
+ explicit_bzero(privsep_pw->pw_passwd, | |
+ strlen(privsep_pw->pw_passwd)); | |
+ privsep_pw = pwcopy(privsep_pw); | |
+ free(privsep_pw->pw_passwd); | |
+ privsep_pw->pw_passwd = xstrdup("*"); | |
+ } | |
+ endpwent(); | |
+ | |
+ /* load host keys */ | |
+ sensitive_data.host_keys = xcalloc(options.num_host_key_files, | |
+ sizeof(Key *)); | |
+ sensitive_data.host_pubkeys = xcalloc(options.num_host_key_files, | |
+ sizeof(Key *)); | |
+ for (i = 0; i < options.num_host_key_files; i++) { | |
+ sensitive_data.host_keys[i] = NULL; | |
+ sensitive_data.host_pubkeys[i] = NULL; | |
+ } | |
+ | |
+ if (options.host_key_agent) { | |
+ if (strcmp(options.host_key_agent, SSH_AUTHSOCKET_ENV_NAME)) | |
+ setenv(SSH_AUTHSOCKET_ENV_NAME, | |
+ options.host_key_agent, 1); | |
+ have_agent = ssh_agent_present(); | |
+ } | |
+ | |
+ for (i = 0; i < options.num_host_key_files; i++) { | |
+ key = key_load_private(options.host_key_files[i], "", NULL); | |
+ pubkey = key_load_public(options.host_key_files[i], NULL); | |
+ sensitive_data.host_keys[i] = key; | |
+ sensitive_data.host_pubkeys[i] = pubkey; | |
+ | |
+ if (key == NULL && pubkey != NULL && pubkey->type != KEY_RSA1 && | |
+ have_agent) { | |
+ debug("will rely on agent for hostkey %s", | |
+ options.host_key_files[i]); | |
+ keytype = pubkey->type; | |
+ } else if (key != NULL) { | |
+ keytype = key->type; | |
+ } else { | |
+ error("Could not load host key: %s", | |
+ options.host_key_files[i]); | |
+ sensitive_data.host_keys[i] = NULL; | |
+ sensitive_data.host_pubkeys[i] = NULL; | |
+ continue; | |
+ } | |
+ | |
+ switch (keytype) { | |
+ case KEY_RSA1: | |
+ sensitive_data.ssh1_host_key = key; | |
+ sensitive_data.have_ssh1_key = 1; | |
+ break; | |
+ case KEY_RSA: | |
+ case KEY_DSA: | |
+ case KEY_ECDSA: | |
+ case KEY_ED25519: | |
+ sensitive_data.have_ssh2_key = 1; | |
+ break; | |
+ } | |
+ debug("private host key: #%d type %d %s", i, keytype, | |
+ key_type(key ? key : pubkey)); | |
+ } | |
+ if ((options.protocol & SSH_PROTO_1) && !sensitive_data.have_ssh1_key) { | |
+ logit("Disabling protocol version 1. Could not load host key"); | |
+ options.protocol &= ~SSH_PROTO_1; | |
+ } | |
+ 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; | |
+ } | |
+ if (!(options.protocol & (SSH_PROTO_1|SSH_PROTO_2))) { | |
+ logit("sshd: no hostkeys available -- exiting."); | |
+ exit(1); | |
+ } | |
+ | |
+ /* | |
+ * Load certificates. They are stored in an array at identical | |
+ * indices to the public keys that they relate to. | |
+ */ | |
+ sensitive_data.host_certificates = xcalloc(options.num_host_key_files, | |
+ sizeof(Key *)); | |
+ for (i = 0; i < options.num_host_key_files; i++) | |
+ sensitive_data.host_certificates[i] = NULL; | |
+ | |
+ for (i = 0; i < options.num_host_cert_files; i++) { | |
+ key = key_load_public(options.host_cert_files[i], NULL); | |
+ if (key == NULL) { | |
+ error("Could not load host certificate: %s", | |
+ options.host_cert_files[i]); | |
+ continue; | |
+ } | |
+ if (!key_is_cert(key)) { | |
+ error("Certificate file is not a certificate: %s", | |
+ options.host_cert_files[i]); | |
+ key_free(key); | |
+ continue; | |
+ } | |
+ /* Find matching private key */ | |
+ for (j = 0; j < options.num_host_key_files; j++) { | |
+ if (key_equal_public(key, | |
+ sensitive_data.host_keys[j])) { | |
+ sensitive_data.host_certificates[j] = key; | |
+ break; | |
+ } | |
+ } | |
+ if (j >= options.num_host_key_files) { | |
+ error("No matching private key for certificate: %s", | |
+ options.host_cert_files[i]); | |
+ key_free(key); | |
+ continue; | |
+ } | |
+ sensitive_data.host_certificates[j] = key; | |
+ debug("host certificate: #%d type %d %s", j, key->type, | |
+ key_type(key)); | |
+ } | |
+ | |
+#ifdef WITH_SSH1 | |
+ /* Check certain values for sanity. */ | |
+ if (options.protocol & SSH_PROTO_1) { | |
+ if (options.server_key_bits < 512 || | |
+ options.server_key_bits > 32768) { | |
+ fprintf(stderr, "Bad server key size.\n"); | |
+ exit(1); | |
+ } | |
+ /* | |
+ * Check that server and host key lengths differ sufficiently. This | |
+ * is necessary to make double encryption work with rsaref. Oh, I | |
+ * hate software patents. I dont know if this can go? Niels | |
+ */ | |
+ if (options.server_key_bits > | |
+ BN_num_bits(sensitive_data.ssh1_host_key->rsa->n) - | |
+ SSH_KEY_BITS_RESERVED && options.server_key_bits < | |
+ BN_num_bits(sensitive_data.ssh1_host_key->rsa->n) + | |
+ SSH_KEY_BITS_RESERVED) { | |
+ options.server_key_bits = | |
+ BN_num_bits(sensitive_data.ssh1_host_key->rsa->n) + | |
+ SSH_KEY_BITS_RESERVED; | |
+ debug("Forcing server key to %d bits to make it differ from host key.", | |
+ options.server_key_bits); | |
+ } | |
+ } | |
+#endif | |
+ | |
+ if (use_privsep) { | |
+ struct stat st; | |
+ | |
+ if ((stat(_PATH_PRIVSEP_CHROOT_DIR, &st) == -1) || | |
+ (S_ISDIR(st.st_mode) == 0)) | |
+ fatal("Missing privilege separation directory: %s", | |
+ _PATH_PRIVSEP_CHROOT_DIR); | |
+ | |
+#ifdef HAVE_CYGWIN | |
+ if (check_ntsec(_PATH_PRIVSEP_CHROOT_DIR) && | |
+ (st.st_uid != getuid () || | |
+ (st.st_mode & (S_IWGRP|S_IWOTH)) != 0)) | |
+#else | |
+ if (st.st_uid != 0 || (st.st_mode & (S_IWGRP|S_IWOTH)) != 0) | |
+#endif | |
+ fatal("%s must be owned by root and not group or " | |
+ "world-writable.", _PATH_PRIVSEP_CHROOT_DIR); | |
+ } | |
+ | |
+ if (test_flag > 1) { | |
+ if (server_match_spec_complete(connection_info) == 1) | |
+ parse_server_match_config(&options, connection_info); | |
+ dump_config(&options); | |
+ } | |
+ | |
+ /* Configuration looks good, so exit if in test mode. */ | |
+ if (test_flag) | |
+ exit(0); | |
+ | |
+ /* | |
+ * Clear out any supplemental groups we may have inherited. This | |
+ * prevents inadvertent creation of files with bad modes (in the | |
+ * portable version at least, it's certainly possible for PAM | |
+ * to create a file, and we can't control the code in every | |
+ * module which might be used). | |
+ */ | |
+ if (setgroups(0, NULL) < 0) | |
+ debug("setgroups() failed: %.200s", strerror(errno)); | |
+ | |
+ if (rexec_flag) { | |
+ rexec_argv = xcalloc(rexec_argc + 2, sizeof(char *)); | |
+ for (i = 0; i < rexec_argc; i++) { | |
+ debug("rexec_argv[%d]='%s'", i, saved_argv[i]); | |
+ rexec_argv[i] = saved_argv[i]; | |
+ } | |
+ rexec_argv[rexec_argc] = "-R"; | |
+ rexec_argv[rexec_argc + 1] = NULL; | |
+ } | |
+ | |
+ /* Ensure that umask disallows at least group and world write */ | |
+ new_umask = umask(0077) | 0022; | |
+ (void) umask(new_umask); | |
+ | |
+ /* Initialize the log (it is reinitialized below in case we forked). */ | |
+ if (debug_flag && (!inetd_flag || rexeced_flag)) | |
+ log_stderr = 1; | |
+ log_init(__progname, options.log_level, options.log_facility, log_stderr); | |
+ | |
+ /* | |
+ * If not in debugging mode, and not started from inetd, disconnect | |
+ * from the controlling terminal, and fork. The original process | |
+ * exits. | |
+ */ | |
+ if (!(debug_flag || inetd_flag || no_daemon_flag)) { | |
+#ifdef TIOCNOTTY | |
+ int fd; | |
+#endif /* TIOCNOTTY */ | |
+ if (daemon(0, 0) < 0) | |
+ fatal("daemon() failed: %.200s", strerror(errno)); | |
+ | |
+ /* Disconnect from the controlling tty. */ | |
+#ifdef TIOCNOTTY | |
+ fd = open(_PATH_TTY, O_RDWR | O_NOCTTY); | |
+ if (fd >= 0) { | |
+ (void) ioctl(fd, TIOCNOTTY, NULL); | |
+ close(fd); | |
+ } | |
+#endif /* TIOCNOTTY */ | |
+ } | |
+ /* Reinitialize the log (because of the fork above). */ | |
+ log_init(__progname, options.log_level, options.log_facility, log_stderr); | |
+ | |
+ /* Chdir to the root directory so that the current disk can be | |
+ unmounted if desired. */ | |
+ if (chdir("/") == -1) | |
+ error("chdir(\"/\"): %s", strerror(errno)); | |
+ | |
+ /* ignore SIGPIPE */ | |
+ signal(SIGPIPE, SIG_IGN); | |
+ | |
+ /* Get a connection, either from inetd or a listening TCP socket */ | |
+ if (inetd_flag) { | |
+ server_accept_inetd(&sock_in, &sock_out); | |
+ } else { | |
+ platform_pre_listen(); | |
+ server_listen(); | |
+ | |
+ if (options.protocol & SSH_PROTO_1) | |
+ generate_ephemeral_server_key(); | |
+ | |
+ signal(SIGHUP, sighup_handler); | |
+ signal(SIGCHLD, main_sigchld_handler); | |
+ signal(SIGTERM, sigterm_handler); | |
+ signal(SIGQUIT, sigterm_handler); | |
+ | |
+ /* | |
+ * Write out the pid file after the sigterm handler | |
+ * is setup and the listen sockets are bound | |
+ */ | |
+ if (!debug_flag) { | |
+ FILE *f = fopen(options.pid_file, "w"); | |
+ | |
+ if (f == NULL) { | |
+ error("Couldn't create pid file \"%s\": %s", | |
+ options.pid_file, strerror(errno)); | |
+ } else { | |
+ fprintf(f, "%ld\n", (long) getpid()); | |
+ fclose(f); | |
+ } | |
+ } | |
+ | |
+ /* Accept a connection and return in a forked child */ | |
+ server_accept_loop(&sock_in, &sock_out, | |
+ &newsock, config_s); | |
+ } | |
+ | |
+ /* This is the child processing a new connection. */ | |
+ setproctitle("%s", "[accepted]"); | |
+ | |
+ /* | |
+ * Create a new session and process group since the 4.4BSD | |
+ * setlogin() affects the entire process group. We don't | |
+ * want the child to be able to affect the parent. | |
+ */ | |
+#if !defined(SSHD_ACQUIRES_CTTY) | |
+ /* | |
+ * If setsid is called, on some platforms sshd will later acquire a | |
+ * controlling terminal which will result in "could not set | |
+ * controlling tty" errors. | |
+ */ | |
+ if (!debug_flag && !inetd_flag && setsid() < 0) | |
+ error("setsid: %.100s", strerror(errno)); | |
+#endif | |
+ | |
+ if (rexec_flag) { | |
+ int fd; | |
+ | |
+ debug("rexec start in %d out %d newsock %d pipe %d sock %d", | |
+ sock_in, sock_out, newsock, startup_pipe, config_s[0]); | |
+ dup2(newsock, STDIN_FILENO); | |
+ dup2(STDIN_FILENO, STDOUT_FILENO); | |
+ if (startup_pipe == -1) | |
+ close(REEXEC_STARTUP_PIPE_FD); | |
+ else if (startup_pipe != REEXEC_STARTUP_PIPE_FD) { | |
+ dup2(startup_pipe, REEXEC_STARTUP_PIPE_FD); | |
+ close(startup_pipe); | |
+ startup_pipe = REEXEC_STARTUP_PIPE_FD; | |
+ } | |
+ | |
+ dup2(config_s[1], REEXEC_CONFIG_PASS_FD); | |
+ close(config_s[1]); | |
+ | |
+ execv(rexec_argv[0], rexec_argv); | |
+ | |
+ /* Reexec has failed, fall back and continue */ | |
+ error("rexec of %s failed: %s", rexec_argv[0], strerror(errno)); | |
+ recv_rexec_state(REEXEC_CONFIG_PASS_FD, NULL); | |
+ log_init(__progname, options.log_level, | |
+ options.log_facility, log_stderr); | |
+ | |
+ /* Clean up fds */ | |
+ close(REEXEC_CONFIG_PASS_FD); | |
+ newsock = sock_out = sock_in = dup(STDIN_FILENO); | |
+ if ((fd = open(_PATH_DEVNULL, O_RDWR, 0)) != -1) { | |
+ dup2(fd, STDIN_FILENO); | |
+ dup2(fd, STDOUT_FILENO); | |
+ if (fd > STDERR_FILENO) | |
+ close(fd); | |
+ } | |
+ debug("rexec cleanup in %d out %d newsock %d pipe %d sock %d", | |
+ sock_in, sock_out, newsock, startup_pipe, config_s[0]); | |
+ } | |
+ | |
+ /* Executed child processes don't need these. */ | |
+ fcntl(sock_out, F_SETFD, FD_CLOEXEC); | |
+ fcntl(sock_in, F_SETFD, FD_CLOEXEC); | |
+ | |
+ /* | |
+ * Disable the key regeneration alarm. We will not regenerate the | |
+ * key since we are no longer in a position to give it to anyone. We | |
+ * will not restart on SIGHUP since it no longer makes sense. | |
+ */ | |
+ alarm(0); | |
+ signal(SIGALRM, SIG_DFL); | |
+ signal(SIGHUP, SIG_DFL); | |
+ signal(SIGTERM, SIG_DFL); | |
+ signal(SIGQUIT, SIG_DFL); | |
+ signal(SIGCHLD, SIG_DFL); | |
+ signal(SIGINT, SIG_DFL); | |
+ | |
+ /* | |
+ * Register our connection. This turns encryption off because we do | |
+ * not have a key. | |
+ */ | |
+ packet_set_connection(sock_in, sock_out); | |
+ packet_set_server(); | |
+ | |
+ /* Set SO_KEEPALIVE if requested. */ | |
+ if (options.tcp_keep_alive && packet_connection_is_on_socket() && | |
+ setsockopt(sock_in, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on)) < 0) | |
+ error("setsockopt SO_KEEPALIVE: %.100s", strerror(errno)); | |
+ | |
+ if ((remote_port = get_remote_port()) < 0) { | |
+ debug("get_remote_port failed"); | |
+ cleanup_exit(255); | |
+ } | |
+ | |
+ /* | |
+ * We use get_canonical_hostname with usedns = 0 instead of | |
+ * get_remote_ipaddr here so IP options will be checked. | |
+ */ | |
+ (void) get_canonical_hostname(0); | |
+ /* | |
+ * The rest of the code depends on the fact that | |
+ * get_remote_ipaddr() caches the remote ip, even if | |
+ * the socket goes away. | |
+ */ | |
+ remote_ip = get_remote_ipaddr(); | |
+ | |
+#ifdef SSH_AUDIT_EVENTS | |
+ audit_connection_from(remote_ip, remote_port); | |
+#endif | |
+ | |
+ /* Log the connection. */ | |
+ verbose("Connection from %s port %d on %s port %d", | |
+ remote_ip, remote_port, | |
+ get_local_ipaddr(sock_in), get_local_port()); | |
+ | |
+ /* | |
+ * We don't want to listen forever unless the other side | |
+ * successfully authenticates itself. So we set up an alarm which is | |
+ * cleared after successful authentication. A limit of zero | |
+ * indicates no limit. Note that we don't set the alarm in debugging | |
+ * mode; it is just annoying to have the server exit just when you | |
+ * are about to discover the bug. | |
+ */ | |
+ signal(SIGALRM, grace_alarm_handler); | |
+ if (!debug_flag) | |
+ alarm(options.login_grace_time); | |
+ | |
+ sshd_exchange_identification(sock_in, sock_out); | |
+ | |
+ /* In inetd mode, generate ephemeral key only for proto 1 connections */ | |
+ if (!compat20 && inetd_flag && sensitive_data.server_key == NULL) | |
+ generate_ephemeral_server_key(); | |
+ | |
+ packet_set_nonblocking(); | |
+ | |
+ /* allocate authentication context */ | |
+ authctxt = xcalloc(1, sizeof(*authctxt)); | |
+ | |
+ authctxt->loginmsg = &loginmsg; | |
+ | |
+ /* XXX global for cleanup, access from other modules */ | |
+ the_authctxt = authctxt; | |
+ | |
+ /* prepare buffer to collect messages to display to user after login */ | |
+ buffer_init(&loginmsg); | |
+ auth_debug_reset(); | |
+ | |
+ if (use_privsep) { | |
+ if (privsep_preauth(authctxt) == 1) | |
+ goto authenticated; | |
+ } else if (compat20 && have_agent) | |
+ auth_conn = ssh_get_authentication_connection(); | |
+ | |
+ /* perform the key exchange */ | |
+ /* authenticate user and start session */ | |
+ if (compat20) { | |
+ do_ssh2_kex(); | |
+ do_authentication2(authctxt); | |
+ } else { | |
+#ifdef WITH_SSH1 | |
+ do_ssh1_kex(); | |
+ do_authentication(authctxt); | |
+#else | |
+ fatal("ssh1 not supported"); | |
+#endif | |
+ } | |
+ /* | |
+ * If we use privilege separation, the unprivileged child transfers | |
+ * the current keystate and exits | |
+ */ | |
+ if (use_privsep) { | |
+ mm_send_keystate(pmonitor); | |
+ exit(0); | |
+ } | |
+ | |
+ authenticated: | |
+ /* | |
+ * Cancel the alarm we set to limit the time taken for | |
+ * authentication. | |
+ */ | |
+ alarm(0); | |
+ signal(SIGALRM, SIG_DFL); | |
+ authctxt->authenticated = 1; | |
+ if (startup_pipe != -1) { | |
+ close(startup_pipe); | |
+ startup_pipe = -1; | |
+ } | |
+ | |
+#ifdef SSH_AUDIT_EVENTS | |
+ audit_event(SSH_AUTH_SUCCESS); | |
+#endif | |
+ | |
+#ifdef GSSAPI | |
+ if (options.gss_authentication) { | |
+ temporarily_use_uid(authctxt->pw); | |
+ ssh_gssapi_storecreds(); | |
+ restore_uid(); | |
+ } | |
+#endif | |
+#ifdef USE_PAM | |
+ if (options.use_pam) { | |
+ do_pam_setcred(1); | |
+ do_pam_session(); | |
+ } | |
+#endif | |
+ | |
+ /* | |
+ * In privilege separation, we fork another child and prepare | |
+ * file descriptor passing. | |
+ */ | |
+ if (use_privsep) { | |
+ privsep_postauth(authctxt); | |
+ /* the monitor process [priv] will not return */ | |
+ if (!compat20) | |
+ destroy_sensitive_data(); | |
+ } | |
+ | |
+ packet_set_timeout(options.client_alive_interval, | |
+ options.client_alive_count_max); | |
+ | |
+ /* Start session. */ | |
+ do_authenticated(authctxt); | |
+ | |
+ /* The connection has been terminated. */ | |
+ packet_get_state(MODE_IN, NULL, NULL, NULL, &ibytes); | |
+ packet_get_state(MODE_OUT, NULL, NULL, NULL, &obytes); | |
+ verbose("Transferred: sent %llu, received %llu bytes", | |
+ (unsigned long long)obytes, (unsigned long long)ibytes); | |
+ | |
+ verbose("Closing connection to %.500s port %d", remote_ip, remote_port); | |
+ | |
+#ifdef USE_PAM | |
+ if (options.use_pam) | |
+ finish_pam(); | |
+#endif /* USE_PAM */ | |
+ | |
+#ifdef SSH_AUDIT_EVENTS | |
+ PRIVSEP(audit_event(SSH_CONNECTION_CLOSE)); | |
+#endif | |
+ | |
+ packet_close(); | |
+ | |
+ if (use_privsep) | |
+ mm_terminate(); | |
+ | |
+ exit(0); | |
+} | |
+ | |
+#ifdef WITH_SSH1 | |
+/* | |
+ * Decrypt session_key_int using our private server key and private host key | |
+ * (key with larger modulus first). | |
+ */ | |
+int | |
+ssh1_session_key(BIGNUM *session_key_int) | |
+{ | |
+ int rsafail = 0; | |
+ | |
+ if (BN_cmp(sensitive_data.server_key->rsa->n, | |
+ sensitive_data.ssh1_host_key->rsa->n) > 0) { | |
+ /* Server key has bigger modulus. */ | |
+ if (BN_num_bits(sensitive_data.server_key->rsa->n) < | |
+ BN_num_bits(sensitive_data.ssh1_host_key->rsa->n) + | |
+ SSH_KEY_BITS_RESERVED) { | |
+ fatal("do_connection: %s: " | |
+ "server_key %d < host_key %d + SSH_KEY_BITS_RESERVED %d", | |
+ get_remote_ipaddr(), | |
+ BN_num_bits(sensitive_data.server_key->rsa->n), | |
+ BN_num_bits(sensitive_data.ssh1_host_key->rsa->n), | |
+ SSH_KEY_BITS_RESERVED); | |
+ } | |
+ if (rsa_private_decrypt(session_key_int, session_key_int, | |
+ sensitive_data.server_key->rsa) != 0) | |
+ rsafail++; | |
+ if (rsa_private_decrypt(session_key_int, session_key_int, | |
+ sensitive_data.ssh1_host_key->rsa) != 0) | |
+ rsafail++; | |
+ } else { | |
+ /* Host key has bigger modulus (or they are equal). */ | |
+ if (BN_num_bits(sensitive_data.ssh1_host_key->rsa->n) < | |
+ BN_num_bits(sensitive_data.server_key->rsa->n) + | |
+ SSH_KEY_BITS_RESERVED) { | |
+ fatal("do_connection: %s: " | |
+ "host_key %d < server_key %d + SSH_KEY_BITS_RESERVED %d", | |
+ get_remote_ipaddr(), | |
+ BN_num_bits(sensitive_data.ssh1_host_key->rsa->n), | |
+ BN_num_bits(sensitive_data.server_key->rsa->n), | |
+ SSH_KEY_BITS_RESERVED); | |
+ } | |
+ if (rsa_private_decrypt(session_key_int, session_key_int, | |
+ sensitive_data.ssh1_host_key->rsa) != 0) | |
+ rsafail++; | |
+ if (rsa_private_decrypt(session_key_int, session_key_int, | |
+ sensitive_data.server_key->rsa) != 0) | |
+ rsafail++; | |
+ } | |
+ return (rsafail); | |
+} | |
+ | |
+/* | |
+ * SSH1 key exchange | |
+ */ | |
+static void | |
+do_ssh1_kex(void) | |
+{ | |
+ int i, len; | |
+ int rsafail = 0; | |
+ BIGNUM *session_key_int; | |
+ u_char session_key[SSH_SESSION_KEY_LENGTH]; | |
+ u_char cookie[8]; | |
+ u_int cipher_type, auth_mask, protocol_flags; | |
+ | |
+ /* | |
+ * Generate check bytes that the client must send back in the user | |
+ * packet in order for it to be accepted; this is used to defy ip | |
+ * spoofing attacks. Note that this only works against somebody | |
+ * doing IP spoofing from a remote machine; any machine on the local | |
+ * network can still see outgoing packets and catch the random | |
+ * cookie. This only affects rhosts authentication, and this is one | |
+ * of the reasons why it is inherently insecure. | |
+ */ | |
+ arc4random_buf(cookie, sizeof(cookie)); | |
+ | |
+ /* | |
+ * Send our public key. We include in the packet 64 bits of random | |
+ * data that must be matched in the reply in order to prevent IP | |
+ * spoofing. | |
+ */ | |
+ packet_start(SSH_SMSG_PUBLIC_KEY); | |
+ for (i = 0; i < 8; i++) | |
+ packet_put_char(cookie[i]); | |
+ | |
+ /* Store our public server RSA key. */ | |
+ packet_put_int(BN_num_bits(sensitive_data.server_key->rsa->n)); | |
+ packet_put_bignum(sensitive_data.server_key->rsa->e); | |
+ packet_put_bignum(sensitive_data.server_key->rsa->n); | |
+ | |
+ /* Store our public host RSA key. */ | |
+ packet_put_int(BN_num_bits(sensitive_data.ssh1_host_key->rsa->n)); | |
+ packet_put_bignum(sensitive_data.ssh1_host_key->rsa->e); | |
+ packet_put_bignum(sensitive_data.ssh1_host_key->rsa->n); | |
+ | |
+ /* Put protocol flags. */ | |
+ packet_put_int(SSH_PROTOFLAG_HOST_IN_FWD_OPEN); | |
+ | |
+ /* Declare which ciphers we support. */ | |
+ packet_put_int(cipher_mask_ssh1(0)); | |
+ | |
+ /* Declare supported authentication types. */ | |
+ auth_mask = 0; | |
+ if (options.rhosts_rsa_authentication) | |
+ auth_mask |= 1 << SSH_AUTH_RHOSTS_RSA; | |
+ if (options.rsa_authentication) | |
+ auth_mask |= 1 << SSH_AUTH_RSA; | |
+ if (options.challenge_response_authentication == 1) | |
+ auth_mask |= 1 << SSH_AUTH_TIS; | |
+ if (options.password_authentication) | |
+ auth_mask |= 1 << SSH_AUTH_PASSWORD; | |
+ packet_put_int(auth_mask); | |
+ | |
+ /* Send the packet and wait for it to be sent. */ | |
+ packet_send(); | |
+ packet_write_wait(); | |
+ | |
+ debug("Sent %d bit server key and %d bit host key.", | |
+ BN_num_bits(sensitive_data.server_key->rsa->n), | |
+ BN_num_bits(sensitive_data.ssh1_host_key->rsa->n)); | |
+ | |
+ /* Read clients reply (cipher type and session key). */ | |
+ packet_read_expect(SSH_CMSG_SESSION_KEY); | |
+ | |
+ /* Get cipher type and check whether we accept this. */ | |
+ cipher_type = packet_get_char(); | |
+ | |
+ if (!(cipher_mask_ssh1(0) & (1 << cipher_type))) | |
+ packet_disconnect("Warning: client selects unsupported cipher."); | |
+ | |
+ /* Get check bytes from the packet. These must match those we | |
+ sent earlier with the public key packet. */ | |
+ for (i = 0; i < 8; i++) | |
+ if (cookie[i] != packet_get_char()) | |
+ packet_disconnect("IP Spoofing check bytes do not match."); | |
+ | |
+ debug("Encryption type: %.200s", cipher_name(cipher_type)); | |
+ | |
+ /* Get the encrypted integer. */ | |
+ if ((session_key_int = BN_new()) == NULL) | |
+ fatal("do_ssh1_kex: BN_new failed"); | |
+ packet_get_bignum(session_key_int); | |
+ | |
+ protocol_flags = packet_get_int(); | |
+ packet_set_protocol_flags(protocol_flags); | |
+ packet_check_eom(); | |
+ | |
+ /* Decrypt session_key_int using host/server keys */ | |
+ rsafail = PRIVSEP(ssh1_session_key(session_key_int)); | |
+ | |
+ /* | |
+ * Extract session key from the decrypted integer. The key is in the | |
+ * least significant 256 bits of the integer; the first byte of the | |
+ * key is in the highest bits. | |
+ */ | |
+ if (!rsafail) { | |
+ (void) BN_mask_bits(session_key_int, sizeof(session_key) * 8); | |
+ len = BN_num_bytes(session_key_int); | |
+ if (len < 0 || (u_int)len > sizeof(session_key)) { | |
+ error("do_ssh1_kex: bad session key len from %s: " | |
+ "session_key_int %d > sizeof(session_key) %lu", | |
+ get_remote_ipaddr(), len, (u_long)sizeof(session_key)); | |
+ rsafail++; | |
+ } else { | |
+ explicit_bzero(session_key, sizeof(session_key)); | |
+ BN_bn2bin(session_key_int, | |
+ session_key + sizeof(session_key) - len); | |
+ | |
+ derive_ssh1_session_id( | |
+ sensitive_data.ssh1_host_key->rsa->n, | |
+ sensitive_data.server_key->rsa->n, | |
+ cookie, session_id); | |
+ /* | |
+ * Xor the first 16 bytes of the session key with the | |
+ * session id. | |
+ */ | |
+ for (i = 0; i < 16; i++) | |
+ session_key[i] ^= session_id[i]; | |
+ } | |
+ } | |
+ if (rsafail) { | |
+ int bytes = BN_num_bytes(session_key_int); | |
+ u_char *buf = xmalloc(bytes); | |
+ struct ssh_digest_ctx *md; | |
+ | |
+ logit("do_connection: generating a fake encryption key"); | |
+ BN_bn2bin(session_key_int, buf); | |
+ if ((md = ssh_digest_start(SSH_DIGEST_MD5)) == NULL || | |
+ ssh_digest_update(md, buf, bytes) < 0 || | |
+ ssh_digest_update(md, sensitive_data.ssh1_cookie, | |
+ SSH_SESSION_KEY_LENGTH) < 0 || | |
+ ssh_digest_final(md, session_key, sizeof(session_key)) < 0) | |
+ fatal("%s: md5 failed", __func__); | |
+ ssh_digest_free(md); | |
+ if ((md = ssh_digest_start(SSH_DIGEST_MD5)) == NULL || | |
+ ssh_digest_update(md, session_key, 16) < 0 || | |
+ ssh_digest_update(md, sensitive_data.ssh1_cookie, | |
+ SSH_SESSION_KEY_LENGTH) < 0 || | |
+ ssh_digest_final(md, session_key + 16, | |
+ sizeof(session_key) - 16) < 0) | |
+ fatal("%s: md5 failed", __func__); | |
+ ssh_digest_free(md); | |
+ explicit_bzero(buf, bytes); | |
+ free(buf); | |
+ for (i = 0; i < 16; i++) | |
+ session_id[i] = session_key[i] ^ session_key[i + 16]; | |
+ } | |
+ /* Destroy the private and public keys. No longer. */ | |
+ destroy_sensitive_data(); | |
+ | |
+ if (use_privsep) | |
+ mm_ssh1_session_id(session_id); | |
+ | |
+ /* Destroy the decrypted integer. It is no longer needed. */ | |
+ BN_clear_free(session_key_int); | |
+ | |
+ /* Set the session key. From this on all communications will be encrypted. */ | |
+ packet_set_encryption_key(session_key, SSH_SESSION_KEY_LENGTH, cipher_type); | |
+ | |
+ /* Destroy our copy of the session key. It is no longer needed. */ | |
+ explicit_bzero(session_key, sizeof(session_key)); | |
+ | |
+ debug("Received session key; encryption turned on."); | |
+ | |
+ /* Send an acknowledgment packet. Note that this packet is sent encrypted. */ | |
+ packet_start(SSH_SMSG_SUCCESS); | |
+ packet_send(); | |
+ packet_write_wait(); | |
+} | |
+#endif | |
+ | |
+void | |
+sshd_hostkey_sign(Key *privkey, Key *pubkey, u_char **signature, u_int *slen, | |
+ u_char *data, u_int dlen) | |
+{ | |
+ if (privkey) { | |
+ if (PRIVSEP(key_sign(privkey, signature, slen, data, dlen) < 0)) | |
+ fatal("%s: key_sign failed", __func__); | |
+ } else if (use_privsep) { | |
+ if (mm_key_sign(pubkey, signature, slen, data, dlen) < 0) | |
+ fatal("%s: pubkey_sign failed", __func__); | |
+ } else { | |
+ if (ssh_agent_sign(auth_conn, pubkey, signature, slen, data, | |
+ dlen)) | |
+ fatal("%s: ssh_agent_sign failed", __func__); | |
+ } | |
+} | |
+ | |
+/* | |
+ * SSH2 key exchange: diffie-hellman-group1-sha1 | |
+ */ | |
+static void | |
+do_ssh2_kex(void) | |
+{ | |
+ char *myproposal[PROPOSAL_MAX] = { KEX_SERVER }; | |
+ Kex *kex; | |
+ | |
+ if (options.ciphers != NULL) { | |
+ myproposal[PROPOSAL_ENC_ALGS_CTOS] = | |
+ myproposal[PROPOSAL_ENC_ALGS_STOC] = options.ciphers; | |
+ } | |
+ myproposal[PROPOSAL_ENC_ALGS_CTOS] = | |
+ compat_cipher_proposal(myproposal[PROPOSAL_ENC_ALGS_CTOS]); | |
+ myproposal[PROPOSAL_ENC_ALGS_STOC] = | |
+ compat_cipher_proposal(myproposal[PROPOSAL_ENC_ALGS_STOC]); | |
+ | |
+ if (options.macs != NULL) { | |
+ myproposal[PROPOSAL_MAC_ALGS_CTOS] = | |
+ myproposal[PROPOSAL_MAC_ALGS_STOC] = options.macs; | |
+ } | |
+ if (options.compression == COMP_NONE) { | |
+ myproposal[PROPOSAL_COMP_ALGS_CTOS] = | |
+ myproposal[PROPOSAL_COMP_ALGS_STOC] = "none"; | |
+ } else if (options.compression == COMP_DELAYED) { | |
+ myproposal[PROPOSAL_COMP_ALGS_CTOS] = | |
+ myproposal[PROPOSAL_COMP_ALGS_STOC] = "none,zlib@openssh.com"; | |
+ } | |
+ if (options.kex_algorithms != NULL) | |
+ myproposal[PROPOSAL_KEX_ALGS] = options.kex_algorithms; | |
+ | |
+ myproposal[PROPOSAL_KEX_ALGS] = compat_kex_proposal( | |
+ myproposal[PROPOSAL_KEX_ALGS]); | |
+ | |
+ if (options.rekey_limit || options.rekey_interval) | |
+ packet_set_rekey_limits((u_int32_t)options.rekey_limit, | |
+ (time_t)options.rekey_interval); | |
+ | |
+ myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = compat_pkalg_proposal( | |
+ list_hostkey_types()); | |
+ | |
+ /* start key exchange */ | |
+ kex = kex_setup(myproposal); | |
+#ifdef WITH_OPENSSL | |
+ 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; | |
+ kex->kex[KEX_ECDH_SHA2] = kexecdh_server; | |
+#endif | |
+ kex->kex[KEX_C25519_SHA256] = kexc25519_server; | |
+ kex->server = 1; | |
+ kex->client_version_string=client_version_string; | |
+ kex->server_version_string=server_version_string; | |
+ kex->load_host_public_key=&get_hostkey_public_by_type; | |
+ kex->load_host_private_key=&get_hostkey_private_by_type; | |
+ kex->host_key_index=&get_hostkey_index; | |
+ kex->sign = sshd_hostkey_sign; | |
+ | |
+ xxx_kex = kex; | |
+ | |
+ dispatch_run(DISPATCH_BLOCK, &kex->done, kex); | |
+ | |
+ session_id2 = kex->session_id; | |
+ session_id2_len = kex->session_id_len; | |
+ | |
+#ifdef DEBUG_KEXDH | |
+ /* send 1st encrypted/maced/compressed message */ | |
+ packet_start(SSH2_MSG_IGNORE); | |
+ packet_put_cstring("markus"); | |
+ packet_send(); | |
+ packet_write_wait(); | |
+#endif | |
+ debug("KEX done"); | |
+} | |
+ | |
+/* server specific fatal cleanup */ | |
+void | |
+cleanup_exit(int i) | |
+{ | |
+ if (the_authctxt) { | |
+ do_cleanup(the_authctxt); | |
+ if (use_privsep && privsep_is_preauth && | |
+ pmonitor != NULL && pmonitor->m_pid > 1) { | |
+ debug("Killing privsep child %d", pmonitor->m_pid); | |
+ if (kill(pmonitor->m_pid, SIGKILL) != 0 && | |
+ errno != ESRCH) | |
+ error("%s: kill(%d): %s", __func__, | |
+ pmonitor->m_pid, strerror(errno)); | |
+ } | |
+ } | |
+#ifdef SSH_AUDIT_EVENTS | |
+ /* done after do_cleanup so it can cancel the PAM auth 'thread' */ | |
+ if (!use_privsep || mm_is_monitor()) | |
+ audit_event(SSH_CONNECTION_ABANDON); | |
+#endif | |
+ _exit(i); | |
+} | |
diff -urp --new-file openssh-6.7p1/sshd_config openssh-6.7p1.patched/sshd_config | |
--- openssh-6.7p1/sshd_config 2014-01-12 00:20:47.000000000 -0800 | |
+++ openssh-6.7p1.patched/sshd_config 2014-10-11 21:18:37.000000000 -0700 | |
@@ -35,7 +35,7 @@ | |
# Logging | |
# obsoletes QuietMode and FascistLogging | |
-#SyslogFacility AUTH | |
+SyslogFacility AUTHPRIV | |
#LogLevel INFO | |
# Authentication: | |
@@ -68,8 +68,9 @@ AuthorizedKeysFile .ssh/authorized_keys | |
# Don't read the user's ~/.rhosts and ~/.shosts files | |
#IgnoreRhosts yes | |
-# To disable tunneled clear text passwords, change to no here! | |
-#PasswordAuthentication yes | |
+# To disable tunneled clear text passwords, change to no here! Also, | |
+# remember to set the UsePAM setting to 'no'. | |
+#PasswordAuthentication no | |
#PermitEmptyPasswords no | |
# Change to no to disable s/key passwords | |
@@ -94,7 +95,10 @@ AuthorizedKeysFile .ssh/authorized_keys | |
# If you just want the PAM account and session checks to run without | |
# PAM authentication, then enable this but set PasswordAuthentication | |
# and ChallengeResponseAuthentication to 'no'. | |
-#UsePAM no | |
+# Also, PAM will deny null passwords by default. If you need to allow | |
+# null passwords, add the " nullok" option to the end of the | |
+# securityserver.so line in /etc/pam.d/sshd. | |
+#UsePAM yes | |
#AllowAgentForwarding yes | |
#AllowTcpForwarding yes | |
diff -urp --new-file openssh-6.7p1/sshd_config.0 openssh-6.7p1.patched/sshd_config.0 | |
--- openssh-6.7p1/sshd_config.0 2014-10-05 20:39:38.000000000 -0700 | |
+++ openssh-6.7p1.patched/sshd_config.0 2014-10-11 21:18:37.000000000 -0700 | |
@@ -571,7 +571,7 @@ DESCRIPTION | |
PasswordAuthentication | |
Specifies whether password authentication is allowed. The | |
- default is ``yes''. | |
+ default is ``no''. | |
PermitEmptyPasswords | |
When password authentication is allowed, it specifies whether the | |
@@ -802,7 +802,7 @@ DESCRIPTION | |
either PasswordAuthentication or ChallengeResponseAuthentication. | |
If UsePAM is enabled, you will not be able to run sshd(8) as a | |
- non-root user. The default is ``no''. | |
+ non-root user. The default is ``yes''. | |
UsePrivilegeSeparation | |
Specifies whether sshd(8) separates privileges by creating an | |
diff -urp --new-file openssh-6.7p1/sshd_config.0.orig openssh-6.7p1.patched/sshd_config.0.orig | |
--- openssh-6.7p1/sshd_config.0.orig 1969-12-31 16:00:00.000000000 -0800 | |
+++ openssh-6.7p1.patched/sshd_config.0.orig 2014-10-05 20:39:38.000000000 -0700 | |
@@ -0,0 +1,906 @@ | |
+SSHD_CONFIG(5) File Formats Manual SSHD_CONFIG(5) | |
+ | |
+NAME | |
+ sshd_config - OpenSSH SSH daemon configuration file | |
+ | |
+SYNOPSIS | |
+ /etc/ssh/sshd_config | |
+ | |
+DESCRIPTION | |
+ sshd(8) reads configuration data from /etc/ssh/sshd_config (or the file | |
+ specified with -f on the command line). The file contains keyword- | |
+ argument pairs, one per line. Lines starting with `#' and empty lines | |
+ are interpreted as comments. Arguments may optionally be enclosed in | |
+ double quotes (") in order to represent arguments containing spaces. | |
+ | |
+ The possible keywords and their meanings are as follows (note that | |
+ keywords are case-insensitive and arguments are case-sensitive): | |
+ | |
+ AcceptEnv | |
+ Specifies what environment variables sent by the client will be | |
+ copied into the session's environ(7). See SendEnv in | |
+ ssh_config(5) for how to configure the client. Note that | |
+ environment passing is only supported for protocol 2. Variables | |
+ are specified by name, which may contain the wildcard characters | |
+ `*' and `?'. Multiple environment variables may be separated by | |
+ whitespace or spread across multiple AcceptEnv directives. Be | |
+ warned that some environment variables could be used to bypass | |
+ restricted user environments. For this reason, care should be | |
+ taken in the use of this directive. The default is not to accept | |
+ any environment variables. | |
+ | |
+ AddressFamily | |
+ Specifies which address family should be used by sshd(8). Valid | |
+ arguments are ``any'', ``inet'' (use IPv4 only), or ``inet6'' | |
+ (use IPv6 only). The default is ``any''. | |
+ | |
+ AllowAgentForwarding | |
+ Specifies whether ssh-agent(1) forwarding is permitted. The | |
+ default is ``yes''. Note that disabling agent forwarding does | |
+ not improve security unless users are also denied shell access, | |
+ as they can always install their own forwarders. | |
+ | |
+ AllowGroups | |
+ This keyword can be followed by a list of group name patterns, | |
+ separated by spaces. If specified, login is allowed only for | |
+ users whose primary group or supplementary group list matches one | |
+ of the patterns. Only group names are valid; a numerical group | |
+ ID is not recognized. By default, login is allowed for all | |
+ groups. The allow/deny directives are processed in the following | |
+ order: DenyUsers, AllowUsers, DenyGroups, and finally | |
+ AllowGroups. | |
+ | |
+ See PATTERNS in ssh_config(5) for more information on patterns. | |
+ | |
+ AllowTcpForwarding | |
+ Specifies whether TCP forwarding is permitted. The available | |
+ options are ``yes'' or ``all'' to allow TCP forwarding, ``no'' to | |
+ prevent all TCP forwarding, ``local'' to allow local (from the | |
+ perspective of ssh(1)) forwarding only or ``remote'' to allow | |
+ remote forwarding only. The default is ``yes''. Note that | |
+ disabling TCP forwarding does not improve security unless users | |
+ are also denied shell access, as they can always install their | |
+ own forwarders. | |
+ | |
+ AllowStreamLocalForwarding | |
+ Specifies whether StreamLocal (Unix-domain socket) forwarding is | |
+ permitted. The available options are ``yes'' or ``all'' to allow | |
+ StreamLocal forwarding, ``no'' to prevent all StreamLocal | |
+ forwarding, ``local'' to allow local (from the perspective of | |
+ ssh(1)) forwarding only or ``remote'' to allow remote forwarding | |
+ only. The default is ``yes''. Note that disabling StreamLocal | |
+ forwarding does not improve security unless users are also denied | |
+ shell access, as they can always install their own forwarders. | |
+ | |
+ AllowUsers | |
+ This keyword can be followed by a list of user name patterns, | |
+ separated by spaces. If specified, login is allowed only for | |
+ user names that match one of the patterns. Only user names are | |
+ valid; a numerical user ID is not recognized. By default, login | |
+ is allowed for all users. If the pattern takes the form | |
+ USER@HOST then USER and HOST are separately checked, restricting | |
+ logins to particular users from particular hosts. The allow/deny | |
+ directives are processed in the following order: DenyUsers, | |
+ AllowUsers, DenyGroups, and finally AllowGroups. | |
+ | |
+ See PATTERNS in ssh_config(5) for more information on patterns. | |
+ | |
+ AuthenticationMethods | |
+ Specifies the authentication methods that must be successfully | |
+ completed for a user to be granted access. This option must be | |
+ followed by one or more comma-separated lists of authentication | |
+ method names. Successful authentication requires completion of | |
+ every method in at least one of these lists. | |
+ | |
+ For example, an argument of ``publickey,password | |
+ publickey,keyboard-interactive'' would require the user to | |
+ complete public key authentication, followed by either password | |
+ or keyboard interactive authentication. Only methods that are | |
+ next in one or more lists are offered at each stage, so for this | |
+ example, it would not be possible to attempt password or | |
+ keyboard-interactive authentication before public key. | |
+ | |
+ For keyboard interactive authentication it is also possible to | |
+ restrict authentication to a specific device by appending a colon | |
+ followed by the device identifier ``bsdauth'', ``pam'', or | |
+ ``skey'', depending on the server configuration. For example, | |
+ ``keyboard-interactive:bsdauth'' would restrict keyboard | |
+ interactive authentication to the ``bsdauth'' device. | |
+ | |
+ This option is only available for SSH protocol 2 and will yield a | |
+ fatal error if enabled if protocol 1 is also enabled. Note that | |
+ each authentication method listed should also be explicitly | |
+ enabled in the configuration. The default is not to require | |
+ multiple authentication; successful completion of a single | |
+ authentication method is sufficient. | |
+ | |
+ AuthorizedKeysCommand | |
+ Specifies a program to be used to look up the user's public keys. | |
+ The program must be owned by root and not writable by group or | |
+ others. It will be invoked with a single argument of the | |
+ username being authenticated, and should produce on standard | |
+ output zero or more lines of authorized_keys output (see | |
+ AUTHORIZED_KEYS in sshd(8)). If a key supplied by | |
+ AuthorizedKeysCommand does not successfully authenticate and | |
+ authorize the user then public key authentication continues using | |
+ the usual AuthorizedKeysFile files. By default, no | |
+ AuthorizedKeysCommand is run. | |
+ | |
+ AuthorizedKeysCommandUser | |
+ Specifies the user under whose account the AuthorizedKeysCommand | |
+ is run. It is recommended to use a dedicated user that has no | |
+ other role on the host than running authorized keys commands. | |
+ | |
+ AuthorizedKeysFile | |
+ Specifies the file that contains the public keys that can be used | |
+ for user authentication. The format is described in the | |
+ AUTHORIZED_KEYS FILE FORMAT section of sshd(8). | |
+ AuthorizedKeysFile may contain tokens of the form %T which are | |
+ substituted during connection setup. The following tokens are | |
+ defined: %% is replaced by a literal '%', %h is replaced by the | |
+ home directory of the user being authenticated, and %u is | |
+ replaced by the username of that user. After expansion, | |
+ AuthorizedKeysFile is taken to be an absolute path or one | |
+ relative to the user's home directory. Multiple files may be | |
+ listed, separated by whitespace. The default is | |
+ ``.ssh/authorized_keys .ssh/authorized_keys2''. | |
+ | |
+ AuthorizedPrincipalsFile | |
+ Specifies a file that lists principal names that are accepted for | |
+ certificate authentication. When using certificates signed by a | |
+ key listed in TrustedUserCAKeys, this file lists names, one of | |
+ which must appear in the certificate for it to be accepted for | |
+ authentication. Names are listed one per line preceded by key | |
+ options (as described in AUTHORIZED_KEYS FILE FORMAT in sshd(8)). | |
+ Empty lines and comments starting with `#' are ignored. | |
+ | |
+ AuthorizedPrincipalsFile may contain tokens of the form %T which | |
+ are substituted during connection setup. The following tokens | |
+ are defined: %% is replaced by a literal '%', %h is replaced by | |
+ the home directory of the user being authenticated, and %u is | |
+ replaced by the username of that user. After expansion, | |
+ AuthorizedPrincipalsFile is taken to be an absolute path or one | |
+ relative to the user's home directory. | |
+ | |
+ The default is ``none'', i.e. not to use a principals file - in | |
+ this case, the username of the user must appear in a | |
+ certificate's principals list for it to be accepted. Note that | |
+ AuthorizedPrincipalsFile is only used when authentication | |
+ proceeds using a CA listed in TrustedUserCAKeys and is not | |
+ consulted for certification authorities trusted via | |
+ ~/.ssh/authorized_keys, though the principals= key option offers | |
+ a similar facility (see sshd(8) for details). | |
+ | |
+ Banner The contents of the specified file are sent to the remote user | |
+ before authentication is allowed. If the argument is ``none'' | |
+ then no banner is displayed. This option is only available for | |
+ protocol version 2. By default, no banner is displayed. | |
+ | |
+ ChallengeResponseAuthentication | |
+ Specifies whether challenge-response authentication is allowed | |
+ (e.g. via PAM or through authentication styles supported in | |
+ login.conf(5)) The default is ``yes''. | |
+ | |
+ ChrootDirectory | |
+ Specifies the pathname of a directory to chroot(2) to after | |
+ authentication. All components of the pathname must be root- | |
+ owned directories that are not writable by any other user or | |
+ group. After the chroot, sshd(8) changes the working directory | |
+ to the user's home directory. | |
+ | |
+ The pathname may contain the following tokens that are expanded | |
+ at runtime once the connecting user has been authenticated: %% is | |
+ replaced by a literal '%', %h is replaced by the home directory | |
+ of the user being authenticated, and %u is replaced by the | |
+ username of that user. | |
+ | |
+ The ChrootDirectory must contain the necessary files and | |
+ directories to support the user's session. For an interactive | |
+ session this requires at least a shell, typically sh(1), and | |
+ basic /dev nodes such as null(4), zero(4), stdin(4), stdout(4), | |
+ stderr(4), arandom(4) and tty(4) devices. For file transfer | |
+ sessions using ``sftp'', no additional configuration of the | |
+ environment is necessary if the in-process sftp server is used, | |
+ though sessions which use logging may require /dev/log inside the | |
+ chroot directory on some operating systems (see sftp-server(8) | |
+ for details). | |
+ | |
+ The default is not to chroot(2). | |
+ | |
+ Ciphers | |
+ Specifies the ciphers allowed for protocol version 2. Multiple | |
+ ciphers must be comma-separated. The supported ciphers are: | |
+ | |
+ 3des-cbc | |
+ aes128-cbc | |
+ aes192-cbc | |
+ aes256-cbc | |
+ aes128-ctr | |
+ aes192-ctr | |
+ aes256-ctr | |
+ aes128-gcm@openssh.com | |
+ aes256-gcm@openssh.com | |
+ arcfour | |
+ arcfour128 | |
+ arcfour256 | |
+ blowfish-cbc | |
+ cast128-cbc | |
+ chacha20-poly1305@openssh.com | |
+ | |
+ The default is: | |
+ | |
+ aes128-ctr,aes192-ctr,aes256-ctr, | |
+ aes128-gcm@openssh.com,aes256-gcm@openssh.com, | |
+ chacha20-poly1305@openssh.com | |
+ | |
+ The list of available ciphers may also be obtained using the -Q | |
+ option of ssh(1). | |
+ | |
+ ClientAliveCountMax | |
+ Sets the number of client alive messages (see below) which may be | |
+ sent without sshd(8) receiving any messages back from the client. | |
+ If this threshold is reached while client alive messages are | |
+ being sent, sshd will disconnect the client, terminating the | |
+ session. It is important to note that the use of client alive | |
+ messages is very different from TCPKeepAlive (below). The client | |
+ alive messages are sent through the encrypted channel and | |
+ therefore will not be spoofable. The TCP keepalive option | |
+ enabled by TCPKeepAlive is spoofable. The client alive mechanism | |
+ is valuable when the client or server depend on knowing when a | |
+ connection has become inactive. | |
+ | |
+ The default value is 3. If ClientAliveInterval (see below) is | |
+ set to 15, and ClientAliveCountMax is left at the default, | |
+ unresponsive SSH clients will be disconnected after approximately | |
+ 45 seconds. This option applies to protocol version 2 only. | |
+ | |
+ ClientAliveInterval | |
+ Sets a timeout interval in seconds after which if no data has | |
+ been received from the client, sshd(8) will send a message | |
+ through the encrypted channel to request a response from the | |
+ client. The default is 0, indicating that these messages will | |
+ not be sent to the client. This option applies to protocol | |
+ version 2 only. | |
+ | |
+ Compression | |
+ Specifies whether compression is allowed, or delayed until the | |
+ user has authenticated successfully. The argument must be | |
+ ``yes'', ``delayed'', or ``no''. The default is ``delayed''. | |
+ | |
+ DenyGroups | |
+ This keyword can be followed by a list of group name patterns, | |
+ separated by spaces. Login is disallowed for users whose primary | |
+ group or supplementary group list matches one of the patterns. | |
+ Only group names are valid; a numerical group ID is not | |
+ recognized. By default, login is allowed for all groups. The | |
+ allow/deny directives are processed in the following order: | |
+ DenyUsers, AllowUsers, DenyGroups, and finally AllowGroups. | |
+ | |
+ See PATTERNS in ssh_config(5) for more information on patterns. | |
+ | |
+ DenyUsers | |
+ This keyword can be followed by a list of user name patterns, | |
+ separated by spaces. Login is disallowed for user names that | |
+ match one of the patterns. Only user names are valid; a | |
+ numerical user ID is not recognized. By default, login is | |
+ allowed for all users. If the pattern takes the form USER@HOST | |
+ then USER and HOST are separately checked, restricting logins to | |
+ particular users from particular hosts. The allow/deny | |
+ directives are processed in the following order: DenyUsers, | |
+ AllowUsers, DenyGroups, and finally AllowGroups. | |
+ | |
+ See PATTERNS in ssh_config(5) for more information on patterns. | |
+ | |
+ ForceCommand | |
+ Forces the execution of the command specified by ForceCommand, | |
+ ignoring any command supplied by the client and ~/.ssh/rc if | |
+ present. The command is invoked by using the user's login shell | |
+ with the -c option. This applies to shell, command, or subsystem | |
+ execution. It is most useful inside a Match block. The command | |
+ originally supplied by the client is available in the | |
+ SSH_ORIGINAL_COMMAND environment variable. Specifying a command | |
+ of ``internal-sftp'' will force the use of an in-process sftp | |
+ server that requires no support files when used with | |
+ ChrootDirectory. | |
+ | |
+ GatewayPorts | |
+ Specifies whether remote hosts are allowed to connect to ports | |
+ forwarded for the client. By default, sshd(8) binds remote port | |
+ forwardings to the loopback address. This prevents other remote | |
+ hosts from connecting to forwarded ports. GatewayPorts can be | |
+ used to specify that sshd should allow remote port forwardings to | |
+ bind to non-loopback addresses, thus allowing other hosts to | |
+ connect. The argument may be ``no'' to force remote port | |
+ forwardings to be available to the local host only, ``yes'' to | |
+ force remote port forwardings to bind to the wildcard address, or | |
+ ``clientspecified'' to allow the client to select the address to | |
+ which the forwarding is bound. The default is ``no''. | |
+ | |
+ GSSAPIAuthentication | |
+ Specifies whether user authentication based on GSSAPI is allowed. | |
+ The default is ``no''. Note that this option applies to protocol | |
+ version 2 only. | |
+ | |
+ GSSAPICleanupCredentials | |
+ Specifies whether to automatically destroy the user's credentials | |
+ cache on logout. The default is ``yes''. Note that this option | |
+ applies to protocol version 2 only. | |
+ | |
+ HostbasedAuthentication | |
+ Specifies whether rhosts or /etc/hosts.equiv authentication | |
+ together with successful public key client host authentication is | |
+ allowed (host-based authentication). This option is similar to | |
+ RhostsRSAAuthentication and applies to protocol version 2 only. | |
+ The default is ``no''. | |
+ | |
+ HostbasedUsesNameFromPacketOnly | |
+ Specifies whether or not the server will attempt to perform a | |
+ reverse name lookup when matching the name in the ~/.shosts, | |
+ ~/.rhosts, and /etc/hosts.equiv files during | |
+ HostbasedAuthentication. A setting of ``yes'' means that sshd(8) | |
+ uses the name supplied by the client rather than attempting to | |
+ resolve the name from the TCP connection itself. The default is | |
+ ``no''. | |
+ | |
+ HostCertificate | |
+ Specifies a file containing a public host certificate. The | |
+ certificate's public key must match a private host key already | |
+ specified by HostKey. The default behaviour of sshd(8) is not to | |
+ load any certificates. | |
+ | |
+ HostKey | |
+ Specifies a file containing a private host key used by SSH. The | |
+ default is /etc/ssh/ssh_host_key for protocol version 1, and | |
+ /etc/ssh/ssh_host_dsa_key, /etc/ssh/ssh_host_ecdsa_key, | |
+ /etc/ssh/ssh_host_ed25519_key and /etc/ssh/ssh_host_rsa_key for | |
+ protocol version 2. Note that sshd(8) will refuse to use a file | |
+ if it is group/world-accessible. It is possible to have multiple | |
+ host key files. ``rsa1'' keys are used for version 1 and | |
+ ``dsa'', ``ecdsa'', ``ed25519'' or ``rsa'' are used for version 2 | |
+ of the SSH protocol. It is also possible to specify public host | |
+ key files instead. In this case operations on the private key | |
+ will be delegated to an ssh-agent(1). | |
+ | |
+ HostKeyAgent | |
+ Identifies the UNIX-domain socket used to communicate with an | |
+ agent that has access to the private host keys. If | |
+ ``SSH_AUTH_SOCK'' is specified, the location of the socket will | |
+ be read from the SSH_AUTH_SOCK environment variable. | |
+ | |
+ IgnoreRhosts | |
+ Specifies that .rhosts and .shosts files will not be used in | |
+ RhostsRSAAuthentication or HostbasedAuthentication. | |
+ | |
+ /etc/hosts.equiv and /etc/shosts.equiv are still used. The | |
+ default is ``yes''. | |
+ | |
+ IgnoreUserKnownHosts | |
+ Specifies whether sshd(8) should ignore the user's | |
+ ~/.ssh/known_hosts during RhostsRSAAuthentication or | |
+ HostbasedAuthentication. The default is ``no''. | |
+ | |
+ IPQoS Specifies the IPv4 type-of-service or DSCP class for the | |
+ connection. Accepted values are ``af11'', ``af12'', ``af13'', | |
+ ``af21'', ``af22'', ``af23'', ``af31'', ``af32'', ``af33'', | |
+ ``af41'', ``af42'', ``af43'', ``cs0'', ``cs1'', ``cs2'', ``cs3'', | |
+ ``cs4'', ``cs5'', ``cs6'', ``cs7'', ``ef'', ``lowdelay'', | |
+ ``throughput'', ``reliability'', or a numeric value. This option | |
+ may take one or two arguments, separated by whitespace. If one | |
+ argument is specified, it is used as the packet class | |
+ unconditionally. If two values are specified, the first is | |
+ automatically selected for interactive sessions and the second | |
+ for non-interactive sessions. The default is ``lowdelay'' for | |
+ interactive sessions and ``throughput'' for non-interactive | |
+ sessions. | |
+ | |
+ KbdInteractiveAuthentication | |
+ Specifies whether to allow keyboard-interactive authentication. | |
+ The argument to this keyword must be ``yes'' or ``no''. The | |
+ default is to use whatever value ChallengeResponseAuthentication | |
+ is set to (by default ``yes''). | |
+ | |
+ KerberosAuthentication | |
+ Specifies whether the password provided by the user for | |
+ PasswordAuthentication will be validated through the Kerberos | |
+ KDC. To use this option, the server needs a Kerberos servtab | |
+ which allows the verification of the KDC's identity. The default | |
+ is ``no''. | |
+ | |
+ KerberosGetAFSToken | |
+ If AFS is active and the user has a Kerberos 5 TGT, attempt to | |
+ acquire an AFS token before accessing the user's home directory. | |
+ The default is ``no''. | |
+ | |
+ KerberosOrLocalPasswd | |
+ If password authentication through Kerberos fails then the | |
+ password will be validated via any additional local mechanism | |
+ such as /etc/passwd. The default is ``yes''. | |
+ | |
+ KerberosTicketCleanup | |
+ Specifies whether to automatically destroy the user's ticket | |
+ cache file on logout. The default is ``yes''. | |
+ | |
+ KexAlgorithms | |
+ Specifies the available KEX (Key Exchange) algorithms. Multiple | |
+ algorithms must be comma-separated. The supported algorithms | |
+ are: | |
+ | |
+ curve25519-sha256@libssh.org | |
+ diffie-hellman-group1-sha1 | |
+ diffie-hellman-group14-sha1 | |
+ diffie-hellman-group-exchange-sha1 | |
+ diffie-hellman-group-exchange-sha256 | |
+ ecdh-sha2-nistp256 | |
+ ecdh-sha2-nistp384 | |
+ ecdh-sha2-nistp521 | |
+ | |
+ The default is: | |
+ | |
+ curve25519-sha256@libssh.org, | |
+ ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521, | |
+ diffie-hellman-group-exchange-sha256, | |
+ diffie-hellman-group14-sha1 | |
+ | |
+ KeyRegenerationInterval | |
+ In protocol version 1, the ephemeral server key is automatically | |
+ regenerated after this many seconds (if it has been used). The | |
+ purpose of regeneration is to prevent decrypting captured | |
+ sessions by later breaking into the machine and stealing the | |
+ keys. The key is never stored anywhere. If the value is 0, the | |
+ key is never regenerated. The default is 3600 (seconds). | |
+ | |
+ ListenAddress | |
+ Specifies the local addresses sshd(8) should listen on. The | |
+ following forms may be used: | |
+ | |
+ ListenAddress host|IPv4_addr|IPv6_addr | |
+ ListenAddress host|IPv4_addr:port | |
+ ListenAddress [host|IPv6_addr]:port | |
+ | |
+ If port is not specified, sshd will listen on the address and all | |
+ prior Port options specified. The default is to listen on all | |
+ local addresses. Multiple ListenAddress options are permitted. | |
+ Additionally, any Port options must precede this option for non- | |
+ port qualified addresses. | |
+ | |
+ LoginGraceTime | |
+ The server disconnects after this time if the user has not | |
+ successfully logged in. If the value is 0, there is no time | |
+ limit. The default is 120 seconds. | |
+ | |
+ LogLevel | |
+ Gives the verbosity level that is used when logging messages from | |
+ sshd(8). The possible values are: 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. Logging with a DEBUG level | |
+ violates the privacy of users and is not recommended. | |
+ | |
+ MACs Specifies the available MAC (message authentication code) | |
+ algorithms. The MAC algorithm is used in protocol version 2 for | |
+ data integrity protection. Multiple algorithms must be comma- | |
+ separated. The algorithms that contain ``-etm'' calculate the | |
+ MAC after encryption (encrypt-then-mac). These are considered | |
+ safer and their use recommended. The supported MACs are: | |
+ | |
+ hmac-md5 | |
+ hmac-md5-96 | |
+ hmac-ripemd160 | |
+ hmac-sha1 | |
+ hmac-sha1-96 | |
+ hmac-sha2-256 | |
+ hmac-sha2-512 | |
+ umac-64@openssh.com | |
+ umac-128@openssh.com | |
+ hmac-md5-etm@openssh.com | |
+ hmac-md5-96-etm@openssh.com | |
+ hmac-ripemd160-etm@openssh.com | |
+ hmac-sha1-etm@openssh.com | |
+ hmac-sha1-96-etm@openssh.com | |
+ hmac-sha2-256-etm@openssh.com | |
+ hmac-sha2-512-etm@openssh.com | |
+ umac-64-etm@openssh.com | |
+ umac-128-etm@openssh.com | |
+ | |
+ The default is: | |
+ | |
+ umac-64-etm@openssh.com,umac-128-etm@openssh.com, | |
+ hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com, | |
+ umac-64@openssh.com,umac-128@openssh.com, | |
+ hmac-sha2-256,hmac-sha2-512 | |
+ | |
+ Match Introduces a conditional block. If all of the criteria on the | |
+ Match line are satisfied, the keywords on the following lines | |
+ override those set in the global section of the config file, | |
+ until either another Match line or the end of the file. If a | |
+ keyword appears in multiple Match blocks that are satisified, | |
+ only the first instance of the keyword is applied. | |
+ | |
+ The arguments to Match are one or more criteria-pattern pairs or | |
+ the single token All which matches all criteria. The available | |
+ criteria are User, Group, Host, LocalAddress, LocalPort, and | |
+ Address. The match patterns may consist of single entries or | |
+ comma-separated lists and may use the wildcard and negation | |
+ operators described in the PATTERNS section of ssh_config(5). | |
+ | |
+ The patterns in an Address criteria may additionally contain | |
+ addresses to match in CIDR address/masklen format, e.g. | |
+ ``192.0.2.0/24'' or ``3ffe:ffff::/32''. Note that the mask | |
+ length provided must be consistent with the address - it is an | |
+ error to specify a mask length that is too long for the address | |
+ or one with bits set in this host portion of the address. For | |
+ example, ``192.0.2.0/33'' and ``192.0.2.0/8'' respectively. | |
+ | |
+ Only a subset of keywords may be used on the lines following a | |
+ Match keyword. Available keywords are AcceptEnv, | |
+ AllowAgentForwarding, AllowGroups, AllowTcpForwarding, | |
+ AllowUsers, AuthenticationMethods, AuthorizedKeysCommand, | |
+ AuthorizedKeysCommandUser, AuthorizedKeysFile, | |
+ AuthorizedPrincipalsFile, Banner, ChrootDirectory, DenyGroups, | |
+ DenyUsers, ForceCommand, GatewayPorts, GSSAPIAuthentication, | |
+ HostbasedAuthentication, HostbasedUsesNameFromPacketOnly, | |
+ KbdInteractiveAuthentication, KerberosAuthentication, | |
+ MaxAuthTries, MaxSessions, PasswordAuthentication, | |
+ PermitEmptyPasswords, PermitOpen, PermitRootLogin, PermitTTY, | |
+ PermitTunnel, PermitUserRC, PubkeyAuthentication, RekeyLimit, | |
+ RhostsRSAAuthentication, RSAAuthentication, X11DisplayOffset, | |
+ X11Forwarding and X11UseLocalHost. | |
+ | |
+ MaxAuthTries | |
+ Specifies the maximum number of authentication attempts permitted | |
+ per connection. Once the number of failures reaches half this | |
+ value, additional failures are logged. The default is 6. | |
+ | |
+ MaxSessions | |
+ Specifies the maximum number of open sessions permitted per | |
+ network connection. The default is 10. | |
+ | |
+ MaxStartups | |
+ Specifies the maximum number of concurrent unauthenticated | |
+ connections to the SSH daemon. Additional connections will be | |
+ dropped until authentication succeeds or the LoginGraceTime | |
+ expires for a connection. The default is 10:30:100. | |
+ | |
+ Alternatively, random early drop can be enabled by specifying the | |
+ three colon separated values ``start:rate:full'' (e.g. | |
+ "10:30:60"). sshd(8) will refuse connection attempts with a | |
+ probability of ``rate/100'' (30%) if there are currently | |
+ ``start'' (10) unauthenticated connections. The probability | |
+ increases linearly and all connection attempts are refused if the | |
+ number of unauthenticated connections reaches ``full'' (60). | |
+ | |
+ PasswordAuthentication | |
+ Specifies whether password authentication is allowed. The | |
+ default is ``yes''. | |
+ | |
+ PermitEmptyPasswords | |
+ When password authentication is allowed, it specifies whether the | |
+ server allows login to accounts with empty password strings. The | |
+ default is ``no''. | |
+ | |
+ PermitOpen | |
+ Specifies the destinations to which TCP port forwarding is | |
+ permitted. The forwarding specification must be one of the | |
+ following forms: | |
+ | |
+ PermitOpen host:port | |
+ PermitOpen IPv4_addr:port | |
+ PermitOpen [IPv6_addr]:port | |
+ | |
+ Multiple forwards may be specified by separating them with | |
+ whitespace. An argument of ``any'' can be used to remove all | |
+ restrictions and permit any forwarding requests. An argument of | |
+ ``none'' can be used to prohibit all forwarding requests. By | |
+ default all port forwarding requests are permitted. | |
+ | |
+ PermitRootLogin | |
+ Specifies whether root can log in using ssh(1). The argument | |
+ must be ``yes'', ``without-password'', ``forced-commands-only'', | |
+ or ``no''. The default is ``yes''. | |
+ | |
+ If this option is set to ``without-password'', password | |
+ authentication is disabled for root. | |
+ | |
+ If this option is set to ``forced-commands-only'', root login | |
+ with public key authentication will be allowed, but only if the | |
+ command option has been specified (which may be useful for taking | |
+ remote backups even if root login is normally not allowed). All | |
+ other authentication methods are disabled for root. | |
+ | |
+ If this option is set to ``no'', root is not allowed to log in. | |
+ | |
+ PermitTunnel | |
+ Specifies whether tun(4) device forwarding is allowed. The | |
+ argument must be ``yes'', ``point-to-point'' (layer 3), | |
+ ``ethernet'' (layer 2), or ``no''. Specifying ``yes'' permits | |
+ both ``point-to-point'' and ``ethernet''. The default is ``no''. | |
+ | |
+ PermitTTY | |
+ Specifies whether pty(4) allocation is permitted. The default is | |
+ ``yes''. | |
+ | |
+ PermitUserEnvironment | |
+ Specifies whether ~/.ssh/environment and environment= options in | |
+ ~/.ssh/authorized_keys are processed by sshd(8). The default is | |
+ ``no''. Enabling environment processing may enable users to | |
+ bypass access restrictions in some configurations using | |
+ mechanisms such as LD_PRELOAD. | |
+ | |
+ PermitUserRC | |
+ Specifies whether any ~/.ssh/rc file is executed. The default is | |
+ ``yes''. | |
+ | |
+ PidFile | |
+ Specifies the file that contains the process ID of the SSH | |
+ daemon. The default is /var/run/sshd.pid. | |
+ | |
+ Port Specifies the port number that sshd(8) listens on. The default | |
+ is 22. Multiple options of this type are permitted. See also | |
+ ListenAddress. | |
+ | |
+ PrintLastLog | |
+ Specifies whether sshd(8) should print the date and time of the | |
+ last user login when a user logs in interactively. The default | |
+ is ``yes''. | |
+ | |
+ PrintMotd | |
+ Specifies whether sshd(8) should print /etc/motd when a user logs | |
+ in interactively. (On some systems it is also printed by the | |
+ shell, /etc/profile, or equivalent.) The default is ``yes''. | |
+ | |
+ Protocol | |
+ Specifies the protocol versions sshd(8) supports. The possible | |
+ values are `1' and `2'. Multiple versions must be comma- | |
+ separated. The default is `2'. Note that the order of the | |
+ protocol list does not indicate preference, because the client | |
+ selects among multiple protocol versions offered by the server. | |
+ Specifying ``2,1'' is identical to ``1,2''. | |
+ | |
+ PubkeyAuthentication | |
+ Specifies whether public key authentication is allowed. The | |
+ default is ``yes''. Note that this option applies to protocol | |
+ version 2 only. | |
+ | |
+ RekeyLimit | |
+ Specifies the maximum amount of data that may be transmitted | |
+ before the session key is renegotiated, optionally followed a | |
+ maximum amount of time that may pass before the session key is | |
+ renegotiated. The first argument is specified in bytes and may | |
+ have a suffix of `K', `M', or `G' to indicate Kilobytes, | |
+ Megabytes, or Gigabytes, respectively. The default is between | |
+ `1G' and `4G', depending on the cipher. The optional second | |
+ value is specified in seconds and may use any of the units | |
+ documented in the TIME FORMATS section. The default value for | |
+ RekeyLimit is ``default none'', which means that rekeying is | |
+ performed after the cipher's default amount of data has been sent | |
+ or received and no time based rekeying is done. This option | |
+ applies to protocol version 2 only. | |
+ | |
+ RevokedKeys | |
+ Specifies revoked public keys. Keys listed in this file will be | |
+ refused for public key authentication. Note that if this file is | |
+ not readable, then public key authentication will be refused for | |
+ all users. Keys may be specified as a text file, listing one | |
+ public key per line, or as an OpenSSH Key Revocation List (KRL) | |
+ as generated by ssh-keygen(1). For more information on KRLs, see | |
+ the KEY REVOCATION LISTS section in ssh-keygen(1). | |
+ | |
+ RhostsRSAAuthentication | |
+ Specifies whether rhosts or /etc/hosts.equiv authentication | |
+ together with successful RSA host authentication is allowed. The | |
+ default is ``no''. This option applies to protocol version 1 | |
+ only. | |
+ | |
+ RSAAuthentication | |
+ Specifies whether pure RSA authentication is allowed. The | |
+ default is ``yes''. This option applies to protocol version 1 | |
+ only. | |
+ | |
+ ServerKeyBits | |
+ Defines the number of bits in the ephemeral protocol version 1 | |
+ server key. The minimum value is 512, and the default is 1024. | |
+ | |
+ StreamLocalBindMask | |
+ Sets the octal file creation mode mask (umask) used when creating | |
+ a Unix-domain socket file for local or remote port forwarding. | |
+ This option is only used for port forwarding to a Unix-domain | |
+ socket file. | |
+ | |
+ The default value is 0177, which creates a Unix-domain socket | |
+ file that is readable and writable only by the owner. Note that | |
+ not all operating systems honor the file mode on Unix-domain | |
+ socket files. | |
+ | |
+ StreamLocalBindUnlink | |
+ Specifies whether to remove an existing Unix-domain socket file | |
+ for local or remote port forwarding before creating a new one. | |
+ If the socket file already exists and StreamLocalBindUnlink is | |
+ not enabled, sshd will be unable to forward the port to the Unix- | |
+ domain socket file. This option is only used for port forwarding | |
+ to a Unix-domain socket file. | |
+ | |
+ The argument must be ``yes'' or ``no''. The default is ``no''. | |
+ | |
+ StrictModes | |
+ Specifies whether sshd(8) should check file modes and ownership | |
+ of the user's files and home directory before accepting login. | |
+ This is normally desirable because novices sometimes accidentally | |
+ leave their directory or files world-writable. The default is | |
+ ``yes''. Note that this does not apply to ChrootDirectory, whose | |
+ permissions and ownership are checked unconditionally. | |
+ | |
+ Subsystem | |
+ Configures an external subsystem (e.g. file transfer daemon). | |
+ Arguments should be a subsystem name and a command (with optional | |
+ arguments) to execute upon subsystem request. | |
+ | |
+ The command sftp-server(8) implements the ``sftp'' file transfer | |
+ subsystem. | |
+ | |
+ Alternately the name ``internal-sftp'' implements an in-process | |
+ ``sftp'' server. This may simplify configurations using | |
+ ChrootDirectory to force a different filesystem root on clients. | |
+ | |
+ By default no subsystems are defined. Note that this option | |
+ applies to protocol version 2 only. | |
+ | |
+ SyslogFacility | |
+ Gives the facility code that is used when logging messages from | |
+ sshd(8). The possible values are: DAEMON, USER, AUTH, LOCAL0, | |
+ LOCAL1, LOCAL2, LOCAL3, LOCAL4, LOCAL5, LOCAL6, LOCAL7. The | |
+ default is AUTH. | |
+ | |
+ TCPKeepAlive | |
+ Specifies whether the system should send TCP keepalive messages | |
+ to the other side. If they are sent, death of the connection or | |
+ crash of one of the machines will be properly noticed. However, | |
+ this means that connections will die if the route is down | |
+ temporarily, and some people find it annoying. On the other | |
+ hand, if TCP keepalives are not sent, sessions may hang | |
+ indefinitely on the server, leaving ``ghost'' users and consuming | |
+ server resources. | |
+ | |
+ The default is ``yes'' (to send TCP keepalive messages), and the | |
+ server will notice if the network goes down or the client host | |
+ crashes. This avoids infinitely hanging sessions. | |
+ | |
+ To disable TCP keepalive messages, the value should be set to | |
+ ``no''. | |
+ | |
+ TrustedUserCAKeys | |
+ Specifies a file containing public keys of certificate | |
+ authorities that are trusted to sign user certificates for | |
+ authentication. Keys are listed one per line; empty lines and | |
+ comments starting with `#' are allowed. If a certificate is | |
+ presented for authentication and has its signing CA key listed in | |
+ this file, then it may be used for authentication for any user | |
+ listed in the certificate's principals list. Note that | |
+ certificates that lack a list of principals will not be permitted | |
+ for authentication using TrustedUserCAKeys. For more details on | |
+ certificates, see the CERTIFICATES section in ssh-keygen(1). | |
+ | |
+ UseDNS Specifies whether sshd(8) should look up the remote host name and | |
+ check that the resolved host name for the remote IP address maps | |
+ back to the very same IP address. The default is ``yes''. | |
+ | |
+ UseLogin | |
+ Specifies whether login(1) is used for interactive login | |
+ sessions. The default is ``no''. Note that login(1) is never | |
+ used for remote command execution. Note also, that if this is | |
+ enabled, X11Forwarding will be disabled because login(1) does not | |
+ know how to handle xauth(1) cookies. If UsePrivilegeSeparation | |
+ is specified, it will be disabled after authentication. | |
+ | |
+ UsePAM Enables the Pluggable Authentication Module interface. If set to | |
+ ``yes'' this will enable PAM authentication using | |
+ ChallengeResponseAuthentication and PasswordAuthentication in | |
+ addition to PAM account and session module processing for all | |
+ authentication types. | |
+ | |
+ Because PAM challenge-response authentication usually serves an | |
+ equivalent role to password authentication, you should disable | |
+ either PasswordAuthentication or ChallengeResponseAuthentication. | |
+ | |
+ If UsePAM is enabled, you will not be able to run sshd(8) as a | |
+ non-root user. The default is ``no''. | |
+ | |
+ UsePrivilegeSeparation | |
+ Specifies whether sshd(8) separates privileges by creating an | |
+ unprivileged child process to deal with incoming network traffic. | |
+ After successful authentication, another process will be created | |
+ that has the privilege of the authenticated user. The goal of | |
+ privilege separation is to prevent privilege escalation by | |
+ containing any corruption within the unprivileged processes. The | |
+ default is ``yes''. If UsePrivilegeSeparation is set to | |
+ ``sandbox'' then the pre-authentication unprivileged process is | |
+ subject to additional restrictions. | |
+ | |
+ VersionAddendum | |
+ Optionally specifies additional text to append to the SSH | |
+ protocol banner sent by the server upon connection. The default | |
+ is ``none''. | |
+ | |
+ X11DisplayOffset | |
+ Specifies the first display number available for sshd(8)'s X11 | |
+ forwarding. This prevents sshd from interfering with real X11 | |
+ servers. The default is 10. | |
+ | |
+ X11Forwarding | |
+ Specifies whether X11 forwarding is permitted. The argument must | |
+ be ``yes'' or ``no''. The default is ``no''. | |
+ | |
+ When X11 forwarding is enabled, there may be additional exposure | |
+ to the server and to client displays if the sshd(8) proxy display | |
+ is configured to listen on the wildcard address (see | |
+ X11UseLocalhost below), though this is not the default. | |
+ Additionally, the authentication spoofing and authentication data | |
+ verification and substitution occur on the client side. The | |
+ security risk of using X11 forwarding is that the client's X11 | |
+ display server may be exposed to attack when the SSH client | |
+ requests forwarding (see the warnings for ForwardX11 in | |
+ ssh_config(5)). A system administrator may have a stance in | |
+ which they want to protect clients that may expose themselves to | |
+ attack by unwittingly requesting X11 forwarding, which can | |
+ warrant a ``no'' setting. | |
+ | |
+ Note that disabling X11 forwarding does not prevent users from | |
+ forwarding X11 traffic, as users can always install their own | |
+ forwarders. X11 forwarding is automatically disabled if UseLogin | |
+ is enabled. | |
+ | |
+ X11UseLocalhost | |
+ Specifies whether sshd(8) should bind the X11 forwarding server | |
+ to the loopback address or to the wildcard address. By default, | |
+ sshd binds the forwarding server to the loopback address and sets | |
+ the hostname part of the DISPLAY environment variable to | |
+ ``localhost''. This prevents remote hosts from connecting to the | |
+ proxy display. However, some older X11 clients may not function | |
+ with this configuration. X11UseLocalhost may be set to ``no'' to | |
+ specify that the forwarding server should be bound to the | |
+ wildcard address. The argument must be ``yes'' or ``no''. The | |
+ default is ``yes''. | |
+ | |
+ XAuthLocation | |
+ Specifies the full pathname of the xauth(1) program. The default | |
+ is /usr/X11R6/bin/xauth. | |
+ | |
+TIME FORMATS | |
+ sshd(8) command-line arguments and configuration file options that | |
+ specify time may be expressed using a sequence of the form: | |
+ time[qualifier], where time is a positive integer value and qualifier is | |
+ one of the following: | |
+ | |
+ <none> seconds | |
+ s | S seconds | |
+ m | M minutes | |
+ h | H hours | |
+ d | D days | |
+ w | W weeks | |
+ | |
+ Each member of the sequence is added together to calculate the total time | |
+ value. | |
+ | |
+ Time format examples: | |
+ | |
+ 600 600 seconds (10 minutes) | |
+ 10m 10 minutes | |
+ 1h30m 1 hour 30 minutes (90 minutes) | |
+ | |
+FILES | |
+ /etc/ssh/sshd_config | |
+ Contains configuration data for sshd(8). This file should be | |
+ writable by root only, but it is recommended (though not | |
+ necessary) that it be world-readable. | |
+ | |
+SEE ALSO | |
+ sshd(8) | |
+ | |
+AUTHORS | |
+ OpenSSH is a derivative of the original and free ssh 1.2.12 release by | |
+ Tatu Ylonen. Aaron Campbell, Bob Beck, Markus Friedl, Niels Provos, Theo | |
+ de Raadt and Dug Song removed many bugs, re-added newer features and | |
+ created OpenSSH. Markus Friedl contributed the support for SSH protocol | |
+ versions 1.5 and 2.0. Niels Provos and Markus Friedl contributed support | |
+ for privilege separation. | |
+ | |
+OpenBSD 5.6 July 28, 2014 OpenBSD 5.6 | |
diff -urp --new-file openssh-6.7p1/sshd_config.5 openssh-6.7p1.patched/sshd_config.5 | |
--- openssh-6.7p1/sshd_config.5 2014-10-02 16:24:57.000000000 -0700 | |
+++ openssh-6.7p1.patched/sshd_config.5 2014-10-11 21:18:37.000000000 -0700 | |
@@ -977,7 +977,7 @@ are refused if the number of unauthentic | |
.It Cm PasswordAuthentication | |
Specifies whether password authentication is allowed. | |
The default is | |
-.Dq yes . | |
+.Dq no . | |
.It Cm PermitEmptyPasswords | |
When password authentication is allowed, it specifies whether the | |
server allows login to accounts with empty password strings. | |
@@ -1343,7 +1343,7 @@ is enabled, you will not be able to run | |
.Xr sshd 8 | |
as a non-root user. | |
The default is | |
-.Dq no . | |
+.Dq yes . | |
.It Cm UsePrivilegeSeparation | |
Specifies whether | |
.Xr sshd 8 | |
diff -urp --new-file openssh-6.7p1/sshd_config.5.orig openssh-6.7p1.patched/sshd_config.5.orig | |
--- openssh-6.7p1/sshd_config.5.orig 1969-12-31 16:00:00.000000000 -0800 | |
+++ openssh-6.7p1.patched/sshd_config.5.orig 2014-10-02 16:24:57.000000000 -0700 | |
@@ -0,0 +1,1502 @@ | |
+.\" | |
+.\" Author: Tatu Ylonen <ylo@cs.hut.fi> | |
+.\" Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | |
+.\" All rights reserved | |
+.\" | |
+.\" As far as I am concerned, the code I have written for this software | |
+.\" can be used freely for any purpose. Any derived versions of this | |
+.\" software must be clearly marked as such, and if the derived work is | |
+.\" incompatible with the protocol description in the RFC file, it must be | |
+.\" called by a name other than "ssh" or "Secure Shell". | |
+.\" | |
+.\" Copyright (c) 1999,2000 Markus Friedl. All rights reserved. | |
+.\" Copyright (c) 1999 Aaron Campbell. All rights reserved. | |
+.\" Copyright (c) 1999 Theo de Raadt. 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. | |
+.\" | |
+.\" $OpenBSD: sshd_config.5,v 1.176 2014/07/28 15:40:08 schwarze Exp $ | |
+.Dd $Mdocdate: July 28 2014 $ | |
+.Dt SSHD_CONFIG 5 | |
+.Os | |
+.Sh NAME | |
+.Nm sshd_config | |
+.Nd OpenSSH SSH daemon configuration file | |
+.Sh SYNOPSIS | |
+.Nm /etc/ssh/sshd_config | |
+.Sh DESCRIPTION | |
+.Xr sshd 8 | |
+reads configuration data from | |
+.Pa /etc/ssh/sshd_config | |
+(or the file specified with | |
+.Fl f | |
+on the command line). | |
+The file contains keyword-argument pairs, one per line. | |
+Lines starting with | |
+.Ql # | |
+and empty lines are interpreted as comments. | |
+Arguments may optionally be enclosed in double quotes | |
+.Pq \&" | |
+in order to represent arguments containing spaces. | |
+.Pp | |
+The possible | |
+keywords and their meanings are as follows (note that | |
+keywords are case-insensitive and arguments are case-sensitive): | |
+.Bl -tag -width Ds | |
+.It Cm AcceptEnv | |
+Specifies what environment variables sent by the client will be copied into | |
+the session's | |
+.Xr environ 7 . | |
+See | |
+.Cm SendEnv | |
+in | |
+.Xr ssh_config 5 | |
+for how to configure the client. | |
+Note that environment passing is only supported for protocol 2. | |
+Variables are specified by name, which may contain the wildcard characters | |
+.Ql * | |
+and | |
+.Ql \&? . | |
+Multiple environment variables may be separated by whitespace or spread | |
+across multiple | |
+.Cm AcceptEnv | |
+directives. | |
+Be warned that some environment variables could be used to bypass restricted | |
+user environments. | |
+For this reason, care should be taken in the use of this directive. | |
+The default is not to accept any environment variables. | |
+.It Cm AddressFamily | |
+Specifies which address family should be used by | |
+.Xr sshd 8 . | |
+Valid arguments are | |
+.Dq any , | |
+.Dq inet | |
+(use IPv4 only), or | |
+.Dq inet6 | |
+(use IPv6 only). | |
+The default is | |
+.Dq any . | |
+.It Cm AllowAgentForwarding | |
+Specifies whether | |
+.Xr ssh-agent 1 | |
+forwarding is permitted. | |
+The default is | |
+.Dq yes . | |
+Note that disabling agent forwarding does not improve security | |
+unless users are also denied shell access, as they can always install | |
+their own forwarders. | |
+.It Cm AllowGroups | |
+This keyword can be followed by a list of group name patterns, separated | |
+by spaces. | |
+If specified, login is allowed only for users whose primary | |
+group or supplementary group list matches one of the patterns. | |
+Only group names are valid; a numerical group ID is not recognized. | |
+By default, login is allowed for all groups. | |
+The allow/deny directives are processed in the following order: | |
+.Cm DenyUsers , | |
+.Cm AllowUsers , | |
+.Cm DenyGroups , | |
+and finally | |
+.Cm AllowGroups . | |
+.Pp | |
+See PATTERNS in | |
+.Xr ssh_config 5 | |
+for more information on patterns. | |
+.It Cm AllowTcpForwarding | |
+Specifies whether TCP forwarding is permitted. | |
+The available options are | |
+.Dq yes | |
+or | |
+.Dq all | |
+to allow TCP forwarding, | |
+.Dq no | |
+to prevent all TCP forwarding, | |
+.Dq local | |
+to allow local (from the perspective of | |
+.Xr ssh 1 ) | |
+forwarding only or | |
+.Dq remote | |
+to allow remote forwarding only. | |
+The default is | |
+.Dq yes . | |
+Note that disabling TCP forwarding does not improve security unless | |
+users are also denied shell access, as they can always install their | |
+own forwarders. | |
+.It Cm AllowStreamLocalForwarding | |
+Specifies whether StreamLocal (Unix-domain socket) forwarding is permitted. | |
+The available options are | |
+.Dq yes | |
+or | |
+.Dq all | |
+to allow StreamLocal forwarding, | |
+.Dq no | |
+to prevent all StreamLocal forwarding, | |
+.Dq local | |
+to allow local (from the perspective of | |
+.Xr ssh 1 ) | |
+forwarding only or | |
+.Dq remote | |
+to allow remote forwarding only. | |
+The default is | |
+.Dq yes . | |
+Note that disabling StreamLocal forwarding does not improve security unless | |
+users are also denied shell access, as they can always install their | |
+own forwarders. | |
+.It Cm AllowUsers | |
+This keyword can be followed by a list of user name patterns, separated | |
+by spaces. | |
+If specified, login is allowed only for user names that | |
+match one of the patterns. | |
+Only user names are valid; a numerical user ID is not recognized. | |
+By default, login is allowed for all users. | |
+If the pattern takes the form USER@HOST then USER and HOST | |
+are separately checked, restricting logins to particular | |
+users from particular hosts. | |
+The allow/deny directives are processed in the following order: | |
+.Cm DenyUsers , | |
+.Cm AllowUsers , | |
+.Cm DenyGroups , | |
+and finally | |
+.Cm AllowGroups . | |
+.Pp | |
+See PATTERNS in | |
+.Xr ssh_config 5 | |
+for more information on patterns. | |
+.It Cm AuthenticationMethods | |
+Specifies the authentication methods that must be successfully completed | |
+for a user to be granted access. | |
+This option must be followed by one or more comma-separated lists of | |
+authentication method names. | |
+Successful authentication requires completion of every method in at least | |
+one of these lists. | |
+.Pp | |
+For example, an argument of | |
+.Dq publickey,password publickey,keyboard-interactive | |
+would require the user to complete public key authentication, followed by | |
+either password or keyboard interactive authentication. | |
+Only methods that are next in one or more lists are offered at each stage, | |
+so for this example, it would not be possible to attempt password or | |
+keyboard-interactive authentication before public key. | |
+.Pp | |
+For keyboard interactive authentication it is also possible to | |
+restrict authentication to a specific device by appending a | |
+colon followed by the device identifier | |
+.Dq bsdauth , | |
+.Dq pam , | |
+or | |
+.Dq skey , | |
+depending on the server configuration. | |
+For example, | |
+.Dq keyboard-interactive:bsdauth | |
+would restrict keyboard interactive authentication to the | |
+.Dq bsdauth | |
+device. | |
+.Pp | |
+This option is only available for SSH protocol 2 and will yield a fatal | |
+error if enabled if protocol 1 is also enabled. | |
+Note that each authentication method listed should also be explicitly enabled | |
+in the configuration. | |
+The default is not to require multiple authentication; successful completion | |
+of a single authentication method is sufficient. | |
+.It Cm AuthorizedKeysCommand | |
+Specifies a program to be used to look up the user's public keys. | |
+The program must be owned by root and not writable by group or others. | |
+It will be invoked with a single argument of the username | |
+being authenticated, and should produce on standard output zero or | |
+more lines of authorized_keys output (see AUTHORIZED_KEYS in | |
+.Xr sshd 8 ) . | |
+If a key supplied by AuthorizedKeysCommand does not successfully authenticate | |
+and authorize the user then public key authentication continues using the usual | |
+.Cm AuthorizedKeysFile | |
+files. | |
+By default, no AuthorizedKeysCommand is run. | |
+.It Cm AuthorizedKeysCommandUser | |
+Specifies the user under whose account the AuthorizedKeysCommand is run. | |
+It is recommended to use a dedicated user that has no other role on the host | |
+than running authorized keys commands. | |
+.It Cm AuthorizedKeysFile | |
+Specifies the file that contains the public keys that can be used | |
+for user authentication. | |
+The format is described in the | |
+AUTHORIZED_KEYS FILE FORMAT | |
+section of | |
+.Xr sshd 8 . | |
+.Cm AuthorizedKeysFile | |
+may contain tokens of the form %T which are substituted during connection | |
+setup. | |
+The following tokens are defined: %% is replaced by a literal '%', | |
+%h is replaced by the home directory of the user being authenticated, and | |
+%u is replaced by the username of that user. | |
+After expansion, | |
+.Cm AuthorizedKeysFile | |
+is taken to be an absolute path or one relative to the user's home | |
+directory. | |
+Multiple files may be listed, separated by whitespace. | |
+The default is | |
+.Dq .ssh/authorized_keys .ssh/authorized_keys2 . | |
+.It Cm AuthorizedPrincipalsFile | |
+Specifies a file that lists principal names that are accepted for | |
+certificate authentication. | |
+When using certificates signed by a key listed in | |
+.Cm TrustedUserCAKeys , | |
+this file lists names, one of which must appear in the certificate for it | |
+to be accepted for authentication. | |
+Names are listed one per line preceded by key options (as described | |
+in AUTHORIZED_KEYS FILE FORMAT in | |
+.Xr sshd 8 ) . | |
+Empty lines and comments starting with | |
+.Ql # | |
+are ignored. | |
+.Pp | |
+.Cm AuthorizedPrincipalsFile | |
+may contain tokens of the form %T which are substituted during connection | |
+setup. | |
+The following tokens are defined: %% is replaced by a literal '%', | |
+%h is replaced by the home directory of the user being authenticated, and | |
+%u is replaced by the username of that user. | |
+After expansion, | |
+.Cm AuthorizedPrincipalsFile | |
+is taken to be an absolute path or one relative to the user's home | |
+directory. | |
+.Pp | |
+The default is | |
+.Dq none , | |
+i.e. not to use a principals file \(en in this case, the username | |
+of the user must appear in a certificate's principals list for it to be | |
+accepted. | |
+Note that | |
+.Cm AuthorizedPrincipalsFile | |
+is only used when authentication proceeds using a CA listed in | |
+.Cm TrustedUserCAKeys | |
+and is not consulted for certification authorities trusted via | |
+.Pa ~/.ssh/authorized_keys , | |
+though the | |
+.Cm principals= | |
+key option offers a similar facility (see | |
+.Xr sshd 8 | |
+for details). | |
+.It Cm Banner | |
+The contents of the specified file are sent to the remote user before | |
+authentication is allowed. | |
+If the argument is | |
+.Dq none | |
+then no banner is displayed. | |
+This option is only available for protocol version 2. | |
+By default, no banner is displayed. | |
+.It Cm ChallengeResponseAuthentication | |
+Specifies whether challenge-response authentication is allowed (e.g. via | |
+PAM or through authentication styles supported in | |
+.Xr login.conf 5 ) | |
+The default is | |
+.Dq yes . | |
+.It Cm ChrootDirectory | |
+Specifies the pathname of a directory to | |
+.Xr chroot 2 | |
+to after authentication. | |
+All components of the pathname must be root-owned directories that are | |
+not writable by any other user or group. | |
+After the chroot, | |
+.Xr sshd 8 | |
+changes the working directory to the user's home directory. | |
+.Pp | |
+The pathname may contain the following tokens that are expanded at runtime once | |
+the connecting user has been authenticated: %% is replaced by a literal '%', | |
+%h is replaced by the home directory of the user being authenticated, and | |
+%u is replaced by the username of that user. | |
+.Pp | |
+The | |
+.Cm ChrootDirectory | |
+must contain the necessary files and directories to support the | |
+user's session. | |
+For an interactive session this requires at least a shell, typically | |
+.Xr sh 1 , | |
+and basic | |
+.Pa /dev | |
+nodes such as | |
+.Xr null 4 , | |
+.Xr zero 4 , | |
+.Xr stdin 4 , | |
+.Xr stdout 4 , | |
+.Xr stderr 4 , | |
+.Xr arandom 4 | |
+and | |
+.Xr tty 4 | |
+devices. | |
+For file transfer sessions using | |
+.Dq sftp , | |
+no additional configuration of the environment is necessary if the | |
+in-process sftp server is used, | |
+though sessions which use logging may require | |
+.Pa /dev/log | |
+inside the chroot directory on some operating systems (see | |
+.Xr sftp-server 8 | |
+for details). | |
+.Pp | |
+The default is not to | |
+.Xr chroot 2 . | |
+.It Cm Ciphers | |
+Specifies the ciphers allowed for protocol version 2. | |
+Multiple ciphers must be comma-separated. | |
+The supported ciphers are: | |
+.Pp | |
+.Bl -item -compact -offset indent | |
+.It | |
+3des-cbc | |
+.It | |
+aes128-cbc | |
+.It | |
+aes192-cbc | |
+.It | |
+aes256-cbc | |
+.It | |
+aes128-ctr | |
+.It | |
+aes192-ctr | |
+.It | |
+aes256-ctr | |
+.It | |
+aes128-gcm@openssh.com | |
+.It | |
+aes256-gcm@openssh.com | |
+.It | |
+arcfour | |
+.It | |
+arcfour128 | |
+.It | |
+arcfour256 | |
+.It | |
+blowfish-cbc | |
+.It | |
+cast128-cbc | |
+.It | |
+chacha20-poly1305@openssh.com | |
+.El | |
+.Pp | |
+The default is: | |
+.Bd -literal -offset indent | |
+aes128-ctr,aes192-ctr,aes256-ctr, | |
+aes128-gcm@openssh.com,aes256-gcm@openssh.com, | |
+chacha20-poly1305@openssh.com | |
+.Ed | |
+.Pp | |
+The list of available ciphers may also be obtained using the | |
+.Fl Q | |
+option of | |
+.Xr ssh 1 . | |
+.It Cm ClientAliveCountMax | |
+Sets the number of client alive messages (see below) which may be | |
+sent without | |
+.Xr sshd 8 | |
+receiving any messages back from the client. | |
+If this threshold is reached while client alive messages are being sent, | |
+sshd will disconnect the client, terminating the session. | |
+It is important to note that the use of client alive messages is very | |
+different from | |
+.Cm TCPKeepAlive | |
+(below). | |
+The client alive messages are sent through the encrypted channel | |
+and therefore will not be spoofable. | |
+The TCP keepalive option enabled by | |
+.Cm TCPKeepAlive | |
+is spoofable. | |
+The client alive mechanism is valuable when the client or | |
+server depend on knowing when a connection has become inactive. | |
+.Pp | |
+The default value is 3. | |
+If | |
+.Cm ClientAliveInterval | |
+(see below) is set to 15, and | |
+.Cm ClientAliveCountMax | |
+is left at the default, unresponsive SSH clients | |
+will be disconnected after approximately 45 seconds. | |
+This option applies to protocol version 2 only. | |
+.It Cm ClientAliveInterval | |
+Sets a timeout interval in seconds after which if no data has been received | |
+from the client, | |
+.Xr sshd 8 | |
+will send a message through the encrypted | |
+channel to request a response from the client. | |
+The default | |
+is 0, indicating that these messages will not be sent to the client. | |
+This option applies to protocol version 2 only. | |
+.It Cm Compression | |
+Specifies whether compression is allowed, or delayed until | |
+the user has authenticated successfully. | |
+The argument must be | |
+.Dq yes , | |
+.Dq delayed , | |
+or | |
+.Dq no . | |
+The default is | |
+.Dq delayed . | |
+.It Cm DenyGroups | |
+This keyword can be followed by a list of group name patterns, separated | |
+by spaces. | |
+Login is disallowed for users whose primary group or supplementary | |
+group list matches one of the patterns. | |
+Only group names are valid; a numerical group ID is not recognized. | |
+By default, login is allowed for all groups. | |
+The allow/deny directives are processed in the following order: | |
+.Cm DenyUsers , | |
+.Cm AllowUsers , | |
+.Cm DenyGroups , | |
+and finally | |
+.Cm AllowGroups . | |
+.Pp | |
+See PATTERNS in | |
+.Xr ssh_config 5 | |
+for more information on patterns. | |
+.It Cm DenyUsers | |
+This keyword can be followed by a list of user name patterns, separated | |
+by spaces. | |
+Login is disallowed for user names that match one of the patterns. | |
+Only user names are valid; a numerical user ID is not recognized. | |
+By default, login is allowed for all users. | |
+If the pattern takes the form USER@HOST then USER and HOST | |
+are separately checked, restricting logins to particular | |
+users from particular hosts. | |
+The allow/deny directives are processed in the following order: | |
+.Cm DenyUsers , | |
+.Cm AllowUsers , | |
+.Cm DenyGroups , | |
+and finally | |
+.Cm AllowGroups . | |
+.Pp | |
+See PATTERNS in | |
+.Xr ssh_config 5 | |
+for more information on patterns. | |
+.It Cm ForceCommand | |
+Forces the execution of the command specified by | |
+.Cm ForceCommand , | |
+ignoring any command supplied by the client and | |
+.Pa ~/.ssh/rc | |
+if present. | |
+The command is invoked by using the user's login shell with the -c option. | |
+This applies to shell, command, or subsystem execution. | |
+It is most useful inside a | |
+.Cm Match | |
+block. | |
+The command originally supplied by the client is available in the | |
+.Ev SSH_ORIGINAL_COMMAND | |
+environment variable. | |
+Specifying a command of | |
+.Dq internal-sftp | |
+will force the use of an in-process sftp server that requires no support | |
+files when used with | |
+.Cm ChrootDirectory . | |
+.It Cm GatewayPorts | |
+Specifies whether remote hosts are allowed to connect to ports | |
+forwarded for the client. | |
+By default, | |
+.Xr sshd 8 | |
+binds remote port forwardings to the loopback address. | |
+This prevents other remote hosts from connecting to forwarded ports. | |
+.Cm GatewayPorts | |
+can be used to specify that sshd | |
+should allow remote port forwardings to bind to non-loopback addresses, thus | |
+allowing other hosts to connect. | |
+The argument may be | |
+.Dq no | |
+to force remote port forwardings to be available to the local host only, | |
+.Dq yes | |
+to force remote port forwardings to bind to the wildcard address, or | |
+.Dq clientspecified | |
+to allow the client to select the address to which the forwarding is bound. | |
+The default is | |
+.Dq no . | |
+.It Cm GSSAPIAuthentication | |
+Specifies whether user authentication based on GSSAPI is allowed. | |
+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 HostbasedAuthentication | |
+Specifies whether rhosts or /etc/hosts.equiv authentication together | |
+with successful public key client host authentication is allowed | |
+(host-based authentication). | |
+This option is similar to | |
+.Cm RhostsRSAAuthentication | |
+and applies to protocol version 2 only. | |
+The default is | |
+.Dq no . | |
+.It Cm HostbasedUsesNameFromPacketOnly | |
+Specifies whether or not the server will attempt to perform a reverse | |
+name lookup when matching the name in the | |
+.Pa ~/.shosts , | |
+.Pa ~/.rhosts , | |
+and | |
+.Pa /etc/hosts.equiv | |
+files during | |
+.Cm HostbasedAuthentication . | |
+A setting of | |
+.Dq yes | |
+means that | |
+.Xr sshd 8 | |
+uses the name supplied by the client rather than | |
+attempting to resolve the name from the TCP connection itself. | |
+The default is | |
+.Dq no . | |
+.It Cm HostCertificate | |
+Specifies a file containing a public host certificate. | |
+The certificate's public key must match a private host key already specified | |
+by | |
+.Cm HostKey . | |
+The default behaviour of | |
+.Xr sshd 8 | |
+is not to load any certificates. | |
+.It Cm HostKey | |
+Specifies a file containing a private host key | |
+used by SSH. | |
+The default is | |
+.Pa /etc/ssh/ssh_host_key | |
+for protocol version 1, and | |
+.Pa /etc/ssh/ssh_host_dsa_key , | |
+.Pa /etc/ssh/ssh_host_ecdsa_key , | |
+.Pa /etc/ssh/ssh_host_ed25519_key | |
+and | |
+.Pa /etc/ssh/ssh_host_rsa_key | |
+for protocol version 2. | |
+Note that | |
+.Xr sshd 8 | |
+will refuse to use a file if it is group/world-accessible. | |
+It is possible to have multiple host key files. | |
+.Dq rsa1 | |
+keys are used for version 1 and | |
+.Dq dsa , | |
+.Dq ecdsa , | |
+.Dq ed25519 | |
+or | |
+.Dq rsa | |
+are used for version 2 of the SSH protocol. | |
+It is also possible to specify public host key files instead. | |
+In this case operations on the private key will be delegated | |
+to an | |
+.Xr ssh-agent 1 . | |
+.It Cm HostKeyAgent | |
+Identifies the UNIX-domain socket used to communicate | |
+with an agent that has access to the private host keys. | |
+If | |
+.Dq SSH_AUTH_SOCK | |
+is specified, the location of the socket will be read from the | |
+.Ev SSH_AUTH_SOCK | |
+environment variable. | |
+.It Cm IgnoreRhosts | |
+Specifies that | |
+.Pa .rhosts | |
+and | |
+.Pa .shosts | |
+files will not be used in | |
+.Cm RhostsRSAAuthentication | |
+or | |
+.Cm HostbasedAuthentication . | |
+.Pp | |
+.Pa /etc/hosts.equiv | |
+and | |
+.Pa /etc/shosts.equiv | |
+are still used. | |
+The default is | |
+.Dq yes . | |
+.It Cm IgnoreUserKnownHosts | |
+Specifies whether | |
+.Xr sshd 8 | |
+should ignore the user's | |
+.Pa ~/.ssh/known_hosts | |
+during | |
+.Cm RhostsRSAAuthentication | |
+or | |
+.Cm HostbasedAuthentication . | |
+The default is | |
+.Dq no . | |
+.It Cm IPQoS | |
+Specifies the IPv4 type-of-service or DSCP class for the connection. | |
+Accepted values are | |
+.Dq af11 , | |
+.Dq af12 , | |
+.Dq af13 , | |
+.Dq af21 , | |
+.Dq af22 , | |
+.Dq af23 , | |
+.Dq af31 , | |
+.Dq af32 , | |
+.Dq af33 , | |
+.Dq af41 , | |
+.Dq af42 , | |
+.Dq af43 , | |
+.Dq cs0 , | |
+.Dq cs1 , | |
+.Dq cs2 , | |
+.Dq cs3 , | |
+.Dq cs4 , | |
+.Dq cs5 , | |
+.Dq cs6 , | |
+.Dq cs7 , | |
+.Dq ef , | |
+.Dq lowdelay , | |
+.Dq throughput , | |
+.Dq reliability , | |
+or a numeric value. | |
+This option may take one or two arguments, separated by whitespace. | |
+If one argument is specified, it is used as the packet class unconditionally. | |
+If two values are specified, the first is automatically selected for | |
+interactive sessions and the second for non-interactive sessions. | |
+The default is | |
+.Dq lowdelay | |
+for interactive sessions and | |
+.Dq throughput | |
+for non-interactive sessions. | |
+.It Cm KbdInteractiveAuthentication | |
+Specifies whether to allow keyboard-interactive authentication. | |
+The argument to this keyword must be | |
+.Dq yes | |
+or | |
+.Dq no . | |
+The default is to use whatever value | |
+.Cm ChallengeResponseAuthentication | |
+is set to | |
+(by default | |
+.Dq yes ) . | |
+.It Cm KerberosAuthentication | |
+Specifies whether the password provided by the user for | |
+.Cm PasswordAuthentication | |
+will be validated through the Kerberos KDC. | |
+To use this option, the server needs a | |
+Kerberos servtab which allows the verification of the KDC's identity. | |
+The default is | |
+.Dq no . | |
+.It Cm KerberosGetAFSToken | |
+If AFS is active and the user has a Kerberos 5 TGT, attempt to acquire | |
+an AFS token before accessing the user's home directory. | |
+The default is | |
+.Dq no . | |
+.It Cm KerberosOrLocalPasswd | |
+If password authentication through Kerberos fails then | |
+the password will be validated via any additional local mechanism | |
+such as | |
+.Pa /etc/passwd . | |
+The default is | |
+.Dq yes . | |
+.It Cm KerberosTicketCleanup | |
+Specifies whether to automatically destroy the user's ticket cache | |
+file on logout. | |
+The default is | |
+.Dq yes . | |
+.It Cm KexAlgorithms | |
+Specifies the available KEX (Key Exchange) algorithms. | |
+Multiple algorithms must be comma-separated. | |
+The supported algorithms are: | |
+.Pp | |
+.Bl -item -compact -offset indent | |
+.It | |
+curve25519-sha256@libssh.org | |
+.It | |
+diffie-hellman-group1-sha1 | |
+.It | |
+diffie-hellman-group14-sha1 | |
+.It | |
+diffie-hellman-group-exchange-sha1 | |
+.It | |
+diffie-hellman-group-exchange-sha256 | |
+.It | |
+ecdh-sha2-nistp256 | |
+.It | |
+ecdh-sha2-nistp384 | |
+.It | |
+ecdh-sha2-nistp521 | |
+.El | |
+.Pp | |
+The default is: | |
+.Bd -literal -offset indent | |
+curve25519-sha256@libssh.org, | |
+ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521, | |
+diffie-hellman-group-exchange-sha256, | |
+diffie-hellman-group14-sha1 | |
+.Ed | |
+.It Cm KeyRegenerationInterval | |
+In protocol version 1, the ephemeral server key is automatically regenerated | |
+after this many seconds (if it has been used). | |
+The purpose of regeneration is to prevent | |
+decrypting captured sessions by later breaking into the machine and | |
+stealing the keys. | |
+The key is never stored anywhere. | |
+If the value is 0, the key is never regenerated. | |
+The default is 3600 (seconds). | |
+.It Cm ListenAddress | |
+Specifies the local addresses | |
+.Xr sshd 8 | |
+should listen on. | |
+The following forms may be used: | |
+.Pp | |
+.Bl -item -offset indent -compact | |
+.It | |
+.Cm ListenAddress | |
+.Sm off | |
+.Ar host No | Ar IPv4_addr No | Ar IPv6_addr | |
+.Sm on | |
+.It | |
+.Cm ListenAddress | |
+.Sm off | |
+.Ar host No | Ar IPv4_addr No : Ar port | |
+.Sm on | |
+.It | |
+.Cm ListenAddress | |
+.Sm off | |
+.Oo | |
+.Ar host No | Ar IPv6_addr Oc : Ar port | |
+.Sm on | |
+.El | |
+.Pp | |
+If | |
+.Ar port | |
+is not specified, | |
+sshd will listen on the address and all prior | |
+.Cm Port | |
+options specified. | |
+The default is to listen on all local addresses. | |
+Multiple | |
+.Cm ListenAddress | |
+options are permitted. | |
+Additionally, any | |
+.Cm Port | |
+options must precede this option for non-port qualified addresses. | |
+.It Cm LoginGraceTime | |
+The server disconnects after this time if the user has not | |
+successfully logged in. | |
+If the value is 0, there is no time limit. | |
+The default is 120 seconds. | |
+.It Cm LogLevel | |
+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. | |
+The default is INFO. | |
+DEBUG and DEBUG1 are equivalent. | |
+DEBUG2 and DEBUG3 each specify higher levels of debugging output. | |
+Logging with a DEBUG level violates the privacy of users and is not recommended. | |
+.It Cm MACs | |
+Specifies the available MAC (message authentication code) algorithms. | |
+The MAC algorithm is used in protocol version 2 | |
+for data integrity protection. | |
+Multiple algorithms must be comma-separated. | |
+The algorithms that contain | |
+.Dq -etm | |
+calculate the MAC after encryption (encrypt-then-mac). | |
+These are considered safer and their use recommended. | |
+The supported MACs are: | |
+.Pp | |
+.Bl -item -compact -offset indent | |
+.It | |
+hmac-md5 | |
+.It | |
+hmac-md5-96 | |
+.It | |
+hmac-ripemd160 | |
+.It | |
+hmac-sha1 | |
+.It | |
+hmac-sha1-96 | |
+.It | |
+hmac-sha2-256 | |
+.It | |
+hmac-sha2-512 | |
+.It | |
+umac-64@openssh.com | |
+.It | |
+umac-128@openssh.com | |
+.It | |
+hmac-md5-etm@openssh.com | |
+.It | |
+hmac-md5-96-etm@openssh.com | |
+.It | |
+hmac-ripemd160-etm@openssh.com | |
+.It | |
+hmac-sha1-etm@openssh.com | |
+.It | |
+hmac-sha1-96-etm@openssh.com | |
+.It | |
+hmac-sha2-256-etm@openssh.com | |
+.It | |
+hmac-sha2-512-etm@openssh.com | |
+.It | |
+umac-64-etm@openssh.com | |
+.It | |
+umac-128-etm@openssh.com | |
+.El | |
+.Pp | |
+The default is: | |
+.Bd -literal -offset indent | |
+umac-64-etm@openssh.com,umac-128-etm@openssh.com, | |
+hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com, | |
+umac-64@openssh.com,umac-128@openssh.com, | |
+hmac-sha2-256,hmac-sha2-512 | |
+.Ed | |
+.It Cm Match | |
+Introduces a conditional block. | |
+If all of the criteria on the | |
+.Cm Match | |
+line are satisfied, the keywords on the following lines override those | |
+set in the global section of the config file, until either another | |
+.Cm Match | |
+line or the end of the file. | |
+If a keyword appears in multiple | |
+.Cm Match | |
+blocks that are satisified, only the first instance of the keyword is | |
+applied. | |
+.Pp | |
+The arguments to | |
+.Cm Match | |
+are one or more criteria-pattern pairs or the single token | |
+.Cm All | |
+which matches all criteria. | |
+The available criteria are | |
+.Cm User , | |
+.Cm Group , | |
+.Cm Host , | |
+.Cm LocalAddress , | |
+.Cm LocalPort , | |
+and | |
+.Cm Address . | |
+The match patterns may consist of single entries or comma-separated | |
+lists and may use the wildcard and negation operators described in the | |
+PATTERNS section of | |
+.Xr ssh_config 5 . | |
+.Pp | |
+The patterns in an | |
+.Cm Address | |
+criteria may additionally contain addresses to match in CIDR | |
+address/masklen format, e.g.\& | |
+.Dq 192.0.2.0/24 | |
+or | |
+.Dq 3ffe:ffff::/32 . | |
+Note that the mask length provided must be consistent with the address - | |
+it is an error to specify a mask length that is too long for the address | |
+or one with bits set in this host portion of the address. | |
+For example, | |
+.Dq 192.0.2.0/33 | |
+and | |
+.Dq 192.0.2.0/8 | |
+respectively. | |
+.Pp | |
+Only a subset of keywords may be used on the lines following a | |
+.Cm Match | |
+keyword. | |
+Available keywords are | |
+.Cm AcceptEnv , | |
+.Cm AllowAgentForwarding , | |
+.Cm AllowGroups , | |
+.Cm AllowTcpForwarding , | |
+.Cm AllowUsers , | |
+.Cm AuthenticationMethods , | |
+.Cm AuthorizedKeysCommand , | |
+.Cm AuthorizedKeysCommandUser , | |
+.Cm AuthorizedKeysFile , | |
+.Cm AuthorizedPrincipalsFile , | |
+.Cm Banner , | |
+.Cm ChrootDirectory , | |
+.Cm DenyGroups , | |
+.Cm DenyUsers , | |
+.Cm ForceCommand , | |
+.Cm GatewayPorts , | |
+.Cm GSSAPIAuthentication , | |
+.Cm HostbasedAuthentication , | |
+.Cm HostbasedUsesNameFromPacketOnly , | |
+.Cm KbdInteractiveAuthentication , | |
+.Cm KerberosAuthentication , | |
+.Cm MaxAuthTries , | |
+.Cm MaxSessions , | |
+.Cm PasswordAuthentication , | |
+.Cm PermitEmptyPasswords , | |
+.Cm PermitOpen , | |
+.Cm PermitRootLogin , | |
+.Cm PermitTTY , | |
+.Cm PermitTunnel , | |
+.Cm PermitUserRC , | |
+.Cm PubkeyAuthentication , | |
+.Cm RekeyLimit , | |
+.Cm RhostsRSAAuthentication , | |
+.Cm RSAAuthentication , | |
+.Cm X11DisplayOffset , | |
+.Cm X11Forwarding | |
+and | |
+.Cm X11UseLocalHost . | |
+.It Cm MaxAuthTries | |
+Specifies the maximum number of authentication attempts permitted per | |
+connection. | |
+Once the number of failures reaches half this value, | |
+additional failures are logged. | |
+The default is 6. | |
+.It Cm MaxSessions | |
+Specifies the maximum number of open sessions permitted per network connection. | |
+The default is 10. | |
+.It Cm MaxStartups | |
+Specifies the maximum number of concurrent unauthenticated connections to the | |
+SSH daemon. | |
+Additional connections will be dropped until authentication succeeds or the | |
+.Cm LoginGraceTime | |
+expires for a connection. | |
+The default is 10:30:100. | |
+.Pp | |
+Alternatively, random early drop can be enabled by specifying | |
+the three colon separated values | |
+.Dq start:rate:full | |
+(e.g. "10:30:60"). | |
+.Xr sshd 8 | |
+will refuse connection attempts with a probability of | |
+.Dq rate/100 | |
+(30%) | |
+if there are currently | |
+.Dq start | |
+(10) | |
+unauthenticated connections. | |
+The probability increases linearly and all connection attempts | |
+are refused if the number of unauthenticated connections reaches | |
+.Dq full | |
+(60). | |
+.It Cm PasswordAuthentication | |
+Specifies whether password authentication is allowed. | |
+The default is | |
+.Dq yes . | |
+.It Cm PermitEmptyPasswords | |
+When password authentication is allowed, it specifies whether the | |
+server allows login to accounts with empty password strings. | |
+The default is | |
+.Dq no . | |
+.It Cm PermitOpen | |
+Specifies the destinations to which TCP port forwarding is permitted. | |
+The forwarding specification must be one of the following forms: | |
+.Pp | |
+.Bl -item -offset indent -compact | |
+.It | |
+.Cm PermitOpen | |
+.Sm off | |
+.Ar host : port | |
+.Sm on | |
+.It | |
+.Cm PermitOpen | |
+.Sm off | |
+.Ar IPv4_addr : port | |
+.Sm on | |
+.It | |
+.Cm PermitOpen | |
+.Sm off | |
+.Ar \&[ IPv6_addr \&] : port | |
+.Sm on | |
+.El | |
+.Pp | |
+Multiple forwards may be specified by separating them with whitespace. | |
+An argument of | |
+.Dq any | |
+can be used to remove all restrictions and permit any forwarding requests. | |
+An argument of | |
+.Dq none | |
+can be used to prohibit all forwarding requests. | |
+By default all port forwarding requests are permitted. | |
+.It Cm PermitRootLogin | |
+Specifies whether root can log in using | |
+.Xr ssh 1 . | |
+The argument must be | |
+.Dq yes , | |
+.Dq without-password , | |
+.Dq forced-commands-only , | |
+or | |
+.Dq no . | |
+The default is | |
+.Dq yes . | |
+.Pp | |
+If this option is set to | |
+.Dq without-password , | |
+password authentication is disabled for root. | |
+.Pp | |
+If this option is set to | |
+.Dq forced-commands-only , | |
+root login with public key authentication will be allowed, | |
+but only if the | |
+.Ar command | |
+option has been specified | |
+(which may be useful for taking remote backups even if root login is | |
+normally not allowed). | |
+All other authentication methods are disabled for root. | |
+.Pp | |
+If this option is set to | |
+.Dq no , | |
+root is not allowed to log in. | |
+.It Cm PermitTunnel | |
+Specifies whether | |
+.Xr tun 4 | |
+device forwarding is allowed. | |
+The argument must be | |
+.Dq yes , | |
+.Dq point-to-point | |
+(layer 3), | |
+.Dq ethernet | |
+(layer 2), or | |
+.Dq no . | |
+Specifying | |
+.Dq yes | |
+permits both | |
+.Dq point-to-point | |
+and | |
+.Dq ethernet . | |
+The default is | |
+.Dq no . | |
+.It Cm PermitTTY | |
+Specifies whether | |
+.Xr pty 4 | |
+allocation is permitted. | |
+The default is | |
+.Dq yes . | |
+.It Cm PermitUserEnvironment | |
+Specifies whether | |
+.Pa ~/.ssh/environment | |
+and | |
+.Cm environment= | |
+options in | |
+.Pa ~/.ssh/authorized_keys | |
+are processed by | |
+.Xr sshd 8 . | |
+The default is | |
+.Dq no . | |
+Enabling environment processing may enable users to bypass access | |
+restrictions in some configurations using mechanisms such as | |
+.Ev LD_PRELOAD . | |
+.It Cm PermitUserRC | |
+Specifies whether any | |
+.Pa ~/.ssh/rc | |
+file is executed. | |
+The default is | |
+.Dq yes . | |
+.It Cm PidFile | |
+Specifies the file that contains the process ID of the | |
+SSH daemon. | |
+The default is | |
+.Pa /var/run/sshd.pid . | |
+.It Cm Port | |
+Specifies the port number that | |
+.Xr sshd 8 | |
+listens on. | |
+The default is 22. | |
+Multiple options of this type are permitted. | |
+See also | |
+.Cm ListenAddress . | |
+.It Cm PrintLastLog | |
+Specifies whether | |
+.Xr sshd 8 | |
+should print the date and time of the last user login when a user logs | |
+in interactively. | |
+The default is | |
+.Dq yes . | |
+.It Cm PrintMotd | |
+Specifies whether | |
+.Xr sshd 8 | |
+should print | |
+.Pa /etc/motd | |
+when a user logs in interactively. | |
+(On some systems it is also printed by the shell, | |
+.Pa /etc/profile , | |
+or equivalent.) | |
+The default is | |
+.Dq yes . | |
+.It Cm Protocol | |
+Specifies the protocol versions | |
+.Xr sshd 8 | |
+supports. | |
+The possible values are | |
+.Sq 1 | |
+and | |
+.Sq 2 . | |
+Multiple versions must be comma-separated. | |
+The default is | |
+.Sq 2 . | |
+Note that the order of the protocol list does not indicate preference, | |
+because the client selects among multiple protocol versions offered | |
+by the server. | |
+Specifying | |
+.Dq 2,1 | |
+is identical to | |
+.Dq 1,2 . | |
+.It Cm PubkeyAuthentication | |
+Specifies whether public key authentication is allowed. | |
+The default is | |
+.Dq yes . | |
+Note that this option applies to protocol version 2 only. | |
+.It Cm RekeyLimit | |
+Specifies the maximum amount of data that may be transmitted before the | |
+session key is renegotiated, optionally followed a maximum amount of | |
+time that may pass before the session key is renegotiated. | |
+The first argument is specified in bytes and may have a suffix of | |
+.Sq K , | |
+.Sq M , | |
+or | |
+.Sq G | |
+to indicate Kilobytes, Megabytes, or Gigabytes, respectively. | |
+The default is between | |
+.Sq 1G | |
+and | |
+.Sq 4G , | |
+depending on the cipher. | |
+The optional second value is specified in seconds and may use any of the | |
+units documented in the | |
+.Sx TIME FORMATS | |
+section. | |
+The default value for | |
+.Cm RekeyLimit | |
+is | |
+.Dq default none , | |
+which means that rekeying is performed after the cipher's default amount | |
+of data has been sent or received and no time based rekeying is done. | |
+This option applies to protocol version 2 only. | |
+.It Cm RevokedKeys | |
+Specifies revoked public keys. | |
+Keys listed in this file will be refused for public key authentication. | |
+Note that if this file is not readable, then public key authentication will | |
+be refused for all users. | |
+Keys may be specified as a text file, listing one public key per line, or as | |
+an OpenSSH Key Revocation List (KRL) as generated by | |
+.Xr ssh-keygen 1 . | |
+For more information on KRLs, see the KEY REVOCATION LISTS section in | |
+.Xr ssh-keygen 1 . | |
+.It Cm RhostsRSAAuthentication | |
+Specifies whether rhosts or /etc/hosts.equiv authentication together | |
+with successful RSA host authentication is allowed. | |
+The default is | |
+.Dq no . | |
+This option applies to protocol version 1 only. | |
+.It Cm RSAAuthentication | |
+Specifies whether pure RSA authentication is allowed. | |
+The default is | |
+.Dq yes . | |
+This option applies to protocol version 1 only. | |
+.It Cm ServerKeyBits | |
+Defines the number of bits in the ephemeral protocol version 1 server key. | |
+The minimum value is 512, and the default is 1024. | |
+.It Cm StreamLocalBindMask | |
+Sets the octal file creation mode mask | |
+.Pq umask | |
+used when creating a Unix-domain socket file for local or remote | |
+port forwarding. | |
+This option is only used for port forwarding to a Unix-domain socket file. | |
+.Pp | |
+The default value is 0177, which creates a Unix-domain socket file that is | |
+readable and writable only by the owner. | |
+Note that not all operating systems honor the file mode on Unix-domain | |
+socket files. | |
+.It Cm StreamLocalBindUnlink | |
+Specifies whether to remove an existing Unix-domain socket file for local | |
+or remote port forwarding before creating a new one. | |
+If the socket file already exists and | |
+.Cm StreamLocalBindUnlink | |
+is not enabled, | |
+.Nm sshd | |
+will be unable to forward the port to the Unix-domain socket file. | |
+This option is only used for port forwarding to a Unix-domain socket file. | |
+.Pp | |
+The argument must be | |
+.Dq yes | |
+or | |
+.Dq no . | |
+The default is | |
+.Dq no . | |
+.It Cm StrictModes | |
+Specifies whether | |
+.Xr sshd 8 | |
+should check file modes and ownership of the | |
+user's files and home directory before accepting login. | |
+This is normally desirable because novices sometimes accidentally leave their | |
+directory or files world-writable. | |
+The default is | |
+.Dq yes . | |
+Note that this does not apply to | |
+.Cm ChrootDirectory , | |
+whose permissions and ownership are checked unconditionally. | |
+.It Cm Subsystem | |
+Configures an external subsystem (e.g. file transfer daemon). | |
+Arguments should be a subsystem name and a command (with optional arguments) | |
+to execute upon subsystem request. | |
+.Pp | |
+The command | |
+.Xr sftp-server 8 | |
+implements the | |
+.Dq sftp | |
+file transfer subsystem. | |
+.Pp | |
+Alternately the name | |
+.Dq internal-sftp | |
+implements an in-process | |
+.Dq sftp | |
+server. | |
+This may simplify configurations using | |
+.Cm ChrootDirectory | |
+to force a different filesystem root on clients. | |
+.Pp | |
+By default no subsystems are defined. | |
+Note that this option applies to protocol version 2 only. | |
+.It Cm SyslogFacility | |
+Gives the facility code that is used when logging messages from | |
+.Xr sshd 8 . | |
+The possible values are: DAEMON, USER, AUTH, LOCAL0, LOCAL1, LOCAL2, | |
+LOCAL3, LOCAL4, LOCAL5, LOCAL6, LOCAL7. | |
+The default is AUTH. | |
+.It Cm TCPKeepAlive | |
+Specifies whether the system should send TCP keepalive messages to the | |
+other side. | |
+If they are sent, death of the connection or crash of one | |
+of the machines will be properly noticed. | |
+However, this means that | |
+connections will die if the route is down temporarily, and some people | |
+find it annoying. | |
+On the other hand, if TCP keepalives are not sent, | |
+sessions may hang indefinitely on the server, leaving | |
+.Dq ghost | |
+users and consuming server resources. | |
+.Pp | |
+The default is | |
+.Dq yes | |
+(to send TCP keepalive messages), and the server will notice | |
+if the network goes down or the client host crashes. | |
+This avoids infinitely hanging sessions. | |
+.Pp | |
+To disable TCP keepalive messages, the value should be set to | |
+.Dq no . | |
+.It Cm TrustedUserCAKeys | |
+Specifies a file containing public keys of certificate authorities that are | |
+trusted to sign user certificates for authentication. | |
+Keys are listed one per line; empty lines and comments starting with | |
+.Ql # | |
+are allowed. | |
+If a certificate is presented for authentication and has its signing CA key | |
+listed in this file, then it may be used for authentication for any user | |
+listed in the certificate's principals list. | |
+Note that certificates that lack a list of principals will not be permitted | |
+for authentication using | |
+.Cm TrustedUserCAKeys . | |
+For more details on certificates, see the CERTIFICATES section in | |
+.Xr ssh-keygen 1 . | |
+.It Cm UseDNS | |
+Specifies whether | |
+.Xr sshd 8 | |
+should look up the remote host name and check that | |
+the resolved host name for the remote IP address maps back to the | |
+very same IP address. | |
+The default is | |
+.Dq yes . | |
+.It Cm UseLogin | |
+Specifies whether | |
+.Xr login 1 | |
+is used for interactive login sessions. | |
+The default is | |
+.Dq no . | |
+Note that | |
+.Xr login 1 | |
+is never used for remote command execution. | |
+Note also, that if this is enabled, | |
+.Cm X11Forwarding | |
+will be disabled because | |
+.Xr login 1 | |
+does not know how to handle | |
+.Xr xauth 1 | |
+cookies. | |
+If | |
+.Cm UsePrivilegeSeparation | |
+is specified, it will be disabled after authentication. | |
+.It Cm UsePAM | |
+Enables the Pluggable Authentication Module interface. | |
+If set to | |
+.Dq yes | |
+this will enable PAM authentication using | |
+.Cm ChallengeResponseAuthentication | |
+and | |
+.Cm PasswordAuthentication | |
+in addition to PAM account and session module processing for all | |
+authentication types. | |
+.Pp | |
+Because PAM challenge-response authentication usually serves an equivalent | |
+role to password authentication, you should disable either | |
+.Cm PasswordAuthentication | |
+or | |
+.Cm ChallengeResponseAuthentication. | |
+.Pp | |
+If | |
+.Cm UsePAM | |
+is enabled, you will not be able to run | |
+.Xr sshd 8 | |
+as a non-root user. | |
+The default is | |
+.Dq no . | |
+.It Cm UsePrivilegeSeparation | |
+Specifies whether | |
+.Xr sshd 8 | |
+separates privileges by creating an unprivileged child process | |
+to deal with incoming network traffic. | |
+After successful authentication, another process will be created that has | |
+the privilege of the authenticated user. | |
+The goal of privilege separation is to prevent privilege | |
+escalation by containing any corruption within the unprivileged processes. | |
+The default is | |
+.Dq yes . | |
+If | |
+.Cm UsePrivilegeSeparation | |
+is set to | |
+.Dq sandbox | |
+then the pre-authentication unprivileged process is subject to additional | |
+restrictions. | |
+.It Cm VersionAddendum | |
+Optionally specifies additional text to append to the SSH protocol banner | |
+sent by the server upon connection. | |
+The default is | |
+.Dq none . | |
+.It Cm X11DisplayOffset | |
+Specifies the first display number available for | |
+.Xr sshd 8 Ns 's | |
+X11 forwarding. | |
+This prevents sshd from interfering with real X11 servers. | |
+The default is 10. | |
+.It Cm X11Forwarding | |
+Specifies whether X11 forwarding is permitted. | |
+The argument must be | |
+.Dq yes | |
+or | |
+.Dq no . | |
+The default is | |
+.Dq no . | |
+.Pp | |
+When X11 forwarding is enabled, there may be additional exposure to | |
+the server and to client displays if the | |
+.Xr sshd 8 | |
+proxy display is configured to listen on the wildcard address (see | |
+.Cm X11UseLocalhost | |
+below), though this is not the default. | |
+Additionally, the authentication spoofing and authentication data | |
+verification and substitution occur on the client side. | |
+The security risk of using X11 forwarding is that the client's X11 | |
+display server may be exposed to attack when the SSH client requests | |
+forwarding (see the warnings for | |
+.Cm ForwardX11 | |
+in | |
+.Xr ssh_config 5 ) . | |
+A system administrator may have a stance in which they want to | |
+protect clients that may expose themselves to attack by unwittingly | |
+requesting X11 forwarding, which can warrant a | |
+.Dq no | |
+setting. | |
+.Pp | |
+Note that disabling X11 forwarding does not prevent users from | |
+forwarding X11 traffic, as users can always install their own forwarders. | |
+X11 forwarding is automatically disabled if | |
+.Cm UseLogin | |
+is enabled. | |
+.It Cm X11UseLocalhost | |
+Specifies whether | |
+.Xr sshd 8 | |
+should bind the X11 forwarding server to the loopback address or to | |
+the wildcard address. | |
+By default, | |
+sshd binds the forwarding server to the loopback address and sets the | |
+hostname part of the | |
+.Ev DISPLAY | |
+environment variable to | |
+.Dq localhost . | |
+This prevents remote hosts from connecting to the proxy display. | |
+However, some older X11 clients may not function with this | |
+configuration. | |
+.Cm X11UseLocalhost | |
+may be set to | |
+.Dq no | |
+to specify that the forwarding server should be bound to the wildcard | |
+address. | |
+The argument must be | |
+.Dq yes | |
+or | |
+.Dq no . | |
+The default is | |
+.Dq yes . | |
+.It Cm XAuthLocation | |
+Specifies the full pathname of the | |
+.Xr xauth 1 | |
+program. | |
+The default is | |
+.Pa /usr/X11R6/bin/xauth . | |
+.El | |
+.Sh TIME FORMATS | |
+.Xr sshd 8 | |
+command-line arguments and configuration file options that specify time | |
+may be expressed using a sequence of the form: | |
+.Sm off | |
+.Ar time Op Ar qualifier , | |
+.Sm on | |
+where | |
+.Ar time | |
+is a positive integer value and | |
+.Ar qualifier | |
+is one of the following: | |
+.Pp | |
+.Bl -tag -width Ds -compact -offset indent | |
+.It Aq Cm none | |
+seconds | |
+.It Cm s | Cm S | |
+seconds | |
+.It Cm m | Cm M | |
+minutes | |
+.It Cm h | Cm H | |
+hours | |
+.It Cm d | Cm D | |
+days | |
+.It Cm w | Cm W | |
+weeks | |
+.El | |
+.Pp | |
+Each member of the sequence is added together to calculate | |
+the total time value. | |
+.Pp | |
+Time format examples: | |
+.Pp | |
+.Bl -tag -width Ds -compact -offset indent | |
+.It 600 | |
+600 seconds (10 minutes) | |
+.It 10m | |
+10 minutes | |
+.It 1h30m | |
+1 hour 30 minutes (90 minutes) | |
+.El | |
+.Sh FILES | |
+.Bl -tag -width Ds | |
+.It Pa /etc/ssh/sshd_config | |
+Contains configuration data for | |
+.Xr sshd 8 . | |
+This file should be writable by root only, but it is recommended | |
+(though not necessary) that it be world-readable. | |
+.El | |
+.Sh SEE ALSO | |
+.Xr sshd 8 | |
+.Sh AUTHORS | |
+OpenSSH is a derivative of the original and free | |
+ssh 1.2.12 release by Tatu Ylonen. | |
+Aaron Campbell, Bob Beck, Markus Friedl, Niels Provos, | |
+Theo de Raadt and Dug Song | |
+removed many bugs, re-added newer features and | |
+created OpenSSH. | |
+Markus Friedl contributed the support for SSH | |
+protocol versions 1.5 and 2.0. | |
+Niels Provos and Markus Friedl contributed support | |
+for privilege separation. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment